fix(clickclack): clarify command menu permission errors (#126802)

This commit is contained in:
Peter Steinberger
2026-08-20 12:29:38 -07:00
committed by GitHub
parent 5e7f98ec1d
commit af858dff8e
4 changed files with 26 additions and 9 deletions
@@ -39,6 +39,7 @@ async function syncNativeCommands(
mocks.listNativeCommandSpecsForConfig.mockReturnValue(specs);
await syncClickClackCommandMenu({
accountId: "default",
cfg: {} as CoreConfig,
client: { setBotCommands } as unknown as ReturnType<typeof createClickClackClient>,
log,
@@ -130,6 +131,7 @@ describe("ClickClack command menu", () => {
]);
await syncClickClackCommandMenu({
accountId: "default",
cfg,
client: { setBotCommands } as unknown as ReturnType<typeof createClickClackClient>,
});
+7 -3
View File
@@ -86,6 +86,7 @@ function mapNativeCommandSpecsToClickClackMenu(
}
export async function syncClickClackCommandMenu(params: {
accountId: string;
cfg: CoreConfig;
client: ReturnType<typeof createClickClackClient>;
log?: ClickClackCommandMenuLogger;
@@ -98,16 +99,19 @@ export async function syncClickClackCommandMenu(params: {
await params.client.setBotCommands(commands);
} catch (error) {
const status = errorStatus(error);
const messagePrefix = `[${params.accountId}] ClickClack command menu sync`;
if (status === 403) {
params.log?.warn?.("ClickClack command menu sync skipped: bot token lacks commands:write");
params.log?.warn?.(
`${messagePrefix} skipped: ${formatErrorMessage(error)}; verify token/workspace command permissions or set commandMenu: false if menus are not needed`,
);
return;
}
if (status === 404) {
params.log?.debug?.(
"ClickClack command menu sync skipped: server does not support /api/bots/self/commands",
`${messagePrefix} skipped: server does not support /api/bots/self/commands`,
);
return;
}
params.log?.warn?.(`ClickClack command menu sync failed: ${formatErrorMessage(error)}`);
params.log?.warn?.(`${messagePrefix} failed: ${formatErrorMessage(error)}`);
}
}
+11 -5
View File
@@ -61,6 +61,7 @@ vi.mock("./resolve.js", () => ({
}));
import { startClickClackGatewayAccount } from "./gateway.js";
import { ClickClackHttpError } from "./http-client.js";
function createGatewayContext(
abortSignal: AbortSignal,
@@ -247,23 +248,28 @@ describe("ClickClack gateway", () => {
it.each([
{
label: "missing command scope",
error: { status: 403 },
label: "workspace command permission rejection",
error: new ClickClackHttpError(
403,
"workspace role no longer permits command updates",
new Headers(),
),
level: "warn" as const,
message: "ClickClack command menu sync skipped: bot token lacks commands:write",
message:
"[default] ClickClack command menu sync skipped: ClickClack 403: workspace role no longer permits command updates; verify token/workspace command permissions or set commandMenu: false if menus are not needed",
},
{
label: "older server",
error: { status: 404 },
level: "debug" as const,
message:
"ClickClack command menu sync skipped: server does not support /api/bots/self/commands",
"[default] ClickClack command menu sync skipped: server does not support /api/bots/self/commands",
},
{
label: "network failure",
error: new Error("network unavailable"),
level: "warn" as const,
message: "ClickClack command menu sync failed: network unavailable",
message: "[default] ClickClack command menu sync failed: network unavailable",
},
])("continues startup after $label", async ({ error, level, message }) => {
mocks.client.setBotCommands.mockRejectedValueOnce(error);
+6 -1
View File
@@ -191,7 +191,12 @@ export async function startClickClackGatewayAccount(
log: ctx.log,
});
if (account.commandMenu) {
await syncClickClackCommandMenu({ cfg: ctx.cfg, client, log: ctx.log });
await syncClickClackCommandMenu({
cfg: ctx.cfg,
client,
log: ctx.log,
accountId: account.accountId,
});
}
ctx.setStatus({
accountId: account.accountId,