feat: add ClickClack setup inputs

This commit is contained in:
Shakker
2026-07-15 13:05:10 +01:00
committed by Shakker
parent ed05dae2d8
commit b63ce98809
6 changed files with 75 additions and 2 deletions
+8 -1
View File
@@ -28,6 +28,7 @@
"extensions": [
"./index.ts"
],
"setupEntry": "./setup-entry.ts",
"channel": {
"id": "clickclack",
"label": "ClickClack",
@@ -43,7 +44,13 @@
"commands": {
"nativeCommandsAutoEnabled": false,
"nativeSkillsAutoEnabled": false
}
},
"cliAddOptions": [
{
"flags": "--workspace <workspace>",
"description": "ClickClack workspace id, slug, or name"
}
]
},
"install": {
"clawhubSpec": "clawhub:@openclaw/clickclack",
@@ -1,4 +1,7 @@
// Clickclack tests cover accounts plugin behavior.
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import {
listClickClackAccountIds,
@@ -120,6 +123,48 @@ describe("ClickClack account resolution", () => {
});
});
it("uses the default ClickClack env token only for the default account", () => {
const cfg = {
channels: {
clickclack: {
enabled: true,
baseUrl: "https://app.clickclack.chat",
workspace: "wsp_1",
accounts: {
work: {},
},
},
},
} satisfies CoreConfig;
const env = { CLICKCLACK_BOT_TOKEN: " default-env-token " };
expect(resolveClickClackAccount({ cfg, env }).token).toBe("default-env-token");
expect(resolveClickClackAccount({ cfg, accountId: "work", env }).token).toBe("");
});
it("reads tokenFile credentials and treats them as an implicit default account", () => {
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "clickclack-token-"));
const tokenFile = path.join(tempDir, "token");
fs.writeFileSync(tokenFile, " file-token \n", "utf8");
try {
const cfg = {
channels: {
clickclack: {
enabled: true,
baseUrl: "https://app.clickclack.chat",
workspace: "wsp_1",
tokenFile,
},
},
} satisfies CoreConfig;
expect(listClickClackAccountIds(cfg)).toEqual(["default"]);
expect(resolveClickClackAccount({ cfg }).token).toBe("file-token");
} finally {
fs.rmSync(tempDir, { recursive: true, force: true });
}
});
it("resolves model-mode bot account policy", () => {
const cfg = {
channels: {
+19 -1
View File
@@ -10,6 +10,7 @@ import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/acco
import { resolveMergedAccountConfig } from "openclaw/plugin-sdk/account-resolution";
import { resolveIntegerOption } from "openclaw/plugin-sdk/number-runtime";
import { resolveDefaultSecretProviderAlias } from "openclaw/plugin-sdk/provider-auth";
import { tryReadSecretFileSync } from "openclaw/plugin-sdk/secret-file-runtime";
import {
normalizeSecretInputString,
normalizeResolvedSecretInputString,
@@ -31,7 +32,7 @@ const {
const channel = cfg.channels?.clickclack;
return Boolean(
channel?.baseUrl?.trim() &&
hasConfiguredAccountValue(channel.token) &&
(hasConfiguredAccountValue(channel.token) || Boolean(channel.tokenFile?.trim())) &&
channel.workspace?.trim(),
);
},
@@ -55,9 +56,22 @@ function resolveMergedClickClackAccountConfig(
function resolveClickClackToken(params: {
cfg: CoreConfig;
value: unknown;
tokenFile?: string;
accountId: string;
env?: NodeJS.ProcessEnv;
}): string {
const tokenFile = params.tokenFile?.trim();
if (tokenFile) {
return (
tryReadSecretFileSync(
tokenFile,
params.accountId === DEFAULT_ACCOUNT_ID
? "channels.clickclack.tokenFile"
: `channels.clickclack.accounts.${params.accountId}.tokenFile`,
{ rejectSymlink: true },
) ?? ""
);
}
const resolved = resolveSecretInputString({
value: params.value,
path:
@@ -68,6 +82,9 @@ function resolveClickClackToken(params: {
mode: "inspect",
});
if (resolved.status !== "available") {
if (resolved.status === "missing" && params.accountId === DEFAULT_ACCOUNT_ID) {
return normalizeSecretInputString((params.env ?? process.env).CLICKCLACK_BOT_TOKEN) ?? "";
}
if (resolved.status === "configured_unavailable" && resolved.ref.source === "env") {
const providerConfig = params.cfg.secrets?.providers?.[resolved.ref.provider];
if (providerConfig) {
@@ -118,6 +135,7 @@ export function resolveClickClackAccount(params: {
const token = resolveClickClackToken({
cfg: params.cfg,
value: merged.token,
tokenFile: merged.tokenFile,
accountId,
env: params.env,
});
@@ -11,6 +11,7 @@ const ClickClackAccountConfigSchema = z
enabled: z.boolean().optional(),
baseUrl: z.string().url().optional(),
token: buildSecretInputSchema().optional(),
tokenFile: z.string().optional(),
workspace: z.string().optional(),
botUserId: z.string().optional(),
agentId: z.string().optional(),
+1
View File
@@ -9,6 +9,7 @@ export type ClickClackAccountConfig = {
enabled?: boolean;
baseUrl?: string;
token?: unknown;
tokenFile?: string;
workspace?: string;
botUserId?: string;
agentId?: string;
+1
View File
@@ -139,6 +139,7 @@ export type ChannelSetupInput = {
groupChannels?: string[];
dmAllowlist?: string[];
autoDiscoverChannels?: boolean;
workspace?: string;
};
export type ChannelStatusIssue = {