zigrix 0.1.1 → 0.2.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/dist/config/defaults.d.ts +8 -0
- package/dist/config/defaults.js +8 -1
- package/dist/config/schema.d.ts +112 -0
- package/dist/config/schema.js +186 -12
- package/dist/dashboard/.next/BUILD_ID +1 -1
- package/dist/dashboard/.next/app-build-manifest.json +10 -10
- package/dist/dashboard/.next/app-path-routes-manifest.json +2 -2
- package/dist/dashboard/.next/build-manifest.json +2 -2
- package/dist/dashboard/.next/prerender-manifest.json +6 -6
- package/dist/dashboard/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/_not-found.html +1 -1
- package/dist/dashboard/.next/server/app/_not-found.rsc +1 -1
- package/dist/dashboard/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/api/auth/session/route_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/api/auth/setup/route_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/api/overview/route_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/api/stream/route_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/api/tasks/[taskId]/conversation/route_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/login.html +1 -1
- package/dist/dashboard/.next/server/app/login.rsc +1 -1
- package/dist/dashboard/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/dist/dashboard/.next/server/app/setup.html +1 -1
- package/dist/dashboard/.next/server/app/setup.rsc +1 -1
- package/dist/dashboard/.next/server/app-paths-manifest.json +2 -2
- package/dist/dashboard/.next/server/functions-config-manifest.json +2 -2
- package/dist/dashboard/.next/server/pages/404.html +1 -1
- package/dist/dashboard/.next/server/pages/500.html +1 -1
- package/dist/doctor.d.ts +3 -0
- package/dist/doctor.js +233 -60
- package/dist/index.js +262 -32
- package/dist/migrate/import-orchestration.d.ts +31 -0
- package/dist/migrate/import-orchestration.js +638 -0
- package/dist/onboard.js +130 -35
- package/dist/orchestration/evidence.d.ts +7 -0
- package/dist/orchestration/evidence.js +79 -4
- package/dist/orchestration/pipeline.d.ts +1 -0
- package/dist/orchestration/pipeline.js +26 -1
- package/dist/state/tasks.d.ts +37 -2
- package/dist/state/tasks.js +242 -10
- package/dist/state/verify.js +89 -11
- package/package.json +1 -1
- /package/dist/dashboard/.next/static/{iKGx5hWe1zbwJZWchF9kg → EZjkAnODdTglaMXuBw76E}/_buildManifest.js +0 -0
- /package/dist/dashboard/.next/static/{iKGx5hWe1zbwJZWchF9kg → EZjkAnODdTglaMXuBw76E}/_ssgManifest.js +0 -0
package/dist/doctor.js
CHANGED
|
@@ -1,34 +1,128 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { STANDARD_AGENT_ROLES } from './agents/roles.js';
|
|
4
|
+
import { resolveCanonicalConfigHome, resolveCanonicalConfigPath } from './config/defaults.js';
|
|
5
|
+
import { STANDARD_SCALES } from './config/schema.js';
|
|
6
|
+
const BASELINE_REQUIRED_ROLES = ['orchestrator', 'qa'];
|
|
7
|
+
function unique(items) {
|
|
8
|
+
return [...new Set(items)];
|
|
9
|
+
}
|
|
3
10
|
function existsWritable(targetPath) {
|
|
4
11
|
try {
|
|
5
|
-
return fs.existsSync(targetPath)
|
|
12
|
+
return fs.existsSync(targetPath)
|
|
13
|
+
? fs.accessSync(targetPath, fs.constants.W_OK) === undefined
|
|
14
|
+
: fs.accessSync(path.dirname(targetPath), fs.constants.W_OK) === undefined;
|
|
6
15
|
}
|
|
7
16
|
catch {
|
|
8
17
|
return false;
|
|
9
18
|
}
|
|
10
19
|
}
|
|
11
20
|
function detectOpenClawHome() {
|
|
12
|
-
return process.env.OPENCLAW_HOME
|
|
21
|
+
return process.env.OPENCLAW_HOME
|
|
22
|
+
? path.resolve(process.env.OPENCLAW_HOME)
|
|
23
|
+
: path.join(process.env.HOME ?? '~', '.openclaw');
|
|
13
24
|
}
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
function resolveEligibleAgentsByRole(config) {
|
|
26
|
+
const participants = new Set(config.agents.orchestration.participants);
|
|
27
|
+
const excluded = new Set(config.agents.orchestration.excluded);
|
|
28
|
+
const participantMode = participants.size > 0;
|
|
29
|
+
const byRole = Object.fromEntries(STANDARD_AGENT_ROLES.map((role) => [role, []]));
|
|
30
|
+
for (const [agentId, agent] of Object.entries(config.agents.registry).sort(([a], [b]) => a.localeCompare(b))) {
|
|
31
|
+
if (!agent.enabled)
|
|
32
|
+
continue;
|
|
33
|
+
if (excluded.has(agentId))
|
|
34
|
+
continue;
|
|
35
|
+
if (participantMode && !participants.has(agentId))
|
|
36
|
+
continue;
|
|
37
|
+
byRole[agent.role] = [...(byRole[agent.role] ?? []), agentId];
|
|
38
|
+
}
|
|
39
|
+
return byRole;
|
|
40
|
+
}
|
|
41
|
+
function collectPathConflicts(paths) {
|
|
42
|
+
const issues = [];
|
|
43
|
+
const entries = [
|
|
44
|
+
['tasksDir', paths.tasksDir],
|
|
45
|
+
['evidenceDir', paths.evidenceDir],
|
|
46
|
+
['promptsDir', paths.promptsDir],
|
|
47
|
+
['runsDir', paths.runsDir],
|
|
48
|
+
['rulesDir', paths.rulesDir],
|
|
49
|
+
['eventsFile', paths.eventsFile],
|
|
50
|
+
['indexFile', paths.indexFile],
|
|
51
|
+
];
|
|
52
|
+
const seen = new Map();
|
|
53
|
+
for (const [name, rawPath] of entries) {
|
|
54
|
+
const normalized = path.resolve(rawPath);
|
|
55
|
+
const previous = seen.get(normalized);
|
|
56
|
+
if (previous) {
|
|
57
|
+
issues.push(`${name} collides with ${previous}`);
|
|
58
|
+
continue;
|
|
27
59
|
}
|
|
28
|
-
|
|
29
|
-
|
|
60
|
+
seen.set(normalized, name);
|
|
61
|
+
}
|
|
62
|
+
return issues;
|
|
63
|
+
}
|
|
64
|
+
function collectConfigConsistency(config, paths) {
|
|
65
|
+
const issues = collectPathConflicts(paths);
|
|
66
|
+
const warnings = [];
|
|
67
|
+
const missingStandardScales = STANDARD_SCALES.filter((scale) => !(scale in config.rules.scales));
|
|
68
|
+
const eligibleByRole = resolveEligibleAgentsByRole(config);
|
|
69
|
+
const participantMode = config.agents.orchestration.participants.length > 0;
|
|
70
|
+
const orchestratorId = config.agents.orchestration.orchestratorId;
|
|
71
|
+
const orchestratorAgent = config.agents.registry[orchestratorId];
|
|
72
|
+
const orchestratorPresent = Boolean(orchestratorAgent);
|
|
73
|
+
const orchestratorEnabled = orchestratorAgent?.enabled !== false;
|
|
74
|
+
const inParticipants = config.agents.orchestration.participants.includes(orchestratorId);
|
|
75
|
+
const excluded = config.agents.orchestration.excluded.includes(orchestratorId);
|
|
76
|
+
const orchestratorEligible = orchestratorPresent && orchestratorEnabled && !excluded && (!participantMode || inParticipants);
|
|
77
|
+
for (const scale of missingStandardScales) {
|
|
78
|
+
issues.push(`rules.scales is missing '${scale}'`);
|
|
79
|
+
}
|
|
80
|
+
if (orchestratorPresent && !orchestratorEnabled) {
|
|
81
|
+
issues.push(`orchestratorId '${orchestratorId}' points to a disabled agent`);
|
|
82
|
+
}
|
|
83
|
+
if (orchestratorPresent && excluded) {
|
|
84
|
+
issues.push(`orchestratorId '${orchestratorId}' is excluded from orchestration`);
|
|
85
|
+
}
|
|
86
|
+
if (participantMode && orchestratorPresent && !inParticipants) {
|
|
87
|
+
issues.push(`orchestratorId '${orchestratorId}' is not included in participants while participant mode is active`);
|
|
88
|
+
}
|
|
89
|
+
const scaleCoverage = {};
|
|
90
|
+
for (const scale of STANDARD_SCALES) {
|
|
91
|
+
const policy = config.rules.scales[scale];
|
|
92
|
+
if (!policy)
|
|
93
|
+
continue;
|
|
94
|
+
const requiredRoles = unique([...BASELINE_REQUIRED_ROLES, ...policy.requiredRoles]);
|
|
95
|
+
const missingEligibleRoles = requiredRoles.filter((role) => (eligibleByRole[role] ?? []).length === 0);
|
|
96
|
+
scaleCoverage[scale] = {
|
|
97
|
+
requiredRoles,
|
|
98
|
+
eligibleByRole: Object.fromEntries(requiredRoles.map((role) => [role, [...(eligibleByRole[role] ?? [])]])),
|
|
99
|
+
missingEligibleRoles,
|
|
100
|
+
};
|
|
101
|
+
if (missingEligibleRoles.length > 0) {
|
|
102
|
+
warnings.push(`scale '${scale}' has no eligible agents for required roles: ${missingEligibleRoles.join(', ')}`);
|
|
30
103
|
}
|
|
31
|
-
}
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
ok: issues.length === 0,
|
|
107
|
+
issues,
|
|
108
|
+
warnings,
|
|
109
|
+
pathConflicts: collectPathConflicts(paths),
|
|
110
|
+
missingStandardScales,
|
|
111
|
+
orchestrator: {
|
|
112
|
+
id: orchestratorId,
|
|
113
|
+
presentInRegistry: orchestratorPresent,
|
|
114
|
+
enabled: orchestratorEnabled,
|
|
115
|
+
participantMode,
|
|
116
|
+
inParticipants,
|
|
117
|
+
excluded,
|
|
118
|
+
eligible: orchestratorEligible,
|
|
119
|
+
},
|
|
120
|
+
scaleCoverage,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function buildBasePayload(params) {
|
|
124
|
+
const openclawSkillsDir = path.join(params.openclawHome, 'skills');
|
|
125
|
+
const nonLoginShellPaths = ['/usr/local/bin', '/usr/bin', '/bin', '/usr/sbin', '/sbin'];
|
|
32
126
|
const zigrixNonLoginLocation = nonLoginShellPaths.find((dir) => {
|
|
33
127
|
try {
|
|
34
128
|
return fs.existsSync(path.join(dir, 'zigrix'));
|
|
@@ -37,14 +131,6 @@ export function gatherDoctor(loaded, paths) {
|
|
|
37
131
|
return false;
|
|
38
132
|
}
|
|
39
133
|
}) ?? null;
|
|
40
|
-
const openclawInNonLoginPath = nonLoginShellPaths.some((dir) => {
|
|
41
|
-
try {
|
|
42
|
-
return fs.existsSync(path.join(dir, 'openclaw'));
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
134
|
const openclawNonLoginLocation = nonLoginShellPaths.find((dir) => {
|
|
49
135
|
try {
|
|
50
136
|
return fs.existsSync(path.join(dir, 'openclaw'));
|
|
@@ -53,56 +139,100 @@ export function gatherDoctor(loaded, paths) {
|
|
|
53
139
|
return false;
|
|
54
140
|
}
|
|
55
141
|
}) ?? null;
|
|
56
|
-
|
|
142
|
+
return {
|
|
57
143
|
node: {
|
|
58
144
|
executable: process.execPath,
|
|
59
145
|
version: process.versions.node,
|
|
60
146
|
ok: Number(process.versions.node.split('.')[0]) >= 22,
|
|
61
147
|
},
|
|
62
148
|
paths: {
|
|
63
|
-
baseDir:
|
|
64
|
-
configPath:
|
|
65
|
-
tasksDir:
|
|
66
|
-
promptsDir:
|
|
67
|
-
evidenceDir:
|
|
68
|
-
runsDir:
|
|
69
|
-
rulesDir:
|
|
149
|
+
baseDir: params.baseDir,
|
|
150
|
+
configPath: params.configPath,
|
|
151
|
+
tasksDir: null,
|
|
152
|
+
promptsDir: null,
|
|
153
|
+
evidenceDir: null,
|
|
154
|
+
runsDir: null,
|
|
155
|
+
rulesDir: null,
|
|
70
156
|
},
|
|
71
157
|
files: {
|
|
72
|
-
configExists:
|
|
73
|
-
baseDirExists: fs.existsSync(
|
|
74
|
-
indexExists:
|
|
75
|
-
eventsExists:
|
|
158
|
+
configExists: fs.existsSync(params.configPath),
|
|
159
|
+
baseDirExists: fs.existsSync(params.baseDir),
|
|
160
|
+
indexExists: false,
|
|
161
|
+
eventsExists: false,
|
|
76
162
|
},
|
|
77
163
|
rules: {
|
|
78
|
-
dir:
|
|
79
|
-
exists:
|
|
80
|
-
files:
|
|
81
|
-
count:
|
|
164
|
+
dir: null,
|
|
165
|
+
exists: false,
|
|
166
|
+
files: [],
|
|
167
|
+
count: 0,
|
|
82
168
|
},
|
|
83
169
|
writeAccess: {
|
|
84
|
-
baseDir: existsWritable(
|
|
85
|
-
configPath:
|
|
170
|
+
baseDir: existsWritable(params.baseDir),
|
|
171
|
+
configPath: existsWritable(params.configPath),
|
|
86
172
|
},
|
|
87
173
|
binaries: {
|
|
88
174
|
node: process.execPath,
|
|
89
175
|
npm: process.env.npm_execpath ?? null,
|
|
90
|
-
openclaw:
|
|
176
|
+
openclaw: null,
|
|
91
177
|
},
|
|
92
178
|
openclaw: {
|
|
93
|
-
home: openclawHome,
|
|
94
|
-
exists: fs.existsSync(openclawHome),
|
|
179
|
+
home: params.openclawHome,
|
|
180
|
+
exists: fs.existsSync(params.openclawHome),
|
|
95
181
|
skillsDir: openclawSkillsDir,
|
|
96
182
|
skillsDirExists: fs.existsSync(openclawSkillsDir),
|
|
97
183
|
},
|
|
98
184
|
pathReach: {
|
|
99
185
|
nonLoginShellPaths,
|
|
100
|
-
zigrixInNonLoginPath,
|
|
186
|
+
zigrixInNonLoginPath: zigrixNonLoginLocation !== null,
|
|
101
187
|
zigrixNonLoginLocation,
|
|
102
|
-
openclawInNonLoginPath,
|
|
188
|
+
openclawInNonLoginPath: openclawNonLoginLocation !== null,
|
|
103
189
|
openclawNonLoginLocation,
|
|
104
190
|
},
|
|
105
191
|
};
|
|
192
|
+
}
|
|
193
|
+
export function gatherDoctor(loaded, paths) {
|
|
194
|
+
const openclawHome = detectOpenClawHome();
|
|
195
|
+
const rulesDir = paths.rulesDir;
|
|
196
|
+
const ruleFiles = fs.existsSync(rulesDir)
|
|
197
|
+
? fs
|
|
198
|
+
.readdirSync(rulesDir)
|
|
199
|
+
.filter((f) => f.endsWith('.md'))
|
|
200
|
+
.sort()
|
|
201
|
+
: [];
|
|
202
|
+
const basePayload = buildBasePayload({
|
|
203
|
+
configPath: loaded.configPath,
|
|
204
|
+
baseDir: loaded.baseDir,
|
|
205
|
+
openclawHome,
|
|
206
|
+
});
|
|
207
|
+
const warnings = [];
|
|
208
|
+
const configConsistency = collectConfigConsistency(loaded.config, paths);
|
|
209
|
+
const payload = {
|
|
210
|
+
...basePayload,
|
|
211
|
+
paths: {
|
|
212
|
+
...basePayload.paths,
|
|
213
|
+
tasksDir: paths.tasksDir,
|
|
214
|
+
promptsDir: paths.promptsDir,
|
|
215
|
+
evidenceDir: paths.evidenceDir,
|
|
216
|
+
runsDir: paths.runsDir,
|
|
217
|
+
rulesDir: paths.rulesDir,
|
|
218
|
+
},
|
|
219
|
+
files: {
|
|
220
|
+
...basePayload.files,
|
|
221
|
+
indexExists: fs.existsSync(paths.indexFile),
|
|
222
|
+
eventsExists: fs.existsSync(paths.eventsFile),
|
|
223
|
+
},
|
|
224
|
+
rules: {
|
|
225
|
+
dir: rulesDir,
|
|
226
|
+
exists: fs.existsSync(rulesDir),
|
|
227
|
+
files: ruleFiles,
|
|
228
|
+
count: ruleFiles.length,
|
|
229
|
+
},
|
|
230
|
+
binaries: {
|
|
231
|
+
...basePayload.binaries,
|
|
232
|
+
openclaw: loaded.config.openclaw?.binPath ?? null,
|
|
233
|
+
},
|
|
234
|
+
configConsistency,
|
|
235
|
+
};
|
|
106
236
|
if (!payload.node.ok)
|
|
107
237
|
warnings.push('Node.js 22+ is required.');
|
|
108
238
|
if (!payload.files.configExists)
|
|
@@ -113,8 +243,9 @@ export function gatherDoctor(loaded, paths) {
|
|
|
113
243
|
warnings.push('Base directory is not writable.');
|
|
114
244
|
if (!payload.openclaw.exists)
|
|
115
245
|
warnings.push(`${openclawHome} not found. OpenClaw integration remains optional.`);
|
|
116
|
-
if (payload.rules.count === 0)
|
|
246
|
+
if (payload.rules.count === 0) {
|
|
117
247
|
warnings.push('No rule files found in rules directory. Seed bundled defaults with `zigrix onboard` or `zigrix configure --section rules`.');
|
|
248
|
+
}
|
|
118
249
|
if (!payload.pathReach.zigrixInNonLoginPath) {
|
|
119
250
|
warnings.push('zigrix is not reachable from non-login shell PATH (checked: /usr/local/bin, /usr/bin). ' +
|
|
120
251
|
'Run `zigrix onboard` or manually: sudo ln -sfn $(which zigrix) /usr/local/bin/zigrix');
|
|
@@ -123,27 +254,69 @@ export function gatherDoctor(loaded, paths) {
|
|
|
123
254
|
warnings.push('openclaw is not reachable from non-login shell PATH (checked: /usr/local/bin, /usr/bin). ' +
|
|
124
255
|
'Run `zigrix onboard` or manually: sudo ln -sfn $(which openclaw) /usr/local/bin/openclaw');
|
|
125
256
|
}
|
|
257
|
+
if (typeof payload.binaries.openclaw === 'string' &&
|
|
258
|
+
payload.binaries.openclaw.length > 0 &&
|
|
259
|
+
!fs.existsSync(payload.binaries.openclaw)) {
|
|
260
|
+
warnings.push(`Configured openclaw.binPath does not exist: ${payload.binaries.openclaw}`);
|
|
261
|
+
}
|
|
262
|
+
warnings.push(...configConsistency.issues.map((issue) => `Config issue: ${issue}`));
|
|
263
|
+
warnings.push(...configConsistency.warnings.map((warning) => `Config warning: ${warning}`));
|
|
126
264
|
return {
|
|
127
265
|
...payload,
|
|
128
266
|
summary: {
|
|
129
|
-
ready: payload.node.ok &&
|
|
267
|
+
ready: payload.node.ok &&
|
|
268
|
+
payload.writeAccess.baseDir &&
|
|
269
|
+
payload.files.configExists &&
|
|
270
|
+
payload.configConsistency.ok,
|
|
130
271
|
warnings,
|
|
131
272
|
},
|
|
132
273
|
};
|
|
133
274
|
}
|
|
275
|
+
export function gatherDoctorFailure(error, options) {
|
|
276
|
+
const configPath = options?.configPath ?? resolveCanonicalConfigPath();
|
|
277
|
+
const baseDir = resolveCanonicalConfigHome();
|
|
278
|
+
const openclawHome = detectOpenClawHome();
|
|
279
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
280
|
+
const payload = buildBasePayload({ configPath, baseDir, openclawHome });
|
|
281
|
+
return {
|
|
282
|
+
...payload,
|
|
283
|
+
configConsistency: {
|
|
284
|
+
ok: false,
|
|
285
|
+
issues: [`Config load failed: ${message}`],
|
|
286
|
+
warnings: [],
|
|
287
|
+
pathConflicts: [],
|
|
288
|
+
missingStandardScales: [...STANDARD_SCALES],
|
|
289
|
+
orchestrator: {
|
|
290
|
+
id: 'unknown',
|
|
291
|
+
presentInRegistry: false,
|
|
292
|
+
enabled: false,
|
|
293
|
+
participantMode: false,
|
|
294
|
+
inParticipants: false,
|
|
295
|
+
excluded: false,
|
|
296
|
+
eligible: false,
|
|
297
|
+
},
|
|
298
|
+
scaleCoverage: {},
|
|
299
|
+
},
|
|
300
|
+
summary: {
|
|
301
|
+
ready: false,
|
|
302
|
+
warnings: [`Config load failed: ${message}`],
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
}
|
|
134
306
|
export function renderDoctorText(payload) {
|
|
135
307
|
const lines = [
|
|
136
308
|
'Zigrix Doctor',
|
|
137
|
-
`- Node: ${payload.node
|
|
138
|
-
`- Base dir: ${payload.paths
|
|
139
|
-
`- Config: ${payload.paths
|
|
140
|
-
`- Rules dir: ${payload.rules
|
|
141
|
-
`- OpenClaw home: ${payload.openclaw
|
|
142
|
-
`- Non-login PATH reach (zigrix): ${payload.pathReach
|
|
143
|
-
`- Non-login PATH reach (openclaw): ${payload.pathReach
|
|
144
|
-
`-
|
|
309
|
+
`- Node: ${payload.node?.version ?? 'unknown'} (${payload.node?.ok ? 'ok' : 'too old'})`,
|
|
310
|
+
`- Base dir: ${payload.paths?.baseDir ?? 'missing'}`,
|
|
311
|
+
`- Config: ${payload.paths?.configPath ?? 'missing'}`,
|
|
312
|
+
`- Rules dir: ${payload.rules?.dir ?? 'missing'} (${payload.rules?.count ?? 0} files)`,
|
|
313
|
+
`- OpenClaw home: ${payload.openclaw?.home ?? 'missing'} (${payload.openclaw?.exists ? 'present' : 'missing'})`,
|
|
314
|
+
`- Non-login PATH reach (zigrix): ${payload.pathReach?.zigrixInNonLoginPath ? `yes (${payload.pathReach?.zigrixNonLoginLocation})` : 'no'}`,
|
|
315
|
+
`- Non-login PATH reach (openclaw): ${payload.pathReach?.openclawInNonLoginPath ? `yes (${payload.pathReach?.openclawNonLoginLocation})` : 'no'}`,
|
|
316
|
+
`- Config consistency: ${payload.configConsistency?.ok ? 'ok' : 'issues found'}`,
|
|
317
|
+
`- Ready: ${payload.summary?.ready ? 'yes' : 'no'}`,
|
|
145
318
|
];
|
|
146
|
-
for (const warning of payload.summary
|
|
319
|
+
for (const warning of (payload.summary?.warnings ?? [])) {
|
|
147
320
|
lines.push(`- Warning: ${warning}`);
|
|
148
321
|
}
|
|
149
322
|
return lines.join('\n');
|