@rethinkingstudio/clawpilot 1.0.17 → 1.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ export interface ProviderEntry {
2
+ id: string;
3
+ type: string;
4
+ baseUrl: string;
5
+ keyMasked: string | null;
6
+ hasKey: boolean;
7
+ isDefault: boolean;
8
+ }
9
+ export declare function listProviderEntries(): Promise<ProviderEntry[]>;
10
+ export declare function addProvider(params: {
11
+ id: string;
12
+ type: string;
13
+ apiKey: string | null;
14
+ baseUrl: string;
15
+ api: string;
16
+ apiKeyEnvName: string;
17
+ modelId?: string;
18
+ }): Promise<void>;
19
+ export declare function deleteProvider(id: string): Promise<void>;
20
+ export declare function setDefaultProvider(id: string): Promise<void>;
@@ -0,0 +1,162 @@
1
+ import { readFile, writeFile, mkdir, readdir } from "fs/promises";
2
+ import { join } from "path";
3
+ import { homedir } from "os";
4
+ const OPENCLAW_DIR = join(homedir(), ".openclaw");
5
+ const OPENCLAW_CONFIG = join(OPENCLAW_DIR, "openclaw.json");
6
+ const AGENTS_DIR = join(OPENCLAW_DIR, "agents");
7
+ // ---------------------------------------------------------------------------
8
+ // Internal helpers
9
+ // ---------------------------------------------------------------------------
10
+ function maskKey(key) {
11
+ if (key.length <= 8)
12
+ return key.slice(0, 2) + "***" + key.slice(-2);
13
+ return key.slice(0, 4) + "***" + key.slice(-4);
14
+ }
15
+ async function resolveAgentId() {
16
+ try {
17
+ const entries = await readdir(AGENTS_DIR, { withFileTypes: true });
18
+ const dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
19
+ if (dirs.length > 0) {
20
+ return dirs[0];
21
+ }
22
+ }
23
+ catch {
24
+ // agents dir missing or unreadable
25
+ }
26
+ return "main";
27
+ }
28
+ async function readJson(filePath) {
29
+ try {
30
+ const raw = await readFile(filePath, "utf-8");
31
+ return JSON.parse(raw);
32
+ }
33
+ catch {
34
+ return {};
35
+ }
36
+ }
37
+ async function writeJson(filePath, data) {
38
+ await writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
39
+ }
40
+ function getNestedObj(root, keys) {
41
+ let cur = root;
42
+ for (const key of keys) {
43
+ if (cur[key] == null || typeof cur[key] !== "object" || Array.isArray(cur[key])) {
44
+ cur[key] = {};
45
+ }
46
+ cur = cur[key];
47
+ }
48
+ return cur;
49
+ }
50
+ function authProfilesPath(agentId) {
51
+ return join(AGENTS_DIR, agentId, "agent", "auth-profiles.json");
52
+ }
53
+ // ---------------------------------------------------------------------------
54
+ // Public API
55
+ // ---------------------------------------------------------------------------
56
+ export async function listProviderEntries() {
57
+ const config = await readJson(OPENCLAW_CONFIG);
58
+ // Dig out models.providers
59
+ const models = config["models"] ?? {};
60
+ const providers = models["providers"] ?? {};
61
+ // Determine current default model primary string
62
+ const agents = config["agents"] ?? {};
63
+ const defaults = agents["defaults"] ?? {};
64
+ const model = defaults["model"] ?? {};
65
+ const primaryModel = typeof model["primary"] === "string" ? model["primary"] : "";
66
+ // Read auth profiles for key lookup
67
+ const agentId = await resolveAgentId();
68
+ const authProfiles = await readJson(authProfilesPath(agentId));
69
+ const profiles = authProfiles["profiles"] ?? {};
70
+ const entries = [];
71
+ for (const [id, providerRaw] of Object.entries(providers)) {
72
+ const provider = providerRaw ?? {};
73
+ const type = typeof provider["type"] === "string" ? provider["type"] : id;
74
+ const baseUrl = typeof provider["baseUrl"] === "string" ? provider["baseUrl"] : "";
75
+ // Key lookup: profile key is "<type>:default"
76
+ const profileKey = `${type}:default`;
77
+ const profileRaw = profiles[profileKey];
78
+ const profile = profileRaw != null && typeof profileRaw === "object" && !Array.isArray(profileRaw)
79
+ ? profileRaw
80
+ : null;
81
+ const apiKey = profile != null && typeof profile["apiKey"] === "string"
82
+ ? profile["apiKey"]
83
+ : null;
84
+ const hasKey = apiKey != null && apiKey.length > 0;
85
+ const keyMasked = hasKey ? maskKey(apiKey) : null;
86
+ // isDefault: primary model string starts with "<id>/"
87
+ const isDefault = primaryModel === id || primaryModel.startsWith(`${id}/`);
88
+ entries.push({ id, type, baseUrl, keyMasked, hasKey, isDefault });
89
+ }
90
+ return entries;
91
+ }
92
+ export async function addProvider(params) {
93
+ const { id, type, apiKey, baseUrl, api, apiKeyEnvName, modelId } = params;
94
+ // --- Update openclaw.json ---
95
+ const config = await readJson(OPENCLAW_CONFIG);
96
+ const models = getNestedObj(config, ["models"]);
97
+ const providers = getNestedObj(models, ["providers"]);
98
+ const providerEntry = { type, baseUrl, api, apiKeyEnvName };
99
+ if (modelId !== undefined) {
100
+ providerEntry["modelId"] = modelId;
101
+ }
102
+ providers[id] = providerEntry;
103
+ await writeJson(OPENCLAW_CONFIG, config);
104
+ // Note: writes are not atomic. openclaw.json is updated first, then auth-profiles.json.
105
+ // If auth-profiles write fails, the provider will appear in the list without a key.
106
+ // This is acceptable since config files are user-owned and easily recoverable.
107
+ // --- Update auth-profiles.json (only if apiKey provided) ---
108
+ if (apiKey != null && apiKey.length > 0) {
109
+ const agentId = await resolveAgentId();
110
+ const profilesPath = authProfilesPath(agentId);
111
+ const profilesDir = join(AGENTS_DIR, agentId, "agent");
112
+ await mkdir(profilesDir, { recursive: true });
113
+ const authProfiles = await readJson(profilesPath);
114
+ const profiles = getNestedObj(authProfiles, ["profiles"]);
115
+ const profileKey = `${type}:default`;
116
+ profiles[profileKey] = { apiKey };
117
+ await writeJson(profilesPath, authProfiles);
118
+ }
119
+ }
120
+ export async function deleteProvider(id) {
121
+ // --- Update openclaw.json ---
122
+ const config = await readJson(OPENCLAW_CONFIG);
123
+ const models = getNestedObj(config, ["models"]);
124
+ const providers = getNestedObj(models, ["providers"]);
125
+ const providerRaw = providers[id];
126
+ const type = providerRaw != null &&
127
+ typeof providerRaw === "object" &&
128
+ !Array.isArray(providerRaw) &&
129
+ typeof providerRaw["type"] === "string"
130
+ ? providerRaw["type"]
131
+ : id;
132
+ delete providers[id];
133
+ // Note: writes are not atomic. openclaw.json is updated first, then auth-profiles.json.
134
+ // If auth-profiles write fails, the provider will appear in the list without a key.
135
+ // This is acceptable since config files are user-owned and easily recoverable.
136
+ await writeJson(OPENCLAW_CONFIG, config);
137
+ // --- Update auth-profiles.json ---
138
+ const agentId = await resolveAgentId();
139
+ const profilesPath = authProfilesPath(agentId);
140
+ const authProfiles = await readJson(profilesPath);
141
+ const profiles = getNestedObj(authProfiles, ["profiles"]);
142
+ const profileKey = `${type}:default`;
143
+ if (profileKey in profiles) {
144
+ delete profiles[profileKey];
145
+ await writeJson(profilesPath, authProfiles);
146
+ }
147
+ }
148
+ export async function setDefaultProvider(id) {
149
+ const config = await readJson(OPENCLAW_CONFIG);
150
+ // Preserve existing model suffix if present
151
+ const agents = getNestedObj(config, ["agents"]);
152
+ const defaults = getNestedObj(agents, ["defaults"]);
153
+ const model = getNestedObj(defaults, ["model"]);
154
+ const currentPrimary = typeof model["primary"] === "string" ? model["primary"] : "";
155
+ let modelSuffix = "default";
156
+ if (currentPrimary.includes("/")) {
157
+ modelSuffix = currentPrimary.split("/").slice(1).join("/");
158
+ }
159
+ model["primary"] = `${id}/${modelSuffix}`;
160
+ await writeJson(OPENCLAW_CONFIG, config);
161
+ }
162
+ //# sourceMappingURL=provider-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-config.js","sourceRoot":"","sources":["../../src/commands/provider-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { LocalResult } from "./local-handlers.js";
2
+ export declare function handleProviderCommand(method: string, params: unknown): Promise<LocalResult> | null;
@@ -0,0 +1,197 @@
1
+ import { execSync } from "child_process";
2
+ import { existsSync } from "fs";
3
+ import { dirname } from "path";
4
+ import { homedir } from "os";
5
+ import { randomUUID } from "crypto";
6
+ import { PROVIDER_REGISTRY } from "./provider-registry.js";
7
+ import { listProviderEntries, addProvider, deleteProvider, setDefaultProvider, } from "./provider-config.js";
8
+ // ---------------------------------------------------------------------------
9
+ // Subprocess env (mirrors local-handlers.ts)
10
+ // ---------------------------------------------------------------------------
11
+ const NODE_BIN_DIR = dirname(process.execPath);
12
+ const SUBPROCESS_ENV = {
13
+ ...process.env,
14
+ HOME: homedir(),
15
+ PATH: [
16
+ NODE_BIN_DIR,
17
+ "/opt/homebrew/bin",
18
+ "/opt/homebrew/sbin",
19
+ "/usr/local/bin",
20
+ "/usr/local/sbin",
21
+ process.env.PATH ?? "/usr/bin:/bin",
22
+ ].join(":"),
23
+ };
24
+ function resolveOpenclawBin() {
25
+ try {
26
+ const p = execSync("which openclaw", { stdio: "pipe", env: SUBPROCESS_ENV, timeout: 3000 })
27
+ .toString().trim();
28
+ if (p && existsSync(p))
29
+ return p;
30
+ }
31
+ catch { /* fall through */ }
32
+ return "openclaw";
33
+ }
34
+ const OPENCLAW_BIN = resolveOpenclawBin();
35
+ function restartGateway() {
36
+ try {
37
+ execSync(`"${OPENCLAW_BIN}" gateway restart`, { stdio: "pipe", env: SUBPROCESS_ENV });
38
+ console.log("[provider] gateway restarted");
39
+ }
40
+ catch (err) {
41
+ console.warn("[provider] gateway restart failed:", String(err));
42
+ }
43
+ }
44
+ // ---------------------------------------------------------------------------
45
+ // HTTP key validation
46
+ // ---------------------------------------------------------------------------
47
+ async function validateApiKey(type, apiKey, baseUrl) {
48
+ const info = PROVIDER_REGISTRY[type];
49
+ if (!info)
50
+ return { ok: false, error: `Unknown provider type: ${type}` };
51
+ if (!info.requiresApiKey || !apiKey)
52
+ return { ok: true };
53
+ return new Promise((resolve) => {
54
+ try {
55
+ const url = new URL(info.validationPath, baseUrl);
56
+ if (info.validationAuth === "google-query-param") {
57
+ url.searchParams.set("key", apiKey);
58
+ }
59
+ const headers = {};
60
+ if (info.validationAuth === "bearer") {
61
+ headers["Authorization"] = `Bearer ${apiKey}`;
62
+ }
63
+ else if (info.validationAuth === "x-api-key") {
64
+ headers["x-api-key"] = apiKey;
65
+ headers["anthropic-version"] = "2023-06-01";
66
+ }
67
+ // Use http or https based on protocol
68
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
69
+ const mod = url.protocol === "https:" ? require("https") : require("http");
70
+ const req = mod.get({
71
+ hostname: url.hostname,
72
+ port: url.port || undefined,
73
+ path: url.pathname + url.search,
74
+ headers,
75
+ }, (res) => {
76
+ res.resume(); // drain body
77
+ const status = res.statusCode ?? 0;
78
+ if (status >= 200 && status < 300) {
79
+ resolve({ ok: true });
80
+ }
81
+ else if (status === 401 || status === 403) {
82
+ resolve({ ok: false, error: "API Key 无效" });
83
+ }
84
+ else {
85
+ resolve({ ok: false, error: `验证失败 (HTTP ${status})` });
86
+ }
87
+ });
88
+ req.setTimeout(10_000, () => {
89
+ req.destroy();
90
+ resolve({ ok: false, error: "验证超时" });
91
+ });
92
+ req.on("error", (e) => resolve({ ok: false, error: e.message }));
93
+ }
94
+ catch (e) {
95
+ resolve({ ok: false, error: String(e) });
96
+ }
97
+ });
98
+ }
99
+ // ---------------------------------------------------------------------------
100
+ // Command handlers
101
+ // ---------------------------------------------------------------------------
102
+ async function cmdList() {
103
+ try {
104
+ const providers = await listProviderEntries();
105
+ return { ok: true, payload: { providers } };
106
+ }
107
+ catch (err) {
108
+ return { ok: false, error: String(err) };
109
+ }
110
+ }
111
+ async function cmdValidateKey(params) {
112
+ try {
113
+ const type = params.type;
114
+ const apiKey = params.apiKey;
115
+ const baseUrl = params.baseUrl ?? PROVIDER_REGISTRY[type]?.defaultBaseUrl ?? "";
116
+ const result = await validateApiKey(type, apiKey, baseUrl);
117
+ if (result.ok)
118
+ return { ok: true };
119
+ return { ok: false, error: result.error ?? "验证失败" };
120
+ }
121
+ catch (err) {
122
+ return { ok: false, error: String(err) };
123
+ }
124
+ }
125
+ async function cmdAdd(params) {
126
+ try {
127
+ const type = params.type;
128
+ const apiKey = params.apiKey ?? null;
129
+ const info = PROVIDER_REGISTRY[type];
130
+ if (!info)
131
+ return { ok: false, error: `Unknown provider type: ${type}` };
132
+ const baseUrl = params.baseUrl || info.defaultBaseUrl;
133
+ const modelId = params.modelId ?? info.defaultModelId;
134
+ // Validate key before writing config
135
+ if (info.requiresApiKey && apiKey) {
136
+ const v = await validateApiKey(type, apiKey, baseUrl);
137
+ if (!v.ok)
138
+ return { ok: false, error: v.error ?? "API Key 无效" };
139
+ }
140
+ // custom providers get a UUID suffix; id and apiKeyEnvName share the same UUID so they're traceable
141
+ const uuid = info.allowMultiple ? randomUUID() : null;
142
+ const id = uuid ? `custom-${uuid}` : type;
143
+ const apiKeyEnvName = uuid
144
+ ? `CUSTOM_${uuid.replace(/-/g, "_").toUpperCase()}_API_KEY`
145
+ : info.apiKeyEnvName;
146
+ await addProvider({ id, type, apiKey, baseUrl, api: info.api, apiKeyEnvName, modelId });
147
+ restartGateway();
148
+ return { ok: true, payload: { id } };
149
+ }
150
+ catch (err) {
151
+ return { ok: false, error: String(err) };
152
+ }
153
+ }
154
+ async function cmdDelete(params) {
155
+ try {
156
+ const id = params.id;
157
+ if (!id)
158
+ return { ok: false, error: "id required" };
159
+ await deleteProvider(id);
160
+ restartGateway();
161
+ return { ok: true };
162
+ }
163
+ catch (err) {
164
+ return { ok: false, error: String(err) };
165
+ }
166
+ }
167
+ async function cmdSetDefault(params) {
168
+ try {
169
+ const id = params.id;
170
+ if (!id)
171
+ return { ok: false, error: "id required" };
172
+ await setDefaultProvider(id);
173
+ restartGateway();
174
+ return { ok: true };
175
+ }
176
+ catch (err) {
177
+ return { ok: false, error: String(err) };
178
+ }
179
+ }
180
+ // ---------------------------------------------------------------------------
181
+ // Main dispatch
182
+ // ---------------------------------------------------------------------------
183
+ export function handleProviderCommand(method, params) {
184
+ if (!method.startsWith("clawpilot.provider."))
185
+ return null;
186
+ const p = (params ?? {});
187
+ switch (method) {
188
+ case "clawpilot.provider.list": return cmdList();
189
+ case "clawpilot.provider.validateKey": return cmdValidateKey(p);
190
+ case "clawpilot.provider.add": return cmdAdd(p);
191
+ case "clawpilot.provider.delete": return cmdDelete(p);
192
+ case "clawpilot.provider.setDefault": return cmdSetDefault(p);
193
+ default:
194
+ return Promise.resolve({ ok: false, error: `Unknown provider command: ${method}` });
195
+ }
196
+ }
197
+ //# sourceMappingURL=provider-handlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-handlers.js","sourceRoot":"","sources":["../../src/commands/provider-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAE9B,8EAA8E;AAC9E,6CAA6C;AAC7C,8EAA8E;AAE9E,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;AAE/C,MAAM,cAAc,GAAsB;IACxC,GAAG,OAAO,CAAC,GAAG;IACd,IAAI,EAAE,OAAO,EAAE;IACf,IAAI,EAAE;QACJ,YAAY;QACZ,mBAAmB;QACnB,oBAAoB;QACpB,gBAAgB;QAChB,iBAAiB;QACjB,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,eAAe;KACpC,CAAC,IAAI,CAAC,GAAG,CAAC;CACZ,CAAC;AAEF,SAAS,kBAAkB;IACzB,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,QAAQ,CAAC,gBAAgB,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;aACxF,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;IAC9B,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,YAAY,GAAG,kBAAkB,EAAE,CAAC;AAE1C,SAAS,cAAc;IACrB,IAAI,CAAC;QACH,QAAQ,CAAC,IAAI,YAAY,mBAAmB,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,cAAc,EAAE,CAAC,CAAC;QACtF,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,oCAAoC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAClE,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E,KAAK,UAAU,cAAc,CAC3B,IAAY,EACZ,MAAc,EACd,OAAe;IAEf,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,0BAA0B,IAAI,EAAE,EAAE,CAAC;IACzE,IAAI,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IAEzD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;YAClD,IAAI,IAAI,CAAC,cAAc,KAAK,oBAAoB,EAAE,CAAC;gBACjD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YACtC,CAAC;YAED,MAAM,OAAO,GAA2B,EAAE,CAAC;YAC3C,IAAI,IAAI,CAAC,cAAc,KAAK,QAAQ,EAAE,CAAC;gBACrC,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,MAAM,EAAE,CAAC;YAChD,CAAC;iBAAM,IAAI,IAAI,CAAC,cAAc,KAAK,WAAW,EAAE,CAAC;gBAC/C,OAAO,CAAC,WAAW,CAAC,GAAG,MAAM,CAAC;gBAC9B,OAAO,CAAC,mBAAmB,CAAC,GAAG,YAAY,CAAC;YAC9C,CAAC;YAED,sCAAsC;YACtC,iEAAiE;YACjE,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC3E,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CACjB;gBACE,QAAQ,EAAE,GAAG,CAAC,QAAQ;gBACtB,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,SAAS;gBAC3B,IAAI,EAAE,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM;gBAC/B,OAAO;aACR,EACD,CAAC,GAAgD,EAAE,EAAE;gBACnD,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,aAAa;gBAC3B,MAAM,MAAM,GAAG,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC;gBACnC,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG,EAAE,CAAC;oBAClC,OAAO,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;gBACxB,CAAC;qBAAM,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;oBAC5C,OAAO,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;gBAC9C,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,MAAM,GAAG,EAAE,CAAC,CAAC;gBACzD,CAAC;YACH,CAAC,CACF,CAAC;YACF,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,GAAG,EAAE;gBAC1B,GAAG,CAAC,OAAO,EAAE,CAAC;gBACd,OAAO,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YACxC,CAAC,CAAC,CAAC;YACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC1E,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,KAAK,UAAU,OAAO;IACpB,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,mBAAmB,EAAE,CAAC;QAC9C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC;IAC9C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3C,CAAC;AACH,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,MAA+B;IAC3D,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,CAAC,IAAc,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAgB,CAAC;QACvC,MAAM,OAAO,GAAI,MAAM,CAAC,OAA8B,IAAI,iBAAiB,CAAC,IAAI,CAAC,EAAE,cAAc,IAAI,EAAE,CAAC;QACxG,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QAC3D,IAAI,MAAM,CAAC,EAAE;YAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;QACnC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,MAAM,EAAE,CAAC;IACtD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3C,CAAC;AACH,CAAC;AAED,KAAK,UAAU,MAAM,CAAC,MAA+B;IACnD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,CAAC,IAAc,CAAC;QACnC,MAAM,MAAM,GAAI,MAAM,CAAC,MAAoC,IAAI,IAAI,CAAC;QACpE,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,0BAA0B,IAAI,EAAE,EAAE,CAAC;QAEzE,MAAM,OAAO,GAAI,MAAM,CAAC,OAA8B,IAAI,IAAI,CAAC,cAAc,CAAC;QAC9E,MAAM,OAAO,GAAI,MAAM,CAAC,OAA8B,IAAI,IAAI,CAAC,cAAc,CAAC;QAE9E,qCAAqC;QACrC,IAAI,IAAI,CAAC,cAAc,IAAI,MAAM,EAAE,CAAC;YAClC,MAAM,CAAC,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YACtD,IAAI,CAAC,CAAC,CAAC,EAAE;gBAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,YAAY,EAAE,CAAC;QAClE,CAAC;QAED,oGAAoG;QACpG,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACtD,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC1C,MAAM,aAAa,GAAG,IAAI;YACxB,CAAC,CAAC,UAAU,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,UAAU;YAC3D,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC;QAEvB,MAAM,WAAW,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC;QACxF,cAAc,EAAE,CAAC;QACjB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;IACvC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3C,CAAC;AACH,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,MAA+B;IACtD,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,MAAM,CAAC,EAAY,CAAC;QAC/B,IAAI,CAAC,EAAE;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QACpD,MAAM,cAAc,CAAC,EAAE,CAAC,CAAC;QACzB,cAAc,EAAE,CAAC;QACjB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3C,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,MAA+B;IAC1D,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,MAAM,CAAC,EAAY,CAAC;QAC/B,IAAI,CAAC,EAAE;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QACpD,MAAM,kBAAkB,CAAC,EAAE,CAAC,CAAC;QAC7B,cAAc,EAAE,CAAC;QACjB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3C,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E,MAAM,UAAU,qBAAqB,CACnC,MAAc,EACd,MAAe;IAEf,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,qBAAqB,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3D,MAAM,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAA4B,CAAC;IAEpD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,yBAAyB,CAAC,CAAO,OAAO,OAAO,EAAE,CAAC;QACvD,KAAK,gCAAgC,CAAC,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC,CAAC;QAChE,KAAK,wBAAwB,CAAC,CAAQ,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QACvD,KAAK,2BAA2B,CAAC,CAAK,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC;QAC1D,KAAK,+BAA+B,CAAC,CAAC,OAAO,aAAa,CAAC,CAAC,CAAC,CAAC;QAC9D;YACE,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,6BAA6B,MAAM,EAAE,EAAE,CAAC,CAAC;IACxF,CAAC;AACH,CAAC"}
@@ -0,0 +1,14 @@
1
+ export interface ProviderTypeInfo {
2
+ displayName: string;
3
+ defaultBaseUrl: string;
4
+ api: "anthropic-messages" | "openai-completions" | "openai-responses";
5
+ apiKeyEnvName: string;
6
+ validationPath: string;
7
+ validationAuth: "bearer" | "x-api-key" | "google-query-param" | "none";
8
+ requiresApiKey: boolean;
9
+ allowMultiple: boolean;
10
+ showBaseUrl: boolean;
11
+ showModelId: boolean;
12
+ defaultModelId?: string;
13
+ }
14
+ export declare const PROVIDER_REGISTRY: Record<string, ProviderTypeInfo>;
@@ -0,0 +1,112 @@
1
+ export const PROVIDER_REGISTRY = {
2
+ anthropic: {
3
+ displayName: "Anthropic",
4
+ defaultBaseUrl: "https://api.anthropic.com",
5
+ api: "anthropic-messages",
6
+ apiKeyEnvName: "ANTHROPIC_API_KEY",
7
+ validationPath: "/v1/models",
8
+ validationAuth: "x-api-key",
9
+ requiresApiKey: true,
10
+ allowMultiple: false,
11
+ showBaseUrl: false,
12
+ showModelId: false,
13
+ },
14
+ openai: {
15
+ displayName: "OpenAI",
16
+ defaultBaseUrl: "https://api.openai.com",
17
+ api: "openai-responses",
18
+ apiKeyEnvName: "OPENAI_API_KEY",
19
+ validationPath: "/v1/models",
20
+ validationAuth: "bearer",
21
+ requiresApiKey: true,
22
+ allowMultiple: false,
23
+ showBaseUrl: false,
24
+ showModelId: false,
25
+ },
26
+ google: {
27
+ displayName: "Google",
28
+ defaultBaseUrl: "https://generativelanguage.googleapis.com",
29
+ api: "openai-completions",
30
+ apiKeyEnvName: "GOOGLE_API_KEY",
31
+ validationPath: "/v1beta/models",
32
+ validationAuth: "google-query-param",
33
+ requiresApiKey: true,
34
+ allowMultiple: false,
35
+ showBaseUrl: false,
36
+ showModelId: false,
37
+ },
38
+ openrouter: {
39
+ displayName: "OpenRouter",
40
+ defaultBaseUrl: "https://openrouter.ai",
41
+ api: "openai-completions",
42
+ apiKeyEnvName: "OPENROUTER_API_KEY",
43
+ validationPath: "/api/v1/models",
44
+ validationAuth: "bearer",
45
+ requiresApiKey: true,
46
+ allowMultiple: false,
47
+ showBaseUrl: false,
48
+ showModelId: false,
49
+ },
50
+ ark: {
51
+ displayName: "ByteDance (Ark)",
52
+ defaultBaseUrl: "https://ark.cn-beijing.volces.com",
53
+ api: "openai-completions",
54
+ apiKeyEnvName: "ARK_API_KEY",
55
+ validationPath: "/api/v3/models",
56
+ validationAuth: "bearer",
57
+ requiresApiKey: true,
58
+ allowMultiple: false,
59
+ showBaseUrl: false,
60
+ showModelId: false,
61
+ },
62
+ moonshot: {
63
+ displayName: "Moonshot (Kimi)",
64
+ defaultBaseUrl: "https://api.moonshot.cn",
65
+ api: "openai-completions",
66
+ apiKeyEnvName: "MOONSHOT_API_KEY",
67
+ validationPath: "/v1/models",
68
+ validationAuth: "bearer",
69
+ requiresApiKey: true,
70
+ allowMultiple: false,
71
+ showBaseUrl: false,
72
+ showModelId: false,
73
+ },
74
+ siliconflow: {
75
+ displayName: "SiliconFlow",
76
+ defaultBaseUrl: "https://api.siliconflow.cn",
77
+ api: "openai-completions",
78
+ apiKeyEnvName: "SILICONFLOW_API_KEY",
79
+ validationPath: "/v1/models",
80
+ validationAuth: "bearer",
81
+ requiresApiKey: true,
82
+ allowMultiple: false,
83
+ showBaseUrl: false,
84
+ showModelId: false,
85
+ },
86
+ ollama: {
87
+ displayName: "Ollama",
88
+ defaultBaseUrl: "http://localhost:11434",
89
+ api: "openai-completions",
90
+ apiKeyEnvName: "OLLAMA_API_KEY",
91
+ validationPath: "/api/tags",
92
+ validationAuth: "none",
93
+ requiresApiKey: false,
94
+ allowMultiple: false,
95
+ showBaseUrl: true,
96
+ showModelId: false,
97
+ },
98
+ custom: {
99
+ displayName: "Custom",
100
+ defaultBaseUrl: "",
101
+ api: "openai-completions",
102
+ apiKeyEnvName: "CUSTOM_API_KEY",
103
+ validationPath: "/v1/models",
104
+ validationAuth: "bearer",
105
+ requiresApiKey: false,
106
+ allowMultiple: true,
107
+ showBaseUrl: true,
108
+ showModelId: true,
109
+ defaultModelId: "custom-model",
110
+ },
111
+ };
112
+ //# sourceMappingURL=provider-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-registry.js","sourceRoot":"","sources":["../../src/commands/provider-registry.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { runCommand } from "./commands/run.js";
5
5
  import { installCommand, uninstallCommand, stopCommand, restartCommand, resetCommand } from "./commands/install.js";
