@clawstore/clawstore 1.0.0
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.
- package/index.ts +46 -0
- package/openclaw.plugin.json +24 -0
- package/package.json +30 -0
- package/src/__tests__/cli-sim.test.ts +303 -0
- package/src/__tests__/e2e.test.ts +186 -0
- package/src/cli.ts +513 -0
- package/src/commands.ts +196 -0
- package/src/constants.ts +80 -0
- package/src/core/agent-manager.ts +327 -0
- package/src/core/package-installer.ts +390 -0
- package/src/core/packager.ts +229 -0
- package/src/core/skill-installer.ts +221 -0
- package/src/core/store-client.ts +140 -0
- package/src/core/workspace.ts +269 -0
- package/src/types.ts +167 -0
- package/src/utils/checksum.ts +17 -0
- package/src/utils/output.ts +76 -0
- package/src/utils/zip.ts +55 -0
- package/tsconfig.json +23 -0
- package/typings/openclaw-plugin-sdk/core.d.ts +134 -0
package/src/cli.ts
ADDED
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
|
|
3
|
+
import type { ClawstoreConfig } from "./types.js";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
ensureDirs,
|
|
7
|
+
listInstalledAgents,
|
|
8
|
+
getInstalledAgent,
|
|
9
|
+
enableAgent,
|
|
10
|
+
disableAgent,
|
|
11
|
+
unregisterAgent,
|
|
12
|
+
backupCurrentWorkspace,
|
|
13
|
+
removeAgentFiles,
|
|
14
|
+
getAgentStatus,
|
|
15
|
+
clearSessions,
|
|
16
|
+
} from "./core/agent-manager.js";
|
|
17
|
+
|
|
18
|
+
import { installAgent, updateAgent } from "./core/package-installer.js";
|
|
19
|
+
import { packAgent, packCurrentAgent } from "./core/packager.js";
|
|
20
|
+
import { diagnose, formatDiagnosticReport, listClawstoreAgents } from "./core/workspace.js";
|
|
21
|
+
import { StoreClient } from "./core/store-client.js";
|
|
22
|
+
import { DEFAULT_API_BASE_URL, DEFAULT_AGENT } from "./constants.js";
|
|
23
|
+
import {
|
|
24
|
+
agentListItem,
|
|
25
|
+
detailBlock,
|
|
26
|
+
searchResultItem,
|
|
27
|
+
} from "./utils/output.js";
|
|
28
|
+
|
|
29
|
+
// Shorthand reused by every subcommand that supports --agent
|
|
30
|
+
const A_FLAGS = "--agent <name>";
|
|
31
|
+
const A_DESC = "Target OpenClaw agent (default: main)";
|
|
32
|
+
|
|
33
|
+
// ─── CLI Registration ───────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
export function registerClawstoreCli(api: OpenClawPluginApi): void {
|
|
36
|
+
const rawConfig = api.pluginConfig as Partial<ClawstoreConfig> | undefined;
|
|
37
|
+
const client = new StoreClient(rawConfig);
|
|
38
|
+
|
|
39
|
+
api.registerCli(
|
|
40
|
+
({ program }) => {
|
|
41
|
+
const root = program
|
|
42
|
+
.command("clawstore")
|
|
43
|
+
.description("Clawstore — Agent marketplace for OpenClaw");
|
|
44
|
+
|
|
45
|
+
// ── search ──────────────────────────────────────────────────
|
|
46
|
+
root
|
|
47
|
+
.command("search [query]")
|
|
48
|
+
.description("Search the Clawstore marketplace for agents")
|
|
49
|
+
.option("--category <category>", "Filter by category")
|
|
50
|
+
.option("--sort <order>", "Sort order: popular|newest|rating", "popular")
|
|
51
|
+
.option("--limit <n>", "Max results to show", "10")
|
|
52
|
+
.action(async (query: string | undefined, opts: { category?: string; sort?: string; limit?: string }) => {
|
|
53
|
+
const q = query ?? "";
|
|
54
|
+
try {
|
|
55
|
+
const results = await client.search(q, {
|
|
56
|
+
category: opts.category,
|
|
57
|
+
sort: opts.sort,
|
|
58
|
+
limit: Number(opts.limit) || 10,
|
|
59
|
+
});
|
|
60
|
+
if (results.total === 0) {
|
|
61
|
+
console.log(`\n No agents found for "${q}".\n`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
console.log(`\n Found ${results.total} agent(s)\n`);
|
|
65
|
+
results.agents.forEach((a, i) => {
|
|
66
|
+
console.log(searchResultItem(i + 1, a.id, a.name, a.price, a.currency, a.author, a.rating));
|
|
67
|
+
console.log("");
|
|
68
|
+
});
|
|
69
|
+
} catch (err) {
|
|
70
|
+
console.log(`\n Search failed: ${(err as Error).message}`);
|
|
71
|
+
console.log(` The Clawstore API may not be available yet.`);
|
|
72
|
+
console.log(` Run \`openclaw clawstore doctor\` to check connectivity.\n`);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// ── show ────────────────────────────────────────────────────
|
|
77
|
+
root
|
|
78
|
+
.command("show <agent-id>")
|
|
79
|
+
.description("Show detailed information about an agent")
|
|
80
|
+
.option(A_FLAGS, A_DESC, DEFAULT_AGENT)
|
|
81
|
+
.action(async (agentId: string, opts: { agent?: string }) => {
|
|
82
|
+
const agent = opts.agent ?? DEFAULT_AGENT;
|
|
83
|
+
try {
|
|
84
|
+
const detail = await client.getAgentDetail(agentId);
|
|
85
|
+
const local = await getInstalledAgent(agentId, agent);
|
|
86
|
+
const installStatus = local
|
|
87
|
+
? `installed (v${local.version}, ${local.enabled ? "enabled" : "disabled"})`
|
|
88
|
+
: "not installed";
|
|
89
|
+
|
|
90
|
+
console.log(
|
|
91
|
+
detailBlock({
|
|
92
|
+
Agent: detail.id,
|
|
93
|
+
Name: detail.name,
|
|
94
|
+
Version: detail.version,
|
|
95
|
+
Author: detail.author,
|
|
96
|
+
Price: detail.price === 0 ? "Free" : `$${detail.price}`,
|
|
97
|
+
Category: detail.category,
|
|
98
|
+
Rating: detail.rating?.toFixed(1),
|
|
99
|
+
OpenClaw: detail.openclaw_version,
|
|
100
|
+
Skills:
|
|
101
|
+
detail.skills
|
|
102
|
+
?.map((s) => `${s.id} ${s.version}${s.required ? " (required)" : ""}`)
|
|
103
|
+
.join(", ") || "none",
|
|
104
|
+
Status: installStatus,
|
|
105
|
+
...(agent !== DEFAULT_AGENT ? { "Target agent": agent } : {}),
|
|
106
|
+
}),
|
|
107
|
+
);
|
|
108
|
+
return;
|
|
109
|
+
} catch {
|
|
110
|
+
// Fall back to local
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const local = await getInstalledAgent(agentId, agent);
|
|
114
|
+
if (!local) {
|
|
115
|
+
console.log(`\n Agent '${agentId}' not found (neither remote nor local).\n`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
console.log(
|
|
119
|
+
detailBlock({
|
|
120
|
+
Agent: local.id,
|
|
121
|
+
Name: local.name,
|
|
122
|
+
Version: local.version,
|
|
123
|
+
Author: local.author,
|
|
124
|
+
Category: local.category,
|
|
125
|
+
Status: local.enabled ? "enabled" : "disabled",
|
|
126
|
+
Installed: local.installed_at,
|
|
127
|
+
Updated: local.updated_at,
|
|
128
|
+
Path: local.install_path,
|
|
129
|
+
...(agent !== DEFAULT_AGENT ? { "Target agent": agent } : {}),
|
|
130
|
+
}),
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// ── install ─────────────────────────────────────────────────
|
|
135
|
+
root
|
|
136
|
+
.command("install <source>")
|
|
137
|
+
.description("Install an Agent from local path, ZIP, or marketplace")
|
|
138
|
+
.option("-f, --force", "Force reinstall if already installed")
|
|
139
|
+
.option(A_FLAGS, A_DESC, DEFAULT_AGENT)
|
|
140
|
+
.action(async (source: string, opts: { force?: boolean; agent?: string }) => {
|
|
141
|
+
const agent = opts.agent ?? DEFAULT_AGENT;
|
|
142
|
+
let packagePath = source;
|
|
143
|
+
|
|
144
|
+
const isRemote =
|
|
145
|
+
!source.includes("/") &&
|
|
146
|
+
!source.includes("\\") &&
|
|
147
|
+
!source.endsWith(".zip") &&
|
|
148
|
+
!existsSync(source);
|
|
149
|
+
|
|
150
|
+
if (isRemote) {
|
|
151
|
+
try {
|
|
152
|
+
console.log(`\n Fetching ${source} from Clawstore...`);
|
|
153
|
+
const url = await client.getDownloadUrl(source);
|
|
154
|
+
const { downloadPackage } = await import("./core/package-installer.js");
|
|
155
|
+
packagePath = await downloadPackage(url, source);
|
|
156
|
+
console.log(` Downloaded to: ${packagePath}\n`);
|
|
157
|
+
} catch (err) {
|
|
158
|
+
console.log(`\n Failed to download '${source}' from Clawstore: ${(err as Error).message}`);
|
|
159
|
+
console.log(` If this is a local path, make sure the file/directory exists.\n`);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
await installAgent({
|
|
165
|
+
packagePath,
|
|
166
|
+
force: opts.force === true,
|
|
167
|
+
agent,
|
|
168
|
+
log: console.log,
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// ── installed ───────────────────────────────────────────────
|
|
173
|
+
root
|
|
174
|
+
.command("installed")
|
|
175
|
+
.description("List all installed agents")
|
|
176
|
+
.option("--json", "Output as JSON")
|
|
177
|
+
.option("--all-agents", "Show installations across all OpenClaw agents")
|
|
178
|
+
.option(A_FLAGS, A_DESC, DEFAULT_AGENT)
|
|
179
|
+
.action(async (opts: { json?: boolean; allAgents?: boolean; agent?: string }) => {
|
|
180
|
+
const agent = opts.agent ?? DEFAULT_AGENT;
|
|
181
|
+
|
|
182
|
+
if (opts.allAgents) {
|
|
183
|
+
const agentNames = await listClawstoreAgents();
|
|
184
|
+
if (agentNames.length === 0) {
|
|
185
|
+
console.log("\n No agents with installed Personas found.\n");
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
for (const name of agentNames) {
|
|
189
|
+
const personas = await listInstalledAgents(name);
|
|
190
|
+
if (personas.length === 0) continue;
|
|
191
|
+
console.log(`\n OpenClaw agent: ${name}`);
|
|
192
|
+
console.log(` ${"─".repeat(40)}`);
|
|
193
|
+
personas.forEach((a, i) => {
|
|
194
|
+
console.log(agentListItem(i + 1, a.id, a.name, a.version, a.category, a.enabled));
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
console.log("");
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
await ensureDirs(agent);
|
|
202
|
+
const personas = await listInstalledAgents(agent);
|
|
203
|
+
|
|
204
|
+
if (personas.length === 0) {
|
|
205
|
+
console.log("\n No agents installed.");
|
|
206
|
+
console.log(" Run `openclaw clawstore install <package>` to install one.\n");
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (opts.json) {
|
|
211
|
+
console.log(JSON.stringify(personas, null, 2));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (agent !== DEFAULT_AGENT) {
|
|
216
|
+
console.log(`\n OpenClaw agent: ${agent}`);
|
|
217
|
+
}
|
|
218
|
+
console.log(`\n Installed agents: ${personas.length}\n`);
|
|
219
|
+
personas.forEach((a, i) => {
|
|
220
|
+
console.log(agentListItem(i + 1, a.id, a.name, a.version, a.category, a.enabled));
|
|
221
|
+
console.log(` Installed: ${a.installed_at.slice(0, 10)}`);
|
|
222
|
+
if (a.updated_at !== a.installed_at) {
|
|
223
|
+
console.log(` Updated: ${a.updated_at.slice(0, 10)}`);
|
|
224
|
+
}
|
|
225
|
+
console.log("");
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// ── enable ──────────────────────────────────────────────────
|
|
230
|
+
root
|
|
231
|
+
.command("enable <agent-id>")
|
|
232
|
+
.description("Enable an installed agent")
|
|
233
|
+
.option(A_FLAGS, A_DESC, DEFAULT_AGENT)
|
|
234
|
+
.action(async (agentId: string, opts: { agent?: string }) => {
|
|
235
|
+
const agent = opts.agent ?? DEFAULT_AGENT;
|
|
236
|
+
const result = await enableAgent(agentId, agent);
|
|
237
|
+
if (!result) {
|
|
238
|
+
console.log(`\n Agent '${agentId}' is not installed.\n`);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
console.log(`\n Enabled: ${result.name} (${agentId})`);
|
|
242
|
+
if (agent !== DEFAULT_AGENT) console.log(` OpenClaw agent: ${agent}`);
|
|
243
|
+
console.log(` Workspace files restored. Start a new chat to activate the persona.\n`);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// ── disable ─────────────────────────────────────────────────
|
|
247
|
+
root
|
|
248
|
+
.command("disable <agent-id>")
|
|
249
|
+
.description("Disable an installed agent")
|
|
250
|
+
.option(A_FLAGS, A_DESC, DEFAULT_AGENT)
|
|
251
|
+
.action(async (agentId: string, opts: { agent?: string }) => {
|
|
252
|
+
const agent = opts.agent ?? DEFAULT_AGENT;
|
|
253
|
+
const result = await disableAgent(agentId, agent);
|
|
254
|
+
if (!result) {
|
|
255
|
+
console.log(`\n Agent '${agentId}' is not installed.\n`);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
await clearSessions(agent);
|
|
259
|
+
console.log(`\n Disabled: ${result.agent.name} (${agentId})`);
|
|
260
|
+
if (agent !== DEFAULT_AGENT) console.log(` OpenClaw agent: ${agent}`);
|
|
261
|
+
console.log(` Workspace files backed up and removed.`);
|
|
262
|
+
console.log(` Session history cleared — TUI will start fresh.`);
|
|
263
|
+
console.log(` Backup: ${result.backupPath}`);
|
|
264
|
+
console.log(` Use \`openclaw clawstore enable ${agentId}\` to re-activate.\n`);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// ── update ──────────────────────────────────────────────────
|
|
268
|
+
root
|
|
269
|
+
.command("update [agent-id]")
|
|
270
|
+
.description("Update an installed agent")
|
|
271
|
+
.option("--package <path>", "Path to updated package directory or ZIP")
|
|
272
|
+
.option("--all", "Update all installed agents")
|
|
273
|
+
.option(A_FLAGS, A_DESC, DEFAULT_AGENT)
|
|
274
|
+
.action(async (agentId: string | undefined, opts: { package?: string; all?: boolean; agent?: string }) => {
|
|
275
|
+
const agent = opts.agent ?? DEFAULT_AGENT;
|
|
276
|
+
if (opts.all) {
|
|
277
|
+
const personas = await listInstalledAgents(agent);
|
|
278
|
+
if (personas.length === 0) {
|
|
279
|
+
console.log("\n No agents installed.\n");
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
console.log(`\n Checking updates for ${personas.length} agent(s)...\n`);
|
|
283
|
+
for (const a of personas) {
|
|
284
|
+
try {
|
|
285
|
+
const downloadUrl = await client.getDownloadUrl(a.id);
|
|
286
|
+
const { downloadPackage } = await import("./core/package-installer.js");
|
|
287
|
+
const pkgPath = await downloadPackage(downloadUrl, a.id);
|
|
288
|
+
await updateAgent({ agentId: a.id, packagePath: pkgPath, agent, log: console.log });
|
|
289
|
+
} catch (err) {
|
|
290
|
+
console.log(` ${a.id}: skipped (${(err as Error).message})`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (!agentId) {
|
|
297
|
+
console.log("\n Usage: openclaw clawstore update <agent-id> --package <path>\n");
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const packagePath = opts.package;
|
|
302
|
+
if (!packagePath) {
|
|
303
|
+
try {
|
|
304
|
+
const url = await client.getDownloadUrl(agentId);
|
|
305
|
+
const { downloadPackage } = await import("./core/package-installer.js");
|
|
306
|
+
const dlPath = await downloadPackage(url, agentId);
|
|
307
|
+
await updateAgent({ agentId, packagePath: dlPath, agent, log: console.log });
|
|
308
|
+
} catch (err) {
|
|
309
|
+
console.log(`\n Could not fetch update from Clawstore: ${(err as Error).message}`);
|
|
310
|
+
console.log(` Provide a local package: openclaw clawstore update ${agentId} --package <path>\n`);
|
|
311
|
+
}
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
await updateAgent({ agentId, packagePath, agent, log: console.log });
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// ── uninstall ───────────────────────────────────────────────
|
|
319
|
+
root
|
|
320
|
+
.command("uninstall <agent-id>")
|
|
321
|
+
.description("Uninstall an agent")
|
|
322
|
+
.option("-y, --yes", "Skip confirmation")
|
|
323
|
+
.option(A_FLAGS, A_DESC, DEFAULT_AGENT)
|
|
324
|
+
.action(async (agentId: string, opts: { yes?: boolean; agent?: string }) => {
|
|
325
|
+
const agent = opts.agent ?? DEFAULT_AGENT;
|
|
326
|
+
await ensureDirs(agent);
|
|
327
|
+
const record = await getInstalledAgent(agentId, agent);
|
|
328
|
+
|
|
329
|
+
if (!record) {
|
|
330
|
+
console.log(`\n Agent '${agentId}' is not in the registry.`);
|
|
331
|
+
console.log(` Running cleanup for residual files and sessions...\n`);
|
|
332
|
+
|
|
333
|
+
console.log(" [1/2] Clearing workspace residuals ... ");
|
|
334
|
+
const { removeAllWorkspaceAgentFiles } = await import("./core/agent-manager.js");
|
|
335
|
+
const removed = await removeAllWorkspaceAgentFiles(agent);
|
|
336
|
+
console.log(` [1/2] Clearing workspace residuals ... OK (${removed} files removed)`);
|
|
337
|
+
|
|
338
|
+
console.log(" [2/2] Clearing session history ... ");
|
|
339
|
+
const cleared = await clearSessions(agent);
|
|
340
|
+
console.log(` [2/2] Clearing session history ... OK (${cleared} sessions cleared)`);
|
|
341
|
+
|
|
342
|
+
console.log(`\n Cleanup complete. TUI will start fresh on next launch.\n`);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
console.log(`\n Uninstalling: ${record.name} (${agentId})`);
|
|
347
|
+
console.log(` Version: ${record.version}`);
|
|
348
|
+
if (agent !== DEFAULT_AGENT) console.log(` OpenClaw agent: ${agent}`);
|
|
349
|
+
console.log("");
|
|
350
|
+
|
|
351
|
+
console.log(" [1/4] Creating safety backup ... ");
|
|
352
|
+
const backupPath = await backupCurrentWorkspace(agentId, "uninstall", agent);
|
|
353
|
+
console.log(" [1/4] Creating safety backup ... OK");
|
|
354
|
+
|
|
355
|
+
console.log(" [2/4] Removing agent files ... ");
|
|
356
|
+
const removed = await removeAgentFiles(record, agent);
|
|
357
|
+
console.log(` [2/4] Removing agent files ... OK (${removed} files removed)`);
|
|
358
|
+
|
|
359
|
+
console.log(" [3/4] Updating registry ... ");
|
|
360
|
+
await unregisterAgent(agentId, agent);
|
|
361
|
+
console.log(" [3/4] Updating registry ... OK");
|
|
362
|
+
|
|
363
|
+
console.log(" [4/4] Clearing session history ... ");
|
|
364
|
+
const cleared = await clearSessions(agent);
|
|
365
|
+
console.log(` [4/4] Clearing session history ... OK (${cleared} sessions cleared)`);
|
|
366
|
+
|
|
367
|
+
console.log(`\n Uninstalled: ${record.name}`);
|
|
368
|
+
console.log(` Backup saved: ${backupPath}`);
|
|
369
|
+
console.log(` Sessions cleared — TUI will start fresh on next launch.\n`);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// ── status ──────────────────────────────────────────────────
|
|
373
|
+
root
|
|
374
|
+
.command("status [agent-id]")
|
|
375
|
+
.description("Show detailed status of an installed agent")
|
|
376
|
+
.option(A_FLAGS, A_DESC, DEFAULT_AGENT)
|
|
377
|
+
.action(async (agentId: string | undefined, opts: { agent?: string }) => {
|
|
378
|
+
const agent = opts.agent ?? DEFAULT_AGENT;
|
|
379
|
+
await ensureDirs(agent);
|
|
380
|
+
|
|
381
|
+
if (agentId) {
|
|
382
|
+
const status = await getAgentStatus(agentId, agent);
|
|
383
|
+
if (!status) {
|
|
384
|
+
console.log(`\n Agent '${agentId}' is not installed.\n`);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
printAgentStatus(status, agent);
|
|
388
|
+
} else {
|
|
389
|
+
const personas = await listInstalledAgents(agent);
|
|
390
|
+
if (personas.length === 0) {
|
|
391
|
+
console.log("\n No agents installed.\n");
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
for (const a of personas) {
|
|
395
|
+
const status = await getAgentStatus(a.id, agent);
|
|
396
|
+
if (status) printAgentStatus(status, agent);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// ── pack ────────────────────────────────────────────────────
|
|
402
|
+
root
|
|
403
|
+
.command("pack [path]")
|
|
404
|
+
.description("Pack a package directory into a distributable ZIP")
|
|
405
|
+
.option("-o, --output <dir>", "Output directory for ZIP")
|
|
406
|
+
.option("--name <name>", "Agent name override")
|
|
407
|
+
.option("--category <category>", "Category override")
|
|
408
|
+
.action(async (packagePath: string | undefined, opts: { output?: string; name?: string; category?: string }) => {
|
|
409
|
+
if (packagePath) {
|
|
410
|
+
await packAgent({
|
|
411
|
+
packagePath,
|
|
412
|
+
outputDir: opts.output,
|
|
413
|
+
name: opts.name,
|
|
414
|
+
category: opts.category,
|
|
415
|
+
log: console.log,
|
|
416
|
+
});
|
|
417
|
+
} else {
|
|
418
|
+
await packCurrentAgent({
|
|
419
|
+
outputDir: opts.output,
|
|
420
|
+
name: opts.name,
|
|
421
|
+
category: opts.category,
|
|
422
|
+
log: console.log,
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// ── doctor ──────────────────────────────────────────────────
|
|
428
|
+
root
|
|
429
|
+
.command("doctor")
|
|
430
|
+
.description("Diagnose environment and configuration")
|
|
431
|
+
.option(A_FLAGS, A_DESC, DEFAULT_AGENT)
|
|
432
|
+
.action(async (opts: { agent?: string }) => {
|
|
433
|
+
const agent = opts.agent ?? DEFAULT_AGENT;
|
|
434
|
+
await ensureDirs(agent);
|
|
435
|
+
const apiUrl = rawConfig?.apiBaseUrl ?? DEFAULT_API_BASE_URL;
|
|
436
|
+
const report = await diagnose(apiUrl, agent);
|
|
437
|
+
console.log(formatDiagnosticReport(report));
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
// ── login ───────────────────────────────────────────────────
|
|
441
|
+
root
|
|
442
|
+
.command("login")
|
|
443
|
+
.description("Log in to Clawstore")
|
|
444
|
+
.option("--email <email>", "Email address")
|
|
445
|
+
.option("--password <password>", "Password")
|
|
446
|
+
.action(async (opts: { email?: string; password?: string }) => {
|
|
447
|
+
if (!opts.email || !opts.password) {
|
|
448
|
+
console.log("\n Usage: openclaw clawstore login --email <email> --password <password>\n");
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
try {
|
|
452
|
+
const result = await client.login(opts.email, opts.password);
|
|
453
|
+
client.setAuthToken(result.token);
|
|
454
|
+
console.log(`\n Logged in as: ${result.user.name} (${result.user.id})\n`);
|
|
455
|
+
} catch (err) {
|
|
456
|
+
console.log(`\n Login failed: ${(err as Error).message}\n`);
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// ── whoami ──────────────────────────────────────────────────
|
|
461
|
+
root
|
|
462
|
+
.command("whoami")
|
|
463
|
+
.description("Show current Clawstore user")
|
|
464
|
+
.action(async () => {
|
|
465
|
+
const user = await client.whoami();
|
|
466
|
+
if (user) {
|
|
467
|
+
console.log(`\n Logged in as: ${user.name} <${user.email}>\n`);
|
|
468
|
+
} else {
|
|
469
|
+
console.log("\n Not logged in. Run `openclaw clawstore login` to authenticate.\n");
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
},
|
|
473
|
+
{ commands: ["clawstore"] },
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// ─── Helpers ────────────────────────────────────────────────────────
|
|
478
|
+
|
|
479
|
+
function printAgentStatus(
|
|
480
|
+
status: NonNullable<Awaited<ReturnType<typeof getAgentStatus>>>,
|
|
481
|
+
agent: string = DEFAULT_AGENT,
|
|
482
|
+
): void {
|
|
483
|
+
const { agent: record, fileHealth, skillHealth } = status;
|
|
484
|
+
console.log(`\n Agent: ${record.id}`);
|
|
485
|
+
console.log(` Name: ${record.name}`);
|
|
486
|
+
console.log(` Version: ${record.version}`);
|
|
487
|
+
console.log(` Category: ${record.category || "N/A"}`);
|
|
488
|
+
console.log(` Author: ${record.author || "N/A"}`);
|
|
489
|
+
console.log(` Status: ${record.enabled ? "enabled" : "disabled"}`);
|
|
490
|
+
if (agent !== DEFAULT_AGENT) {
|
|
491
|
+
console.log(` OC Agent: ${agent}`);
|
|
492
|
+
}
|
|
493
|
+
console.log(` Installed: ${record.installed_at}`);
|
|
494
|
+
console.log(` Updated: ${record.updated_at}`);
|
|
495
|
+
console.log(` Path: ${record.install_path}`);
|
|
496
|
+
|
|
497
|
+
console.log(` Files:`);
|
|
498
|
+
for (const f of fileHealth) {
|
|
499
|
+
console.log(` - ${f.file} [${f.exists ? "OK" : "MISSING"}]`);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if (skillHealth.length > 0) {
|
|
503
|
+
console.log(` Skills:`);
|
|
504
|
+
for (const s of skillHealth) {
|
|
505
|
+
console.log(` - ${s.skillId} [${s.installed ? "installed" : "not installed"}]`);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (record.backup_path) {
|
|
510
|
+
console.log(` Backup: ${record.backup_path}`);
|
|
511
|
+
}
|
|
512
|
+
console.log("");
|
|
513
|
+
}
|