fix(cli): harden scoped board key summaries

Recognize validated preset scopes independent of permission ordering and document the scoped create and expiry contract.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-08-19 20:00:46 +00:00
1 parent d262fb5f0d
commit 3834fa70f3
3 files changed
+54 -4

No files matched your search

+33
View File
@@ -1,5 +1,6 @@
import { Command } from "commander";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { BOARD_API_KEY_SCOPE_PRESETS } from "@paperclipai/shared";
import { registerTokenCommands } from "../commands/client/token.js";
const COMPANY_ID = "22222222-2222-4222-8222-222222222222";
@@ -326,4 +327,36 @@ describe("token commands", () => {
expect(fetchMock.mock.calls[1]?.[0]).toBe("http://localhost:3100/api/board-api-keys/board-key-1");
expect(fetchMock.mock.calls[1]?.[1]?.method).toBe("DELETE");
});
it("recognizes a preset scope when its valid permissions use a different order", async () => {
const permissions = [...BOARD_API_KEY_SCOPE_PRESETS.read_only.permissions].reverse();
const fetchMock = vi.fn().mockResolvedValueOnce(new Response(JSON.stringify([{
id: "board-key-1",
name: "reader",
tokenPrefix: "pcp_board_123456789abc",
scopeConfig: {
version: 1,
kind: "scoped",
companyIds: [COMPANY_ID],
permissions,
instanceCapabilities: [],
},
legacyUnrestricted: false,
status: "active",
createdAt: "2026-05-23T00:00:00.000Z",
lastUsedAt: null,
expiresAt: "2026-06-22T00:00:00.000Z",
revokedAt: null,
}]), { status: 200 }));
vi.stubGlobal("fetch", fetchMock);
const log = vi.spyOn(console, "log").mockImplementation(() => {});
await createProgram().parseAsync([
"token", "board", "list",
"--api-base", "http://localhost:3100",
"--api-key", "board-token",
], { from: "user" });
expect(String(log.mock.calls[0]?.[0])).toContain("scope=read_only:1_companies");
});
});
+2 -2
View File
@@ -340,10 +340,10 @@ function summarizeBoardKeyScope(scopeConfig: BoardApiKeyScopeConfig | null): str
[BoardApiKeyScopePreset, (typeof BOARD_API_KEY_SCOPE_PRESETS)[BoardApiKeyScopePreset]]
>).find(([, preset]) =>
preset.permissions.length === scopeConfig.permissions.length
&& preset.permissions.every((permission, index) => permission === scopeConfig.permissions[index])
&& preset.permissions.every((permission) => scopeConfig.permissions.includes(permission))
&& preset.instanceCapabilities.length === scopeConfig.instanceCapabilities.length
&& preset.instanceCapabilities.every(
(capability, index) => capability === scopeConfig.instanceCapabilities[index],
(capability) => scopeConfig.instanceCapabilities.includes(capability),
));
const label = matchingPreset?.[0] ?? "custom";
return `${label}:${scopeConfig.companyIds.length}_companies`;