@deeeed/metamask-harness 0.92.21 → 0.92.23
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/CHANGELOG.md +43 -0
- package/dist/adapters/core/surface.js +2 -3
- package/dist/adapters/extension/surface.js +1 -2
- package/dist/adapters/mobile/devices.js +0 -1
- package/dist/adapters/mobile/overlay.js +3 -2
- package/dist/adapters/mobile/prepare-target.js +13 -0
- package/dist/adapters/mobile/surface.js +1 -2
- package/dist/adapters/mobile/ui.js +51 -4
- package/dist/commands/doctor.js +3 -423
- package/dist/commands/review.js +5 -66
- package/dist/doctor.js +0 -11
- package/dist/mm-harness-cli.js +50 -20
- package/dist/paths.js +0 -2
- package/dist/provider-doctor.js +3 -1
- package/dist/provider-helpers.js +143 -1
- package/dist/provider-lifecycle.js +43 -1
- package/dist/review/knowledge.js +0 -119
- package/docs/CONTRIBUTING.md +3 -2
- package/package.json +3 -3
- package/dist/commands/checklist.js +0 -242
- package/dist/commands/domain.js +0 -57
- package/dist/commands/execution-template.js +0 -28
- package/dist/commands/logs.js +0 -99
- package/dist/commands/prepare.js +0 -433
- package/dist/commands/recipe-quality.js +0 -103
- package/dist/commands/status-watch.js +0 -317
- package/dist/commands/status.js +0 -146
- package/dist/commands/task-init.js +0 -49
- package/dist/task-view.js +0 -332
package/dist/commands/doctor.js
CHANGED
|
@@ -1,308 +1,8 @@
|
|
|
1
|
-
import { execFileSync } from "node:child_process";
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import path from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { createDoctorReport, fixtureSummary, requiredDoctorCheckSummary } from "../doctor.js";
|
|
8
|
-
import { assertAdapter, runnerDir } from "../paths.js";
|
|
9
|
-
import { getAdapterSurface } from "../adapters/surface.js";
|
|
10
|
-
import { loadActionManifest, validateManifest } from "../manifest.js";
|
|
11
|
-
import { applyDeviceTargeting } from "./device-target.js";
|
|
12
|
-
import { farmslotReadyIndicator } from "./farmslot-ready.js";
|
|
13
|
-
import { updateStatus } from "./update.js";
|
|
14
|
-
import { ensureRuntimeContext } from "../runtime-context.js";
|
|
15
|
-
function applyDoctorRuntimePorts(options) {
|
|
16
|
-
const cdpPort = optionString(options, "cdpPort");
|
|
17
|
-
if (cdpPort) {
|
|
18
|
-
process.env.CDP_PORT = cdpPort;
|
|
19
|
-
process.env.RECIPE_CDP_PORT = cdpPort;
|
|
20
|
-
}
|
|
21
|
-
const watcherPort = optionString(options, "watcherPort") ?? optionString(options, "port");
|
|
22
|
-
if (watcherPort) {
|
|
23
|
-
process.env.WATCHER_PORT = watcherPort;
|
|
24
|
-
process.env.METRO_PORT = watcherPort;
|
|
25
|
-
process.env.RECIPE_WATCHER_PORT = watcherPort;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
async function handleDoctor({ options }) {
|
|
29
|
-
applyRuntimeDirOption(options);
|
|
30
|
-
const target = targetPath(options);
|
|
31
|
-
const json = optionFlag(options, "json");
|
|
32
|
-
const printReady = optionFlag(options, "printReady");
|
|
33
|
-
const expectLive = optionFlag(options, "expectLive") || printReady;
|
|
34
|
-
const allDevices = optionFlag(options, "allDevices");
|
|
35
|
-
const platformOption = optionString(options, "platform");
|
|
36
|
-
const adapter = contextAdapter(target);
|
|
37
|
-
if (!adapter) {
|
|
38
|
-
return usageOut(json, "doctor", `could not detect the MetaMask repo type for ${target}`, adapterDetectNext());
|
|
39
|
-
}
|
|
40
|
-
assertAdapter(adapter);
|
|
41
|
-
const { readiness } = getAdapterSurface(adapter);
|
|
42
|
-
if (!fs.existsSync(target)) {
|
|
43
|
-
return usageOut(
|
|
44
|
-
json,
|
|
45
|
-
"doctor",
|
|
46
|
-
`target does not exist: ${target}`,
|
|
47
|
-
"pass --target <metamask-checkout> pointing to an existing checkout"
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
if (printReady && json) {
|
|
51
|
-
return usageOut(
|
|
52
|
-
json,
|
|
53
|
-
"doctor",
|
|
54
|
-
"--print-ready owns stdout for Farmslot health_check; drop --json (use --expect-live --json for the doctor envelope)",
|
|
55
|
-
"mm-harness doctor --print-ready --adapter <adapter> --target <path>"
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
const actionManifestPath = actionManifestPathOption(options, adapter);
|
|
59
|
-
const manifest = loadActionManifest(adapter, optionString(options, "actionManifest"));
|
|
60
|
-
const manifestValidation = await validateManifest(manifest);
|
|
61
|
-
if (optionFlag(options, "fix")) {
|
|
62
|
-
const surface = getAdapterSurface(adapter);
|
|
63
|
-
surface.resolveSlotPorts(target);
|
|
64
|
-
const dtResult = applyDeviceTargeting("doctor", adapter, options, { gate: false, rerun: "" });
|
|
65
|
-
if ("code" in dtResult) {
|
|
66
|
-
return usageOut(json, "doctor", dtResult.message, dtResult.userAction);
|
|
67
|
-
}
|
|
68
|
-
applyDoctorRuntimePorts(options);
|
|
69
|
-
const lock = acquireCheckoutLock(target, "doctor-fix");
|
|
70
|
-
if ("message" in lock) {
|
|
71
|
-
return checkoutBusyOut(json, "doctor", lock.message, lock.path);
|
|
72
|
-
}
|
|
73
|
-
let fixed;
|
|
74
|
-
let failed;
|
|
75
|
-
try {
|
|
76
|
-
({ fixed, failed } = await runDoctorFix(adapter, target, manifestValidation, json));
|
|
77
|
-
} finally {
|
|
78
|
-
lock.release();
|
|
79
|
-
}
|
|
80
|
-
const result2 = createDoctorReport(adapter, target, manifestValidation, actionManifestPath);
|
|
81
|
-
let runtime2;
|
|
82
|
-
let runtimeProbeError2;
|
|
83
|
-
try {
|
|
84
|
-
runtime2 = await surface.runtimeStatus(target);
|
|
85
|
-
} catch (error2) {
|
|
86
|
-
runtimeProbeError2 = error2 instanceof Error ? error2.message : String(error2);
|
|
87
|
-
}
|
|
88
|
-
const recorder2 = runtime2?.decision === "ready" ? await readiness.recorder?.() : void 0;
|
|
89
|
-
const ready2 = doctorReady(runtime2, recorder2);
|
|
90
|
-
const block = readiness.runtimeBlock?.(target, runtime2);
|
|
91
|
-
if (block) failed.push(block.id);
|
|
92
|
-
if (recorder2?.status === "fail") failed.push(recorder2.id);
|
|
93
|
-
const nextActions = doctorFixNextActions(failed, result2.fixture, adapter, target, block);
|
|
94
|
-
if (recorder2?.status === "fail" && recorder2.userAction) nextActions.push(recorder2.userAction);
|
|
95
|
-
if (failed.length === 0 && runtime2?.decision !== "ready" && runtime2?.nextAction) nextActions.push(runtime2.nextAction);
|
|
96
|
-
const status = result2.status === "pass" && failed.length === 0 ? "pass" : "fail";
|
|
97
|
-
const retryDoctor = `mm-harness doctor --fix --adapter ${adapter} --target ${shellQuote(target)} --json`;
|
|
98
|
-
const fixUserAction = failed.includes("wallet-fixture") ? `choose one wallet fixture source from nextActions, run it, then retry: ${retryDoctor}` : nextActions[0] ?? retryDoctor;
|
|
99
|
-
const error = status === "fail" ? {
|
|
100
|
-
code: "DOCTOR_FIX_INCOMPLETE",
|
|
101
|
-
message: "doctor --fix could not make every required check pass",
|
|
102
|
-
userAction: fixUserAction
|
|
103
|
-
} : void 0;
|
|
104
|
-
if (json) console.log(JSON.stringify({ ...result2, status, fixed, failed, ready: ready2, runtime: runtime2, ...recorder2 ? { recorder: recorder2 } : {}, ...runtimeProbeError2 ? { runtimeProbeError: runtimeProbeError2 } : {}, nextActions, ...error ? { error } : {} }, null, 2));
|
|
105
|
-
else {
|
|
106
|
-
console.log(`${status} ${adapter} ${result2.compatibilityMode} ready=${ready2} manifest=${actionManifestPath} fixed=[${fixed.join(",")}] failed=[${failed.join(",")}]`);
|
|
107
|
-
if (runtime2) {
|
|
108
|
-
console.log(`runtime: decision=${runtime2.decision}${runtime2.reasonCode ? ` (${runtime2.reasonCode})` : ""}`);
|
|
109
|
-
for (const reason of runtime2.reasons) console.log(` ${reason}`);
|
|
110
|
-
} else if (runtimeProbeError2) {
|
|
111
|
-
console.log(`runtime probe failed: ${runtimeProbeError2}`);
|
|
112
|
-
}
|
|
113
|
-
renderDoctorRecorder(recorder2);
|
|
114
|
-
for (const action of nextActions) console.error(` Next: ${action}`);
|
|
115
|
-
}
|
|
116
|
-
return status === "pass" ? 0 : 1;
|
|
117
|
-
}
|
|
118
|
-
const result = createDoctorReport(
|
|
119
|
-
adapter,
|
|
120
|
-
target,
|
|
121
|
-
manifestValidation,
|
|
122
|
-
actionManifestPath,
|
|
123
|
-
platformOption
|
|
124
|
-
);
|
|
125
|
-
const view = collectCheckoutView(target, adapter);
|
|
126
|
-
const missingRequiredTool = result.checks.find(
|
|
127
|
-
(check) => check.required && check.status === "fail" && check.id.startsWith("device-tool-") && check.userAction
|
|
128
|
-
);
|
|
129
|
-
if (missingRequiredTool) {
|
|
130
|
-
const error = {
|
|
131
|
-
code: "DOCTOR_CHECKS_FAILED",
|
|
132
|
-
message: missingRequiredTool.message,
|
|
133
|
-
userAction: missingRequiredTool.userAction
|
|
134
|
-
};
|
|
135
|
-
if (json) {
|
|
136
|
-
console.log(JSON.stringify({ ...result, ready: false, error }, null, 2));
|
|
137
|
-
} else {
|
|
138
|
-
console.error(`\u2717 doctor: ${error.message}
|
|
139
|
-
Next: ${error.userAction}`);
|
|
140
|
-
}
|
|
141
|
-
return EXIT.runtime;
|
|
142
|
-
}
|
|
143
|
-
let runtime;
|
|
144
|
-
let runtimeProbeError;
|
|
145
|
-
try {
|
|
146
|
-
const surface = getAdapterSurface(adapter);
|
|
147
|
-
surface.resolveSlotPorts(target);
|
|
148
|
-
const dtResult = applyDeviceTargeting("doctor", adapter, options, { gate: false, rerun: "" });
|
|
149
|
-
if ("code" in dtResult) {
|
|
150
|
-
if (json) console.log(JSON.stringify({ schemaVersion: 1, command: "doctor", adapter, target, error: { code: dtResult.code, message: dtResult.message, userAction: dtResult.userAction } }, null, 2));
|
|
151
|
-
else console.error(`\u2717 doctor: ${dtResult.message}
|
|
152
|
-
Next: ${dtResult.userAction}`);
|
|
153
|
-
return EXIT.usage;
|
|
154
|
-
}
|
|
155
|
-
applyDoctorRuntimePorts(options);
|
|
156
|
-
runtime = await surface.runtimeStatus(target);
|
|
157
|
-
} catch (error) {
|
|
158
|
-
runtimeProbeError = error instanceof Error ? error.message : String(error);
|
|
159
|
-
}
|
|
160
|
-
const runtimeCheck = doctorRuntimeCheck(runtime, runtimeProbeError, expectLive);
|
|
161
|
-
const recorder = runtime?.decision === "ready" ? await readiness.recorder?.() : void 0;
|
|
162
|
-
const checks = [
|
|
163
|
-
...result.checks,
|
|
164
|
-
runtimeCheck,
|
|
165
|
-
...await readiness.liveChecks?.(target) ?? [],
|
|
166
|
-
...recorder ? [recorder] : [],
|
|
167
|
-
...await platformDoctorChecks(adapter, target),
|
|
168
|
-
// Which library plugin code this process loaded: library, module and digest.
|
|
169
|
-
...adapterPluginChecks()
|
|
170
|
-
];
|
|
171
|
-
const requiredChecks = requiredDoctorCheckSummary(checks);
|
|
172
|
-
const doctorResult = {
|
|
173
|
-
...result,
|
|
174
|
-
status: requiredChecks.status,
|
|
175
|
-
checks,
|
|
176
|
-
requiredChecks
|
|
177
|
-
};
|
|
178
|
-
const orphanMetros = readiness.orphanDevServers?.(target, process.env.WATCHER_PORT) ?? [];
|
|
179
|
-
const capture = captureHealth(readiness.captureProviders);
|
|
180
|
-
const deviceSource = await readiness.devices?.();
|
|
181
|
-
const deviceView = deviceSource ? deviceSource.view(allDevices) : null;
|
|
182
|
-
const liveView = deviceSource && deviceView ? await deviceSource.live(target, deviceView) : null;
|
|
183
|
-
const devices = liveView?.devicesWithLive ?? deviceView?.devices ?? [];
|
|
184
|
-
const additionalReachableDevices = liveView?.additionalReachableDevices ?? [];
|
|
185
|
-
const discovery = deviceView?.deviceDiscoveryErrors.length ? { deviceDiscoveryErrors: deviceView.deviceDiscoveryErrors } : {};
|
|
186
|
-
if (expectLive) {
|
|
187
|
-
return emitExpectLive(
|
|
188
|
-
adapter,
|
|
189
|
-
target,
|
|
190
|
-
runtime,
|
|
191
|
-
recorder,
|
|
192
|
-
doctorResult,
|
|
193
|
-
orphanMetros,
|
|
194
|
-
capture,
|
|
195
|
-
devices,
|
|
196
|
-
additionalReachableDevices,
|
|
197
|
-
json,
|
|
198
|
-
printReady,
|
|
199
|
-
discovery
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
const skills = skillsView(target);
|
|
203
|
-
const update = updateStatus();
|
|
204
|
-
const pinnedFlags = readiness.pinnedFlags?.(target) ?? null;
|
|
205
|
-
const featureFlags = pinnedFlags ? featureFlagReport(pinnedFlags.overrides, pinnedFlags) : featureFlagReport(liveView?.featureFlagOverrides ?? null);
|
|
206
|
-
const doctorUserAction = doctorResult.checks.find(
|
|
207
|
-
(check) => check.required && check.status === "fail" && check.userAction
|
|
208
|
-
)?.userAction ?? `mm-harness doctor --fix --adapter ${adapter} --target ${shellQuote(target)} --json`;
|
|
209
|
-
const doctorError = doctorResult.status === "fail" ? { code: "DOCTOR_CHECKS_FAILED", message: "one or more required doctor checks failed", userAction: doctorUserAction } : void 0;
|
|
210
|
-
const next = doctorError?.userAction ?? discovery.deviceDiscoveryErrors?.[0]?.userAction ?? (runtime?.decision === "ready" ? void 0 : runtime?.nextAction);
|
|
211
|
-
const ready = doctorReady(runtime, recorder);
|
|
212
|
-
if (json) console.log(JSON.stringify({ ...doctorResult, ready, runtime, ...recorder ? { recorder } : {}, view, orphanMetros, capture, skills, update, featureFlags, devices, additionalReachableDevices, ...discovery, ...next ? { next } : {}, ...doctorError ? { error: doctorError } : {} }, null, 2));
|
|
213
|
-
else {
|
|
214
|
-
const out = (style, text) => color(style, text, { stream: process.stdout });
|
|
215
|
-
const stateStyle = (value, good) => value === good ? "ok" : "warn";
|
|
216
|
-
console.log(`${out(doctorResult.status === "pass" ? "ok" : "err", doctorResult.status)} ${out("bold", adapter)} ${doctorResult.compatibilityMode} ${out("dim", `manifest=${actionManifestPath}`)}`);
|
|
217
|
-
console.log(
|
|
218
|
-
`${out("label", "harness:")} ${doctorResult.runner.packageName}@${doctorResult.runner.version} ${out("accent", doctorResult.runner.installKind)} ${out("dim", `executable=${doctorResult.runner.executablePath}`)}`
|
|
219
|
-
);
|
|
220
|
-
if (runtime) {
|
|
221
|
-
const provisionedDepsPending = runtime.reasonCode === "app-installed-deps-pending";
|
|
222
|
-
const decisionStyle = provisionedDepsPending ? "info" : runtime.decision === "ready" ? "ok" : runtime.decision === "blocked" ? "err" : "warn";
|
|
223
|
-
const depsStyle = provisionedDepsPending ? "info" : stateStyle(runtime.deps, "current");
|
|
224
|
-
const devServerStyle = provisionedDepsPending ? "info" : void 0;
|
|
225
|
-
const devServer = runtime.devServer ? ` ${runtime.devServer.label}=${out(devServerStyle ?? stateStyle(runtime.devServer.status, "up"), runtime.devServer.status)}` : "";
|
|
226
|
-
console.log(
|
|
227
|
-
`${out("label", "runtime:")} decision=${out(decisionStyle, runtime.decision)}${runtime.reasonCode ? ` ${out("dim", `(${runtime.reasonCode})`)}` : ""} deps=${out(depsStyle, runtime.deps ?? "unknown")}${devServer}`
|
|
228
|
-
);
|
|
229
|
-
for (const reason of runtime.reasons) console.log(` ${out("dim", reason)}`);
|
|
230
|
-
} else if (runtimeProbeError) {
|
|
231
|
-
console.log(`${out("err", "runtime probe failed:")} ${out("dim", runtimeProbeError)}`);
|
|
232
|
-
}
|
|
233
|
-
renderDoctorRecorder(recorder, out);
|
|
234
|
-
console.log(renderCheckoutView(view));
|
|
235
|
-
console.log(`${out("label", "feature flags:")} ${out(
|
|
236
|
-
featureFlags.error || (featureFlags.overrideCount ?? 0) > 0 ? "warn" : "dim",
|
|
237
|
-
featureFlags.summary.replace(/^feature flags: /u, "")
|
|
238
|
-
)}`);
|
|
239
|
-
for (const line of readiness.lines?.(target) ?? []) console.log(line);
|
|
240
|
-
if (orphanMetros.length > 0) {
|
|
241
|
-
console.log(
|
|
242
|
-
`${out("warn", "orphan Metro:")} ${orphanMetros.length} leaked bundler(s) for this checkout (pids ${orphanMetros.join(", ")})`
|
|
243
|
-
);
|
|
244
|
-
console.log(` ${out("dim", "Next: mm-harness stop # reaps every bundler this checkout leaked")}`);
|
|
245
|
-
}
|
|
246
|
-
if (capture) renderCaptureHealth(capture, out);
|
|
247
|
-
renderSkillsView(skills, out);
|
|
248
|
-
renderUpdateStatus(update, out);
|
|
249
|
-
if (deviceSource) {
|
|
250
|
-
if (deviceView) deviceSource.renderList(deviceView, out);
|
|
251
|
-
if (additionalReachableDevices.length > 0) deviceSource.renderAdditional(additionalReachableDevices, out);
|
|
252
|
-
if (liveView) deviceSource.renderLive(deviceView?.devices ?? [], liveView.liveMap, out);
|
|
253
|
-
}
|
|
254
|
-
if (next) console.log(` ${out("dim", `Next: ${next}`)}`);
|
|
255
|
-
}
|
|
256
|
-
return doctorResult.status === "pass" ? 0 : 1;
|
|
257
|
-
}
|
|
258
|
-
import { doctorReady as doctorReady2 } from "@farmslot/recipe-cli/harness";
|
|
259
|
-
async function platformDoctorChecks(adapter, target) {
|
|
260
|
-
try {
|
|
261
|
-
return await getAdapterSurface(adapter).doctor?.(target) ?? [];
|
|
262
|
-
} catch (error) {
|
|
263
|
-
return [{
|
|
264
|
-
id: `adapter-plugin:${adapter}:doctor`,
|
|
265
|
-
status: "fail",
|
|
266
|
-
required: true,
|
|
267
|
-
message: `${adapter}'s doctor checks failed to run.`,
|
|
268
|
-
detail: error instanceof Error ? error.message : String(error),
|
|
269
|
-
userAction: `fix the doctor() of the ${adapter} adapter, then rerun mm-harness doctor --adapter ${adapter}`
|
|
270
|
-
}];
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
function doctorRuntimeCheck(runtime, error, expectLive) {
|
|
274
|
-
if (error) {
|
|
275
|
-
return {
|
|
276
|
-
id: "runtime",
|
|
277
|
-
status: "fail",
|
|
278
|
-
required: true,
|
|
279
|
-
message: "Runtime readiness probe failed.",
|
|
280
|
-
detail: error
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
const ready = runtime?.decision === "ready";
|
|
284
|
-
return {
|
|
285
|
-
id: "runtime",
|
|
286
|
-
status: ready ? "pass" : "fail",
|
|
287
|
-
required: expectLive,
|
|
288
|
-
message: ready ? "Runtime is ready." : `Runtime is not ready${runtime?.reasonCode ? ` (${runtime.reasonCode})` : "."}`,
|
|
289
|
-
detail: runtime?.reasons.join(" | ") || "Runtime readiness probe returned no result."
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
function doctorFixNextActions(failed, fixture, adapter, target, block) {
|
|
293
|
-
const actions = [];
|
|
294
|
-
const executable = process.env.MM_HARNESS_EXECUTABLE ? shellQuote(process.env.MM_HARNESS_EXECUTABLE) : "mm-harness";
|
|
295
|
-
const scope = `--adapter ${adapter} --target ${shellQuote(target)}`;
|
|
296
|
-
actions.push(...doctorFixtureNextActions(failed, fixture, scope, executable));
|
|
297
|
-
if (failed.includes("runtime-context")) {
|
|
298
|
-
actions.push(
|
|
299
|
-
`inspect ${shellQuote(resolveRuntimeContextPath(target))}, then retry ${executable} doctor --fix ${scope}`
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
if (failed.includes("overlay")) actions.push(`${executable} install ${scope}`);
|
|
303
|
-
if (block?.userAction && failed.includes(block.id)) actions.push(block.userAction);
|
|
304
|
-
return actions;
|
|
305
|
-
}
|
|
3
|
+
import { shellQuote, spawnScriptStreaming } from "@farmslot/recipe-cli/harness";
|
|
4
|
+
import { fixtureSummary } from "../doctor.js";
|
|
5
|
+
import { runnerDir } from "../paths.js";
|
|
306
6
|
function doctorFixtureNextActions(failed, fixture, scope, executable = process.env.MM_HARNESS_EXECUTABLE ? shellQuote(process.env.MM_HARNESS_EXECUTABLE) : "mm-harness") {
|
|
307
7
|
const actions = [];
|
|
308
8
|
if (failed.includes("wallet-fixture")) {
|
|
@@ -312,91 +12,6 @@ function doctorFixtureNextActions(failed, fixture, scope, executable = process.e
|
|
|
312
12
|
}
|
|
313
13
|
return actions;
|
|
314
14
|
}
|
|
315
|
-
function emitExpectLiveVerdict(adapter, target, runtime, verdict, recorder) {
|
|
316
|
-
const out = (style, text) => color(style, text, { stream: process.stderr });
|
|
317
|
-
const detail = runtime ? `${runtime.decision}${runtime.reasonCode ? ` (${runtime.reasonCode})` : ""}` : "runtime probe unavailable";
|
|
318
|
-
console.error(`${out("err", verdict)} ${out("bold", adapter)} ${detail} ${out("dim", target)}`);
|
|
319
|
-
for (const reason of runtime?.reasons ?? []) console.error(` ${out("dim", reason)}`);
|
|
320
|
-
if (recorder?.status === "fail") {
|
|
321
|
-
console.error(` ${out("err", "recorder:")} ${recorder.message}${recorder.detail ? ` ${out("dim", recorder.detail)}` : ""}`);
|
|
322
|
-
console.error(` Next: ${recorder.userAction}`);
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
console.error(` Next: ${runtime?.nextAction ?? getAdapterSurface(adapter).hints.runtimeProbeRecovery(target)}`);
|
|
326
|
-
}
|
|
327
|
-
function emitExpectLive(adapter, target, runtime, recorder, result, orphanMetros, capture, devices, additionalReachableDevices, json, printReady, discovery) {
|
|
328
|
-
const live = runtime?.decision === "ready";
|
|
329
|
-
const ready = doctorReady(runtime, recorder);
|
|
330
|
-
if (printReady) {
|
|
331
|
-
if (ready) {
|
|
332
|
-
const indicator = farmslotReadyIndicator(adapter, devices);
|
|
333
|
-
if (indicator) {
|
|
334
|
-
console.log(indicator);
|
|
335
|
-
return EXIT.ok;
|
|
336
|
-
}
|
|
337
|
-
emitExpectLiveVerdict(adapter, target, runtime, "not-ready");
|
|
338
|
-
return EXIT.runtime;
|
|
339
|
-
}
|
|
340
|
-
emitExpectLiveVerdict(adapter, target, runtime, live ? "not-ready" : "not-live", recorder);
|
|
341
|
-
return EXIT.runtime;
|
|
342
|
-
}
|
|
343
|
-
if (json) {
|
|
344
|
-
const userAction = runtime?.nextAction ?? getAdapterSurface(adapter).hints.runtimeProbeRecovery(target);
|
|
345
|
-
const error = !live ? { code: "RUNTIME_NOT_LIVE", message: `${adapter} runtime is not live`, userAction } : !ready ? { code: "RECORDER_UNAVAILABLE", message: recorder.message, userAction: recorder.userAction } : void 0;
|
|
346
|
-
console.log(JSON.stringify({ ...result, ready, runtime, ...recorder ? { recorder } : {}, orphanMetros, capture, devices, additionalReachableDevices, ...discovery, ...error ? { error } : {} }, null, 2));
|
|
347
|
-
return ready ? EXIT.ok : EXIT.runtime;
|
|
348
|
-
}
|
|
349
|
-
const out = (style, text) => color(style, text, { stream: process.stdout });
|
|
350
|
-
if (ready) {
|
|
351
|
-
console.log(`${out("ok", "live")} ${out("bold", adapter)} runtime ready`);
|
|
352
|
-
return EXIT.ok;
|
|
353
|
-
}
|
|
354
|
-
emitExpectLiveVerdict(adapter, target, runtime, live ? "not-ready" : "not-live", recorder);
|
|
355
|
-
return EXIT.runtime;
|
|
356
|
-
}
|
|
357
|
-
function captureHealth(providers) {
|
|
358
|
-
if (!providers) return null;
|
|
359
|
-
const fallback = [...providers];
|
|
360
|
-
const helper = captureHelperHealth();
|
|
361
|
-
if (!helper) {
|
|
362
|
-
return {
|
|
363
|
-
status: "fallback",
|
|
364
|
-
failing: [],
|
|
365
|
-
screenshots: { available: true, providers: fallback },
|
|
366
|
-
recording: { available: false, provider: null }
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
return {
|
|
370
|
-
status: helper.status,
|
|
371
|
-
failing: helper.failing,
|
|
372
|
-
screenshots: { available: true, providers: ["capture-helper", ...fallback] },
|
|
373
|
-
recording: { available: helper.status === "pass", provider: helper.status === "pass" ? "capture-helper" : null }
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
function captureHelperHealth() {
|
|
377
|
-
if (process.platform !== "darwin") return null;
|
|
378
|
-
const bin = process.env.CAPTURE_HELPER_PATH || "capture-helper";
|
|
379
|
-
try {
|
|
380
|
-
const out = execFileSync(bin, ["doctor", "--json"], { encoding: "utf8", timeout: 1e4, stdio: ["ignore", "pipe", "ignore"] });
|
|
381
|
-
const parsed = JSON.parse(out);
|
|
382
|
-
const failing = (parsed.checks ?? []).filter((c) => c.required === true && c.ok === false).map((c) => c.name ?? c.id ?? "check");
|
|
383
|
-
return { status: parsed.ok === true && failing.length === 0 ? "pass" : "warn", failing };
|
|
384
|
-
} catch {
|
|
385
|
-
return null;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
function renderCaptureHealth(capture, out) {
|
|
389
|
-
const video = capture.recording.available ? "capture-helper" : "unavailable";
|
|
390
|
-
console.log(
|
|
391
|
-
`${out("label", "capture:")} ${out(capture.status === "pass" ? "ok" : "warn", capture.status)} ${out("dim", `(screenshots: ${capture.screenshots.providers.join(" \u2192 ")}; video: ${video})`)}`
|
|
392
|
-
);
|
|
393
|
-
if (capture.status === "warn") {
|
|
394
|
-
if (capture.failing.length > 0) console.log(` ${out("dim", `failing: ${capture.failing.join(", ")}`)}`);
|
|
395
|
-
console.log(` ${out("dim", "Next: grant Screen Recording (System Settings \u2192 Privacy & Security \u2192 Screen Recording), or run: capture-helper doctor --open-permissions")}`);
|
|
396
|
-
} else if (capture.status === "fallback") {
|
|
397
|
-
console.log(` ${out("dim", "Next: screenshots work without capture-helper; install/configure capture-helper only when recipe video is required")}`);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
15
|
const SKILL_ROOTS = [path.join(".claude", "skills"), path.join(".agents", "skills")];
|
|
401
16
|
function skillsView(target) {
|
|
402
17
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -432,38 +47,6 @@ function renderUpdateStatus(status, out) {
|
|
|
432
47
|
status.updateAvailable ? `${out("label", "update:")} ${out("warn", `${status.current} \u2192 ${status.latest} available`)} ${out("dim", "(mm-harness update)")}` : status.latest && status.latest !== status.current ? `${out("label", "update:")} ${out("ok", status.current)} ${out("dim", `ahead of registry ${status.latest}`)}` : `${out("label", "update:")} ${out("ok", `${status.current} is latest`)}`
|
|
433
48
|
);
|
|
434
49
|
}
|
|
435
|
-
async function runDoctorFix(adapter, target, manifestValidation, json) {
|
|
436
|
-
const fixed = [];
|
|
437
|
-
const failed = [];
|
|
438
|
-
if (recipeRunning(target)) {
|
|
439
|
-
failed.push("recipe-running");
|
|
440
|
-
return { fixed, failed };
|
|
441
|
-
}
|
|
442
|
-
if (Number(manifestValidation.summary?.errors ?? 0) > 0) failed.push("manifest");
|
|
443
|
-
try {
|
|
444
|
-
const surface = getAdapterSurface(adapter);
|
|
445
|
-
surface.resolveSlotPorts(target);
|
|
446
|
-
const runtimeContext = ensureRuntimeContext(target, adapter);
|
|
447
|
-
if (runtimeContext.created) fixed.push("runtime-context");
|
|
448
|
-
} catch {
|
|
449
|
-
failed.push("runtime-context");
|
|
450
|
-
}
|
|
451
|
-
for (const fix of getAdapterSurface(adapter).readiness.fixes ?? []) {
|
|
452
|
-
try {
|
|
453
|
-
if (fix.apply(target)) fixed.push(fix.id);
|
|
454
|
-
} catch {
|
|
455
|
-
failed.push(fix.id);
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
const state = newHealState();
|
|
459
|
-
const ensured = await ensureOverlay(adapter, target, "infra-only", state, json);
|
|
460
|
-
if (!ensured.ok) failed.push("overlay");
|
|
461
|
-
else if (state.mutations.length > 0) fixed.push("overlay");
|
|
462
|
-
const fixture = await repairDoctorFixture(target, adapter, json);
|
|
463
|
-
fixed.push(...fixture.fixed);
|
|
464
|
-
failed.push(...fixture.failed);
|
|
465
|
-
return { fixed, failed };
|
|
466
|
-
}
|
|
467
50
|
async function repairDoctorFixture(target, adapter, json) {
|
|
468
51
|
if (fixtureSummary(target, adapter).status === "ready") return { fixed: [], failed: [] };
|
|
469
52
|
const syncFixture = path.join(runnerDir, "adapters/shared/sync-wallet-fixture.sh");
|
|
@@ -474,10 +57,7 @@ async function repairDoctorFixture(target, adapter, json) {
|
|
|
474
57
|
return fixtureSummary(target, adapter).status === "ready" ? { fixed: ["wallet-fixture"], failed: [] } : { fixed: [], failed: ["wallet-fixture"] };
|
|
475
58
|
}
|
|
476
59
|
export {
|
|
477
|
-
doctorFixNextActions,
|
|
478
60
|
doctorFixtureNextActions,
|
|
479
|
-
doctorReady2 as doctorReady,
|
|
480
|
-
handleDoctor,
|
|
481
61
|
renderSkillsView,
|
|
482
62
|
renderUpdateStatus,
|
|
483
63
|
repairDoctorFixture,
|
package/dist/commands/review.js
CHANGED
|
@@ -9,11 +9,9 @@ import {
|
|
|
9
9
|
readConfig,
|
|
10
10
|
referenceHint,
|
|
11
11
|
REVIEW_ADAPTERS,
|
|
12
|
-
renderReviewChecklist,
|
|
13
12
|
renderReviewHelp,
|
|
14
13
|
resolveLibrary,
|
|
15
|
-
resolveReference
|
|
16
|
-
defaultBaseRef
|
|
14
|
+
resolveReference
|
|
17
15
|
} from "../review/knowledge.js";
|
|
18
16
|
const EXIT_OK = 0;
|
|
19
17
|
let cachedHarnessVersion;
|
|
@@ -32,16 +30,15 @@ function adapterFromOptions() {
|
|
|
32
30
|
}
|
|
33
31
|
return scope ?? "unscoped";
|
|
34
32
|
}
|
|
35
|
-
function resolveReviewContext(options
|
|
33
|
+
function resolveReviewContext(options) {
|
|
36
34
|
const target = targetPath(options);
|
|
37
35
|
const adapter = adapterFromOptions();
|
|
38
36
|
const domain = optionString(options, "domain") ?? process.env.MM_HARNESS_DOMAIN;
|
|
39
37
|
const config = readConfig(configPath());
|
|
40
38
|
const context = { target, adapter, ...domain ? { domain } : {} };
|
|
41
39
|
if (domain) {
|
|
42
|
-
const library = resolveLibrary(domain, { target, config
|
|
40
|
+
const library = resolveLibrary(domain, { target, config });
|
|
43
41
|
if (library) context.knowledge = loadDomainKnowledge(library, adapter === "unscoped" ? void 0 : adapter);
|
|
44
|
-
else if (fetch) throw usageError(`No library found for domain "${domain}". Set RECIPE_LIBRARY_PATH="${domain}=<path>" or run: mm-harness config set libraries.${domain} <path>`);
|
|
45
42
|
}
|
|
46
43
|
const parity = parityAdapter(adapter);
|
|
47
44
|
if (parity) {
|
|
@@ -53,7 +50,7 @@ function resolveReviewContext(options, { fetch = true } = {}) {
|
|
|
53
50
|
}
|
|
54
51
|
function handleHelpReview(argv, harnessVersion2) {
|
|
55
52
|
const parsed = parseArgs(argv);
|
|
56
|
-
const context = resolveReviewContext(parsed.options
|
|
53
|
+
const context = resolveReviewContext(parsed.options);
|
|
57
54
|
const input = {
|
|
58
55
|
adapter: context.adapter,
|
|
59
56
|
harnessVersion: harnessVersion2,
|
|
@@ -70,66 +67,8 @@ function handleHelpReview(argv, harnessVersion2) {
|
|
|
70
67
|
}
|
|
71
68
|
return EXIT_OK;
|
|
72
69
|
}
|
|
73
|
-
async function handleReview(argv, version = harnessVersion()) {
|
|
74
|
-
const parsed = parseArgs(argv);
|
|
75
|
-
const subcommand = parsed.positional[0];
|
|
76
|
-
if (subcommand !== "checklist") {
|
|
77
|
-
throw usageError("Usage: mm-harness review checklist [--domain <name>] [--since <sha>] [--base <ref>] [--out <file>] [--json]");
|
|
78
|
-
}
|
|
79
|
-
const context = resolveReviewContext(parsed.options);
|
|
80
|
-
const since = optionString(parsed.options, "since");
|
|
81
|
-
const base = optionString(parsed.options, "base") ?? defaultBaseRef(context.target);
|
|
82
|
-
const checklist = renderReviewChecklist({
|
|
83
|
-
adapter: context.adapter,
|
|
84
|
-
target: context.target,
|
|
85
|
-
base,
|
|
86
|
-
harnessVersion: version,
|
|
87
|
-
...since ? { since } : {},
|
|
88
|
-
...context.domain ? { domain: context.domain } : {},
|
|
89
|
-
...context.knowledge ? { knowledge: context.knowledge } : {},
|
|
90
|
-
...context.reference ? { reference: context.reference } : {},
|
|
91
|
-
...context.referenceMissing ? { referenceMissing: context.referenceMissing } : {}
|
|
92
|
-
});
|
|
93
|
-
const out = optionString(parsed.options, "out");
|
|
94
|
-
if (out) {
|
|
95
|
-
const file = path.resolve(out);
|
|
96
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
97
|
-
fs.writeFileSync(file, checklist);
|
|
98
|
-
}
|
|
99
|
-
if (optionFlag(parsed.options, "json")) {
|
|
100
|
-
process.stdout.write(
|
|
101
|
-
`${JSON.stringify(
|
|
102
|
-
{
|
|
103
|
-
command: "review",
|
|
104
|
-
subcommand: "checklist",
|
|
105
|
-
target: context.target,
|
|
106
|
-
adapter: context.adapter,
|
|
107
|
-
domain: context.domain ?? null,
|
|
108
|
-
library: context.knowledge?.library ?? null,
|
|
109
|
-
reference: context.reference ?? null,
|
|
110
|
-
referenceMissing: context.referenceMissing ?? null,
|
|
111
|
-
base,
|
|
112
|
-
since: since ?? null,
|
|
113
|
-
out: out ? path.resolve(out) : null,
|
|
114
|
-
checklist
|
|
115
|
-
},
|
|
116
|
-
null,
|
|
117
|
-
2
|
|
118
|
-
)}
|
|
119
|
-
`
|
|
120
|
-
);
|
|
121
|
-
} else if (out) {
|
|
122
|
-
process.stdout.write(`review checklist: ${path.resolve(out)}
|
|
123
|
-
`);
|
|
124
|
-
} else {
|
|
125
|
-
process.stdout.write(checklist);
|
|
126
|
-
}
|
|
127
|
-
return EXIT_OK;
|
|
128
|
-
}
|
|
129
70
|
export {
|
|
130
71
|
adapterFromOptions,
|
|
131
72
|
handleHelpReview,
|
|
132
|
-
|
|
133
|
-
harnessVersion,
|
|
134
|
-
resolveReviewContext
|
|
73
|
+
harnessVersion
|
|
135
74
|
};
|
package/dist/doctor.js
CHANGED
|
@@ -5,16 +5,6 @@ import { normalizeFeatureFlagRecord } from "./feature-flags.js";
|
|
|
5
5
|
import { getAdapterSurface } from "./adapters/surface.js";
|
|
6
6
|
import { isPin } from "./harness-pin.js";
|
|
7
7
|
import { readJson } from "./paths.js";
|
|
8
|
-
function requiredDoctorCheckSummary(checks) {
|
|
9
|
-
const required = checks.filter((check) => check.required);
|
|
10
|
-
const failed = required.filter((check) => check.status === "fail").map((check) => check.id);
|
|
11
|
-
return {
|
|
12
|
-
status: failed.length === 0 ? "pass" : "fail",
|
|
13
|
-
total: required.length,
|
|
14
|
-
passed: required.length - failed.length,
|
|
15
|
-
failed
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
8
|
function repoShape(target) {
|
|
19
9
|
const exists = (rel) => fs.existsSync(path.join(target, rel));
|
|
20
10
|
const packageInfo = readPackageInfo(target);
|
|
@@ -188,7 +178,6 @@ export {
|
|
|
188
178
|
fixtureSummary,
|
|
189
179
|
renderRuntimeContext,
|
|
190
180
|
repoShape,
|
|
191
|
-
requiredDoctorCheckSummary,
|
|
192
181
|
runnerInstallKind,
|
|
193
182
|
runnerProvenance,
|
|
194
183
|
runtimeContextSummary
|