@clawstore/clawstore 1.0.2 → 1.0.7
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/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +11 -1
- package/dist/src/cli.js.map +1 -1
- package/dist/src/commands.d.ts.map +1 -1
- package/dist/src/commands.js +8 -1
- package/dist/src/commands.js.map +1 -1
- package/dist/src/core/agent-register.d.ts +6 -0
- package/dist/src/core/agent-register.d.ts.map +1 -0
- package/dist/src/core/agent-register.js +48 -0
- package/dist/src/core/agent-register.js.map +1 -0
- package/dist/src/core/api-health.d.ts +9 -0
- package/dist/src/core/api-health.d.ts.map +1 -0
- package/dist/src/core/api-health.js +17 -0
- package/dist/src/core/api-health.js.map +1 -0
- package/dist/src/core/diagnostics.d.ts +7 -0
- package/dist/src/core/diagnostics.d.ts.map +1 -0
- package/dist/src/core/diagnostics.js +145 -0
- package/dist/src/core/diagnostics.js.map +1 -0
- package/dist/src/core/openclaw-profile.d.ts +6 -0
- package/dist/src/core/openclaw-profile.d.ts.map +1 -0
- package/dist/src/core/openclaw-profile.js +12 -0
- package/dist/src/core/openclaw-profile.js.map +1 -0
- package/dist/src/core/workspace.d.ts +0 -9
- package/dist/src/core/workspace.d.ts.map +1 -1
- package/dist/src/core/workspace.js +7 -154
- package/dist/src/core/workspace.js.map +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +18 -3
- package/src/cli.ts +12 -1
- package/src/commands.ts +9 -1
- package/src/core/agent-register.ts +57 -0
- package/src/core/api-health.ts +17 -0
- package/src/core/diagnostics.ts +163 -0
- package/src/core/openclaw-profile.ts +10 -0
- package/src/core/workspace.ts +7 -168
- package/clawstore-clawstore-1.0.2.tgz +0 -0
- package/dist/src/__tests__/cli-sim.test.d.ts +0 -9
- package/dist/src/__tests__/cli-sim.test.d.ts.map +0 -1
- package/dist/src/__tests__/cli-sim.test.js +0 -275
- package/dist/src/__tests__/cli-sim.test.js.map +0 -1
- package/dist/src/__tests__/e2e.test.d.ts +0 -10
- package/dist/src/__tests__/e2e.test.d.ts.map +0 -1
- package/dist/src/__tests__/e2e.test.js +0 -159
- package/dist/src/__tests__/e2e.test.js.map +0 -1
- package/src/__tests__/cli-sim.test.ts +0 -303
- package/src/__tests__/e2e.test.ts +0 -186
- package/tsconfig.json +0 -23
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { readFile, access, readdir } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import { OPENCLAW_HOME, CLAWSTORE_DIR, DEFAULT_AGENT,
|
|
4
|
+
import { OPENCLAW_HOME, CLAWSTORE_DIR, DEFAULT_AGENT, agentRegistryFile, } from "../constants.js";
|
|
5
|
+
import { readOpenclawProfileLabel } from "./openclaw-profile.js";
|
|
5
6
|
/**
|
|
6
7
|
* Resolve the workspace directory for a given OpenClaw agent.
|
|
7
8
|
*
|
|
@@ -26,6 +27,10 @@ export async function resolveWorkspace(agent) {
|
|
|
26
27
|
workspace =
|
|
27
28
|
config?.agents?.[target]?.workspace ??
|
|
28
29
|
config?.agents?.entries?.[target]?.workspace;
|
|
30
|
+
if (!workspace && Array.isArray(config?.agents?.list)) {
|
|
31
|
+
const entry = config.agents.list.find((e) => e?.id === target);
|
|
32
|
+
workspace = entry?.workspace;
|
|
33
|
+
}
|
|
29
34
|
}
|
|
30
35
|
else {
|
|
31
36
|
workspace =
|
|
@@ -40,7 +45,7 @@ export async function resolveWorkspace(agent) {
|
|
|
40
45
|
// config not found or invalid — fall through
|
|
41
46
|
}
|
|
42
47
|
if (target === DEFAULT_AGENT) {
|
|
43
|
-
const profile =
|
|
48
|
+
const profile = readOpenclawProfileLabel();
|
|
44
49
|
if (profile !== "default") {
|
|
45
50
|
return join(OPENCLAW_HOME, `workspace-${profile}`);
|
|
46
51
|
}
|
|
@@ -62,11 +67,9 @@ function expandHome(p) {
|
|
|
62
67
|
*/
|
|
63
68
|
export async function listClawstoreAgents() {
|
|
64
69
|
const agents = [];
|
|
65
|
-
// "main" agent uses the root-level registry file
|
|
66
70
|
if (await exists(agentRegistryFile(DEFAULT_AGENT))) {
|
|
67
71
|
agents.push(DEFAULT_AGENT);
|
|
68
72
|
}
|
|
69
|
-
// Non-main agents live under clawstore/agents/<name>/
|
|
70
73
|
const agentsDir = join(CLAWSTORE_DIR, "agents");
|
|
71
74
|
if (await exists(agentsDir)) {
|
|
72
75
|
const entries = await readdir(agentsDir, { withFileTypes: true });
|
|
@@ -89,154 +92,4 @@ async function exists(p) {
|
|
|
89
92
|
return false;
|
|
90
93
|
}
|
|
91
94
|
}
|
|
92
|
-
async function dirItemCount(p) {
|
|
93
|
-
try {
|
|
94
|
-
const entries = await readdir(p);
|
|
95
|
-
return entries.length;
|
|
96
|
-
}
|
|
97
|
-
catch {
|
|
98
|
-
return -1;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Run a full diagnostic for a specific OpenClaw agent.
|
|
103
|
-
*/
|
|
104
|
-
export async function diagnose(apiBaseUrl, agent) {
|
|
105
|
-
const target = agent ?? DEFAULT_AGENT;
|
|
106
|
-
const checks = [];
|
|
107
|
-
let allOk = true;
|
|
108
|
-
const workspace = await resolveWorkspace(target);
|
|
109
|
-
const registryFile = agentRegistryFile(target);
|
|
110
|
-
const backupDir = agentBackupDir(target);
|
|
111
|
-
if (target !== DEFAULT_AGENT) {
|
|
112
|
-
checks.push({
|
|
113
|
-
label: "Target agent",
|
|
114
|
-
status: "ok",
|
|
115
|
-
detail: target,
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
// 1. OpenClaw home
|
|
119
|
-
const homeOk = await exists(OPENCLAW_HOME);
|
|
120
|
-
checks.push({
|
|
121
|
-
label: "OpenClaw home",
|
|
122
|
-
path: OPENCLAW_HOME,
|
|
123
|
-
status: homeOk ? "ok" : "fail",
|
|
124
|
-
detail: homeOk ? undefined : "NOT FOUND",
|
|
125
|
-
});
|
|
126
|
-
if (!homeOk)
|
|
127
|
-
allOk = false;
|
|
128
|
-
// 2. Workspace
|
|
129
|
-
const wsOk = await exists(workspace);
|
|
130
|
-
checks.push({
|
|
131
|
-
label: "Workspace",
|
|
132
|
-
path: workspace,
|
|
133
|
-
status: wsOk ? "ok" : "fail",
|
|
134
|
-
detail: wsOk ? undefined : "NOT FOUND",
|
|
135
|
-
});
|
|
136
|
-
if (!wsOk)
|
|
137
|
-
allOk = false;
|
|
138
|
-
// 3. Workspace files
|
|
139
|
-
for (const f of [...REQUIRED_FILES, ...OPTIONAL_FILES]) {
|
|
140
|
-
const fp = join(workspace, f);
|
|
141
|
-
const found = await exists(fp);
|
|
142
|
-
checks.push({
|
|
143
|
-
label: `Workspace file: ${f}`,
|
|
144
|
-
path: fp,
|
|
145
|
-
status: found ? "ok" : "warn",
|
|
146
|
-
detail: found ? "found" : "missing",
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
// 4. Registry
|
|
150
|
-
const regOk = await exists(registryFile);
|
|
151
|
-
if (regOk) {
|
|
152
|
-
try {
|
|
153
|
-
const raw = await readFile(registryFile, "utf-8");
|
|
154
|
-
const reg = JSON.parse(raw);
|
|
155
|
-
const count = Object.keys(reg.agents ?? {}).length;
|
|
156
|
-
checks.push({
|
|
157
|
-
label: "Agent registry",
|
|
158
|
-
path: registryFile,
|
|
159
|
-
status: "ok",
|
|
160
|
-
detail: `${count} agent(s) registered`,
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
catch {
|
|
164
|
-
checks.push({
|
|
165
|
-
label: "Agent registry",
|
|
166
|
-
path: registryFile,
|
|
167
|
-
status: "warn",
|
|
168
|
-
detail: "exists but cannot be parsed",
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
else {
|
|
173
|
-
checks.push({
|
|
174
|
-
label: "Agent registry",
|
|
175
|
-
path: registryFile,
|
|
176
|
-
status: "ok",
|
|
177
|
-
detail: "not created yet (OK for first run)",
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
// 5. Skills directory
|
|
181
|
-
const skillsDir = join(workspace, "skills");
|
|
182
|
-
const skillCount = await dirItemCount(skillsDir);
|
|
183
|
-
checks.push({
|
|
184
|
-
label: "Skills directory",
|
|
185
|
-
path: skillsDir,
|
|
186
|
-
status: skillCount >= 0 ? "ok" : "warn",
|
|
187
|
-
detail: skillCount >= 0 ? `${skillCount} skill(s)` : "NOT FOUND",
|
|
188
|
-
});
|
|
189
|
-
// 6. Backup directory
|
|
190
|
-
const backupCount = await dirItemCount(backupDir);
|
|
191
|
-
checks.push({
|
|
192
|
-
label: "Backup directory",
|
|
193
|
-
path: backupDir,
|
|
194
|
-
status: "ok",
|
|
195
|
-
detail: backupCount >= 0 ? `${backupCount} backup(s)` : "will be created on first install",
|
|
196
|
-
});
|
|
197
|
-
// 7. Cache directory
|
|
198
|
-
const cacheCount = await dirItemCount(join(CLAWSTORE_DIR, "cache"));
|
|
199
|
-
checks.push({
|
|
200
|
-
label: "Cache directory",
|
|
201
|
-
path: join(CLAWSTORE_DIR, "cache"),
|
|
202
|
-
status: "ok",
|
|
203
|
-
detail: cacheCount >= 0 ? `${cacheCount} item(s)` : "will be created on first install",
|
|
204
|
-
});
|
|
205
|
-
// 8. API connectivity (optional)
|
|
206
|
-
if (apiBaseUrl) {
|
|
207
|
-
try {
|
|
208
|
-
const res = await fetch(`${apiBaseUrl}/health`, { signal: AbortSignal.timeout(5000) });
|
|
209
|
-
checks.push({
|
|
210
|
-
label: "Clawstore API",
|
|
211
|
-
status: res.ok ? "ok" : "warn",
|
|
212
|
-
detail: res.ok ? "reachable" : `HTTP ${res.status}`,
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
catch (err) {
|
|
216
|
-
checks.push({
|
|
217
|
-
label: "Clawstore API",
|
|
218
|
-
status: "warn",
|
|
219
|
-
detail: `unreachable (${err.message})`,
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
return { checks, allOk };
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* Format a diagnostic report for CLI output.
|
|
227
|
-
*/
|
|
228
|
-
export function formatDiagnosticReport(report) {
|
|
229
|
-
const lines = ["\n Clawstore Doctor\n"];
|
|
230
|
-
for (const c of report.checks) {
|
|
231
|
-
const icon = c.status === "ok" ? "OK" : c.status === "warn" ? "WARN" : "FAIL";
|
|
232
|
-
const pathPart = c.path ? ` ${c.path}` : "";
|
|
233
|
-
const detailPart = c.detail ? ` (${c.detail})` : "";
|
|
234
|
-
lines.push(` ${c.label}:${pathPart} ... ${icon}${detailPart}`);
|
|
235
|
-
}
|
|
236
|
-
lines.push("");
|
|
237
|
-
lines.push(report.allOk
|
|
238
|
-
? " All checks passed.\n"
|
|
239
|
-
: " Some issues found. Run `openclaw setup` to initialize missing directories.\n");
|
|
240
|
-
return lines.join("\n");
|
|
241
|
-
}
|
|
242
95
|
//# sourceMappingURL=workspace.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../../src/core/workspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../../src/core/workspace.ts"],"names":[],"mappings":"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"}
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clawstore/clawstore",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Agent marketplace plugin for OpenClaw — browse, install, manage and pack AI employee agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.ts",
|
|
@@ -15,7 +15,10 @@
|
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "tsc",
|
|
18
|
-
"test": "node --import tsx src/__tests__/e2e.test.ts"
|
|
18
|
+
"test": "node --import tsx src/__tests__/e2e.test.ts",
|
|
19
|
+
"clean": "node -e \"try{require('fs').rmSync('dist',{recursive:true,force:true})}catch(e){if(e.code!=='ENOENT')throw e}\"",
|
|
20
|
+
"prepack": "npm run clean && npm run build",
|
|
21
|
+
"pack:release": "npm pack --pack-destination ../../dist"
|
|
19
22
|
},
|
|
20
23
|
"dependencies": {
|
|
21
24
|
"adm-zip": "^0.5.16",
|
|
@@ -28,5 +31,17 @@
|
|
|
28
31
|
"typescript": "^5.5.0",
|
|
29
32
|
"tsx": "^4.19.0"
|
|
30
33
|
},
|
|
31
|
-
"license": "MIT"
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"files": [
|
|
36
|
+
"index.ts",
|
|
37
|
+
"openclaw.plugin.json",
|
|
38
|
+
"src/cli.ts",
|
|
39
|
+
"src/commands.ts",
|
|
40
|
+
"src/constants.ts",
|
|
41
|
+
"src/types.ts",
|
|
42
|
+
"src/core/*.ts",
|
|
43
|
+
"src/utils/*.ts",
|
|
44
|
+
"dist",
|
|
45
|
+
"typings"
|
|
46
|
+
]
|
|
32
47
|
}
|
package/src/cli.ts
CHANGED
|
@@ -17,7 +17,9 @@ import {
|
|
|
17
17
|
|
|
18
18
|
import { installAgent, updateAgent } from "./core/package-installer.js";
|
|
19
19
|
import { packAgent, packCurrentAgent } from "./core/packager.js";
|
|
20
|
-
import {
|
|
20
|
+
import { listClawstoreAgents } from "./core/workspace.js";
|
|
21
|
+
import { diagnose, formatDiagnosticReport } from "./core/diagnostics.js";
|
|
22
|
+
import { ensureAgentRegistered } from "./core/agent-register.js";
|
|
21
23
|
import { StoreClient } from "./core/store-client.js";
|
|
22
24
|
import { DEFAULT_API_BASE_URL, DEFAULT_AGENT, STORE_DOWNLOAD_BASE_URL } from "./constants.js";
|
|
23
25
|
import {
|
|
@@ -179,6 +181,15 @@ export function registerClawstoreCli(api: OpenClawPluginApi): void {
|
|
|
179
181
|
}
|
|
180
182
|
}
|
|
181
183
|
|
|
184
|
+
if (agent !== DEFAULT_AGENT) {
|
|
185
|
+
const ok = await ensureAgentRegistered(agent, console.log);
|
|
186
|
+
if (!ok) {
|
|
187
|
+
console.log(`\n Cannot proceed: agent "${agent}" could not be registered.`);
|
|
188
|
+
console.log(` Register it manually first: openclaw agents add ${agent} --workspace <path>\n`);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
182
193
|
await installAgent({
|
|
183
194
|
packagePath,
|
|
184
195
|
force: opts.force === true,
|
package/src/commands.ts
CHANGED
|
@@ -9,7 +9,8 @@ import {
|
|
|
9
9
|
disableAgent,
|
|
10
10
|
} from "./core/agent-manager.js";
|
|
11
11
|
import { installAgent } from "./core/package-installer.js";
|
|
12
|
-
import { diagnose } from "./core/
|
|
12
|
+
import { diagnose } from "./core/diagnostics.js";
|
|
13
|
+
import { ensureAgentRegistered } from "./core/agent-register.js";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Register the `/clawstore` slash command.
|
|
@@ -102,6 +103,13 @@ export function registerClawstoreCommand(api: OpenClawPluginApi): void {
|
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
if (agent !== DEFAULT_AGENT) {
|
|
107
|
+
const ok = await ensureAgentRegistered(agent, (msg) => lines.push(msg));
|
|
108
|
+
if (!ok) {
|
|
109
|
+
return { text: `Agent "${agent}" is not registered and auto-registration failed.\nRun: \`openclaw agents add ${agent} --workspace <path>\`` };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
105
113
|
const result = await installAgent({
|
|
106
114
|
packagePath,
|
|
107
115
|
agent,
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { readFile, mkdir } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { OPENCLAW_HOME, DEFAULT_AGENT } from "../constants.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* If the agent is missing from openclaw.json, run `openclaw agents add`.
|
|
8
|
+
* Lives in a dedicated module (child_process) away from workspace resolution.
|
|
9
|
+
*/
|
|
10
|
+
export async function ensureAgentRegistered(
|
|
11
|
+
agent: string,
|
|
12
|
+
log: (msg: string) => void = console.log,
|
|
13
|
+
): Promise<boolean> {
|
|
14
|
+
if (agent === DEFAULT_AGENT) return true;
|
|
15
|
+
|
|
16
|
+
const configFile = join(OPENCLAW_HOME, "openclaw.json");
|
|
17
|
+
try {
|
|
18
|
+
const raw = await readFile(configFile, "utf-8");
|
|
19
|
+
const config = JSON.parse(raw);
|
|
20
|
+
|
|
21
|
+
const inMap =
|
|
22
|
+
config?.agents?.[agent] !== undefined ||
|
|
23
|
+
config?.agents?.entries?.[agent] !== undefined;
|
|
24
|
+
const inList = Array.isArray(config?.agents?.list) &&
|
|
25
|
+
config.agents.list.some((e: { id?: string }) => e?.id === agent);
|
|
26
|
+
|
|
27
|
+
if (inMap || inList) return true;
|
|
28
|
+
} catch {
|
|
29
|
+
// config missing or unreadable — agent is definitely not registered
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const workspace = join(OPENCLAW_HOME, `workspace-${agent}`);
|
|
33
|
+
try {
|
|
34
|
+
await mkdir(workspace, { recursive: true });
|
|
35
|
+
} catch { /* directory may already exist */ }
|
|
36
|
+
|
|
37
|
+
log(`\n OpenClaw agent "${agent}" is not registered. Auto-registering ...`);
|
|
38
|
+
|
|
39
|
+
const cmd = `openclaw agents add ${agent} --workspace "${workspace}"`;
|
|
40
|
+
return new Promise<boolean>((resolve) => {
|
|
41
|
+
exec(
|
|
42
|
+
cmd,
|
|
43
|
+
{ timeout: 15_000 },
|
|
44
|
+
(err, stdout, stderr) => {
|
|
45
|
+
if (err) {
|
|
46
|
+
log(` Auto-register failed: ${err.message}`);
|
|
47
|
+
if (stderr) log(` ${stderr.trim()}`);
|
|
48
|
+
log(` Please run manually: ${cmd}\n`);
|
|
49
|
+
resolve(false);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
log(` Registered agent "${agent}" → ${workspace}`);
|
|
53
|
+
resolve(true);
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clawstore API GET /health — used only by clawstore doctor.
|
|
3
|
+
* Standalone module (no process/env reads) for OpenClaw plugin static analysis.
|
|
4
|
+
*/
|
|
5
|
+
export async function checkClawstoreApiReachable(apiBaseUrl: string): Promise<{
|
|
6
|
+
ok: boolean;
|
|
7
|
+
detail: string;
|
|
8
|
+
}> {
|
|
9
|
+
const base = apiBaseUrl.replace(/\/+$/, "");
|
|
10
|
+
try {
|
|
11
|
+
const res = await fetch(`${base}/health`, { signal: AbortSignal.timeout(5000) });
|
|
12
|
+
if (res.ok) return { ok: true, detail: "reachable" };
|
|
13
|
+
return { ok: false, detail: `HTTP ${res.status}` };
|
|
14
|
+
} catch (err) {
|
|
15
|
+
return { ok: false, detail: `unreachable (${(err as Error).message})` };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { readFile, access, readdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
OPENCLAW_HOME,
|
|
5
|
+
CLAWSTORE_DIR,
|
|
6
|
+
DEFAULT_AGENT,
|
|
7
|
+
REQUIRED_FILES,
|
|
8
|
+
OPTIONAL_FILES,
|
|
9
|
+
agentRegistryFile,
|
|
10
|
+
agentBackupDir,
|
|
11
|
+
} from "../constants.js";
|
|
12
|
+
import type { DiagnosticCheck, DiagnosticReport } from "../types.js";
|
|
13
|
+
import { resolveWorkspace } from "./workspace.js";
|
|
14
|
+
import { checkClawstoreApiReachable } from "./api-health.js";
|
|
15
|
+
|
|
16
|
+
async function pathExists(p: string): Promise<boolean> {
|
|
17
|
+
try {
|
|
18
|
+
await access(p);
|
|
19
|
+
return true;
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function dirItemCount(p: string): Promise<number> {
|
|
26
|
+
try {
|
|
27
|
+
const entries = await readdir(p);
|
|
28
|
+
return entries.length;
|
|
29
|
+
} catch {
|
|
30
|
+
return -1;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Run a full diagnostic for a specific OpenClaw agent.
|
|
36
|
+
*/
|
|
37
|
+
export async function diagnose(apiBaseUrl?: string, agent?: string): Promise<DiagnosticReport> {
|
|
38
|
+
const target = agent ?? DEFAULT_AGENT;
|
|
39
|
+
const checks: DiagnosticCheck[] = [];
|
|
40
|
+
let allOk = true;
|
|
41
|
+
const workspace = await resolveWorkspace(target);
|
|
42
|
+
const registryFile = agentRegistryFile(target);
|
|
43
|
+
const backupDir = agentBackupDir(target);
|
|
44
|
+
|
|
45
|
+
if (target !== DEFAULT_AGENT) {
|
|
46
|
+
checks.push({
|
|
47
|
+
label: "Target agent",
|
|
48
|
+
status: "ok",
|
|
49
|
+
detail: target,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const homeOk = await pathExists(OPENCLAW_HOME);
|
|
54
|
+
checks.push({
|
|
55
|
+
label: "OpenClaw home",
|
|
56
|
+
path: OPENCLAW_HOME,
|
|
57
|
+
status: homeOk ? "ok" : "fail",
|
|
58
|
+
detail: homeOk ? undefined : "NOT FOUND",
|
|
59
|
+
});
|
|
60
|
+
if (!homeOk) allOk = false;
|
|
61
|
+
|
|
62
|
+
const wsOk = await pathExists(workspace);
|
|
63
|
+
checks.push({
|
|
64
|
+
label: "Workspace",
|
|
65
|
+
path: workspace,
|
|
66
|
+
status: wsOk ? "ok" : "fail",
|
|
67
|
+
detail: wsOk ? undefined : "NOT FOUND",
|
|
68
|
+
});
|
|
69
|
+
if (!wsOk) allOk = false;
|
|
70
|
+
|
|
71
|
+
for (const f of [...REQUIRED_FILES, ...OPTIONAL_FILES]) {
|
|
72
|
+
const fp = join(workspace, f);
|
|
73
|
+
const found = await pathExists(fp);
|
|
74
|
+
checks.push({
|
|
75
|
+
label: `Workspace file: ${f}`,
|
|
76
|
+
path: fp,
|
|
77
|
+
status: found ? "ok" : "warn",
|
|
78
|
+
detail: found ? "found" : "missing",
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const regOk = await pathExists(registryFile);
|
|
83
|
+
if (regOk) {
|
|
84
|
+
try {
|
|
85
|
+
const raw = await readFile(registryFile, "utf-8");
|
|
86
|
+
const reg = JSON.parse(raw);
|
|
87
|
+
const count = Object.keys(reg.agents ?? {}).length;
|
|
88
|
+
checks.push({
|
|
89
|
+
label: "Agent registry",
|
|
90
|
+
path: registryFile,
|
|
91
|
+
status: "ok",
|
|
92
|
+
detail: `${count} agent(s) registered`,
|
|
93
|
+
});
|
|
94
|
+
} catch {
|
|
95
|
+
checks.push({
|
|
96
|
+
label: "Agent registry",
|
|
97
|
+
path: registryFile,
|
|
98
|
+
status: "warn",
|
|
99
|
+
detail: "exists but cannot be parsed",
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
checks.push({
|
|
104
|
+
label: "Agent registry",
|
|
105
|
+
path: registryFile,
|
|
106
|
+
status: "ok",
|
|
107
|
+
detail: "not created yet (OK for first run)",
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const skillsDir = join(workspace, "skills");
|
|
112
|
+
const skillCount = await dirItemCount(skillsDir);
|
|
113
|
+
checks.push({
|
|
114
|
+
label: "Skills directory",
|
|
115
|
+
path: skillsDir,
|
|
116
|
+
status: skillCount >= 0 ? "ok" : "warn",
|
|
117
|
+
detail: skillCount >= 0 ? `${skillCount} skill(s)` : "NOT FOUND",
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const backupCount = await dirItemCount(backupDir);
|
|
121
|
+
checks.push({
|
|
122
|
+
label: "Backup directory",
|
|
123
|
+
path: backupDir,
|
|
124
|
+
status: "ok",
|
|
125
|
+
detail: backupCount >= 0 ? `${backupCount} backup(s)` : "will be created on first install",
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const cacheCount = await dirItemCount(join(CLAWSTORE_DIR, "cache"));
|
|
129
|
+
checks.push({
|
|
130
|
+
label: "Cache directory",
|
|
131
|
+
path: join(CLAWSTORE_DIR, "cache"),
|
|
132
|
+
status: "ok",
|
|
133
|
+
detail: cacheCount >= 0 ? `${cacheCount} item(s)` : "will be created on first install",
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (apiBaseUrl) {
|
|
137
|
+
const { ok, detail } = await checkClawstoreApiReachable(apiBaseUrl);
|
|
138
|
+
checks.push({
|
|
139
|
+
label: "Clawstore API",
|
|
140
|
+
status: ok ? "ok" : "warn",
|
|
141
|
+
detail,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return { checks, allOk };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function formatDiagnosticReport(report: DiagnosticReport): string {
|
|
149
|
+
const lines: string[] = ["\n Clawstore Doctor\n"];
|
|
150
|
+
for (const c of report.checks) {
|
|
151
|
+
const icon = c.status === "ok" ? "OK" : c.status === "warn" ? "WARN" : "FAIL";
|
|
152
|
+
const pathPart = c.path ? ` ${c.path}` : "";
|
|
153
|
+
const detailPart = c.detail ? ` (${c.detail})` : "";
|
|
154
|
+
lines.push(` ${c.label}:${pathPart} ... ${icon}${detailPart}`);
|
|
155
|
+
}
|
|
156
|
+
lines.push("");
|
|
157
|
+
lines.push(
|
|
158
|
+
report.allOk
|
|
159
|
+
? " All checks passed.\n"
|
|
160
|
+
: " Some issues found. Run `openclaw setup` to initialize missing directories.\n"
|
|
161
|
+
);
|
|
162
|
+
return lines.join("\n");
|
|
163
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads OPENCLAW_PROFILE for main-agent workspace path resolution.
|
|
3
|
+
* Isolated from modules that perform HTTP or shell execution (OpenClaw plugin scanner).
|
|
4
|
+
*/
|
|
5
|
+
export function readOpenclawProfileLabel(): string {
|
|
6
|
+
const proc = (globalThis as { process?: { env?: NodeJS.ProcessEnv } }).process;
|
|
7
|
+
const raw = proc?.env?.OPENCLAW_PROFILE;
|
|
8
|
+
if (typeof raw === "string" && raw.trim() !== "") return raw.trim();
|
|
9
|
+
return "default";
|
|
10
|
+
}
|