6
6
  import { statusCommand } from "./commands/status.js";
7
7
  import { setTokenCommand } from "./commands/set-token.js";
8
- const version = "1.0.17";
8
+ const version = "1.0.18";
9
9
  const program = new Command();
10
10
  program
11
11
  .name("clawpilot")
@@ -1,6 +1,15 @@
1
1
  import { WebSocket } from "ws";
2
2
  import { OpenClawGatewayClient } from "./gateway-client.js";
3
3
  import { handleLocalCommand } from "../commands/local-handlers.js";
4
+ import { handleProviderCommand } from "../commands/provider-handlers.js";
5
+ import { homedir } from "os";
6
+ import { join } from "path";
7
+ import { mkdir, writeFile } from "fs/promises";
8
+ import { randomUUID } from "crypto";
9
+ // ---------------------------------------------------------------------------
10
+ // Constants
11
+ // ---------------------------------------------------------------------------
12
+ const OUTBOUND_DIR = join(homedir(), ".openclaw", "media", "outbound");
4
13
  // ---------------------------------------------------------------------------
5
14
  // Main entry point
6
15
  // ---------------------------------------------------------------------------
@@ -53,7 +62,7 @@ export async function runRelayManager(opts) {
53
62
  });
54
63
  gatewayClient.start();
