@deeeed/metamask-harness 0.17.3 → 0.17.5
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 +6 -96
- package/README.md +136 -96
- package/adapters/extension/console-tail.mjs +24 -51
- package/adapters/extension/ensure-browser.sh +7 -1
- package/adapters/extension/inject.mjs +15 -5
- package/adapters/extension/launch-browser.cjs +4 -3
- package/adapters/extension/launch.sh +11 -8
- package/adapters/extension/lib/chrome-args.cjs +24 -0
- package/adapters/extension/live.sh +1 -2
- package/adapters/extension/reattach.sh +0 -1
- package/adapters/extension/seed-fixture.sh +12 -4
- package/adapters/extension/sidepanel-toggle.sh +1 -4
- package/adapters/extension/start-watch.sh +4 -17
- package/adapters/extension/stop-viewers.sh +1 -1
- package/adapters/extension/verify.sh +1 -1
- package/adapters/manifest.json +45 -53
- package/adapters/mobile/bridge-runtime/cdp-bridge.cjs +0 -16
- package/adapters/mobile/bridge-runtime/console-forwarder.cjs +1 -1
- package/adapters/mobile/bridge-runtime/lib/match-bridge-target.cjs +1 -3
- package/adapters/mobile/bridge-runtime/lib/target-discovery.cjs +3 -2
- package/adapters/mobile/cleanup.sh +176 -16
- package/adapters/mobile/inject.sh +671 -90
- package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +185 -0
- package/adapters/mobile/overlay/app/dev-tools/AgenticService/AgenticService.ts.patch +1662 -0
- package/adapters/mobile/overlay/compat/README.md +28 -0
- package/adapters/mobile/overlay/compat/rn81-message-event-source.patch +42 -0
- package/adapters/mobile/start-metro.sh +41 -0
- package/adapters/mobile/verify.sh +61 -4
- package/adapters/mobile/wait-for-bridge.sh +8 -22
- package/adapters/shared/harness-source-fingerprint.mjs +49 -0
- package/adapters/shared/install-repo-deps.sh +1 -5
- package/adapters/shared/open-debug.mjs +184 -103
- package/adapters/shared/resolve-farmslot-ports-core.mjs +3 -0
- package/adapters/shared/resolve-farmslot-ports.mjs +5 -0
- package/adapters/shared/resolve-farmslot-ports.sh +7 -0
- package/adapters/shared/resolve-slot-ports-core.mjs +6 -0
- package/adapters/shared/resolve-slot-ports.sh +7 -2
- package/bin/mm-harness +0 -7
- package/dist/adapters/extension/harness-freshness.js +39 -0
- package/dist/adapters/extension/runtime-decision.js +4 -73
- package/dist/adapters/extension/surface.js +2 -3
- package/dist/adapters/mobile/perps-env.js +3 -43
- package/dist/adapters/mobile/prepare.js +7 -22
- package/dist/adapters/mobile/source-freshness.js +26 -41
- package/dist/adapters/mobile/surface.js +1 -1
- package/dist/adapters/resolve-slot-ports.js +2 -0
- package/dist/adapters/slot-ports.js +0 -16
- package/dist/adapters.js +5 -12
- package/dist/cli-commands.js +1 -1
- package/dist/cli.js +12 -6
- package/dist/commands/call.js +22 -56
- package/dist/commands/check.js +3 -9
- package/dist/commands/checklist.js +4 -1
- package/dist/commands/debug.js +31 -38
- package/dist/commands/device-target.js +12 -27
- package/dist/commands/doctor.js +23 -77
- package/dist/commands/fixtures.js +17 -27
- package/dist/commands/launch/extension.js +2 -0
- package/dist/commands/launch/index.js +59 -167
- package/dist/commands/logs.js +6 -8
- package/dist/commands/manifest.js +25 -151
- package/dist/commands/parse-args.js +0 -2
- package/dist/commands/provision.js +3 -10
- package/dist/commands/run-engine.js +69 -71
- package/dist/commands/run-report.js +3 -12
- package/dist/commands/run.js +35 -245
- package/dist/commands/shared.js +3 -55
- package/dist/commands/status-probe.js +1 -1
- package/dist/commands/status.js +1 -1
- package/dist/commands/stop.js +2 -7
- package/dist/commands/update.js +28 -16
- package/dist/doctor.js +4 -1
- package/dist/harness.js +134 -43
- package/dist/heal-bounds.js +1 -1
- package/dist/live-adapter-contract.js +20 -6
- package/dist/mm-harness-cli.js +48 -123
- package/dist/paths.js +5 -2
- package/dist/runner.js +2 -37
- package/dist/runtime-context.js +8 -26
- package/docs/ADAPTER-SURFACE.md +112 -19
- package/docs/CHEATSHEET.md +52 -39
- package/docs/CLI-SPEC.md +1098 -0
- package/docs/CODE-MAP.md +1 -1
- package/docs/DEBUG-HANDOVER.md +36 -0
- package/docs/MENTAL-MODEL.md +295 -0
- package/docs/UX-PRINCIPLES.md +1 -3
- package/docs/architecture.md +375 -65
- package/docs/live-adapter-contract.md +2 -4
- package/docs/recipe-libraries.md +275 -45
- package/library/README.md +0 -4
- package/library/actions/core/perps/_controller.mjs +55 -10
- package/library/actions/mobile/platform/bridge.mjs +1 -5
- package/library/manifests/core.action-manifest.json +7 -7
- package/library/manifests/mobile.action-manifest.json +3 -28
- package/package.json +1 -4
- package/adapters/extension/launch-webpack.cjs +0 -83
- package/adapters/extension/stamp-runtime-title.cjs +0 -49
- package/adapters/extension/sync-webpack-dist.cjs +0 -105
- package/adapters/mobile/launch-console-forwarder.cjs +0 -39
- package/adapters/mobile/start-console-forwarder.sh +0 -70
- package/adapters/shared/update-check-worker.mjs +0 -35
- package/dist/adapters/extension/console-capture.js +0 -93
- package/dist/command-contract.js +0 -441
- package/dist/command-journal.js +0 -225
- package/dist/commands/last.js +0 -52
- package/dist/json-stream.js +0 -57
- package/dist/run-diagnostics.js +0 -261
- package/docs/CLI-ERGONOMICS-AUDIT.md +0 -32
- package/docs/CLI-ERGONOMICS-HUMAN-QA.md +0 -64
- package/docs/VIDEO-DEMO-VALIDATION.md +0 -74
- package/library/recipes/runner/smoke.core.recipe.json +0 -27
- package/library/recipes/wallet/smoke.extension.recipe.json +0 -42
- package/library/recipes/wallet/smoke.mobile.recipe.json +0 -42
- package/scripts/validate-human-outcomes.mjs +0 -169
package/dist/doctor.js
CHANGED
|
@@ -93,7 +93,10 @@ function fixtureFileSummary(file, adapter, reportedPath = file) {
|
|
|
93
93
|
const RUNTIME_CONTEXT_FIELDS = [
|
|
94
94
|
{ key: "slotId", envVars: ["RECIPE_SLOT_ID"], envVar: "RECIPE_SLOT_ID", customize: "doctor --fix creates local identity; Farmslot may override", adapters: ["mobile", "extension", "core"] },
|
|
95
95
|
{ key: "extensionId", envVars: ["RECIPE_HARNESS_EXTENSION_ID"], envVar: "RECIPE_HARNESS_EXTENSION_ID", customize: "resolved from the built extension", adapters: ["extension"] },
|
|
96
|
-
{ key: "cdpPort", envVars: ["RECIPE_CDP_PORT", "CDP_PORT"], envVar: "CDP_PORT", customize: "doctor --fix claims one; --cdp-port overrides", adapters: ["extension"] }
|
|
96
|
+
{ key: "cdpPort", envVars: ["RECIPE_CDP_PORT", "CDP_PORT"], envVar: "CDP_PORT", customize: "doctor --fix claims one; --cdp-port overrides", adapters: ["extension"] },
|
|
97
|
+
{ key: "runtimeStart.approved", envVars: ["RECIPE_RUNTIME_START_APPROVED"], envVar: "RECIPE_RUNTIME_START_APPROVED", customize: "optional orchestrator-provided legacy live context", adapters: ["mobile", "extension"] },
|
|
98
|
+
{ key: "runtimeStart.command", envVars: [], envVar: null, customize: "optional orchestrator-provided legacy live context", adapters: ["mobile", "extension"] },
|
|
99
|
+
{ key: "runtimeStart.readyUrl", envVars: ["RECIPE_RUNTIME_READY_URL"], envVar: "RECIPE_RUNTIME_READY_URL", customize: "optional orchestrator-provided legacy live context", adapters: ["mobile", "extension"] }
|
|
97
100
|
];
|
|
98
101
|
function runtimeContextSummary(target, adapter) {
|
|
99
102
|
const contextPath = resolveRuntimeContextPath(target);
|
package/dist/harness.js
CHANGED
|
@@ -4,8 +4,13 @@ import path from "node:path";
|
|
|
4
4
|
import { resolveLeafInvoke, shellLeafMissing, missingShellLeafMessage } from "./leaf-invoke.js";
|
|
5
5
|
import { resolveExtensionSlotPorts } from "./adapters/slot-ports.js";
|
|
6
6
|
import { recipeHarnessPath, recipeRuntimeDir, runnerDir } from "./paths.js";
|
|
7
|
-
|
|
7
|
+
import { prepareMobile } from "./adapters/mobile/prepare.js";
|
|
8
|
+
const HARNESS_ACTIONS = ["install", "verify", "cleanup", "live"];
|
|
8
9
|
const ADAPTERS = ["mobile", "extension", "core"];
|
|
10
|
+
function isValidAdapterAction(adapter, action) {
|
|
11
|
+
if (adapter === "core") return action === "install" || action === "verify" || action === "cleanup";
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
9
14
|
function harnessUsage() {
|
|
10
15
|
console.error(`mm-harness \u2014 install and validate the MetaMask recipe runtime in a checkout.
|
|
11
16
|
|
|
@@ -45,12 +50,6 @@ function failureHint(adapter, action) {
|
|
|
45
50
|
const helper = adapter === "mobile" ? "mm-harness launch ios # or launch android" : "mm-harness launch";
|
|
46
51
|
return `Read the error above for the specific cause. To (re)start the runtime, run: ${helper}`;
|
|
47
52
|
}
|
|
48
|
-
function successNext(adapter, action, target) {
|
|
49
|
-
const base = `--adapter ${adapter} --target ${shellQuote(target)}`;
|
|
50
|
-
if (action === "install") return `mm-harness verify ${base}`;
|
|
51
|
-
if (action === "cleanup") return `mm-harness install ${base}`;
|
|
52
|
-
return `mm-harness status --target ${shellQuote(target)} --json`;
|
|
53
|
-
}
|
|
54
53
|
function hasArg(args, needle) {
|
|
55
54
|
return args.some((arg) => arg === needle || arg.startsWith(`${needle}=`));
|
|
56
55
|
}
|
|
@@ -71,7 +70,6 @@ function parseHarnessArgs(args) {
|
|
|
71
70
|
const forward = [];
|
|
72
71
|
let adapter;
|
|
73
72
|
let json = false;
|
|
74
|
-
let quiet = false;
|
|
75
73
|
let separator = false;
|
|
76
74
|
for (let i = 0; i < args.length; i += 1) {
|
|
77
75
|
const arg = args[i];
|
|
@@ -87,10 +85,6 @@ function parseHarnessArgs(args) {
|
|
|
87
85
|
json = true;
|
|
88
86
|
continue;
|
|
89
87
|
}
|
|
90
|
-
if (arg === "--quiet") {
|
|
91
|
-
quiet = true;
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
88
|
if (arg === "--adapter" || arg === "--platform") {
|
|
95
89
|
const value = args[i + 1];
|
|
96
90
|
if (arg === "--adapter") {
|
|
@@ -121,7 +115,7 @@ function parseHarnessArgs(args) {
|
|
|
121
115
|
}
|
|
122
116
|
forward.push(arg);
|
|
123
117
|
}
|
|
124
|
-
return { adapter, json,
|
|
118
|
+
return { adapter, json, forward };
|
|
125
119
|
}
|
|
126
120
|
const ADAPTER_DETECT_NEXT = "cd into a MetaMask checkout or pass --target <path>, or force it with --adapter <mobile|extension|core>";
|
|
127
121
|
function detectAdapter(target) {
|
|
@@ -167,7 +161,7 @@ function readRuntimeContextField(contextPath, field) {
|
|
|
167
161
|
if (node === void 0 || node === null || node === "") return void 0;
|
|
168
162
|
return String(node);
|
|
169
163
|
}
|
|
170
|
-
function applyExtensionRuntimeEnv(target, args) {
|
|
164
|
+
function applyExtensionRuntimeEnv(target, action, args) {
|
|
171
165
|
const contextPath = resolveRuntimeContextPath(target);
|
|
172
166
|
const contextExists = fs.existsSync(contextPath);
|
|
173
167
|
if (contextExists) {
|
|
@@ -180,6 +174,19 @@ function applyExtensionRuntimeEnv(target, args) {
|
|
|
180
174
|
const extensionId = readRuntimeContextField(contextPath, "extensionId");
|
|
181
175
|
if (extensionId) process.env.RECIPE_HARNESS_EXTENSION_ID = extensionId;
|
|
182
176
|
}
|
|
177
|
+
if (process.env.RECIPE_RUNTIME_START_APPROVED === void 0) {
|
|
178
|
+
const approved = readRuntimeContextField(contextPath, "runtimeStart.approved");
|
|
179
|
+
if (approved === "true" || approved === "True" || approved === "1") process.env.RECIPE_RUNTIME_START_APPROVED = "1";
|
|
180
|
+
else if (approved === "false" || approved === "False" || approved === "0") process.env.RECIPE_RUNTIME_START_APPROVED = "0";
|
|
181
|
+
}
|
|
182
|
+
if (!process.env.RECIPE_RUNTIME_START_CMD) {
|
|
183
|
+
const command = readRuntimeContextField(contextPath, "runtimeStart.command");
|
|
184
|
+
if (command) process.env.RECIPE_RUNTIME_START_CMD = command;
|
|
185
|
+
}
|
|
186
|
+
if (!process.env.RECIPE_RUNTIME_READY_URL) {
|
|
187
|
+
const readyUrl = readRuntimeContextField(contextPath, "runtimeStart.readyUrl");
|
|
188
|
+
if (readyUrl) process.env.RECIPE_RUNTIME_READY_URL = readyUrl;
|
|
189
|
+
}
|
|
183
190
|
} else {
|
|
184
191
|
delete process.env.RECIPE_RUNTIME_CONTEXT;
|
|
185
192
|
}
|
|
@@ -193,6 +200,11 @@ function applyExtensionRuntimeEnv(target, args) {
|
|
|
193
200
|
result = [...result, "--cdp-port", cdpPort];
|
|
194
201
|
}
|
|
195
202
|
}
|
|
203
|
+
if (action === "live" && !hasArg(result, "--prepare-cmd")) {
|
|
204
|
+
if (process.env.RECIPE_RUNTIME_START_APPROVED === "1" && process.env.RECIPE_RUNTIME_START_CMD) {
|
|
205
|
+
result = [...result, "--prepare-cmd", process.env.RECIPE_RUNTIME_START_CMD];
|
|
206
|
+
}
|
|
207
|
+
}
|
|
196
208
|
return result;
|
|
197
209
|
}
|
|
198
210
|
function resolveEntry(base, candidates, mode) {
|
|
@@ -269,6 +281,81 @@ Next: mm-harness install --platform core --target ${target}`
|
|
|
269
281
|
);
|
|
270
282
|
return { command, prefixArgs: [] };
|
|
271
283
|
}
|
|
284
|
+
async function handleMobileLive(target, forwardArgs, json, autoDetected) {
|
|
285
|
+
const platform = argValue(forwardArgs, "--platform") ?? "ios";
|
|
286
|
+
const watcherPortStr = argValue(forwardArgs, "--watcher-port") ?? process.env.WATCHER_PORT;
|
|
287
|
+
const watcherPort = watcherPortStr ? parseInt(watcherPortStr, 10) : void 0;
|
|
288
|
+
const start = Date.now();
|
|
289
|
+
if (!json) {
|
|
290
|
+
const detected = autoDetected ? ", auto-detected" : "";
|
|
291
|
+
console.error(`\u2192 live (mobile${detected}) \u2014 target: ${target}`);
|
|
292
|
+
}
|
|
293
|
+
const prepResult = await prepareMobile(target, { platform, json, watcherPort });
|
|
294
|
+
if (prepResult.status !== 0) {
|
|
295
|
+
const elapsed2 = ((Date.now() - start) / 1e3).toFixed(1);
|
|
296
|
+
if (json) {
|
|
297
|
+
console.log(
|
|
298
|
+
harnessSummary("live", "mobile", target, "fail", prepResult.status, autoDetected, {
|
|
299
|
+
code: "MOBILE_PREPARE_FAILED",
|
|
300
|
+
message: `mobile prepare failed (exit ${prepResult.status})`,
|
|
301
|
+
userAction: failureHint("mobile", "live")
|
|
302
|
+
})
|
|
303
|
+
);
|
|
304
|
+
} else {
|
|
305
|
+
console.error(
|
|
306
|
+
`\u2717 live mobile failed (exit ${prepResult.status}, ${elapsed2}s)
|
|
307
|
+
${failureHint("mobile", "live")}`
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
return prepResult.status;
|
|
311
|
+
}
|
|
312
|
+
const installedVerify = path.join(recipeHarnessPath(target, "mobile"), "scripts", "verify.sh");
|
|
313
|
+
const verifySh = fs.existsSync(installedVerify) ? installedVerify : resolveEntry(runnerDir, ["adapters/mobile/verify.sh", "scripts/mobile/verify.sh"], "file");
|
|
314
|
+
const verifyArgs = hasArg(forwardArgs, "--no-auto-start") ? [...forwardArgs] : ["--no-auto-start", ...forwardArgs];
|
|
315
|
+
if (shellLeafMissing(verifySh)) {
|
|
316
|
+
const message = missingShellLeafMessage(verifySh);
|
|
317
|
+
if (json) {
|
|
318
|
+
console.log(
|
|
319
|
+
harnessSummary("live", "mobile", target, "fail", 1, autoDetected, {
|
|
320
|
+
code: "HARNESS_SPAWN_FAILED",
|
|
321
|
+
message,
|
|
322
|
+
userAction: failureHint("mobile", "live")
|
|
323
|
+
})
|
|
324
|
+
);
|
|
325
|
+
} else {
|
|
326
|
+
console.error(`\u2717 ${message}`);
|
|
327
|
+
}
|
|
328
|
+
return 1;
|
|
329
|
+
}
|
|
330
|
+
const { bin: verifyBin, args: verifySpawnArgs } = resolveLeafInvoke(verifySh, verifyArgs);
|
|
331
|
+
const result = spawnSync(verifyBin, verifySpawnArgs, {
|
|
332
|
+
stdio: json ? ["inherit", 2, "inherit"] : "inherit",
|
|
333
|
+
env: process.env
|
|
334
|
+
});
|
|
335
|
+
const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
|
|
336
|
+
const exitCode = result.status ?? 1;
|
|
337
|
+
if (json) {
|
|
338
|
+
console.log(
|
|
339
|
+
harnessSummary(
|
|
340
|
+
"live",
|
|
341
|
+
"mobile",
|
|
342
|
+
target,
|
|
343
|
+
exitCode === 0 ? "pass" : "fail",
|
|
344
|
+
exitCode,
|
|
345
|
+
autoDetected,
|
|
346
|
+
exitCode === 0 ? void 0 : { code: "MOBILE_VERIFY_FAILED", message: `mobile live verify failed (exit ${exitCode})`, userAction: failureHint("mobile", "live") }
|
|
347
|
+
)
|
|
348
|
+
);
|
|
349
|
+
} else if (exitCode === 0) {
|
|
350
|
+
console.error(`\u2713 live mobile passed (${elapsed}s)`);
|
|
351
|
+
} else {
|
|
352
|
+
console.error(
|
|
353
|
+
`\u2717 live mobile failed (exit ${exitCode}, ${elapsed}s)
|
|
354
|
+
${failureHint("mobile", "live")}`
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
return exitCode;
|
|
358
|
+
}
|
|
272
359
|
async function handleHarness(argv) {
|
|
273
360
|
const action = argv[0];
|
|
274
361
|
if (!action || action === "-h" || action === "--help") {
|
|
@@ -281,14 +368,12 @@ async function handleHarness(argv) {
|
|
|
281
368
|
return 2;
|
|
282
369
|
}
|
|
283
370
|
const harnessAction = action;
|
|
284
|
-
const { adapter: parsedAdapter, json,
|
|
371
|
+
const { adapter: parsedAdapter, json, forward } = parseHarnessArgs(argv.slice(1));
|
|
285
372
|
const rawTarget = argValue(forward, "--target");
|
|
286
373
|
const target = path.resolve(rawTarget ?? process.cwd());
|
|
287
374
|
const adapter = parsedAdapter ?? detectAdapter(target);
|
|
288
375
|
if (!adapter || !isAdapter(adapter)) {
|
|
289
|
-
if (
|
|
290
|
-
return 2;
|
|
291
|
-
} else if (json) {
|
|
376
|
+
if (json) {
|
|
292
377
|
const detectError = adapter ? { code: "UNSUPPORTED_PLATFORM", message: `unsupported platform: ${adapter}`, userAction: "pass --adapter <mobile|extension|core> to specify a supported adapter" } : { code: "ADAPTER_DETECTION_FAILED", message: `could not detect the MetaMask repo type for ${target}`, userAction: ADAPTER_DETECT_NEXT };
|
|
293
378
|
console.log(harnessSummary(harnessAction, void 0, target, "fail", 2, false, detectError));
|
|
294
379
|
} else {
|
|
@@ -302,19 +387,36 @@ async function handleHarness(argv) {
|
|
|
302
387
|
}
|
|
303
388
|
return 2;
|
|
304
389
|
}
|
|
390
|
+
if (!isValidAdapterAction(adapter, harnessAction)) {
|
|
391
|
+
if (json) {
|
|
392
|
+
console.log(
|
|
393
|
+
harnessSummary(harnessAction, adapter, target, "fail", 2, parsedAdapter === void 0, {
|
|
394
|
+
code: "UNSUPPORTED_ACTION",
|
|
395
|
+
message: `${harnessAction} is not supported for ${adapter} (core supports install, verify, cleanup).`,
|
|
396
|
+
userAction: "run mm-harness doctor to see which actions are supported for this adapter"
|
|
397
|
+
})
|
|
398
|
+
);
|
|
399
|
+
} else {
|
|
400
|
+
console.error(
|
|
401
|
+
`\u2717 ${harnessAction} is not supported for ${adapter} (core supports install, verify, cleanup).`
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
return 2;
|
|
405
|
+
}
|
|
305
406
|
const autoDetected = parsedAdapter === void 0;
|
|
306
407
|
let forwardArgs = hasArg(forward, "--target") ? [...forward] : ["--target", target, ...forward];
|
|
307
408
|
if (harnessAction === "install" && hasArg(forward, "--runway")) {
|
|
308
409
|
return handleRunwayInstall(adapter, target, forward, json);
|
|
309
410
|
}
|
|
310
|
-
if (adapter === "extension" && harnessAction === "verify") {
|
|
311
|
-
forwardArgs = applyExtensionRuntimeEnv(target, forwardArgs);
|
|
411
|
+
if (adapter === "extension" && (harnessAction === "live" || harnessAction === "verify")) {
|
|
412
|
+
forwardArgs = applyExtensionRuntimeEnv(target, harnessAction, forwardArgs);
|
|
413
|
+
}
|
|
414
|
+
if (adapter === "mobile" && harnessAction === "live") {
|
|
415
|
+
return handleMobileLive(target, forwardArgs, json, autoDetected);
|
|
312
416
|
}
|
|
313
417
|
const dispatch = resolveHarnessDispatch(adapter, harnessAction, target);
|
|
314
418
|
if ("error" in dispatch) {
|
|
315
|
-
if (
|
|
316
|
-
return 1;
|
|
317
|
-
} else if (json) {
|
|
419
|
+
if (json) {
|
|
318
420
|
console.log(
|
|
319
421
|
harnessSummary(harnessAction, adapter, target, "fail", 1, autoDetected, {
|
|
320
422
|
code: "DISPATCH_UNAVAILABLE",
|
|
@@ -330,16 +432,14 @@ async function handleHarness(argv) {
|
|
|
330
432
|
}
|
|
331
433
|
return 1;
|
|
332
434
|
}
|
|
333
|
-
if (!json
|
|
435
|
+
if (!json) {
|
|
334
436
|
const detected = autoDetected ? ", auto-detected" : "";
|
|
335
437
|
console.error(`\u2192 ${harnessAction} (${adapter}${detected}) \u2014 target: ${target}`);
|
|
336
438
|
}
|
|
337
439
|
const start = Date.now();
|
|
338
440
|
if (shellLeafMissing(dispatch.command)) {
|
|
339
441
|
const message = missingShellLeafMessage(dispatch.command);
|
|
340
|
-
if (
|
|
341
|
-
return 1;
|
|
342
|
-
} else if (json) {
|
|
442
|
+
if (json) {
|
|
343
443
|
console.log(
|
|
344
444
|
harnessSummary(harnessAction, adapter, target, "fail", 1, autoDetected, {
|
|
345
445
|
code: "HARNESS_SPAWN_FAILED",
|
|
@@ -359,14 +459,12 @@ async function handleHarness(argv) {
|
|
|
359
459
|
const result = spawnSync(dispatchBin, dispatchArgs, {
|
|
360
460
|
// Default: stream child output verbatim (byte-identical to the skill path).
|
|
361
461
|
// --json: route child output to our stderr so stdout carries only the summary.
|
|
362
|
-
stdio:
|
|
462
|
+
stdio: json ? ["inherit", 2, "inherit"] : "inherit",
|
|
363
463
|
env: process.env
|
|
364
464
|
});
|
|
365
465
|
const seconds = ((Date.now() - start) / 1e3).toFixed(1);
|
|
366
466
|
if (result.error) {
|
|
367
|
-
if (
|
|
368
|
-
return 1;
|
|
369
|
-
} else if (json) {
|
|
467
|
+
if (json) {
|
|
370
468
|
console.log(
|
|
371
469
|
harnessSummary(harnessAction, adapter, target, "fail", 1, autoDetected, {
|
|
372
470
|
code: "HARNESS_SPAWN_FAILED",
|
|
@@ -381,9 +479,7 @@ async function handleHarness(argv) {
|
|
|
381
479
|
return 1;
|
|
382
480
|
}
|
|
383
481
|
const exitCode = result.status ?? 1;
|
|
384
|
-
if (
|
|
385
|
-
return exitCode;
|
|
386
|
-
} else if (json) {
|
|
482
|
+
if (json) {
|
|
387
483
|
console.log(
|
|
388
484
|
harnessSummary(
|
|
389
485
|
harnessAction,
|
|
@@ -396,8 +492,7 @@ async function handleHarness(argv) {
|
|
|
396
492
|
)
|
|
397
493
|
);
|
|
398
494
|
} else if (exitCode === 0) {
|
|
399
|
-
console.error(`\u2713 ${harnessAction} ${adapter} passed (${seconds}s)
|
|
400
|
-
Next: ${successNext(adapter, harnessAction, target)}`);
|
|
495
|
+
console.error(`\u2713 ${harnessAction} ${adapter} passed (${seconds}s)`);
|
|
401
496
|
} else {
|
|
402
497
|
console.error(`\u2717 ${harnessAction} ${adapter} failed (exit ${exitCode}, ${seconds}s)
|
|
403
498
|
${failureHint(adapter, harnessAction)}`);
|
|
@@ -427,10 +522,8 @@ async function handleRunwayInstall(adapter, target, forward, json) {
|
|
|
427
522
|
resolveOnly: hasArg(forward, "--resolve-only"),
|
|
428
523
|
rerunCommand
|
|
429
524
|
});
|
|
430
|
-
const next = result.status === "pass" && adapter === "mobile" && result.resolveOnly !== true ? `mm-harness launch ${String(result.platform ?? "ios")} --adapter mobile --target ${shellQuote(target)}` : void 0;
|
|
431
|
-
const output = next ? { ...result, next } : result;
|
|
432
525
|
if (json) {
|
|
433
|
-
console.log(JSON.stringify(
|
|
526
|
+
console.log(JSON.stringify(result, null, 2));
|
|
434
527
|
} else if (result.status === "pass") {
|
|
435
528
|
const cache = typeof result.cache === "object" && result.cache ? result.cache : void 0;
|
|
436
529
|
const simulator = typeof result.simulator === "object" && result.simulator ? result.simulator : void 0;
|
|
@@ -441,7 +534,6 @@ async function handleRunwayInstall(adapter, target, forward, json) {
|
|
|
441
534
|
const action = result.skipped ? "already provisioned" : "installed Runway app";
|
|
442
535
|
console.error(`\u2713 ${action} for ${adapter} ${result.platform ?? ""} simulator=${simulator?.name ?? "unknown"} cache=${cache?.status ?? "skip"}`);
|
|
443
536
|
}
|
|
444
|
-
if (next) console.error(` Next: ${next}`);
|
|
445
537
|
} else {
|
|
446
538
|
console.error(`\u2717 mm-harness install --runway: ${result.error?.message ?? "runway install failed"}
|
|
447
539
|
Next: ${result.error?.userAction ?? rerunCommand}`);
|
|
@@ -484,10 +576,9 @@ function harnessSummary(action, adapter, target, status, exitCode, autoDetected,
|
|
|
484
576
|
status,
|
|
485
577
|
exitCode,
|
|
486
578
|
// Error contract: every --json failure carries a stable machine code + human
|
|
487
|
-
// message. userAction is included when present so callers
|
|
579
|
+
// message (CLI-SPEC.md §5.1). userAction is included when present so callers
|
|
488
580
|
// can surface the reachable escape without parsing the human message.
|
|
489
|
-
...status === "fail" && error ? { error } : {}
|
|
490
|
-
...status === "pass" && adapter ? { next: successNext(adapter, action, target) } : {}
|
|
581
|
+
...status === "fail" && error ? { error } : {}
|
|
491
582
|
});
|
|
492
583
|
}
|
|
493
584
|
export {
|
package/dist/heal-bounds.js
CHANGED
|
@@ -103,7 +103,7 @@ function checkHealBounds(target, output, state) {
|
|
|
103
103
|
return {
|
|
104
104
|
code: "SAME_RECOVERY_TWICE",
|
|
105
105
|
exitCode: EXIT.bounded,
|
|
106
|
-
message:
|
|
106
|
+
message: "same recovery already failed once this invocation \u2014 refusing to loop.",
|
|
107
107
|
originalError
|
|
108
108
|
};
|
|
109
109
|
}
|
|
@@ -46,16 +46,29 @@ function candidatePaths(platform, action) {
|
|
|
46
46
|
}
|
|
47
47
|
pushDomainDispatcherFiles(files, root, platform, family);
|
|
48
48
|
}
|
|
49
|
+
for (const candidateStem of stems) {
|
|
50
|
+
pushFlatCandidateFiles(files, root, platform, candidateStem);
|
|
51
|
+
}
|
|
49
52
|
}
|
|
50
53
|
return files;
|
|
51
54
|
}
|
|
52
55
|
function pushCandidateFiles(files, root, platform, family, stem) {
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
for (const extension of ["mjs", "js", "sh"]) {
|
|
57
|
+
files.push(path.join(root, platform, family, `${stem}.${extension}`));
|
|
58
|
+
files.push(path.join(root, "shared", family, `${stem}.${extension}`));
|
|
59
|
+
}
|
|
55
60
|
}
|
|
56
61
|
function pushDomainDispatcherFiles(files, root, platform, family) {
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
for (const extension of ["mjs", "js", "sh"]) {
|
|
63
|
+
files.push(path.join(root, platform, family, `${family}.${extension}`));
|
|
64
|
+
files.push(path.join(root, "shared", family, `${family}.${extension}`));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function pushFlatCandidateFiles(files, root, platform, stem) {
|
|
68
|
+
for (const extension of ["mjs", "js", "sh"]) {
|
|
69
|
+
files.push(path.join(root, platform, `${stem}.${extension}`));
|
|
70
|
+
files.push(path.join(root, "shared", `${stem}.${extension}`));
|
|
71
|
+
}
|
|
59
72
|
}
|
|
60
73
|
async function firstExecutablePath(paths) {
|
|
61
74
|
for (const file of paths) {
|
|
@@ -107,8 +120,9 @@ function runProcess(command, args, options) {
|
|
|
107
120
|
});
|
|
108
121
|
}
|
|
109
122
|
function commandFor(file, projectRoot, platform) {
|
|
123
|
+
if (file.endsWith(".sh")) return { command: "bash", args: [file] };
|
|
110
124
|
const needsTsx = platform === "core" || importsSourceTypescript(file);
|
|
111
|
-
if (!needsTsx) {
|
|
125
|
+
if (!needsTsx && (file.endsWith(".mjs") || file.endsWith(".js"))) {
|
|
112
126
|
return { command: process.execPath, args: [file] };
|
|
113
127
|
}
|
|
114
128
|
const tsxBin = resolveTsxBin(projectRoot);
|
|
@@ -151,7 +165,7 @@ function importsSourceTypescriptFrom(file, visited) {
|
|
|
151
165
|
const specifier = match[1] ?? "";
|
|
152
166
|
if (specifier.endsWith(".ts")) return true;
|
|
153
167
|
if (!specifier.startsWith(".")) continue;
|
|
154
|
-
if (!specifier.endsWith(".mjs")) continue;
|
|
168
|
+
if (!specifier.endsWith(".mjs") && !specifier.endsWith(".js")) continue;
|
|
155
169
|
if (importsSourceTypescriptFrom(path.resolve(path.dirname(absolute), specifier), visited)) {
|
|
156
170
|
return true;
|
|
157
171
|
}
|