@deeeed/metamask-harness 0.2.0 → 0.3.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/CHANGELOG.md +17 -0
- package/README.md +20 -17
- package/{orchestration → adapters}/core/cleanup.sh +2 -2
- package/{orchestration → adapters}/core/inject.sh +8 -8
- package/{orchestration → adapters}/extension/cleanup.mjs +1 -1
- package/{orchestration → adapters}/extension/console-tail.mjs +3 -0
- package/{orchestration → adapters}/extension/ensure-browser.sh +4 -4
- package/{orchestration → adapters}/extension/inject.mjs +27 -31
- package/{orchestration → adapters}/extension/launch.sh +4 -4
- package/{orchestration → adapters}/extension/live.sh +5 -5
- package/{orchestration → adapters}/extension/readiness.mjs +12 -2
- package/{orchestration → adapters}/extension/refresh-build.sh +1 -1
- package/{orchestration → adapters}/extension/seed-fixture.sh +1 -1
- package/{orchestration → adapters}/extension/start-watch.sh +7 -7
- package/{runner → adapters}/extension/verify.sh +11 -11
- package/{orchestration → adapters}/extension/wallet-fixture-state.cjs +2 -2
- package/{orchestration → adapters}/manifest.json +96 -96
- package/{library/actions → adapters}/mobile/bridge-runtime/cdp-bridge.cjs +5 -5
- package/{orchestration → adapters}/mobile/cleanup.sh +3 -3
- package/{orchestration → adapters}/mobile/inject.sh +22 -24
- package/adapters/mobile/open-device.sh +250 -0
- package/{orchestration/compat-overlays → adapters/mobile/overlay/compat}/README.md +13 -4
- package/adapters/mobile/prewarm-bundle.sh +95 -0
- package/adapters/mobile/start-metro.sh +228 -0
- package/{runner → adapters}/mobile/verify.sh +6 -6
- package/adapters/mobile/wait-for-bridge.sh +108 -0
- package/adapters/mobile/yarn-setup.sh +20 -0
- package/{orchestration/lib → adapters/shared}/cli-ux.sh +5 -9
- package/{orchestration/lib → adapters/shared}/log-tui.mjs +57 -1
- package/{orchestration/lib → adapters/shared}/open-debug.mjs +32 -10
- package/{orchestration/lib → adapters/shared}/resolve-farmslot-ports.sh +47 -0
- package/adapters/shared/sync-wallet-fixture.sh +78 -0
- package/bin/mm-harness +5 -6
- package/docs/CLI-SPEC.md +41 -2
- package/docs/architecture.md +69 -38
- package/docs/runtime-file-conventions.md +3 -3
- package/library/actions/core/perps/_controller.mjs +1 -1
- package/library/actions/extension/platform/cdp.mjs +2 -2
- package/library/actions/extension/wallet/ensure_unlocked.mjs +1 -1
- package/library/actions/mobile/platform/bridge.mjs +1 -1
- package/library/actions/mobile/wallet/ensure_unlocked.mjs +1 -1
- package/library/actions/mobile/wallet/setup.mjs +2 -2
- package/library/library.json +1 -1
- package/package.json +8 -8
- package/{orchestration → src/adapters}/extension/runtime-decision.ts +5 -5
- package/{orchestration → src/adapters}/extension/runtime.ts +1 -1
- package/src/adapters/mobile/prepare.ts +210 -0
- package/{orchestration → src/adapters}/mobile/runtime-decision.ts +11 -3
- package/{runner/src → src}/adapters.ts +3 -3
- package/{orchestration/lib/cli-color.mjs → src/cli-color.ts} +21 -13
- package/{orchestration/lib/cli-commands.mjs → src/cli-commands.ts} +52 -45
- package/{orchestration/lib/cli-version.mjs → src/cli-version.ts} +35 -17
- package/{runner/src → src}/cli.ts +25 -18
- package/src/commands/debug.ts +61 -0
- package/src/commands/fixtures.ts +196 -0
- package/{runner/src → src}/commands/launch.ts +185 -41
- package/src/commands/logs.ts +86 -0
- package/{runner/src → src}/commands/shared.ts +26 -26
- package/{runner/src → src}/harness.ts +97 -10
- package/src/index.ts +15 -0
- package/{runner/src → src}/live-adapter-contract.ts +1 -1
- package/{runner/src → src}/mm-harness-cli.ts +19 -3
- package/{runner/src → src}/paths.ts +2 -2
- package/{orchestration/lib/progress.mjs → src/progress.ts} +31 -21
- package/docs/extension-runtime-commands.md +0 -60
- package/docs/harness-cli.md +0 -43
- package/orchestration/compat-overlays/mobile/README.md +0 -13
- package/orchestration/doctor.mjs +0 -72
- package/orchestration/lib/cli-home.mjs +0 -354
- package/orchestration/lib/recipe-paths.mjs +0 -26
- package/orchestration/mobile/launch.sh +0 -137
- package/orchestration/mobile/live.sh +0 -125
- package/orchestration/porcelain/metamask-recipe +0 -99
- package/orchestration/porcelain/mm-recipe +0 -1591
- package/orchestration/porcelain/mme-recipe +0 -1181
- package/runner/src/commands/debug.ts +0 -44
- package/runner/src/commands/fixtures.ts +0 -99
- package/runner/src/commands/logs.ts +0 -60
- package/runner/src/index.ts +0 -6
- /package/{orchestration → adapters}/extension/launch-browser.cjs +0 -0
- /package/{orchestration → adapters}/extension/pin-remote-flags.cjs +0 -0
- /package/{orchestration → adapters}/extension/sidepanel-toggle.sh +0 -0
- /package/{orchestration → adapters}/extension/snapshot-dist.sh +0 -0
- /package/{library/actions → adapters}/mobile/bridge-runtime/lib/cdp-eval.cjs +0 -0
- /package/{library/actions → adapters}/mobile/bridge-runtime/lib/config.cjs +0 -0
- /package/{library/actions → adapters}/mobile/bridge-runtime/lib/issue-capture.cjs +0 -0
- /package/{library/actions → adapters}/mobile/bridge-runtime/lib/target-discovery.cjs +0 -0
- /package/{library/actions → adapters}/mobile/bridge-runtime/lib/ws-client.cjs +0 -0
- /package/{library/actions → adapters}/mobile/bridge-runtime/setup-wallet.sh +0 -0
- /package/{library/actions/mobile/app-overlay → adapters/mobile/overlay}/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +0 -0
- /package/{library/actions/mobile/app-overlay → adapters/mobile/overlay}/app/dev-tools/AgenticService/AgenticService.ts.patch +0 -0
- /package/{orchestration/compat-overlays/mobile → adapters/mobile/overlay/compat}/rn81-message-event-source.patch +0 -0
- /package/{orchestration/lib → adapters/shared}/activate-repo-node.sh +0 -0
- /package/{orchestration/lib → adapters/shared}/ensure-runner-deps.sh +0 -0
- /package/{orchestration/lib → adapters/shared}/harness-path.sh +0 -0
- /package/{orchestration/lib → adapters/shared}/hash-helpers.sh +0 -0
- /package/{orchestration/lib → adapters/shared}/json-field.sh +0 -0
- /package/{orchestration/lib → adapters/shared}/path-defaults.json +0 -0
- /package/{orchestration → src/adapters}/extension/ensure-ready.ts +0 -0
- /package/{orchestration → src/adapters}/extension/extension-id.ts +0 -0
- /package/{orchestration → src/adapters}/mobile/deps-markers.ts +0 -0
- /package/{runner/src → src}/completions-cache.ts +0 -0
- /package/{runner/src → src}/doctor.ts +0 -0
- /package/{runner/src → src}/heal-bounds.ts +0 -0
- /package/{runner/src → src}/manifest.ts +0 -0
- /package/{runner/src → src}/recording-target.ts +0 -0
- /package/{runner/src → src}/run-recording.ts +0 -0
- /package/{runner/src → src}/runner.ts +0 -0
- /package/{runner/src → src}/types.ts +0 -0
|
@@ -1,354 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* cli-home.mjs — recipe CLI home screen and ports output.
|
|
4
|
-
*/
|
|
5
|
-
import fs from 'node:fs';
|
|
6
|
-
import http from 'node:http';
|
|
7
|
-
import path from 'node:path';
|
|
8
|
-
import { parseArgs } from 'node:util';
|
|
9
|
-
import {
|
|
10
|
-
color,
|
|
11
|
-
colorKv,
|
|
12
|
-
colorStatusWord,
|
|
13
|
-
} from './cli-color.mjs';
|
|
14
|
-
|
|
15
|
-
const OUT = process.stdout;
|
|
16
|
-
|
|
17
|
-
function readJson(file) {
|
|
18
|
-
if (!file || !fs.existsSync(file)) return null;
|
|
19
|
-
try {
|
|
20
|
-
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
21
|
-
} catch {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function httpProbe(url, timeoutMs = 1500) {
|
|
27
|
-
return new Promise((resolve) => {
|
|
28
|
-
const req = http.get(url, { timeout: timeoutMs }, (res) => {
|
|
29
|
-
let body = '';
|
|
30
|
-
res.on('data', (chunk) => { body += chunk; });
|
|
31
|
-
res.on('end', () => resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, body }));
|
|
32
|
-
});
|
|
33
|
-
req.on('timeout', () => { req.destroy(); resolve({ ok: false, body: '' }); });
|
|
34
|
-
req.on('error', () => resolve({ ok: false, body: '' }));
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function watcherPhase(logPath) {
|
|
39
|
-
if (!logPath || !fs.existsSync(logPath)) return 'stopped';
|
|
40
|
-
const text = fs.readFileSync(logPath, 'utf8');
|
|
41
|
-
if (/Module build failed|^ERROR in /m.test(text)) return 'failed';
|
|
42
|
-
if (/compiled successfully|MetaMask .* compiled|Bundle end:/m.test(text)) return 'ready';
|
|
43
|
-
if (/webpack.Progress|Building|Compiling|yarn start/m.test(text)) return 'compiling';
|
|
44
|
-
return 'unknown';
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function metroPhase(logPath) {
|
|
48
|
-
if (!logPath || !fs.existsSync(logPath)) return 'stopped';
|
|
49
|
-
const text = fs.readFileSync(logPath, 'utf8');
|
|
50
|
-
if (/Bundl(ed|ing)|Finished/m.test(text)) {
|
|
51
|
-
const match = text.match(/(iOS|Android).*?([0-9]+(?:\.[0-9]+)?)%/);
|
|
52
|
-
if (match && Number(match[2]) < 100) return 'bundling';
|
|
53
|
-
return 'ready';
|
|
54
|
-
}
|
|
55
|
-
return 'starting';
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function mobileBridgeSnapshot(runtimeStatus) {
|
|
59
|
-
const raw = runtimeStatus?.bridge?.status;
|
|
60
|
-
const targets = Array.isArray(raw) ? raw : raw ? [raw] : [];
|
|
61
|
-
const ready = targets.find((item) => item && typeof item === 'object' && item.route);
|
|
62
|
-
if (!ready) return { route: null, account: null, deviceName: null };
|
|
63
|
-
return {
|
|
64
|
-
route: ready.route?.name || null,
|
|
65
|
-
account: ready.account?.name || ready.account?.address || null,
|
|
66
|
-
deviceName: ready.deviceName || null,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function watcherPhaseLabel(phase) {
|
|
71
|
-
switch (phase) {
|
|
72
|
-
case 'ready': return 'ready';
|
|
73
|
-
case 'compiling': return 'compiling';
|
|
74
|
-
case 'failed': return 'failed';
|
|
75
|
-
case 'stopped': return 'stopped';
|
|
76
|
-
default: return phase || 'unknown';
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function metroPhaseLabel(phase) {
|
|
81
|
-
switch (phase) {
|
|
82
|
-
case 'running':
|
|
83
|
-
case 'ready': return 'ready';
|
|
84
|
-
case 'bundling': return 'bundling';
|
|
85
|
-
case 'starting': return 'starting';
|
|
86
|
-
case 'down': return 'down';
|
|
87
|
-
default: return phase || 'unknown';
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function nextActions(adapter, state) {
|
|
92
|
-
const actions = [];
|
|
93
|
-
if (state.harnessStale) {
|
|
94
|
-
actions.push({ command: 'recipe sync', reason: 'refresh harness scripts + fixture context' });
|
|
95
|
-
}
|
|
96
|
-
if (adapter === 'extension') {
|
|
97
|
-
if (state.ready) {
|
|
98
|
-
actions.push({ command: 'recipe refresh', reason: 'fast incremental update when watcher is active' });
|
|
99
|
-
actions.push({ command: 'recipe debug', reason: 'open Chrome DevTools' });
|
|
100
|
-
actions.push({ command: 'recipe logs', reason: 'compact last build events (recipe logs --full for raw tail)' });
|
|
101
|
-
} else if (state.watcher === 'ready' && state.decision === 'build') {
|
|
102
|
-
actions.push({ command: 'recipe refresh', reason: 'watcher is up; wait for dist to catch up' });
|
|
103
|
-
actions.push({ command: 'recipe rebuild', reason: 'heavy clean rebuild if refresh stalls' });
|
|
104
|
-
} else if (state.watcher === 'compiling') {
|
|
105
|
-
actions.push({ command: 'recipe logs', reason: 'compact webpack progress' });
|
|
106
|
-
actions.push({ command: 'recipe status', reason: 're-check readiness' });
|
|
107
|
-
} else {
|
|
108
|
-
actions.push({ command: 'recipe up', reason: 'decide and run the minimum path to ready' });
|
|
109
|
-
actions.push({ command: 'recipe rebuild', reason: 'clean webpack + browser launch (2-8 min)' });
|
|
110
|
-
actions.push({ command: 'recipe watch', reason: 'start watcher only' });
|
|
111
|
-
}
|
|
112
|
-
if (!state.fixturePresent) actions.unshift({ command: 'recipe sync', reason: 'install fixture + harness context' });
|
|
113
|
-
} else {
|
|
114
|
-
if (state.ready) {
|
|
115
|
-
actions.push({ command: 'recipe debug', reason: 'open React Native DevTools' });
|
|
116
|
-
actions.push({ command: 'recipe logs', reason: 'compact Metro events (recipe logs --full for raw tail)' });
|
|
117
|
-
} else if (state.metro === 'ready' && !state.bridgeReady) {
|
|
118
|
-
actions.push({ command: 'recipe up', reason: 'launch dev client and wait for bridge' });
|
|
119
|
-
actions.push({ command: 'recipe logs', reason: 'compact bundle progress' });
|
|
120
|
-
} else {
|
|
121
|
-
actions.push({ command: 'recipe up', reason: 'start Metro + launch dev client as needed' });
|
|
122
|
-
actions.push({ command: 'recipe ios', reason: 'launch iOS dev client' });
|
|
123
|
-
actions.push({ command: 'recipe android', reason: 'launch Android dev client' });
|
|
124
|
-
}
|
|
125
|
-
if (!state.fixturePresent) actions.unshift({ command: 'recipe sync', reason: 'install fixture + harness context' });
|
|
126
|
-
}
|
|
127
|
-
return actions.slice(0, 5);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function renderHuman(payload) {
|
|
131
|
-
const lines = [];
|
|
132
|
-
const head = payload.adapter === 'extension' ? 'Extension recipe' : 'Mobile recipe';
|
|
133
|
-
lines.push(`${color('bold', head, { stream: OUT })} ${color('dim', '|', { stream: OUT })} ${color('path', payload.root, { stream: OUT })}`);
|
|
134
|
-
lines.push(`${colorKv('slot', payload.slotId || '?', 'info', { stream: OUT })} ${colorKv('runtime', payload.runtimeDir, 'path', { stream: OUT })}`);
|
|
135
|
-
if (payload.harnessStale) {
|
|
136
|
-
lines.push(color('warn', 'harness: stale - run: recipe sync', { stream: OUT }));
|
|
137
|
-
}
|
|
138
|
-
if (payload.adapter === 'extension') {
|
|
139
|
-
lines.push(
|
|
140
|
-
`${colorKv('cdp', String(payload.ports.cdp ?? '?'), 'info', { stream: OUT })} ${colorKv('watcher-port', String(payload.ports.watcher ?? '?'), 'info', { stream: OUT })}`,
|
|
141
|
-
);
|
|
142
|
-
lines.push([
|
|
143
|
-
`${color('label', 'browser:', { stream: OUT })} ${colorStatusWord(payload.browser, { stream: OUT })}`,
|
|
144
|
-
`${color('label', 'webpack:', { stream: OUT })} ${colorStatusWord(watcherPhaseLabel(payload.watcher), { stream: OUT })}`,
|
|
145
|
-
`${color('label', 'ready:', { stream: OUT })} ${colorStatusWord(payload.ready ? 'yes' : 'no', { stream: OUT })}`,
|
|
146
|
-
payload.reason ? color('comment', ` (${payload.reason})`, { stream: OUT }) : '',
|
|
147
|
-
].join(' '));
|
|
148
|
-
lines.push(`${color('label', 'fixture:', { stream: OUT })} ${colorStatusWord(payload.fixturePresent ? 'present' : 'missing', { stream: OUT })}`);
|
|
149
|
-
} else {
|
|
150
|
-
const bridgeBits = [];
|
|
151
|
-
if (payload.appRoute) bridgeBits.push(`route=${payload.appRoute}`);
|
|
152
|
-
if (payload.appAccount) bridgeBits.push(`account=${payload.appAccount}`);
|
|
153
|
-
const bridgeDetail = bridgeBits.length > 0 ? color('comment', ` (${bridgeBits.join(', ')})`, { stream: OUT }) : '';
|
|
154
|
-
lines.push(
|
|
155
|
-
`${colorKv('metro-port', String(payload.ports.watcher ?? '?'), 'info', { stream: OUT })} ${colorKv('device', payload.device || payload.appDevice || '?', 'info', { stream: OUT })} ${color('label', 'metro:', { stream: OUT })} ${colorStatusWord(metroPhaseLabel(payload.metro), { stream: OUT })} ${color('label', 'bridge:', { stream: OUT })} ${colorStatusWord(payload.bridge, { stream: OUT })}${bridgeDetail}`,
|
|
156
|
-
);
|
|
157
|
-
lines.push([
|
|
158
|
-
`${color('label', 'fixture:', { stream: OUT })} ${colorStatusWord(payload.fixturePresent ? 'present' : 'missing', { stream: OUT })}`,
|
|
159
|
-
`${color('label', 'ready:', { stream: OUT })} ${colorStatusWord(payload.ready ? 'yes' : 'no', { stream: OUT })}`,
|
|
160
|
-
payload.reason ? color('comment', ` (${payload.reason})`, { stream: OUT }) : '',
|
|
161
|
-
].join(' '));
|
|
162
|
-
}
|
|
163
|
-
if (payload.decision) {
|
|
164
|
-
lines.push(`${colorKv('decision', payload.decision, 'ok', { stream: OUT })}${payload.reasonCode ? color('comment', ` - ${payload.reasonCode}`, { stream: OUT }) : ''}`);
|
|
165
|
-
}
|
|
166
|
-
if (payload.tmux) {
|
|
167
|
-
lines.push(`${colorKv('tmux', payload.tmux, 'accent', { stream: OUT })}${color('comment', ' (webpack window may still stream full logs)', { stream: OUT })}`);
|
|
168
|
-
}
|
|
169
|
-
lines.push('');
|
|
170
|
-
lines.push(color('label', 'next:', { stream: OUT }));
|
|
171
|
-
for (const item of payload.nextActions) {
|
|
172
|
-
lines.push(` ${color('cmd', item.command, { stream: OUT })} ${color('comment', `# ${item.reason}`, { stream: OUT })}`);
|
|
173
|
-
}
|
|
174
|
-
lines.push('');
|
|
175
|
-
lines.push(color('comment', 'speed: fast -> watch -> refresh slow -> rebuild inspect -> status | logs | logs --full | debug', { stream: OUT }));
|
|
176
|
-
return `${lines.join('\n')}\n`;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function renderPorts(payload) {
|
|
180
|
-
const lines = [
|
|
181
|
-
colorKv('slot', payload.slotId || '?', 'info', { stream: OUT }),
|
|
182
|
-
`${colorKv('cdp', String(payload.ports.cdp ?? '?'), 'info', { stream: OUT })} ${colorKv('watcher', String(payload.ports.watcher ?? '?'), 'info', { stream: OUT })}`,
|
|
183
|
-
`${colorKv('runtime', payload.runtimeDir, 'path', { stream: OUT })} ${colorKv('harness', payload.harnessDir, 'path', { stream: OUT })}`,
|
|
184
|
-
];
|
|
185
|
-
if (payload.tmux) lines.push(colorKv('tmux', payload.tmux, 'accent', { stream: OUT }));
|
|
186
|
-
return `${lines.join('\n')}\n`;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
async function buildHome({ adapter, target, runtimeDir, ports, runtimeStatus, decision, harnessStale = false }) {
|
|
190
|
-
const runtimeAbs = path.join(target, runtimeDir);
|
|
191
|
-
const context = readJson(path.join(runtimeAbs, 'agentic-runtime.json'))
|
|
192
|
-
|| readJson(path.join(runtimeAbs, 'recipe-runtime.json'))
|
|
193
|
-
|| {};
|
|
194
|
-
const slotId = context.slotId || null;
|
|
195
|
-
const fixturePresent = Boolean(
|
|
196
|
-
runtimeStatus?.fixture?.present
|
|
197
|
-
|| fs.existsSync(path.join(runtimeAbs, 'wallet-fixture.json')),
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
let browser = 'down';
|
|
201
|
-
let watcher = 'stopped';
|
|
202
|
-
let metro = 'stopped';
|
|
203
|
-
let bridge = 'down';
|
|
204
|
-
let bridgeReady = false;
|
|
205
|
-
let device = process.env.IOS_SIMULATOR || process.env.ANDROID_DEVICE || process.env.ADB_SERIAL || null;
|
|
206
|
-
let tmux = null;
|
|
207
|
-
|
|
208
|
-
if (adapter === 'extension') {
|
|
209
|
-
const cdp = ports.cdp;
|
|
210
|
-
if (cdp) {
|
|
211
|
-
const probe = await httpProbe(`http://127.0.0.1:${cdp}/json/version`);
|
|
212
|
-
browser = probe.ok ? 'up' : 'down';
|
|
213
|
-
}
|
|
214
|
-
const logPath = path.join(runtimeAbs, 'webpack.log');
|
|
215
|
-
watcher = watcherPhase(logPath);
|
|
216
|
-
const slot = (path.basename(target).match(/-(\d+)$/) || [])[1];
|
|
217
|
-
if (slot) tmux = `mme-${slot}:webpack-${ports.watcher || '?'}`;
|
|
218
|
-
} else {
|
|
219
|
-
const port = ports.watcher;
|
|
220
|
-
if (port) {
|
|
221
|
-
const status = await httpProbe(`http://127.0.0.1:${port}/status`);
|
|
222
|
-
metro = status.ok && status.body.includes('packager-status:running') ? 'running' : 'down';
|
|
223
|
-
if (metro === 'running') metro = metroPhase(path.join(runtimeAbs, 'metro.log'));
|
|
224
|
-
}
|
|
225
|
-
bridgeReady = Boolean(runtimeStatus?.bridge?.ready);
|
|
226
|
-
bridge = bridgeReady ? 'ready' : (runtimeStatus?.debugTarget?.present ? 'waiting' : 'down');
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const ready = Boolean(runtimeStatus?.ready);
|
|
230
|
-
const decisionValue = decision?.decision || runtimeStatus?.decision?.decision || null;
|
|
231
|
-
const reasonCode = decision?.reasonCode || runtimeStatus?.decision?.reasonCode || runtimeStatus?.reason || null;
|
|
232
|
-
const bridgeSnap = adapter === 'mobile' ? mobileBridgeSnapshot(runtimeStatus) : null;
|
|
233
|
-
|
|
234
|
-
const payload = {
|
|
235
|
-
schemaVersion: 1,
|
|
236
|
-
kind: 'home',
|
|
237
|
-
adapter,
|
|
238
|
-
root: target,
|
|
239
|
-
runtimeDir,
|
|
240
|
-
slotId,
|
|
241
|
-
ports,
|
|
242
|
-
fixturePresent,
|
|
243
|
-
harnessStale,
|
|
244
|
-
ready,
|
|
245
|
-
reason: runtimeStatus?.reason || null,
|
|
246
|
-
appRoute: bridgeSnap?.route || null,
|
|
247
|
-
appAccount: bridgeSnap?.account || null,
|
|
248
|
-
appDevice: bridgeSnap?.deviceName || null,
|
|
249
|
-
decision: decisionValue,
|
|
250
|
-
reasonCode,
|
|
251
|
-
browser: adapter === 'extension' ? browser : undefined,
|
|
252
|
-
watcher: adapter === 'extension' ? watcher : undefined,
|
|
253
|
-
metro: adapter === 'mobile' ? metro : undefined,
|
|
254
|
-
bridge: adapter === 'mobile' ? bridge : undefined,
|
|
255
|
-
bridgeReady: adapter === 'mobile' ? bridgeReady : undefined,
|
|
256
|
-
device: adapter === 'mobile' ? device : undefined,
|
|
257
|
-
tmux: adapter === 'extension' ? tmux : undefined,
|
|
258
|
-
runtimeStatus,
|
|
259
|
-
nextActions: [],
|
|
260
|
-
};
|
|
261
|
-
payload.nextActions = nextActions(adapter, {
|
|
262
|
-
ready,
|
|
263
|
-
watcher,
|
|
264
|
-
metro,
|
|
265
|
-
bridgeReady,
|
|
266
|
-
fixturePresent,
|
|
267
|
-
harnessStale,
|
|
268
|
-
decision: decisionValue,
|
|
269
|
-
});
|
|
270
|
-
return payload;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
function buildPorts({ adapter, target, runtimeDir, ports, slotId }) {
|
|
274
|
-
return {
|
|
275
|
-
schemaVersion: 1,
|
|
276
|
-
kind: 'ports',
|
|
277
|
-
adapter,
|
|
278
|
-
root: target,
|
|
279
|
-
runtimeDir,
|
|
280
|
-
slotId: slotId || null,
|
|
281
|
-
ports,
|
|
282
|
-
tmux: adapter === 'extension' && ports.watcher
|
|
283
|
-
? `mme-${(path.basename(target).match(/-(\d+)$/) || ['', ''])[1]}:webpack-${ports.watcher}`
|
|
284
|
-
: null,
|
|
285
|
-
harnessDir: process.env.RECIPE_HARNESS_ROOT || 'temp/recipe/harness',
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
async function main() {
|
|
290
|
-
const { values } = parseArgs({
|
|
291
|
-
args: process.argv.slice(2),
|
|
292
|
-
options: {
|
|
293
|
-
adapter: { type: 'string' },
|
|
294
|
-
target: { type: 'string' },
|
|
295
|
-
'runtime-dir': { type: 'string' },
|
|
296
|
-
'cdp-port': { type: 'string' },
|
|
297
|
-
'watcher-port': { type: 'string' },
|
|
298
|
-
'slot-id': { type: 'string' },
|
|
299
|
-
mode: { type: 'string', default: 'home' },
|
|
300
|
-
'runtime-status-file': { type: 'string' },
|
|
301
|
-
'decision-file': { type: 'string' },
|
|
302
|
-
'harness-stale': { type: 'boolean', default: false },
|
|
303
|
-
json: { type: 'boolean', default: false },
|
|
304
|
-
},
|
|
305
|
-
allowPositionals: false,
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
if (!values.adapter || !values.target) {
|
|
309
|
-
console.error('cli-home.mjs requires --adapter and --target');
|
|
310
|
-
process.exit(2);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const ports = {
|
|
314
|
-
cdp: values['cdp-port'] ? Number(values['cdp-port']) : null,
|
|
315
|
-
watcher: values['watcher-port'] ? Number(values['watcher-port']) : null,
|
|
316
|
-
};
|
|
317
|
-
const runtimeStatus = readJson(values['runtime-status-file']);
|
|
318
|
-
const decision = readJson(values['decision-file']);
|
|
319
|
-
|
|
320
|
-
const payload = values.mode === 'ports'
|
|
321
|
-
? buildPorts({
|
|
322
|
-
adapter: values.adapter,
|
|
323
|
-
target: values.target,
|
|
324
|
-
runtimeDir: values['runtime-dir'] || 'temp/recipe/runtime',
|
|
325
|
-
ports,
|
|
326
|
-
slotId: values['slot-id'],
|
|
327
|
-
})
|
|
328
|
-
: await buildHome({
|
|
329
|
-
adapter: values.adapter,
|
|
330
|
-
target: values.target,
|
|
331
|
-
runtimeDir: values['runtime-dir'] || 'temp/recipe/runtime',
|
|
332
|
-
ports,
|
|
333
|
-
runtimeStatus,
|
|
334
|
-
decision,
|
|
335
|
-
harnessStale: values['harness-stale'],
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
if (values.json) {
|
|
339
|
-
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
if (payload.kind === 'ports') {
|
|
344
|
-
process.stdout.write(renderPorts(payload));
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
process.stdout.write(renderHuman(payload));
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
main().catch((error) => {
|
|
352
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
353
|
-
process.exit(1);
|
|
354
|
-
});
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
|
|
5
|
-
const defaultsPath = path.join(path.dirname(fileURLToPath(import.meta.url)), 'path-defaults.json');
|
|
6
|
-
const defaults = JSON.parse(fs.readFileSync(defaultsPath, 'utf8'));
|
|
7
|
-
|
|
8
|
-
export const DEFAULT_RECIPE_HARNESS_ROOT = defaults.recipeHarnessRoot;
|
|
9
|
-
export const DEFAULT_RECIPE_RUNTIME_DIR = defaults.recipeRuntimeDir;
|
|
10
|
-
|
|
11
|
-
export function validateRelativeRecipePath(name, value) {
|
|
12
|
-
if (!value || path.isAbsolute(value)) throw new Error(`${name} must be a non-empty relative path: ${value}`);
|
|
13
|
-
if (!/^[A-Za-z0-9._/-]+$/u.test(value)) throw new Error(`${name} contains unsupported characters: ${value}`);
|
|
14
|
-
for (const part of value.split('/')) {
|
|
15
|
-
if (!part || part === '.' || part === '..') throw new Error(`${name} contains unsafe path component: ${value}`);
|
|
16
|
-
}
|
|
17
|
-
return value;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function recipeHarnessRoot(env = process.env) {
|
|
21
|
-
return validateRelativeRecipePath('RECIPE_HARNESS_ROOT', env.RECIPE_HARNESS_ROOT || DEFAULT_RECIPE_HARNESS_ROOT);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function recipeRuntimeDir(env = process.env) {
|
|
25
|
-
return validateRelativeRecipePath('RECIPE_RUNTIME_DIR', env.RECIPE_RUNTIME_DIR || DEFAULT_RECIPE_RUNTIME_DIR);
|
|
26
|
-
}
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# launch.sh — mobile instance identity intake + prepare/preflight launch
|
|
3
|
-
#
|
|
4
|
-
# Purpose:
|
|
5
|
-
# Resolves the per-instance tuple (port, simulator/adb-serial, runtime dir,
|
|
6
|
-
# platform, preflight mode) and runs the installed runner's mobile prepare.
|
|
7
|
-
#
|
|
8
|
-
# Inputs (flags / env precedence):
|
|
9
|
-
# --target <metamask-mobile> (default $PWD)
|
|
10
|
-
# --platform ios|android (default ios)
|
|
11
|
-
# --preflight-mode|--mode fast|auto|default|rebuild-native|clean
|
|
12
|
-
# (env RECIPE_HARNESS_MOBILE_PREFLIGHT_MODE, default fast)
|
|
13
|
-
# --port|--watcher-port|--cdp-port <port> (env WATCHER_PORT > METRO_PORT > CDP_PORT)
|
|
14
|
-
# --simulator <sim> (env IOS_SIMULATOR > SIM_UDID)
|
|
15
|
-
# --adb-serial <serial> (env ADB_SERIAL > ANDROID_SERIAL)
|
|
16
|
-
# --runtime-dir <dir> (env RECIPE_RUNTIME_DIR)
|
|
17
|
-
# --wallet-setup / --no-wallet-setup, --wallet-fixture <json>
|
|
18
|
-
# --artifacts-dir <dir>
|
|
19
|
-
#
|
|
20
|
-
# Outputs:
|
|
21
|
-
# <artifacts>/summary.json (adapter/action/status/runtime/generatedAt),
|
|
22
|
-
# <artifacts>/runtime-status.json, <artifacts>/logs/prepare.log
|
|
23
|
-
# Exit 0 — launch pass; 1 — runner missing or prepare failed; 2 — bad args.
|
|
24
|
-
#
|
|
25
|
-
# Never touches: product source files; anything outside the target's harness
|
|
26
|
-
# dir and the artifacts dir.
|
|
27
|
-
set -euo pipefail
|
|
28
|
-
|
|
29
|
-
TARGET="$PWD"
|
|
30
|
-
PLATFORM="ios"
|
|
31
|
-
PREFLIGHT_MODE="${RECIPE_HARNESS_MOBILE_PREFLIGHT_MODE:-fast}"
|
|
32
|
-
ARTIFACTS=""
|
|
33
|
-
PORT="${WATCHER_PORT:-${METRO_PORT:-${CDP_PORT:-}}}"
|
|
34
|
-
SIMULATOR="${IOS_SIMULATOR:-${SIM_UDID:-}}"
|
|
35
|
-
ADB_SERIAL_ARG="${ADB_SERIAL:-${ANDROID_SERIAL:-}}"
|
|
36
|
-
RUNTIME_DIR_ARG="${RECIPE_RUNTIME_DIR:-}"
|
|
37
|
-
WALLET_SETUP=false
|
|
38
|
-
WALLET_FIXTURE_ARG=""
|
|
39
|
-
require_value() { [ "$#" -ge 2 ] || { echo "Missing value for $1" >&2; exit 2; }; }
|
|
40
|
-
while [ "$#" -gt 0 ]; do
|
|
41
|
-
case "$1" in
|
|
42
|
-
--target) require_value "$@"; TARGET="$2"; shift 2 ;;
|
|
43
|
-
--platform) require_value "$@"; PLATFORM="$2"; shift 2 ;;
|
|
44
|
-
--preflight-mode|--mode) require_value "$@"; PREFLIGHT_MODE="$2"; shift 2 ;;
|
|
45
|
-
--artifacts-dir) require_value "$@"; ARTIFACTS="$2"; shift 2 ;;
|
|
46
|
-
--port|--watcher-port|--cdp-port) require_value "$@"; PORT="$2"; shift 2 ;;
|
|
47
|
-
--simulator) require_value "$@"; SIMULATOR="$2"; shift 2 ;;
|
|
48
|
-
--adb-serial) require_value "$@"; ADB_SERIAL_ARG="$2"; shift 2 ;;
|
|
49
|
-
--runtime-dir) require_value "$@"; RUNTIME_DIR_ARG="$2"; shift 2 ;;
|
|
50
|
-
--wallet-setup) WALLET_SETUP=true; shift ;;
|
|
51
|
-
--no-wallet-setup) WALLET_SETUP=false; shift ;;
|
|
52
|
-
--wallet-fixture) require_value "$@"; WALLET_FIXTURE_ARG="$2"; shift 2 ;;
|
|
53
|
-
-h|--help)
|
|
54
|
-
echo "Usage: launch.sh [--target <metamask-mobile>] [--platform ios|android] [--preflight-mode fast|auto|default|rebuild-native|clean] [--port <port>] [--simulator <sim>] [--runtime-dir <dir>] [--wallet-setup] [--wallet-fixture <json>]"
|
|
55
|
-
exit 0
|
|
56
|
-
;;
|
|
57
|
-
*) echo "Unknown arg: $1" >&2; exit 2 ;;
|
|
58
|
-
esac
|
|
59
|
-
done
|
|
60
|
-
|
|
61
|
-
case "$PLATFORM" in ios|android) ;; *) echo "Unknown --platform: $PLATFORM" >&2; exit 2 ;; esac
|
|
62
|
-
case "$PREFLIGHT_MODE" in fast|auto|default|rebuild-native|clean) ;; *) echo "Unknown --preflight-mode: $PREFLIGHT_MODE" >&2; exit 2 ;; esac
|
|
63
|
-
|
|
64
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
65
|
-
# shellcheck disable=SC1091
|
|
66
|
-
for _hp in "$SCRIPT_DIR/lib/harness-path.sh" "$SCRIPT_DIR/../lib/harness-path.sh"; do
|
|
67
|
-
[ -f "$_hp" ] && { . "$_hp"; break; }
|
|
68
|
-
done
|
|
69
|
-
unset _hp
|
|
70
|
-
if ! command -v harness_root >/dev/null 2>&1; then
|
|
71
|
-
echo "mm-harness: shared lib orchestration/lib/harness-path.sh not found; reinstall the runner." >&2
|
|
72
|
-
exit 1
|
|
73
|
-
fi
|
|
74
|
-
TARGET="$(cd "$TARGET" && pwd)"
|
|
75
|
-
HARNESS_DIR="$(harness_dir "$TARGET" mobile)"
|
|
76
|
-
RUNNER_BIN="$HARNESS_DIR/runner/bin/metamask-recipe"
|
|
77
|
-
ARTIFACTS="${ARTIFACTS:-$HARNESS_DIR/launch/$(date -u +%Y%m%dT%H%M%SZ)}"
|
|
78
|
-
mkdir -p "$ARTIFACTS/logs"
|
|
79
|
-
|
|
80
|
-
if [ ! -x "$RUNNER_BIN" ]; then
|
|
81
|
-
echo "Mobile recipe runner is not installed in $TARGET. Run metamask-recipe mobile install --target $TARGET first." >&2
|
|
82
|
-
exit 1
|
|
83
|
-
fi
|
|
84
|
-
|
|
85
|
-
prepare_args=(mobile prepare --target "$TARGET" --platform "$PLATFORM" --preflight-mode "$PREFLIGHT_MODE")
|
|
86
|
-
[ -n "$PORT" ] && prepare_args+=(--port "$PORT")
|
|
87
|
-
[ -n "$SIMULATOR" ] && prepare_args+=(--simulator "$SIMULATOR")
|
|
88
|
-
[ -n "$ADB_SERIAL_ARG" ] && prepare_args+=(--adb-serial "$ADB_SERIAL_ARG")
|
|
89
|
-
[ -n "$RUNTIME_DIR_ARG" ] && prepare_args+=(--runtime-dir "$RUNTIME_DIR_ARG")
|
|
90
|
-
$WALLET_SETUP && prepare_args+=(--wallet-setup)
|
|
91
|
-
[ -n "$WALLET_FIXTURE_ARG" ] && prepare_args+=(--wallet-fixture "$WALLET_FIXTURE_ARG")
|
|
92
|
-
|
|
93
|
-
status="pass"
|
|
94
|
-
if (cd "$TARGET" && "$RUNNER_BIN" "${prepare_args[@]}") > "$ARTIFACTS/logs/prepare.log" 2>&1; then
|
|
95
|
-
runtime_status="pass"
|
|
96
|
-
else
|
|
97
|
-
runtime_status="fail"
|
|
98
|
-
status="fail"
|
|
99
|
-
fi
|
|
100
|
-
|
|
101
|
-
status_args=(mobile runtime-status --target "$TARGET" --json)
|
|
102
|
-
[ -n "$PORT" ] && status_args+=(--port "$PORT")
|
|
103
|
-
[ -n "$RUNTIME_DIR_ARG" ] && status_args+=(--runtime-dir "$RUNTIME_DIR_ARG")
|
|
104
|
-
(cd "$TARGET" && "$RUNNER_BIN" "${status_args[@]}") > "$ARTIFACTS/runtime-status.json" 2> "$ARTIFACTS/logs/runtime-status.err" || true
|
|
105
|
-
|
|
106
|
-
TARGET_FOR_SUMMARY="$TARGET" ARTIFACTS_FOR_SUMMARY="$ARTIFACTS" STATUS_FOR_SUMMARY="$status" RUNTIME_STATUS="$runtime_status" PLATFORM_FOR_SUMMARY="$PLATFORM" MODE_FOR_SUMMARY="$PREFLIGHT_MODE" node <<'NODE'
|
|
107
|
-
const fs = require('fs');
|
|
108
|
-
const path = require('path');
|
|
109
|
-
const artifacts = process.env.ARTIFACTS_FOR_SUMMARY;
|
|
110
|
-
let runtime = null;
|
|
111
|
-
try { runtime = JSON.parse(fs.readFileSync(path.join(artifacts, 'runtime-status.json'), 'utf8')); } catch {}
|
|
112
|
-
fs.writeFileSync(path.join(artifacts, 'summary.json'), `${JSON.stringify({
|
|
113
|
-
adapter: 'mobile',
|
|
114
|
-
action: 'launch',
|
|
115
|
-
status: process.env.STATUS_FOR_SUMMARY,
|
|
116
|
-
platform: process.env.PLATFORM_FOR_SUMMARY,
|
|
117
|
-
preflightMode: process.env.MODE_FOR_SUMMARY,
|
|
118
|
-
target: process.env.TARGET_FOR_SUMMARY,
|
|
119
|
-
runtime: {
|
|
120
|
-
status: process.env.RUNTIME_STATUS,
|
|
121
|
-
ready: runtime?.ready === true,
|
|
122
|
-
reason: runtime?.reason || null,
|
|
123
|
-
port: runtime?.metro?.port || null,
|
|
124
|
-
prepareLogPath: path.join(artifacts, 'logs/prepare.log'),
|
|
125
|
-
runtimeStatusPath: path.join(artifacts, 'runtime-status.json'),
|
|
126
|
-
},
|
|
127
|
-
runtimePolicy: {
|
|
128
|
-
nativeBuildPolicy: process.env.MODE_FOR_SUMMARY === 'fast'
|
|
129
|
-
? 'fast mode never runs a native build; it requires an installed dev client, starts/reuses Metro, prewarms the JS bundle, opens the dev client, and waits for the bridge'
|
|
130
|
-
: 'non-fast modes may install or rebuild the native dev client when needed; then start/reuse Metro, prewarm the JS bundle, open the dev client, and wait for the bridge',
|
|
131
|
-
},
|
|
132
|
-
generatedAt: new Date().toISOString(),
|
|
133
|
-
}, null, 2)}\n`);
|
|
134
|
-
NODE
|
|
135
|
-
|
|
136
|
-
echo "Mobile harness launch $status: $ARTIFACTS/summary.json"
|
|
137
|
-
[ "$status" = "pass" ]
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# live.sh — mobile launch-then-verify chaining for one live slot run
|
|
3
|
-
#
|
|
4
|
-
# Purpose:
|
|
5
|
-
# Runs launch.sh then verify.sh (verify with --no-auto-start; launch owns
|
|
6
|
-
# startup) under a timestamped artifact dir and aggregates both results.
|
|
7
|
-
#
|
|
8
|
-
# Inputs (flags / env):
|
|
9
|
-
# --target <metamask-mobile> (default $PWD)
|
|
10
|
-
# --platform ios|android (default ios)
|
|
11
|
-
# --preflight-mode|--mode fast|auto|default|rebuild-native|clean
|
|
12
|
-
# (env RECIPE_HARNESS_MOBILE_PREFLIGHT_MODE, default fast)
|
|
13
|
-
# --artifacts-dir <dir> (default <harness>/live/<UTC>)
|
|
14
|
-
# --wallet-setup|--no-wallet-setup (default auto), --wallet-fixture <json>
|
|
15
|
-
#
|
|
16
|
-
# Outputs:
|
|
17
|
-
# <artifacts>/summary.json (adapter/action/status, launch/verify exit codes
|
|
18
|
-
# + child summary paths, easyCommand), <artifacts>/{launch,verify}/.
|
|
19
|
-
# Exit 0 — launch and verify both passed; 1 — either failed; 2 — bad args.
|
|
20
|
-
#
|
|
21
|
-
# Never touches: product source files; anything outside the target's harness
|
|
22
|
-
# dir and the artifacts dir.
|
|
23
|
-
set -euo pipefail
|
|
24
|
-
|
|
25
|
-
TARGET="$PWD"
|
|
26
|
-
PLATFORM="ios"
|
|
27
|
-
PREFLIGHT_MODE="${RECIPE_HARNESS_MOBILE_PREFLIGHT_MODE:-fast}"
|
|
28
|
-
ARTIFACTS=""
|
|
29
|
-
WALLET_SETUP="auto"
|
|
30
|
-
WALLET_FIXTURE=""
|
|
31
|
-
while [ "$#" -gt 0 ]; do
|
|
32
|
-
case "$1" in
|
|
33
|
-
--target) TARGET="$2"; shift 2 ;;
|
|
34
|
-
--platform) PLATFORM="$2"; shift 2 ;;
|
|
35
|
-
--preflight-mode|--mode) PREFLIGHT_MODE="$2"; shift 2 ;;
|
|
36
|
-
--artifacts-dir) ARTIFACTS="$2"; shift 2 ;;
|
|
37
|
-
--wallet-setup) WALLET_SETUP="true"; shift ;;
|
|
38
|
-
--no-wallet-setup) WALLET_SETUP="false"; shift ;;
|
|
39
|
-
--wallet-fixture) WALLET_FIXTURE="$2"; shift 2 ;;
|
|
40
|
-
-h|--help) echo "Usage: live.sh [--target <metamask-mobile>] [--platform ios|android] [--preflight-mode fast|auto|default|rebuild-native|clean] [--artifacts-dir <dir>] [--wallet-setup|--no-wallet-setup]"; exit 0 ;;
|
|
41
|
-
*) echo "Unknown arg: $1" >&2; exit 2 ;;
|
|
42
|
-
esac
|
|
43
|
-
done
|
|
44
|
-
|
|
45
|
-
case "$PLATFORM" in ios|android) ;; *) echo "Unknown --platform: $PLATFORM" >&2; exit 2 ;; esac
|
|
46
|
-
case "$PREFLIGHT_MODE" in fast|auto|default|rebuild-native|clean) ;; *) echo "Unknown --preflight-mode: $PREFLIGHT_MODE" >&2; exit 2 ;; esac
|
|
47
|
-
|
|
48
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
49
|
-
# shellcheck disable=SC1091
|
|
50
|
-
for _hp in "$SCRIPT_DIR/lib/harness-path.sh" "$SCRIPT_DIR/../lib/harness-path.sh"; do
|
|
51
|
-
[ -f "$_hp" ] && { . "$_hp"; break; }
|
|
52
|
-
done
|
|
53
|
-
unset _hp
|
|
54
|
-
if ! command -v harness_root >/dev/null 2>&1; then
|
|
55
|
-
echo "mm-harness: shared lib orchestration/lib/harness-path.sh not found; reinstall the runner." >&2
|
|
56
|
-
exit 1
|
|
57
|
-
fi
|
|
58
|
-
# verify.sh is a recipe-tree feature: co-located in installed copies, under
|
|
59
|
-
# runner/mobile in a repo checkout.
|
|
60
|
-
VERIFY_SH=""
|
|
61
|
-
for _v in "$SCRIPT_DIR/verify.sh" "$SCRIPT_DIR/../../runner/mobile/verify.sh"; do
|
|
62
|
-
[ -f "$_v" ] && { VERIFY_SH="$_v"; break; }
|
|
63
|
-
done
|
|
64
|
-
unset _v
|
|
65
|
-
if [ -z "$VERIFY_SH" ]; then
|
|
66
|
-
echo "mm-harness: mobile verify.sh not found next to $SCRIPT_DIR; reinstall the runner." >&2
|
|
67
|
-
exit 1
|
|
68
|
-
fi
|
|
69
|
-
TARGET="$(cd "$TARGET" && pwd)"
|
|
70
|
-
WALLET_FIXTURE="${WALLET_FIXTURE:-$(recipe_runtime_dir)/wallet-fixture.json}"
|
|
71
|
-
ARTIFACTS="${ARTIFACTS:-$(harness_dir "$TARGET" mobile)/live/$(date -u +%Y%m%dT%H%M%SZ)}"
|
|
72
|
-
mkdir -p "$ARTIFACTS"
|
|
73
|
-
|
|
74
|
-
launch_args=(--target "$TARGET" --platform "$PLATFORM" --preflight-mode "$PREFLIGHT_MODE" --artifacts-dir "$ARTIFACTS/launch")
|
|
75
|
-
case "$WALLET_SETUP" in
|
|
76
|
-
true) launch_args+=(--wallet-setup) ;;
|
|
77
|
-
false) launch_args+=(--no-wallet-setup) ;;
|
|
78
|
-
esac
|
|
79
|
-
launch_args+=(--wallet-fixture "$WALLET_FIXTURE")
|
|
80
|
-
|
|
81
|
-
echo "Mobile live validation command:"
|
|
82
|
-
echo " mm-harness launch $PLATFORM --verify"
|
|
83
|
-
echo "Launch artifacts: $ARTIFACTS/launch"
|
|
84
|
-
echo "Verify artifacts: $ARTIFACTS/verify"
|
|
85
|
-
|
|
86
|
-
set +e
|
|
87
|
-
"$SCRIPT_DIR/launch.sh" "${launch_args[@]}"
|
|
88
|
-
launch_status=$?
|
|
89
|
-
set -e
|
|
90
|
-
|
|
91
|
-
verify_status=1
|
|
92
|
-
if [ "$launch_status" -eq 0 ]; then
|
|
93
|
-
set +e
|
|
94
|
-
"$VERIFY_SH" --target "$TARGET" --platform "$PLATFORM" --preflight-mode "$PREFLIGHT_MODE" --no-auto-start --artifacts-dir "$ARTIFACTS/verify"
|
|
95
|
-
verify_status=$?
|
|
96
|
-
set -e
|
|
97
|
-
else
|
|
98
|
-
echo "Skipping Mobile live verify because launch failed; see $ARTIFACTS/launch/summary.json" >&2
|
|
99
|
-
fi
|
|
100
|
-
|
|
101
|
-
TARGET_FOR_SUMMARY="$TARGET" ARTIFACTS_FOR_SUMMARY="$ARTIFACTS" PLATFORM_FOR_SUMMARY="$PLATFORM" MODE_FOR_SUMMARY="$PREFLIGHT_MODE" LAUNCH_STATUS="$launch_status" VERIFY_STATUS="$verify_status" node <<'NODE'
|
|
102
|
-
const fs = require('fs');
|
|
103
|
-
const path = require('path');
|
|
104
|
-
const artifacts = process.env.ARTIFACTS_FOR_SUMMARY;
|
|
105
|
-
const launchSummary = path.join(artifacts, 'launch', 'summary.json');
|
|
106
|
-
const verifySummary = path.join(artifacts, 'verify', 'summary.json');
|
|
107
|
-
const launchStatus = Number(process.env.LAUNCH_STATUS);
|
|
108
|
-
const verifyStatus = Number(process.env.VERIFY_STATUS);
|
|
109
|
-
fs.writeFileSync(path.join(artifacts, 'summary.json'), `${JSON.stringify({
|
|
110
|
-
adapter: 'mobile',
|
|
111
|
-
action: 'live',
|
|
112
|
-
status: launchStatus === 0 && verifyStatus === 0 ? 'pass' : 'fail',
|
|
113
|
-
target: process.env.TARGET_FOR_SUMMARY,
|
|
114
|
-
platform: process.env.PLATFORM_FOR_SUMMARY,
|
|
115
|
-
preflightMode: process.env.MODE_FOR_SUMMARY,
|
|
116
|
-
launch: { exitCode: launchStatus, summaryPath: fs.existsSync(launchSummary) ? launchSummary : null },
|
|
117
|
-
verify: { exitCode: verifyStatus, summaryPath: fs.existsSync(verifySummary) ? verifySummary : null },
|
|
118
|
-
easyCommand: `mm-harness launch ${process.env.PLATFORM_FOR_SUMMARY} --verify`,
|
|
119
|
-
note: 'Runs launch then live verify so a developer can validate app startup, runner-owned CDP bridge, screenshot capture, and tiny recipe control from one runner-owned command.',
|
|
120
|
-
generatedAt: new Date().toISOString(),
|
|
121
|
-
}, null, 2)}\n`);
|
|
122
|
-
NODE
|
|
123
|
-
|
|
124
|
-
echo "Mobile live validation summary: $ARTIFACTS/summary.json"
|
|
125
|
-
[ "$launch_status" -eq 0 ] && [ "$verify_status" -eq 0 ]
|