@deeeed/metamask-harness 0.4.0 → 0.5.1
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 +33 -0
- package/adapters/extension/inject.mjs +2 -0
- package/adapters/extension/refresh-build.sh +2 -2
- package/adapters/extension/start-watch.sh +2 -2
- package/adapters/manifest.json +19 -3
- package/adapters/mobile/start-metro.sh +1 -1
- package/adapters/mobile/verify.sh +1 -1
- package/adapters/shared/activate-repo-node.sh +1 -1
- package/adapters/shared/cli-ux.sh +24 -23
- package/adapters/shared/log-tui.mjs +4 -4
- package/adapters/shared/open-debug.mjs +1 -1
- package/adapters/shared/resolve-farmslot-ports-core.mjs +196 -0
- package/adapters/shared/resolve-farmslot-ports.mjs +20 -0
- package/adapters/shared/resolve-farmslot-ports.sh +19 -126
- package/dist/adapters/core/surface.js +53 -0
- package/dist/adapters/extension/ensure-ready.js +109 -0
- package/dist/adapters/extension/extension-id.js +62 -0
- package/dist/adapters/extension/runtime-decision.js +305 -0
- package/dist/adapters/extension/runtime.js +324 -0
- package/dist/adapters/extension/surface.js +69 -0
- package/dist/adapters/mobile/deps-markers.js +22 -0
- package/dist/adapters/mobile/prepare.js +146 -0
- package/dist/adapters/mobile/provision.js +465 -0
- package/dist/adapters/mobile/runtime-decision.js +317 -0
- package/dist/adapters/mobile/surface.js +54 -0
- package/dist/adapters/resolve-farmslot-ports.js +22 -0
- package/dist/adapters/slot-ports.js +140 -0
- package/dist/adapters/surface.js +14 -0
- package/dist/adapters.js +485 -0
- package/dist/cli-color.js +79 -0
- package/dist/cli-commands.js +224 -0
- package/dist/cli-version.js +111 -0
- package/dist/cli.js +1571 -0
- package/dist/commands/debug.js +56 -0
- package/dist/commands/fixtures.js +153 -0
- package/dist/commands/launch.js +325 -0
- package/dist/commands/logs.js +73 -0
- package/dist/commands/shared.js +157 -0
- package/dist/commands/update.js +243 -0
- package/dist/completions-cache.js +53 -0
- package/dist/doctor.js +169 -0
- package/dist/harness.js +627 -0
- package/dist/heal-bounds.js +120 -0
- package/dist/index.js +25 -0
- package/dist/leaf-invoke.js +19 -0
- package/dist/live-adapter-contract.js +240 -0
- package/dist/manifest.js +37 -0
- package/dist/mm-harness-cli.js +521 -0
- package/dist/paths.js +179 -0
- package/dist/progress.js +94 -0
- package/dist/recording-target.js +133 -0
- package/dist/run-recording.js +271 -0
- package/dist/runner.js +88 -0
- package/dist/types.js +0 -0
- package/docs/CLI-SPEC.md +26 -3
- package/library/actions/core/perps/_controller.mjs +1 -1
- package/library/actions/extension/platform/cdp.mjs +1 -1
- package/library/actions/extension/wallet/ensure_unlocked.mjs +1 -1
- package/library/actions/harness-exports.mjs +27 -0
- package/library/actions/mobile/wallet/ensure_unlocked.mjs +1 -1
- package/library/actions/mobile/wallet/setup.mjs +1 -1
- package/package.json +5 -1
- package/src/adapters/core/surface.ts +15 -0
- package/src/adapters/extension/surface.ts +20 -3
- package/src/adapters/mobile/provision.ts +594 -0
- package/src/adapters/mobile/runtime-decision.ts +8 -1
- package/src/adapters/mobile/surface.ts +16 -4
- package/src/adapters/resolve-farmslot-ports.ts +13 -0
- package/src/adapters/slot-ports.ts +18 -25
- package/src/adapters/surface.ts +35 -0
- package/src/cli-commands.ts +1 -1
- package/src/cli.ts +149 -6
- package/src/harness.ts +140 -3
- package/src/mm-harness-cli.ts +52 -5
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const SPEC = {
|
|
5
|
+
shim: [
|
|
6
|
+
{ name: "sync", desc: "Copy fixture, write slot context, install harness", flags: ["--dry-run", "--no-install"] },
|
|
7
|
+
{ name: "help", aliases: ["-h", "--help"], desc: "Show usage" }
|
|
8
|
+
],
|
|
9
|
+
shared: [
|
|
10
|
+
{ name: "status", aliases: ["health", "home"], desc: "Home status + next commands", flags: ["--json"] },
|
|
11
|
+
{ name: "ports", desc: "Slot ports and runtime paths", flags: ["--json"] },
|
|
12
|
+
{ name: "up", desc: "Decide + run minimum work to reach ready", flags: ["--json", "--dry-run"] },
|
|
13
|
+
{ name: "sync", desc: "Refresh harness + canonicalize wallet fixture", flags: ["--json"] },
|
|
14
|
+
{ name: "logs", aliases: ["tail"], desc: "Compact build events or full log", flags: ["--full", "-f"] },
|
|
15
|
+
{ name: "debug", aliases: ["devtools", "inspect"], desc: "Open DevTools UI", flags: ["--json", "--no-open"] },
|
|
16
|
+
{ name: "actions", desc: "List runnable recipe actions", flags: ["--json"] },
|
|
17
|
+
{ name: "doctor", desc: "Check harness/orchestration health", flags: ["--json", "--target", "--adapter", "--runtime-dir"] },
|
|
18
|
+
{ name: "run", desc: "Execute a proof recipe", args: ["recipe.json"] },
|
|
19
|
+
{ name: "interactive", aliases: ["menu"], desc: "Interactive command menu" },
|
|
20
|
+
{ name: "prepare", desc: "Install harness (+ optional validate)", flags: ["--target", "--runtime-dir", "--json"] },
|
|
21
|
+
{ name: "runtime-status", desc: "Structured runtime status JSON", flags: ["--json", "--target", "--cdp-port", "--runtime-dir"] }
|
|
22
|
+
],
|
|
23
|
+
extension: [
|
|
24
|
+
{ name: "decision", aliases: ["decide"], desc: "Runtime decision (what next?)" },
|
|
25
|
+
{ name: "update", aliases: ["sync-runtime"], desc: "Perform recommended runtime action" },
|
|
26
|
+
{ name: "ready", aliases: ["ensure-ready"], desc: "Ensure one healthy extension home tab" },
|
|
27
|
+
{ name: "watch", aliases: ["start-watch", "watcher"], desc: "Start/reuse webpack watcher", flags: ["--full", "-f"] },
|
|
28
|
+
{ name: "stop", aliases: ["stop-watch", "stop-watcher"], desc: "Stop this checkout watcher" },
|
|
29
|
+
{ name: "rebuild", aliases: ["reset", "reopen", "reload", "browser", "launch", "full-launch", "runtime-launch", "build", "build:once", "build-once", "refresh-once"], desc: "Clean webpack + browser launch", flags: ["--json", "--dry-run", "--full"] },
|
|
30
|
+
{ name: "sidepanel", desc: "Side panel helper", args: ["cycle", "open", "close", "toggle", "status"] },
|
|
31
|
+
{ name: "prepare", flags: ["--target", "--cdp-port", "--runtime-dir", "--validate"] },
|
|
32
|
+
{ name: "debug", args: ["page", "worker"], flags: ["--json", "--no-open"] }
|
|
33
|
+
],
|
|
34
|
+
mobile: [
|
|
35
|
+
{ name: "ios", aliases: ["start"], desc: "Start Metro + launch iOS dev client" },
|
|
36
|
+
{ name: "android", desc: "Start Metro + launch Android dev client" },
|
|
37
|
+
{ name: "setup:ios", desc: "iOS launch + wallet fixture setup" },
|
|
38
|
+
{ name: "setup:android", desc: "Android launch + wallet fixture setup" },
|
|
39
|
+
{ name: "stop", desc: "Stop Metro for this slot" },
|
|
40
|
+
{ name: "app-status", aliases: ["bridge-status"], desc: "Bridge/runtime status" },
|
|
41
|
+
{ name: "route", aliases: ["get-route"], desc: "Current navigation route" },
|
|
42
|
+
{ name: "navigate", desc: "Navigate by route name", args: ["RouteName"] },
|
|
43
|
+
{ name: "back", aliases: ["go-back"], desc: "Navigate back" },
|
|
44
|
+
{ name: "unlock", desc: "Unlock with fixture password", args: ["password"] },
|
|
45
|
+
{ name: "setup-wallet", aliases: ["wallet-setup"], desc: "Apply wallet fixture", args: ["fixture.json"] },
|
|
46
|
+
{ name: "accounts", aliases: ["list-accounts"], desc: "List accounts" },
|
|
47
|
+
{ name: "select-account", aliases: ["switch-account"], desc: "Switch account", args: ["address"] },
|
|
48
|
+
{ name: "screenshot", desc: "Capture simulator/device screenshot", args: ["path"] },
|
|
49
|
+
{ name: "dev-menu", aliases: ["devmenu"], desc: "Open RN developer menu", flags: ["--json", "--no-open"] },
|
|
50
|
+
{ name: "prepare", flags: ["--target", "--platform", "--preflight-mode", "--port", "--simulator", "--adb-serial", "--runtime-dir", "--wallet-setup", "--wallet-fixture"] },
|
|
51
|
+
{ name: "debug", flags: ["--json", "--no-open", "--action"] }
|
|
52
|
+
]
|
|
53
|
+
};
|
|
54
|
+
const GLOBAL_FLAGS = ["--json", "--dry-run", "--full", "-f", "--help", "-h", "--no-open", "--no-color"];
|
|
55
|
+
function mergeCommands(adapter) {
|
|
56
|
+
const byName = /* @__PURE__ */ new Map();
|
|
57
|
+
const add = (cmd) => {
|
|
58
|
+
const existing = byName.get(cmd.name);
|
|
59
|
+
if (!existing) {
|
|
60
|
+
byName.set(cmd.name, {
|
|
61
|
+
...cmd,
|
|
62
|
+
aliases: [...cmd.aliases || []],
|
|
63
|
+
flags: [...cmd.flags || []],
|
|
64
|
+
args: [...cmd.args || []]
|
|
65
|
+
});
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
existing.aliases = [.../* @__PURE__ */ new Set([...existing.aliases || [], ...cmd.aliases || []])];
|
|
69
|
+
existing.flags = [.../* @__PURE__ */ new Set([...existing.flags || [], ...cmd.flags || []])];
|
|
70
|
+
existing.args = [.../* @__PURE__ */ new Set([...existing.args || [], ...cmd.args || []])];
|
|
71
|
+
if (!existing.desc && cmd.desc) existing.desc = cmd.desc;
|
|
72
|
+
};
|
|
73
|
+
for (const cmd of SPEC.shared) add(cmd);
|
|
74
|
+
for (const cmd of SPEC[adapter] || []) add(cmd);
|
|
75
|
+
return [...byName.values()];
|
|
76
|
+
}
|
|
77
|
+
function commandTokens(adapter, { includeAliases = true } = {}) {
|
|
78
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
79
|
+
for (const cmd of mergeCommands(adapter)) {
|
|
80
|
+
tokens.add(cmd.name);
|
|
81
|
+
if (includeAliases) for (const alias of cmd.aliases || []) tokens.add(alias);
|
|
82
|
+
}
|
|
83
|
+
return [...tokens].sort();
|
|
84
|
+
}
|
|
85
|
+
function shimCommandTokens() {
|
|
86
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
87
|
+
for (const cmd of SPEC.shim) {
|
|
88
|
+
tokens.add(cmd.name);
|
|
89
|
+
for (const alias of cmd.aliases || []) tokens.add(alias);
|
|
90
|
+
}
|
|
91
|
+
return [...tokens].sort();
|
|
92
|
+
}
|
|
93
|
+
function lookupCommand(adapter, token) {
|
|
94
|
+
for (const cmd of mergeCommands(adapter)) {
|
|
95
|
+
if (cmd.name === token) return cmd;
|
|
96
|
+
if ((cmd.aliases || []).includes(token)) return cmd;
|
|
97
|
+
}
|
|
98
|
+
for (const cmd of SPEC.shim) {
|
|
99
|
+
if (cmd.name === token) return cmd;
|
|
100
|
+
if ((cmd.aliases || []).includes(token)) return cmd;
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
function flagsForPosition(adapter, commandToken) {
|
|
105
|
+
const cmd = lookupCommand(adapter, commandToken);
|
|
106
|
+
const flags = new Set(GLOBAL_FLAGS);
|
|
107
|
+
if (cmd) for (const flag of cmd.flags || []) flags.add(flag);
|
|
108
|
+
return [...flags].sort();
|
|
109
|
+
}
|
|
110
|
+
function argsForPosition(adapter, commandToken, argIndex) {
|
|
111
|
+
const cmd = lookupCommand(adapter, commandToken);
|
|
112
|
+
if (!cmd?.args?.length) return [];
|
|
113
|
+
const value = cmd.args[Math.max(0, argIndex)];
|
|
114
|
+
return value ? [value] : cmd.args;
|
|
115
|
+
}
|
|
116
|
+
const RUN_RECIPE_DIRS = [
|
|
117
|
+
(adapter, root) => path.join(root, "temp/recipe/harness", adapter, "runner/recipes"),
|
|
118
|
+
(adapter, root) => path.join(root, "temp/agentic/recipe-harness", adapter, "runner/recipes")
|
|
119
|
+
];
|
|
120
|
+
function listRunRecipes(adapter, root, cwd = process.cwd()) {
|
|
121
|
+
const byBase = /* @__PURE__ */ new Map();
|
|
122
|
+
for (const resolveDir of RUN_RECIPE_DIRS) {
|
|
123
|
+
const dir = resolveDir(adapter, root);
|
|
124
|
+
if (!fs.existsSync(dir)) continue;
|
|
125
|
+
for (const name of fs.readdirSync(dir)) {
|
|
126
|
+
if (!name.endsWith(".recipe.json") || byBase.has(name)) continue;
|
|
127
|
+
byBase.set(name, path.join(dir, name));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return [...byBase.values()].map((abs) => path.relative(cwd, abs)).sort();
|
|
131
|
+
}
|
|
132
|
+
function usage() {
|
|
133
|
+
process.stderr.write(`Usage:
|
|
134
|
+
cli-commands dump --adapter extension|mobile|shim [--aliases]
|
|
135
|
+
cli-commands describe --adapter extension|mobile|shim
|
|
136
|
+
cli-commands flags --adapter <adapter> --command <name>
|
|
137
|
+
cli-commands args --adapter <adapter> --command <name> [--index <n>]
|
|
138
|
+
cli-commands run-recipes --adapter extension|mobile --root <repo> [--cwd <dir>]
|
|
139
|
+
cli-commands global-flags
|
|
140
|
+
`);
|
|
141
|
+
}
|
|
142
|
+
async function main() {
|
|
143
|
+
const { parseArgs: parseArgsUtil } = await import("node:util");
|
|
144
|
+
const { values, positionals } = parseArgsUtil({
|
|
145
|
+
allowPositionals: true,
|
|
146
|
+
args: process.argv.slice(2),
|
|
147
|
+
options: {
|
|
148
|
+
adapter: { type: "string" },
|
|
149
|
+
command: { type: "string" },
|
|
150
|
+
aliases: { type: "boolean", default: true },
|
|
151
|
+
index: { type: "string", default: "0" },
|
|
152
|
+
root: { type: "string" },
|
|
153
|
+
cwd: { type: "string" }
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
const command = positionals[0];
|
|
157
|
+
if (!command || command === "--help" || command === "-h") {
|
|
158
|
+
usage();
|
|
159
|
+
process.exit(command ? 0 : 2);
|
|
160
|
+
}
|
|
161
|
+
if (command === "global-flags") {
|
|
162
|
+
process.stdout.write(`${GLOBAL_FLAGS.join("\n")}
|
|
163
|
+
`);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const adapter = values.adapter;
|
|
167
|
+
if (!adapter && command !== "global-flags") {
|
|
168
|
+
usage();
|
|
169
|
+
process.exit(2);
|
|
170
|
+
}
|
|
171
|
+
if (command === "dump") {
|
|
172
|
+
process.stdout.write(`${commandTokens(adapter, { includeAliases: values.aliases }).join("\n")}
|
|
173
|
+
`);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (command === "describe") {
|
|
177
|
+
const rows = adapter === "shim" ? SPEC.shim : mergeCommands(adapter);
|
|
178
|
+
for (const cmd of rows) {
|
|
179
|
+
const aliases = (cmd.aliases || []).join("|");
|
|
180
|
+
const label = aliases ? `${cmd.name}|${aliases}` : cmd.name;
|
|
181
|
+
process.stdout.write(`${label} ${cmd.desc || cmd.name}
|
|
182
|
+
`);
|
|
183
|
+
}
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (command === "flags") {
|
|
187
|
+
process.stdout.write(`${flagsForPosition(adapter, values.command || "").join("\n")}
|
|
188
|
+
`);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (command === "args") {
|
|
192
|
+
const index = Number.parseInt(values.index, 10) || 0;
|
|
193
|
+
process.stdout.write(`${argsForPosition(adapter, values.command || "", index).join("\n")}
|
|
194
|
+
`);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (command === "run-recipes") {
|
|
198
|
+
const root = values.root ? path.resolve(values.root) : process.cwd();
|
|
199
|
+
const cwd = values.cwd ? path.resolve(values.cwd) : process.cwd();
|
|
200
|
+
process.stdout.write(`${listRunRecipes(adapter, root, cwd).join("\n")}
|
|
201
|
+
`);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
usage();
|
|
205
|
+
process.exit(2);
|
|
206
|
+
}
|
|
207
|
+
const _filename = fileURLToPath(import.meta.url);
|
|
208
|
+
const isMain = process.argv[1] && path.resolve(process.argv[1]) === _filename;
|
|
209
|
+
if (isMain) {
|
|
210
|
+
main().catch((error) => {
|
|
211
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}
|
|
212
|
+
`);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
export {
|
|
217
|
+
SPEC,
|
|
218
|
+
argsForPosition,
|
|
219
|
+
commandTokens,
|
|
220
|
+
flagsForPosition,
|
|
221
|
+
listRunRecipes,
|
|
222
|
+
lookupCommand,
|
|
223
|
+
shimCommandTokens
|
|
224
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { parseArgs } from "node:util";
|
|
5
|
+
function readJson(file) {
|
|
6
|
+
try {
|
|
7
|
+
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
8
|
+
} catch {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function readPackageVersion(dir) {
|
|
13
|
+
try {
|
|
14
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(dir, "package.json"), "utf8"));
|
|
15
|
+
return pkg.version || "unknown";
|
|
16
|
+
} catch {
|
|
17
|
+
return "unknown";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function gitHead(dir) {
|
|
21
|
+
const result = spawnSync("git", ["-C", dir, "rev-parse", "HEAD"], { encoding: "utf8" });
|
|
22
|
+
return result.status === 0 ? result.stdout.trim() : "unknown";
|
|
23
|
+
}
|
|
24
|
+
function checkHarness({ runnerRoot, manifestPath }) {
|
|
25
|
+
const manifest = readJson(manifestPath);
|
|
26
|
+
const reasons = [];
|
|
27
|
+
if (!manifest) {
|
|
28
|
+
return { stale: false, reasons: ["no harness manifest"] };
|
|
29
|
+
}
|
|
30
|
+
const currentRevision = gitHead(runnerRoot);
|
|
31
|
+
const currentVersion = readPackageVersion(runnerRoot);
|
|
32
|
+
const manifestRecord = manifest;
|
|
33
|
+
const source = manifestRecord.source;
|
|
34
|
+
const installedRevision = source?.runnerRevision || null;
|
|
35
|
+
const installedVersion = manifestRecord.runnerPackageVersion || null;
|
|
36
|
+
if (installedRevision && installedRevision !== "unknown" && installedRevision !== currentRevision) {
|
|
37
|
+
reasons.push(`runner revision ${installedRevision.slice(0, 8)} != ${currentRevision.slice(0, 8)}`);
|
|
38
|
+
}
|
|
39
|
+
if (installedVersion && installedVersion !== currentVersion) {
|
|
40
|
+
reasons.push(`runner package ${installedVersion} != ${currentVersion}`);
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
stale: reasons.length > 0,
|
|
44
|
+
reasons,
|
|
45
|
+
installedRevision,
|
|
46
|
+
currentRevision,
|
|
47
|
+
installedVersion,
|
|
48
|
+
currentVersion
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function usage() {
|
|
52
|
+
console.error(`Usage:
|
|
53
|
+
cli-version check --runner-root <dir> --manifest <path> [--installed-sha <path>] [--json]`);
|
|
54
|
+
}
|
|
55
|
+
async function main() {
|
|
56
|
+
const [command, ...rest] = process.argv.slice(2);
|
|
57
|
+
const { values } = parseArgs({
|
|
58
|
+
args: rest,
|
|
59
|
+
allowPositionals: false,
|
|
60
|
+
options: {
|
|
61
|
+
"runner-root": { type: "string" },
|
|
62
|
+
manifest: { type: "string" },
|
|
63
|
+
"installed-sha": { type: "string" },
|
|
64
|
+
json: { type: "boolean", default: false }
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
if (!command || command === "--help" || command === "-h") {
|
|
68
|
+
usage();
|
|
69
|
+
process.exit(command ? 0 : 2);
|
|
70
|
+
}
|
|
71
|
+
if (command !== "check") {
|
|
72
|
+
usage();
|
|
73
|
+
process.exit(2);
|
|
74
|
+
}
|
|
75
|
+
const runnerRoot = values["runner-root"];
|
|
76
|
+
const manifestPath = values.manifest;
|
|
77
|
+
if (!runnerRoot || !manifestPath) {
|
|
78
|
+
usage();
|
|
79
|
+
process.exit(2);
|
|
80
|
+
}
|
|
81
|
+
const payload = {
|
|
82
|
+
...checkHarness({
|
|
83
|
+
runnerRoot,
|
|
84
|
+
manifestPath,
|
|
85
|
+
installedShaPath: values["installed-sha"]
|
|
86
|
+
}),
|
|
87
|
+
manifest: manifestPath
|
|
88
|
+
};
|
|
89
|
+
if (values.json) {
|
|
90
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}
|
|
91
|
+
`);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (payload.stale) {
|
|
95
|
+
process.stderr.write(`recipe: harness stale (${payload.reasons.join("; ")})
|
|
96
|
+
`);
|
|
97
|
+
process.stderr.write("recipe: run: recipe sync\n");
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
process.stdout.write("harness up to date\n");
|
|
101
|
+
}
|
|
102
|
+
const isMain = process.argv[1] && path.resolve(process.argv[1]) === path.resolve(import.meta.filename ?? "");
|
|
103
|
+
if (isMain) {
|
|
104
|
+
main().catch((error) => {
|
|
105
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
106
|
+
process.exit(1);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
checkHarness
|
|
111
|
+
};
|