@deeeed/metamask-harness 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +161 -0
- package/README.md +140 -0
- package/bin/mm-harness +99 -0
- package/docs/CHEATSHEET.md +61 -0
- package/docs/CLI-SPEC.md +915 -0
- package/docs/MENTAL-MODEL.md +295 -0
- package/docs/architecture.md +367 -0
- package/docs/extension-runtime-commands.md +60 -0
- package/docs/harness-cli.md +43 -0
- package/docs/live-adapter-contract.md +188 -0
- package/docs/package-boundaries.md +47 -0
- package/docs/perps-flow-catalog.md +235 -0
- package/docs/recipe-libraries.md +95 -0
- package/docs/runtime-file-conventions.md +36 -0
- package/library/actions/core/perps/_controller.mjs +727 -0
- package/library/actions/core/perps/assert_orders.mjs +53 -0
- package/library/actions/core/perps/assert_positions.mjs +52 -0
- package/library/actions/core/perps/close_orders.mjs +97 -0
- package/library/actions/core/perps/close_positions.mjs +118 -0
- package/library/actions/core/perps/ensure_orders.mjs +40 -0
- package/library/actions/core/perps/ensure_positions.mjs +37 -0
- package/library/actions/core/perps/place_order.mjs +201 -0
- package/library/actions/core/perps/read_account.mjs +30 -0
- package/library/actions/core/perps/read_orders.mjs +27 -0
- package/library/actions/core/perps/read_positions.mjs +27 -0
- package/library/actions/core/perps/start_state.mjs +92 -0
- package/library/actions/core/perps/teardown_state.mjs +86 -0
- package/library/actions/extension/perps/assert_orders.mjs +11 -0
- package/library/actions/extension/perps/assert_positions.mjs +11 -0
- package/library/actions/extension/perps/close_orders.mjs +8 -0
- package/library/actions/extension/perps/close_positions.mjs +8 -0
- package/library/actions/extension/perps/ensure_orders.mjs +4 -0
- package/library/actions/extension/perps/ensure_positions.mjs +4 -0
- package/library/actions/extension/perps/perps.mjs +730 -0
- package/library/actions/extension/perps/place_order.mjs +7 -0
- package/library/actions/extension/perps/read_orders.mjs +4 -0
- package/library/actions/extension/perps/read_positions.mjs +3 -0
- package/library/actions/extension/platform/cdp.mjs +541 -0
- package/library/actions/extension/ui/navigate.mjs +44 -0
- package/library/actions/extension/wallet/ensure_unlocked.mjs +36 -0
- package/library/actions/extension/wallet/read_state.mjs +27 -0
- package/library/actions/extension/wallet/select_account.mjs +48 -0
- package/library/actions/extension/wallet/setup.mjs +35 -0
- package/library/actions/mobile/app-overlay/app/dev-tools/AgenticService/AgentStepHud.tsx.patch +185 -0
- package/library/actions/mobile/app-overlay/app/dev-tools/AgenticService/AgenticService.ts.patch +1662 -0
- package/library/actions/mobile/bridge-runtime/cdp-bridge.cjs +686 -0
- package/library/actions/mobile/bridge-runtime/lib/cdp-eval.cjs +110 -0
- package/library/actions/mobile/bridge-runtime/lib/config.cjs +39 -0
- package/library/actions/mobile/bridge-runtime/lib/issue-capture.cjs +446 -0
- package/library/actions/mobile/bridge-runtime/lib/target-discovery.cjs +204 -0
- package/library/actions/mobile/bridge-runtime/lib/ws-client.cjs +108 -0
- package/library/actions/mobile/bridge-runtime/setup-wallet.sh +442 -0
- package/library/actions/mobile/perps/assert_orders.mjs +11 -0
- package/library/actions/mobile/perps/assert_positions.mjs +11 -0
- package/library/actions/mobile/perps/close_orders.mjs +8 -0
- package/library/actions/mobile/perps/close_positions.mjs +8 -0
- package/library/actions/mobile/perps/ensure_orders.mjs +4 -0
- package/library/actions/mobile/perps/ensure_positions.mjs +4 -0
- package/library/actions/mobile/perps/perps.mjs +709 -0
- package/library/actions/mobile/perps/place_order.mjs +7 -0
- package/library/actions/mobile/perps/read_orders.mjs +4 -0
- package/library/actions/mobile/perps/read_positions.mjs +3 -0
- package/library/actions/mobile/platform/bridge.mjs +283 -0
- package/library/actions/mobile/ui/navigate.mjs +38 -0
- package/library/actions/mobile/wallet/ensure_unlocked.mjs +107 -0
- package/library/actions/mobile/wallet/home.mjs +35 -0
- package/library/actions/mobile/wallet/read_state.mjs +40 -0
- package/library/actions/mobile/wallet/select_account.mjs +48 -0
- package/library/actions/mobile/wallet/setup.mjs +220 -0
- package/library/flows/perps.flows.json +64 -0
- package/library/library.json +7 -0
- package/library/manifests/core.action-manifest.json +1282 -0
- package/library/manifests/extension.action-manifest.json +1749 -0
- package/library/manifests/mobile.action-manifest.json +1753 -0
- package/library/recipes/action-validation.extension.recipe.json +417 -0
- package/library/recipes/action-validation.mobile.recipe.json +422 -0
- package/library/recipes/order-lifecycle.core.recipe.json +78 -0
- package/library/recipes/perps-lifecycle.recipe.json +194 -0
- package/library/recipes/read-markets.core.recipe.json +38 -0
- package/library/recipes/smoke.extension.recipe.json +31 -0
- package/library/recipes/smoke.mobile.recipe.json +31 -0
- package/library/recipes/trading-lifecycle.core.recipe.json +76 -0
- package/orchestration/compat-overlays/README.md +19 -0
- package/orchestration/compat-overlays/mobile/README.md +13 -0
- package/orchestration/compat-overlays/mobile/rn81-message-event-source.patch +42 -0
- package/orchestration/core/cleanup.sh +37 -0
- package/orchestration/core/inject.sh +154 -0
- package/orchestration/doctor.mjs +72 -0
- package/orchestration/extension/cleanup.mjs +60 -0
- package/orchestration/extension/console-tail.mjs +228 -0
- package/orchestration/extension/ensure-browser.sh +416 -0
- package/orchestration/extension/ensure-ready.ts +185 -0
- package/orchestration/extension/extension-id.ts +107 -0
- package/orchestration/extension/inject.mjs +266 -0
- package/orchestration/extension/launch-browser.cjs +216 -0
- package/orchestration/extension/launch.sh +175 -0
- package/orchestration/extension/live.sh +320 -0
- package/orchestration/extension/pin-remote-flags.cjs +45 -0
- package/orchestration/extension/readiness.mjs +414 -0
- package/orchestration/extension/refresh-build.sh +190 -0
- package/orchestration/extension/runtime-decision.ts +445 -0
- package/orchestration/extension/runtime.ts +407 -0
- package/orchestration/extension/seed-fixture.sh +177 -0
- package/orchestration/extension/sidepanel-toggle.sh +291 -0
- package/orchestration/extension/snapshot-dist.sh +84 -0
- package/orchestration/extension/start-watch.sh +339 -0
- package/orchestration/extension/wallet-fixture-state.cjs +1086 -0
- package/orchestration/lib/activate-repo-node.sh +144 -0
- package/orchestration/lib/cli-color.mjs +84 -0
- package/orchestration/lib/cli-commands.mjs +243 -0
- package/orchestration/lib/cli-home.mjs +354 -0
- package/orchestration/lib/cli-ux.sh +252 -0
- package/orchestration/lib/cli-version.mjs +123 -0
- package/orchestration/lib/ensure-runner-deps.sh +56 -0
- package/orchestration/lib/harness-path.sh +55 -0
- package/orchestration/lib/hash-helpers.sh +44 -0
- package/orchestration/lib/json-field.sh +23 -0
- package/orchestration/lib/log-tui.mjs +304 -0
- package/orchestration/lib/open-debug.mjs +317 -0
- package/orchestration/lib/path-defaults.json +4 -0
- package/orchestration/lib/progress.mjs +107 -0
- package/orchestration/lib/recipe-paths.mjs +26 -0
- package/orchestration/lib/resolve-farmslot-ports.sh +144 -0
- package/orchestration/manifest.json +358 -0
- package/orchestration/mobile/cleanup.sh +192 -0
- package/orchestration/mobile/deps-markers.ts +21 -0
- package/orchestration/mobile/inject.sh +681 -0
- package/orchestration/mobile/launch.sh +137 -0
- package/orchestration/mobile/live.sh +125 -0
- package/orchestration/mobile/runtime-decision.ts +292 -0
- package/orchestration/porcelain/metamask-recipe +99 -0
- package/orchestration/porcelain/mm-recipe +1591 -0
- package/orchestration/porcelain/mme-recipe +1181 -0
- package/package.json +59 -0
- package/runner/extension/verify.sh +511 -0
- package/runner/mobile/verify.sh +501 -0
- package/runner/src/adapters.ts +601 -0
- package/runner/src/cli.ts +1820 -0
- package/runner/src/commands/debug.ts +44 -0
- package/runner/src/commands/fixtures.ts +99 -0
- package/runner/src/commands/launch.ts +397 -0
- package/runner/src/commands/logs.ts +60 -0
- package/runner/src/commands/shared.ts +138 -0
- package/runner/src/completions-cache.ts +86 -0
- package/runner/src/doctor.ts +203 -0
- package/runner/src/harness.ts +516 -0
- package/runner/src/heal-bounds.ts +179 -0
- package/runner/src/index.ts +6 -0
- package/runner/src/live-adapter-contract.ts +274 -0
- package/runner/src/manifest.ts +47 -0
- package/runner/src/mm-harness-cli.ts +488 -0
- package/runner/src/paths.ts +198 -0
- package/runner/src/recording-target.ts +147 -0
- package/runner/src/run-recording.ts +329 -0
- package/runner/src/runner.ts +108 -0
- package/runner/src/types.ts +57 -0
- package/scripts/completions.sh +125 -0
- package/scripts/install-completions.sh +62 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# activate-repo-node.sh — pin the target repo's declared Node for non-interactive spawns
|
|
2
|
+
#
|
|
3
|
+
# Sourced by orchestration scripts that run yarn/webpack in tmux, nohup, or other
|
|
4
|
+
# environments whose PATH may not match an interactive dev shell (.tool-versions and
|
|
5
|
+
# asdf shims are not applied automatically).
|
|
6
|
+
#
|
|
7
|
+
# On success sets:
|
|
8
|
+
# REPO_NODE_VERSION — e.g. 24.13.0 (no leading v)
|
|
9
|
+
# REPO_NODE_BIN — absolute path to the node binary
|
|
10
|
+
# REPO_NODE_BIN_DIR — dirname of REPO_NODE_BIN
|
|
11
|
+
# REPO_NODE_PATH_PREFIX — "${REPO_NODE_BIN_DIR}:" when resolved, else empty
|
|
12
|
+
#
|
|
13
|
+
# Also prepends REPO_NODE_BIN_DIR to PATH in the current shell when resolved.
|
|
14
|
+
#
|
|
15
|
+
# Usage (after cd to target repo, or pass directory as $1):
|
|
16
|
+
# . /path/to/activate-repo-node.sh
|
|
17
|
+
# activate_repo_node [target-dir]
|
|
18
|
+
#
|
|
19
|
+
# Returns 0 when a declared version exists and a matching node binary was resolved;
|
|
20
|
+
# 1 when no version is declared; 2 when declared but no manager provided it.
|
|
21
|
+
|
|
22
|
+
_repo_node_matches_req() {
|
|
23
|
+
local req="$1" actual="${2:-}"
|
|
24
|
+
[ -n "$actual" ] || actual="$("$REPO_NODE_BIN" -v 2>/dev/null || true)"
|
|
25
|
+
[ -n "$actual" ] || return 1
|
|
26
|
+
actual="${actual#v}"
|
|
27
|
+
[ "$actual" = "$req" ] && return 0
|
|
28
|
+
case "$req" in
|
|
29
|
+
*.*.*) return 1 ;;
|
|
30
|
+
*.*) [[ "$actual" == "$req."* ]] ;;
|
|
31
|
+
*) [[ "$actual" == "$req"* ]] ;;
|
|
32
|
+
esac
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
_repo_node_set_from_bin() {
|
|
36
|
+
local candidate="$1"
|
|
37
|
+
[ -n "$candidate" ] && [ -x "$candidate" ] || return 1
|
|
38
|
+
REPO_NODE_BIN="$candidate"
|
|
39
|
+
REPO_NODE_BIN_DIR="$(cd "$(dirname "$REPO_NODE_BIN")" && pwd)"
|
|
40
|
+
REPO_NODE_PATH_PREFIX="${REPO_NODE_BIN_DIR}:"
|
|
41
|
+
export PATH="${REPO_NODE_BIN_DIR}:${PATH}"
|
|
42
|
+
return 0
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
_repo_node_try_req() {
|
|
46
|
+
local target="$1" req="$2" manager=""
|
|
47
|
+
[ -n "$req" ] || return 1
|
|
48
|
+
REPO_NODE_VERSION="$req"
|
|
49
|
+
|
|
50
|
+
if command -v asdf >/dev/null 2>&1; then
|
|
51
|
+
local asdf_root
|
|
52
|
+
asdf_root="$(asdf where nodejs "$req" 2>/dev/null || true)"
|
|
53
|
+
if [ -n "$asdf_root" ] && _repo_node_set_from_bin "$asdf_root/bin/node" && _repo_node_matches_req "$req"; then
|
|
54
|
+
manager=asdf
|
|
55
|
+
echo "[env] node $("$REPO_NODE_BIN" -v) from .tool-versions/.nvmrc via asdf ($req)" >&2
|
|
56
|
+
return 0
|
|
57
|
+
fi
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
if command -v fnm >/dev/null 2>&1; then
|
|
61
|
+
local fnm_bin
|
|
62
|
+
fnm_bin="$(fnm exec --using "$req" -- which node 2>/dev/null || true)"
|
|
63
|
+
if _repo_node_set_from_bin "$fnm_bin" && _repo_node_matches_req "$req"; then
|
|
64
|
+
echo "[env] node $("$REPO_NODE_BIN" -v) from .tool-versions/.nvmrc via fnm ($req)" >&2
|
|
65
|
+
return 0
|
|
66
|
+
fi
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
if command -v mise >/dev/null 2>&1; then
|
|
70
|
+
local mise_bin
|
|
71
|
+
mise_bin="$(mise x -C "$target" -- which node 2>/dev/null || true)"
|
|
72
|
+
if _repo_node_set_from_bin "$mise_bin" && _repo_node_matches_req "$req"; then
|
|
73
|
+
echo "[env] node $("$REPO_NODE_BIN" -v) from .tool-versions/.nvmrc via mise ($req)" >&2
|
|
74
|
+
return 0
|
|
75
|
+
fi
|
|
76
|
+
fi
|
|
77
|
+
return 1
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
activate_repo_node() {
|
|
81
|
+
local target="${1:-.}"
|
|
82
|
+
REPO_NODE_VERSION=""
|
|
83
|
+
REPO_NODE_BIN=""
|
|
84
|
+
REPO_NODE_BIN_DIR=""
|
|
85
|
+
REPO_NODE_PATH_PREFIX=""
|
|
86
|
+
|
|
87
|
+
local declared=false tool_ver="" nvmrc="" req="" nvm_dir path_node
|
|
88
|
+
if [ -f "$target/.tool-versions" ]; then
|
|
89
|
+
declared=true
|
|
90
|
+
tool_ver="$(awk '/^nodejs / {print $2; exit}' "$target/.tool-versions")"
|
|
91
|
+
_repo_node_try_req "$target" "$tool_ver" && return 0
|
|
92
|
+
fi
|
|
93
|
+
if [ -f "$target/.nvmrc" ]; then
|
|
94
|
+
declared=true
|
|
95
|
+
nvmrc="$(tr -d ' \t\r\nv' < "$target/.nvmrc")"
|
|
96
|
+
_repo_node_try_req "$target" "$nvmrc" && return 0
|
|
97
|
+
case "$nvmrc" in
|
|
98
|
+
*.*.*) ;;
|
|
99
|
+
*.*) _repo_node_try_req "$target" "${nvmrc}.0" && return 0 ;;
|
|
100
|
+
esac
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
if [ "$declared" != true ]; then
|
|
104
|
+
echo "[env] no node version declared in .nvmrc or .tool-versions" >&2
|
|
105
|
+
return 1
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
req="${tool_ver:-$nvmrc}"
|
|
109
|
+
if [ -z "$req" ]; then
|
|
110
|
+
echo "[env] node version file present but no nodejs entry in .tool-versions / .nvmrc" >&2
|
|
111
|
+
return 1
|
|
112
|
+
fi
|
|
113
|
+
REPO_NODE_VERSION="$req"
|
|
114
|
+
nvm_dir="${NVM_DIR:-$HOME/.nvm}"
|
|
115
|
+
if [ -s "$nvm_dir/nvm.sh" ]; then
|
|
116
|
+
# shellcheck disable=SC1090,SC1091
|
|
117
|
+
. "$nvm_dir/nvm.sh"
|
|
118
|
+
nvm use "$req" >/dev/null 2>&1 || nvm install "$req" >/dev/null 2>&1 || true
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
path_node="$(command -v node 2>/dev/null || true)"
|
|
122
|
+
if _repo_node_set_from_bin "$path_node" && _repo_node_matches_req "$req"; then
|
|
123
|
+
echo "[env] node $("$REPO_NODE_BIN" -v) already on PATH ($req)" >&2
|
|
124
|
+
return 0
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
REPO_NODE_BIN=""
|
|
128
|
+
REPO_NODE_BIN_DIR=""
|
|
129
|
+
REPO_NODE_PATH_PREFIX=""
|
|
130
|
+
echo "[env] need node ${req} (from .nvmrc / .tool-versions) but no installed manager provided it" >&2
|
|
131
|
+
return 2
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
require_repo_node() {
|
|
135
|
+
local target="${1:-.}"
|
|
136
|
+
if ! command -v activate_repo_node >/dev/null 2>&1; then
|
|
137
|
+
echo "[env] activate-repo-node.sh is not sourced; reinstall the harness (recipe sync)" >&2
|
|
138
|
+
return 1
|
|
139
|
+
fi
|
|
140
|
+
activate_repo_node "$target" || {
|
|
141
|
+
echo "[env] refusing to run yarn/webpack without a pinned Node for $target" >&2
|
|
142
|
+
return 1
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* cli-color.mjs — shared ANSI styling for recipe CLI human output.
|
|
4
|
+
*
|
|
5
|
+
* Honors NO_COLOR, RECIPE_NO_COLOR, FORCE_COLOR, RECIPE_COLOR, and TTY detection.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const STYLES = {
|
|
9
|
+
reset: '\x1b[0m',
|
|
10
|
+
bold: '\x1b[1m',
|
|
11
|
+
dim: '\x1b[2m',
|
|
12
|
+
label: '\x1b[1;36m',
|
|
13
|
+
cmd: '\x1b[1m',
|
|
14
|
+
ok: '\x1b[1;32m',
|
|
15
|
+
warn: '\x1b[1;33m',
|
|
16
|
+
err: '\x1b[1;31m',
|
|
17
|
+
info: '\x1b[0;37m',
|
|
18
|
+
accent: '\x1b[1;35m',
|
|
19
|
+
path: '\x1b[2m',
|
|
20
|
+
comment: '\x1b[2m',
|
|
21
|
+
active: '\x1b[1m',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function colorEnabled(stream = process.stderr) {
|
|
25
|
+
if (process.env.NO_COLOR != null && process.env.NO_COLOR !== '' && process.env.NO_COLOR !== '0') {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (process.env.RECIPE_NO_COLOR === '1') return false;
|
|
29
|
+
if (process.env.RECIPE_COLOR === '1' || process.env.FORCE_COLOR === '1') return true;
|
|
30
|
+
return Boolean(stream?.isTTY);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function stripAnsi(text) {
|
|
34
|
+
return String(text).replace(/\x1b\[[0-9;]*m/gu, '');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function color(style, text, { stream = process.stderr } = {}) {
|
|
38
|
+
const value = String(text);
|
|
39
|
+
if (!colorEnabled(stream)) return value;
|
|
40
|
+
const code = STYLES[style];
|
|
41
|
+
if (!code) return value;
|
|
42
|
+
return `${code}${value}${STYLES.reset}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function colorKv(label, value, valueStyle = 'info', { stream = process.stderr } = {}) {
|
|
46
|
+
return `${color('label', `${label}:`, { stream })} ${color(valueStyle, value, { stream })}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function colorStatusWord(word, { stream = process.stderr } = {}) {
|
|
50
|
+
const normalized = String(word || '').toLowerCase();
|
|
51
|
+
if (['up', 'ready', 'yes', 'pass', 'running', 'present', 'healthy'].includes(normalized)) {
|
|
52
|
+
return color('ok', word, { stream });
|
|
53
|
+
}
|
|
54
|
+
if (['down', 'stopped', 'no', 'fail', 'failed', 'missing', 'blocked', 'unknown'].includes(normalized)) {
|
|
55
|
+
return color('err', word, { stream });
|
|
56
|
+
}
|
|
57
|
+
if (['compiling', 'bundling', 'starting', 'waiting', 'warn'].includes(normalized)) {
|
|
58
|
+
return color('warn', word, { stream });
|
|
59
|
+
}
|
|
60
|
+
return color('info', word, { stream });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function classifyLogEvent(event) {
|
|
64
|
+
const text = String(event || '');
|
|
65
|
+
if (/Module build failed|^ERROR in |BUILD FAILED|compiled with [1-9][0-9]* error/iu.test(text)) {
|
|
66
|
+
return 'err';
|
|
67
|
+
}
|
|
68
|
+
if (/compiled successfully|compiled with [0-9]+ warning|PASS runtime-launch|launch pass|verify pass|build complete/iu.test(text)) {
|
|
69
|
+
return 'ok';
|
|
70
|
+
}
|
|
71
|
+
if (/phase [0-9]\/[0-9]|prepare pipeline|snapshot|fixture:|dist-freshness|build-health/iu.test(text)) {
|
|
72
|
+
return 'label';
|
|
73
|
+
}
|
|
74
|
+
if (/webpack [0-9]+%|Bundl(ed|ing)|(iOS|Android).*%/iu.test(text)) {
|
|
75
|
+
return 'warn';
|
|
76
|
+
}
|
|
77
|
+
return 'info';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function colorLogEvent(event, { latest = false, stream = process.stderr } = {}) {
|
|
81
|
+
const style = classifyLogEvent(event);
|
|
82
|
+
const painted = color(style === 'label' ? 'label' : style, event, { stream });
|
|
83
|
+
return latest ? color('active', painted, { stream }) : painted;
|
|
84
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* cli-commands.mjs — single source of truth for recipe CLI tab completion.
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
|
|
9
|
+
/** @typedef {{ name: string, aliases?: string[], desc?: string, flags?: string[], args?: string[] }} CommandSpec */
|
|
10
|
+
|
|
11
|
+
/** @type {{ shared: CommandSpec[], extension: CommandSpec[], mobile: CommandSpec[], shim: CommandSpec[] }} */
|
|
12
|
+
export const SPEC = {
|
|
13
|
+
shim: [
|
|
14
|
+
{ name: 'sync', desc: 'Copy fixture, write slot context, install harness', flags: ['--dry-run', '--no-install'] },
|
|
15
|
+
{ name: 'help', aliases: ['-h', '--help'], desc: 'Show usage' },
|
|
16
|
+
],
|
|
17
|
+
shared: [
|
|
18
|
+
{ name: 'status', aliases: ['health', 'home'], desc: 'Home status + next commands', flags: ['--json'] },
|
|
19
|
+
{ name: 'ports', desc: 'Slot ports and runtime paths', flags: ['--json'] },
|
|
20
|
+
{ name: 'up', desc: 'Decide + run minimum work to reach ready', flags: ['--json', '--dry-run'] },
|
|
21
|
+
{ name: 'sync', desc: 'Refresh harness + canonicalize wallet fixture', flags: ['--json'] },
|
|
22
|
+
{ name: 'logs', aliases: ['tail'], desc: 'Compact build events or full log', flags: ['--full', '-f'] },
|
|
23
|
+
{ name: 'debug', aliases: ['devtools', 'inspect'], desc: 'Open DevTools UI', flags: ['--json', '--no-open'] },
|
|
24
|
+
{ name: 'actions', desc: 'List runnable recipe actions', flags: ['--json'] },
|
|
25
|
+
{ name: 'doctor', desc: 'Check harness/orchestration health', flags: ['--json'] },
|
|
26
|
+
{ name: 'run', desc: 'Execute a proof recipe', args: ['recipe.json'] },
|
|
27
|
+
{ name: 'interactive', aliases: ['menu'], desc: 'Interactive command menu' },
|
|
28
|
+
{ name: 'prepare', desc: 'Install harness (+ optional validate)', flags: ['--target', '--runtime-dir', '--json'] },
|
|
29
|
+
{ name: 'runtime-status', desc: 'Structured runtime status JSON', flags: ['--json', '--target', '--cdp-port', '--runtime-dir'] },
|
|
30
|
+
],
|
|
31
|
+
extension: [
|
|
32
|
+
{ name: 'decision', aliases: ['decide'], desc: 'Runtime decision (what next?)' },
|
|
33
|
+
{ name: 'update', aliases: ['sync-runtime'], desc: 'Perform recommended runtime action' },
|
|
34
|
+
{ name: 'ready', aliases: ['ensure-ready'], desc: 'Ensure one healthy extension home tab' },
|
|
35
|
+
{ name: 'watch', aliases: ['start-watch', 'watcher'], desc: 'Start/reuse webpack watcher', flags: ['--full', '-f'] },
|
|
36
|
+
{ name: 'stop', aliases: ['stop-watch', 'stop-watcher'], desc: 'Stop this checkout watcher' },
|
|
37
|
+
{ name: 'rebuild', aliases: ['reset', 'reopen', 'reload', 'browser', 'launch', 'full-launch', 'runtime-launch'], desc: 'Clean webpack + browser launch', flags: ['--json', '--dry-run', '--full'] },
|
|
38
|
+
{ name: 'refresh', aliases: ['build'], desc: 'Fast refresh with active watcher' },
|
|
39
|
+
{ name: 'refresh:once', aliases: ['refresh-once', 'build:once', 'build-once'], desc: 'One-shot refresh-build helper' },
|
|
40
|
+
{ name: 'sidepanel', desc: 'Side panel helper', args: ['cycle', 'open', 'close', 'toggle', 'status'] },
|
|
41
|
+
{ name: 'prepare', flags: ['--target', '--cdp-port', '--runtime-dir', '--validate'] },
|
|
42
|
+
{ name: 'debug', args: ['page', 'worker'], flags: ['--json', '--no-open'] },
|
|
43
|
+
],
|
|
44
|
+
mobile: [
|
|
45
|
+
{ name: 'ios', aliases: ['start'], desc: 'Start Metro + launch iOS dev client' },
|
|
46
|
+
{ name: 'android', desc: 'Start Metro + launch Android dev client' },
|
|
47
|
+
{ name: 'setup:ios', desc: 'iOS launch + wallet fixture setup' },
|
|
48
|
+
{ name: 'setup:android', desc: 'Android launch + wallet fixture setup' },
|
|
49
|
+
{ name: 'refresh', aliases: ['reload', 'relaunch'], desc: 'Refresh Metro + relaunch dev client' },
|
|
50
|
+
{ name: 'stop', desc: 'Stop Metro for this slot' },
|
|
51
|
+
{ name: 'app-status', aliases: ['bridge-status'], desc: 'Bridge/runtime status' },
|
|
52
|
+
{ name: 'route', aliases: ['get-route'], desc: 'Current navigation route' },
|
|
53
|
+
{ name: 'navigate', desc: 'Navigate by route name', args: ['RouteName'] },
|
|
54
|
+
{ name: 'back', aliases: ['go-back'], desc: 'Navigate back' },
|
|
55
|
+
{ name: 'unlock', desc: 'Unlock with fixture password', args: ['password'] },
|
|
56
|
+
{ name: 'setup-wallet', aliases: ['wallet-setup'], desc: 'Apply wallet fixture', args: ['fixture.json'] },
|
|
57
|
+
{ name: 'accounts', aliases: ['list-accounts'], desc: 'List accounts' },
|
|
58
|
+
{ name: 'select-account', aliases: ['switch-account'], desc: 'Switch account', args: ['address'] },
|
|
59
|
+
{ name: 'screenshot', desc: 'Capture simulator/device screenshot', args: ['path'] },
|
|
60
|
+
{ name: 'dev-menu', aliases: ['devmenu'], desc: 'Open RN developer menu', flags: ['--json', '--no-open'] },
|
|
61
|
+
{ name: 'prepare', flags: ['--target', '--platform', '--preflight-mode', '--port', '--simulator', '--adb-serial', '--runtime-dir', '--wallet-setup', '--wallet-fixture'] },
|
|
62
|
+
{ name: 'debug', flags: ['--json', '--no-open', '--action'] },
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const GLOBAL_FLAGS = ['--json', '--dry-run', '--full', '-f', '--help', '-h', '--no-open', '--no-color'];
|
|
67
|
+
|
|
68
|
+
function mergeCommands(adapter) {
|
|
69
|
+
const byName = new Map();
|
|
70
|
+
const add = (cmd) => {
|
|
71
|
+
const existing = byName.get(cmd.name);
|
|
72
|
+
if (!existing) {
|
|
73
|
+
byName.set(cmd.name, {
|
|
74
|
+
...cmd,
|
|
75
|
+
aliases: [...(cmd.aliases || [])],
|
|
76
|
+
flags: [...(cmd.flags || [])],
|
|
77
|
+
args: [...(cmd.args || [])],
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
existing.aliases = [...new Set([...(existing.aliases || []), ...(cmd.aliases || [])])];
|
|
82
|
+
existing.flags = [...new Set([...(existing.flags || []), ...(cmd.flags || [])])];
|
|
83
|
+
existing.args = [...new Set([...(existing.args || []), ...(cmd.args || [])])];
|
|
84
|
+
if (!existing.desc && cmd.desc) existing.desc = cmd.desc;
|
|
85
|
+
};
|
|
86
|
+
for (const cmd of SPEC.shared) add(cmd);
|
|
87
|
+
for (const cmd of SPEC[adapter] || []) add(cmd);
|
|
88
|
+
return [...byName.values()];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function commandTokens(adapter, { includeAliases = true } = {}) {
|
|
92
|
+
const tokens = new Set();
|
|
93
|
+
for (const cmd of mergeCommands(adapter)) {
|
|
94
|
+
tokens.add(cmd.name);
|
|
95
|
+
if (includeAliases) for (const alias of cmd.aliases || []) tokens.add(alias);
|
|
96
|
+
}
|
|
97
|
+
return [...tokens].sort();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function shimCommandTokens() {
|
|
101
|
+
const tokens = new Set();
|
|
102
|
+
for (const cmd of SPEC.shim) {
|
|
103
|
+
tokens.add(cmd.name);
|
|
104
|
+
for (const alias of cmd.aliases || []) tokens.add(alias);
|
|
105
|
+
}
|
|
106
|
+
return [...tokens].sort();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function lookupCommand(adapter, token) {
|
|
110
|
+
for (const cmd of mergeCommands(adapter)) {
|
|
111
|
+
if (cmd.name === token) return cmd;
|
|
112
|
+
if ((cmd.aliases || []).includes(token)) return cmd;
|
|
113
|
+
}
|
|
114
|
+
for (const cmd of SPEC.shim) {
|
|
115
|
+
if (cmd.name === token) return cmd;
|
|
116
|
+
if ((cmd.aliases || []).includes(token)) return cmd;
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function flagsForPosition(adapter, commandToken) {
|
|
122
|
+
const cmd = lookupCommand(adapter, commandToken);
|
|
123
|
+
const flags = new Set(GLOBAL_FLAGS);
|
|
124
|
+
if (cmd) for (const flag of cmd.flags || []) flags.add(flag);
|
|
125
|
+
return [...flags].sort();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function argsForPosition(adapter, commandToken, argIndex) {
|
|
129
|
+
const cmd = lookupCommand(adapter, commandToken);
|
|
130
|
+
if (!cmd?.args?.length) return [];
|
|
131
|
+
const value = cmd.args[Math.max(0, argIndex)];
|
|
132
|
+
return value ? [value] : cmd.args;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const RUN_RECIPE_DIRS = [
|
|
136
|
+
(adapter, root) => path.join(root, 'temp/recipe/harness', adapter, 'runner/recipes'),
|
|
137
|
+
(adapter, root) => path.join(root, 'temp/agentic/recipe-harness', adapter, 'runner/recipes'),
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
/** Harness runner recipes only — used for `recipe run <tab>`. */
|
|
141
|
+
export function listRunRecipes(adapter, root, cwd = process.cwd()) {
|
|
142
|
+
const byBase = new Map();
|
|
143
|
+
for (const resolveDir of RUN_RECIPE_DIRS) {
|
|
144
|
+
const dir = resolveDir(adapter, root);
|
|
145
|
+
if (!fs.existsSync(dir)) continue;
|
|
146
|
+
for (const name of fs.readdirSync(dir)) {
|
|
147
|
+
if (!name.endsWith('.recipe.json') || byBase.has(name)) continue;
|
|
148
|
+
byBase.set(name, path.join(dir, name));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return [...byBase.values()]
|
|
152
|
+
.map((abs) => path.relative(cwd, abs))
|
|
153
|
+
.sort();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function usage() {
|
|
157
|
+
process.stderr.write(`Usage:
|
|
158
|
+
cli-commands.mjs dump --adapter extension|mobile|shim [--aliases]
|
|
159
|
+
cli-commands.mjs describe --adapter extension|mobile|shim
|
|
160
|
+
cli-commands.mjs flags --adapter <adapter> --command <name>
|
|
161
|
+
cli-commands.mjs args --adapter <adapter> --command <name> [--index <n>]
|
|
162
|
+
cli-commands.mjs run-recipes --adapter extension|mobile --root <repo> [--cwd <dir>]
|
|
163
|
+
cli-commands.mjs global-flags
|
|
164
|
+
`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function main() {
|
|
168
|
+
const { parseArgs } = await import('node:util');
|
|
169
|
+
const { values, positionals } = parseArgs({
|
|
170
|
+
allowPositionals: true,
|
|
171
|
+
args: process.argv.slice(2),
|
|
172
|
+
options: {
|
|
173
|
+
adapter: { type: 'string' },
|
|
174
|
+
command: { type: 'string' },
|
|
175
|
+
aliases: { type: 'boolean', default: true },
|
|
176
|
+
index: { type: 'string', default: '0' },
|
|
177
|
+
root: { type: 'string' },
|
|
178
|
+
cwd: { type: 'string' },
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const command = positionals[0];
|
|
183
|
+
if (!command || command === '--help' || command === '-h') {
|
|
184
|
+
usage();
|
|
185
|
+
process.exit(command ? 0 : 2);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (command === 'global-flags') {
|
|
189
|
+
process.stdout.write(`${GLOBAL_FLAGS.join('\n')}\n`);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const adapter = values.adapter;
|
|
194
|
+
if (!adapter && command !== 'global-flags') {
|
|
195
|
+
usage();
|
|
196
|
+
process.exit(2);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (command === 'dump') {
|
|
200
|
+
process.stdout.write(`${commandTokens(adapter, { includeAliases: values.aliases }).join('\n')}\n`);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (command === 'describe') {
|
|
205
|
+
const rows = adapter === 'shim' ? SPEC.shim : mergeCommands(adapter);
|
|
206
|
+
for (const cmd of rows) {
|
|
207
|
+
const aliases = (cmd.aliases || []).join('|');
|
|
208
|
+
const label = aliases ? `${cmd.name}|${aliases}` : cmd.name;
|
|
209
|
+
process.stdout.write(`${label}\t${cmd.desc || cmd.name}\n`);
|
|
210
|
+
}
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (command === 'flags') {
|
|
215
|
+
process.stdout.write(`${flagsForPosition(adapter, values.command || '').join('\n')}\n`);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (command === 'args') {
|
|
220
|
+
const index = Number.parseInt(values.index, 10) || 0;
|
|
221
|
+
process.stdout.write(`${argsForPosition(adapter, values.command || '', index).join('\n')}\n`);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (command === 'run-recipes') {
|
|
226
|
+
const root = values.root ? path.resolve(values.root) : process.cwd();
|
|
227
|
+
const cwd = values.cwd ? path.resolve(values.cwd) : process.cwd();
|
|
228
|
+
process.stdout.write(`${listRunRecipes(adapter, root, cwd).join('\n')}\n`);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
usage();
|
|
233
|
+
process.exit(2);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const entry = process.argv[1] ? path.resolve(process.argv[1]) : '';
|
|
237
|
+
const self = fileURLToPath(import.meta.url);
|
|
238
|
+
if (entry === self) {
|
|
239
|
+
main().catch((error) => {
|
|
240
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
241
|
+
process.exit(1);
|
|
242
|
+
});
|
|
243
|
+
}
|