55
64
  });
56
- relayWs.on("message", (raw) => {
65
+ relayWs.on("message", async (raw) => {
57
66
  let msg;
58
67
  try {
59
68
  msg = JSON.parse(raw.toString());
@@ -65,6 +74,22 @@ export async function runRelayManager(opts) {
65
74
  return;
66
75
  const requestId = msg.id;
67
76
  console.log(`[relay] cmd received method=${msg.method} id=${requestId ?? "(no-id)"}`);
77
+ // Handle clawpilot.provider.* commands locally (async)
78
+ const providerPromise = handleProviderCommand(msg.method, msg.params);
79
+ if (providerPromise !== null) {
80
+ const result = await providerPromise;
81
+ if (requestId) {
82
+ send({
83
+ type: "res",
84
+ id: requestId,
85
+ ok: result.ok,
86
+ ...(result.ok
87
+ ? { payload: result.payload }
88
+ : { error: { message: result.error } }),
89
+ });
90
+ }
91
+ return;
92
+ }
68
93
  // Handle clawpilot.* commands locally without forwarding to the gateway
69
94
  const localResult = handleLocalCommand(msg.method);
70
95
  if (localResult !== null) {
@@ -78,12 +103,37 @@ export async function runRelayManager(opts) {
78
103
  }
79
104
  return;
80
105
  }
81
- // Debug log for chat.send with attachments
106
+ // Handle chat.send with attachments - save to disk and add path reference
82
107
  if (msg.method === "chat.send") {
83
108
  const params = msg.params;
84
109
  if (params.attachments && params.attachments.length > 0) {
85
- console.log(`[relay->gateway] chat.send with ${params.attachments.length} attachment(s), keys: ${Object.keys(params).join(', ')}`);
86
- console.log(`[relay->gateway] first attachment: mimeType=${params.attachments[0].mimeType}, fileName=${params.attachments[0].fileName}, contentLength=${params.attachments[0].content?.length}`);
110
+ const fileReferences = [];
111
+ // Ensure outbound directory exists
112
+ await mkdir(OUTBOUND_DIR, { recursive: true });
113
+ // Save each attachment to disk and create path reference
114
+ for (const att of params.attachments) {
115
+ try {
116
+ // Decode base64 to buffer
117
+ const buffer = Buffer.from(att.content, "base64");
118
+ const ext = att.mimeType === "image/png" ? ".png" : ".jpg";
119
+ const stagedFileName = `${randomUUID()}${ext}`;
120
+ const stagedPath = join(OUTBOUND_DIR, stagedFileName);
121
+ // Write to disk
122
+ await writeFile(stagedPath, buffer);
123
+ console.log(`[relay] Saved attachment to: ${stagedPath}`);
124
+ // Create path reference (same format as ClawX)
125
+ fileReferences.push(`[media attached: ${stagedPath} (${att.mimeType}) | ${stagedPath}]`);
126
+ }
127
+ catch (err) {
128
+ console.error(`[relay] Failed to save attachment: ${err}`);
129
+ }
130
+ }
131
+ // Append file references to message
132
+ if (fileReferences.length > 0) {
133
+ const refs = fileReferences.join("\n");
134
+ params.message = params.message ? `${params.message}\n\n${refs}` : refs;
135
+ console.log(`[relay] Added file references to message`);
136
+ }
87
137
  }
88
138
  }
89
139
  gatewayClient