@heyanon-arp/cli 0.0.20 → 0.0.22
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/dist/cli.js +140 -52
- package/dist/cli.js.map +1 -1
- package/package.json +3 -4
package/dist/cli.js
CHANGED
|
@@ -729,12 +729,11 @@ var init_api = __esm({
|
|
|
729
729
|
// src/cli.ts
|
|
730
730
|
var import_shield3 = require("@heyanon-arp/shield");
|
|
731
731
|
var import_commander = require("commander");
|
|
732
|
-
var import_simple_update_notifier = __toESM(require("simple-update-notifier"));
|
|
733
732
|
|
|
734
733
|
// package.json
|
|
735
734
|
var package_default = {
|
|
736
735
|
name: "@heyanon-arp/cli",
|
|
737
|
-
version: "0.0.
|
|
736
|
+
version: "0.0.22",
|
|
738
737
|
description: "Command-line client for the Agent Relationship Protocol \u2014 register agents, sign envelopes, run escrowed work cycles on Solana.",
|
|
739
738
|
license: "MIT",
|
|
740
739
|
keywords: ["arp", "agent-relationship-protocol", "did", "solana", "escrow", "ed25519", "agents", "a2a", "cli"],
|
|
@@ -763,8 +762,7 @@ var package_default = {
|
|
|
763
762
|
"@solana/web3.js": "^1.98.4",
|
|
764
763
|
chalk: "^4.1.2",
|
|
765
764
|
commander: "^12.1.0",
|
|
766
|
-
prompts: "^2.4.2"
|
|
767
|
-
"simple-update-notifier": "^2.0.0"
|
|
765
|
+
prompts: "^2.4.2"
|
|
768
766
|
},
|
|
769
767
|
devDependencies: {
|
|
770
768
|
"@types/jest": "^29.5.2",
|
|
@@ -832,6 +830,16 @@ function toCliErrorJson(err, includeStack = false) {
|
|
|
832
830
|
if (details !== void 0) out2.details = details;
|
|
833
831
|
return out2;
|
|
834
832
|
}
|
|
833
|
+
if (err instanceof Error && err.code === "OUTBOUND_BLOCKED") {
|
|
834
|
+
const e = err;
|
|
835
|
+
const reasons = e.verdict?.reasons ?? e.reasons;
|
|
836
|
+
const out2 = { code: e.code, message: err.message };
|
|
837
|
+
const details = {};
|
|
838
|
+
if (Array.isArray(reasons) && reasons.length > 0) details.reasons = reasons;
|
|
839
|
+
if (includeStack && err.stack) details.stack = err.stack;
|
|
840
|
+
if (Object.keys(details).length > 0) out2.details = details;
|
|
841
|
+
return out2;
|
|
842
|
+
}
|
|
835
843
|
const message = err instanceof Error ? err.message : String(err);
|
|
836
844
|
const out = { code: "CLI_ERROR", message };
|
|
837
845
|
if (includeStack && err instanceof Error && err.stack) {
|
|
@@ -1780,9 +1788,10 @@ var UNTIL_PHASES = [
|
|
|
1780
1788
|
];
|
|
1781
1789
|
var WAIT_DEFAULT_INTERVAL_SEC = 3;
|
|
1782
1790
|
var WAIT_DEFAULT_TIMEOUT_SEC = 300;
|
|
1783
|
-
var WAIT_ABS_MAX_SEC = 86400;
|
|
1791
|
+
var WAIT_ABS_MAX_SEC = 7 * 86400;
|
|
1784
1792
|
var WAIT_CONTRACT_LAG_MARGIN_SEC = 300;
|
|
1785
1793
|
var WAIT_CONTRACT_CAP_FALLBACK_SEC = 3600;
|
|
1794
|
+
var WAIT_LOCK_FINALIZATION_DEFAULT_SEC = 1200;
|
|
1786
1795
|
function registerStatusCommand(root) {
|
|
1787
1796
|
root.command("status").description("Where am I in the work cycle? FSM state + next-action hint for ONE relationship (signed reads)").argument("<relationship-id>", "Relationship UUID").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Signer DID \u2014 required only if multiple agents are registered against this server").option("--from <name>", "Signer agent NAME (handle) \u2014 alternative to --from-did, resolved against your local agents").option("--json", "Machine-readable: single JSON object with the composed summary. Pipe-safe.", false).option(
|
|
1788
1797
|
"--wait",
|
|
@@ -1790,8 +1799,10 @@ function registerStatusCommand(root) {
|
|
|
1790
1799
|
false
|
|
1791
1800
|
).option(
|
|
1792
1801
|
"--wait-timeout <seconds>",
|
|
1793
|
-
`Max wall-clock seconds to wait when --wait is set
|
|
1794
|
-
|
|
1802
|
+
`Max wall-clock seconds to wait when --wait is set. Default ${WAIT_DEFAULT_TIMEOUT_SEC}s, with two phase-aware exceptions when --until is set: (1) delegation.locked uses a generous lock-finalization default (on-chain confirmation + indexer projection latency, not the lifecycle windows); (2) a lifecycle phase (work/review/dispute-governed, e.g. work.responded) auto-sizes to the live on-chain budget (work+review+dispute windows + margin, read from the contract) so it tracks the windows. An explicit value is always honored as-is (it may legitimately exceed the budget \u2014 a wait can span pre-lock acceptance/funding too); only a generous 7-day wait-loop safety cap is enforced. Exit code 124 on timeout (unix \`timeout\` convention).`
|
|
1803
|
+
// NB: no Commander default — an omitted value must reach resolveWaitTimeoutSec
|
|
1804
|
+
// as `undefined` so it can phase-size the default (a literal '300' here would
|
|
1805
|
+
// mask the contract-budget auto-sizing for --until waits).
|
|
1795
1806
|
).option(
|
|
1796
1807
|
"--wait-interval <seconds>",
|
|
1797
1808
|
`Seconds between polls when --wait is set (default ${WAIT_DEFAULT_INTERVAL_SEC}). Bound to [1, 60].`,
|
|
@@ -1828,7 +1839,7 @@ async function runStatus(relationshipId, opts) {
|
|
|
1828
1839
|
}
|
|
1829
1840
|
const until = parseUntilPhase(opts.until);
|
|
1830
1841
|
const waitInterval = parseWaitInterval(opts.waitInterval);
|
|
1831
|
-
const waitTimeout = await resolveWaitTimeoutSec(api, opts.waitTimeout, {
|
|
1842
|
+
const waitTimeout = await resolveWaitTimeoutSec(api, opts.waitTimeout, { untilPhase: until });
|
|
1832
1843
|
const outcome = await runWaitLoop({
|
|
1833
1844
|
fetchSummary: () => composeStatus(api, sender.did, relationshipId, signer),
|
|
1834
1845
|
waitIntervalSec: waitInterval,
|
|
@@ -1990,7 +2001,7 @@ function parseWaitTimeout(raw) {
|
|
|
1990
2001
|
throw new Error(`status: --wait-timeout must be a positive integer number of seconds (got '${raw}')`);
|
|
1991
2002
|
}
|
|
1992
2003
|
if (n > WAIT_ABS_MAX_SEC) {
|
|
1993
|
-
throw new Error(`status: --wait-timeout must be <= ${WAIT_ABS_MAX_SEC} seconds (
|
|
2004
|
+
throw new Error(`status: --wait-timeout must be <= ${WAIT_ABS_MAX_SEC} seconds (the 7-day wait-loop safety cap). Got ${n}.`);
|
|
1994
2005
|
}
|
|
1995
2006
|
return n;
|
|
1996
2007
|
}
|
|
@@ -2001,21 +2012,25 @@ function contractWaitCapSec(cfg) {
|
|
|
2001
2012
|
if (![w, r, d].every((n) => Number.isFinite(n) && n > 0)) return WAIT_CONTRACT_CAP_FALLBACK_SEC;
|
|
2002
2013
|
return Math.min(WAIT_ABS_MAX_SEC, w + r + d + WAIT_CONTRACT_LAG_MARGIN_SEC);
|
|
2003
2014
|
}
|
|
2015
|
+
var LOCK_FINALIZATION_PHASES = /* @__PURE__ */ new Set(["delegation.locked"]);
|
|
2016
|
+
var CONTRACT_BUDGETED_PHASES = /* @__PURE__ */ new Set([
|
|
2017
|
+
"delegation.disputing",
|
|
2018
|
+
"work.requested",
|
|
2019
|
+
"work.responded",
|
|
2020
|
+
"receipt.proposed",
|
|
2021
|
+
"cycle.released",
|
|
2022
|
+
"cycle.complete"
|
|
2023
|
+
]);
|
|
2004
2024
|
async function resolveWaitTimeoutSec(api, raw, opts) {
|
|
2005
|
-
|
|
2025
|
+
if (raw !== void 0) return parseWaitTimeout(raw);
|
|
2026
|
+
if (opts.untilPhase !== void 0 && LOCK_FINALIZATION_PHASES.has(opts.untilPhase)) return WAIT_LOCK_FINALIZATION_DEFAULT_SEC;
|
|
2027
|
+
const contractBudgeted = opts.untilPhase !== void 0 && CONTRACT_BUDGETED_PHASES.has(opts.untilPhase);
|
|
2028
|
+
if (!contractBudgeted) return WAIT_DEFAULT_TIMEOUT_SEC;
|
|
2006
2029
|
try {
|
|
2007
|
-
|
|
2030
|
+
return contractWaitCapSec(await api.getEscrowConfig());
|
|
2008
2031
|
} catch {
|
|
2009
|
-
|
|
2032
|
+
return WAIT_CONTRACT_CAP_FALLBACK_SEC;
|
|
2010
2033
|
}
|
|
2011
|
-
if (raw === void 0) return opts.hasUntil ? cap : WAIT_DEFAULT_TIMEOUT_SEC;
|
|
2012
|
-
const n = parseWaitTimeout(raw);
|
|
2013
|
-
if (n > cap) {
|
|
2014
|
-
throw new Error(
|
|
2015
|
-
`status: --wait-timeout ${n}s exceeds the live on-chain budget of ${cap}s (work+review+dispute windows + ${WAIT_CONTRACT_LAG_MARGIN_SEC}s lag, read from the contract). Nothing stays pending longer; lower it or script a loop around \`heyarp status\`.`
|
|
2016
|
-
);
|
|
2017
|
-
}
|
|
2018
|
-
return n;
|
|
2019
2034
|
}
|
|
2020
2035
|
function parseUntilPhase(raw) {
|
|
2021
2036
|
if (raw === void 0 || raw === "") return void 0;
|
|
@@ -2334,9 +2349,8 @@ function nextAction(input) {
|
|
|
2334
2349
|
}
|
|
2335
2350
|
const iAmCaller = latestReceipt.callerDid === signerDid;
|
|
2336
2351
|
return {
|
|
2337
|
-
//
|
|
2338
|
-
//
|
|
2339
|
-
// review-window self-claim.
|
|
2352
|
+
// Consent is the on-chain claim_work_payment tx (the buyer's), and
|
|
2353
|
+
// worker recourse is the review-window self-claim.
|
|
2340
2354
|
hint: iAmCaller ? `Receipt PROPOSED \u2014 you (caller): review the deliverable and approve payment ON-CHAIN with \`heyarp escrow claim ${latestReceipt.delegationId}\` (irreversible release; \`heyarp escrow dispute open ${latestReceipt.delegationId}\` within the review window if the work is unacceptable)` : `Receipt PROPOSED \u2014 counterparty (the buyer) owes the on-chain approval (\`heyarp escrow claim\`); once the review window lapses you may self-claim with \`heyarp escrow claim ${latestReceipt.delegationId}\``,
|
|
2341
2355
|
owner: iAmCaller ? "me" : "counterparty",
|
|
2342
2356
|
complete: false
|
|
@@ -2977,7 +2991,7 @@ function registerOffer(parent) {
|
|
|
2977
2991
|
'Block after delivery until the named FSM phase is reached (e.g. delegation.accepted). One of the UNTIL_PHASES from `heyarp status --help`. Exit code 124 on --wait-timeout. Recovers the "sub-agent exits before counterparty accepts" antipattern.'
|
|
2978
2992
|
).option(
|
|
2979
2993
|
"--wait-timeout <seconds>",
|
|
2980
|
-
"When --wait-until is set: max wall-clock wait. Omitted \u2192
|
|
2994
|
+
"When --wait-until is set: max wall-clock wait. Omitted \u2192 delegation.locked uses a generous lock-finalization default (on-chain confirmation + indexer projection); later lifecycle phases (work/review/dispute-governed) auto-size to the live on-chain budget (read from the contract); other phases default to 300s. An explicit value is always honored as-is (a wait may span pre-lock acceptance/funding, so it can exceed the budget), bounded only by a generous 7-day wait-loop safety cap. Exit code 124 on timeout."
|
|
2981
2995
|
).option("--wait-interval <seconds>", "When --wait-until is set: poll cadence (default 3, bound [1, 60]).").option(
|
|
2982
2996
|
"--wait-verbose",
|
|
2983
2997
|
'When --wait-until is set: emit one dim line per poll tick showing the current FSM state. Useful for "is it alive or stuck?" diagnosis on long blocks.',
|
|
@@ -3204,7 +3218,7 @@ After the worker accepts, fund the escrow lock:`));
|
|
|
3204
3218
|
untilPhase,
|
|
3205
3219
|
waitIntervalSec: parseWaitInterval(opts.waitInterval),
|
|
3206
3220
|
// Cap/default the wait against the live on-chain windows (work+review+dispute).
|
|
3207
|
-
waitTimeoutSec: await resolveWaitTimeoutSec(api, opts.waitTimeout, {
|
|
3221
|
+
waitTimeoutSec: await resolveWaitTimeoutSec(api, opts.waitTimeout, { untilPhase }),
|
|
3208
3222
|
waitVerbose: !!opts.waitVerbose,
|
|
3209
3223
|
json: false
|
|
3210
3224
|
// delegation offer is a human-text command (printIngestResult is human-text); JSON mode would be a follow-up.
|
|
@@ -3230,7 +3244,7 @@ function registerFund(parent) {
|
|
|
3230
3244
|
"Block after delivery until the named FSM phase is reached (typically delegation.locked \u2014 resolves once the escrow lock confirms on chain). One of the UNTIL_PHASES from `heyarp status --help`. Exit code 124 on --wait-timeout."
|
|
3231
3245
|
).option(
|
|
3232
3246
|
"--wait-timeout <seconds>",
|
|
3233
|
-
"When --wait-until is set: max wall-clock wait. Omitted \u2192
|
|
3247
|
+
"When --wait-until is set: max wall-clock wait. Omitted \u2192 delegation.locked uses a generous lock-finalization default (on-chain confirmation + indexer projection); later lifecycle phases (work/review/dispute-governed) auto-size to the live on-chain budget (read from the contract); other phases default to 300s. An explicit value is always honored as-is (a wait may span pre-lock acceptance/funding, so it can exceed the budget), bounded only by a generous 7-day wait-loop safety cap. Exit code 124 on timeout."
|
|
3234
3248
|
).option("--wait-interval <seconds>", "When --wait-until is set: poll cadence (default 3, bound [1, 60]).").option("--wait-verbose", "When --wait-until is set: emit one dim line per poll tick showing the current FSM state.", false).action(async (delegationId, opts) => {
|
|
3235
3249
|
await runFund(delegationId, opts);
|
|
3236
3250
|
});
|
|
@@ -3333,7 +3347,7 @@ async function runFund(delegationId, opts) {
|
|
|
3333
3347
|
// slow, so an omitted timeout auto-sizes to the live on-chain
|
|
3334
3348
|
// budget (work+review+dispute + margin) — generous by design;
|
|
3335
3349
|
// a timeout here is almost never a failure, just a slow chain.
|
|
3336
|
-
waitTimeoutSec: await resolveWaitTimeoutSec(api, opts.waitTimeout, {
|
|
3350
|
+
waitTimeoutSec: await resolveWaitTimeoutSec(api, opts.waitTimeout, { untilPhase }),
|
|
3337
3351
|
waitVerbose: !!opts.waitVerbose,
|
|
3338
3352
|
json: false
|
|
3339
3353
|
});
|
|
@@ -6396,8 +6410,8 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
|
|
|
6396
6410
|
);
|
|
6397
6411
|
}
|
|
6398
6412
|
recipientDid = await resolveRecipient(opts.server, "receipt propose", recipientDid, { json: opts.json });
|
|
6399
|
-
delegationId =
|
|
6400
|
-
if (opts.relId) opts.relId =
|
|
6413
|
+
delegationId = requireUuidNormalised("receipt propose", delegationId, "<delegation-id>");
|
|
6414
|
+
if (opts.relId) opts.relId = requireUuidNormalised("receipt propose", opts.relId, "--rel-id");
|
|
6401
6415
|
const verdict = parseVerdict("receipt propose", opts.verdict);
|
|
6402
6416
|
const ttlSeconds = parseTtl2("receipt propose", opts.ttl);
|
|
6403
6417
|
if (opts.notesHash) requireSha256("receipt propose", opts.notesHash, "--notes-hash");
|
|
@@ -6658,10 +6672,6 @@ function parseInteger(cmdName, flag, raw) {
|
|
|
6658
6672
|
}
|
|
6659
6673
|
return n;
|
|
6660
6674
|
}
|
|
6661
|
-
function requireUuidNormalised2(cmdName, raw, label) {
|
|
6662
|
-
requireUuid3(cmdName, raw, label);
|
|
6663
|
-
return raw.toLowerCase();
|
|
6664
|
-
}
|
|
6665
6675
|
function requireUuid3(cmdName, raw, label) {
|
|
6666
6676
|
requireUuid(cmdName, raw, label);
|
|
6667
6677
|
}
|
|
@@ -7897,7 +7907,7 @@ async function runRequest(recipientDid, delegationId, opts) {
|
|
|
7897
7907
|
);
|
|
7898
7908
|
}
|
|
7899
7909
|
recipientDid = await resolveRecipient(opts.server, "work request", recipientDid, { json: opts.json });
|
|
7900
|
-
delegationId =
|
|
7910
|
+
delegationId = requireUuidNormalised("work request", delegationId, "<delegation-id>");
|
|
7901
7911
|
const ttlSeconds = parseTtl5("work request", opts.ttl);
|
|
7902
7912
|
const params = parseParamsInput("work request", opts);
|
|
7903
7913
|
const requestId = parseRequestId("work request", opts.requestId);
|
|
@@ -7954,8 +7964,8 @@ async function runRespond(relationshipId, delegationId, requestId, opts) {
|
|
|
7954
7964
|
"work respond: --verbose and --json are mutually exclusive. --json emits the structured server response; --verbose adds dumps that would break `--json | jq`."
|
|
7955
7965
|
);
|
|
7956
7966
|
}
|
|
7957
|
-
relationshipId =
|
|
7958
|
-
delegationId =
|
|
7967
|
+
relationshipId = requireUuidNormalised("work respond", relationshipId, "<relationship-id>");
|
|
7968
|
+
delegationId = requireUuidNormalised("work respond", delegationId, "<delegation-id>");
|
|
7959
7969
|
const ttlSeconds = parseTtl5("work respond", opts.ttl);
|
|
7960
7970
|
const responsePayload = parseResponsePayload("work respond", opts);
|
|
7961
7971
|
const api = new ArpApiClient(opts.server);
|
|
@@ -8098,13 +8108,13 @@ function parseParamsInput(cmdName, opts) {
|
|
|
8098
8108
|
return parseJsonObject(cmdName, "--params", opts.params ?? "{}");
|
|
8099
8109
|
}
|
|
8100
8110
|
function readJsonObjectFile(cmdName, flagName, path) {
|
|
8101
|
-
const { existsSync: existsSync7, readFileSync:
|
|
8111
|
+
const { existsSync: existsSync7, readFileSync: readFileSync10 } = require("fs");
|
|
8102
8112
|
if (!existsSync7(path)) {
|
|
8103
8113
|
throw new Error(`${cmdName}: ${flagName} file not found at ${path}`);
|
|
8104
8114
|
}
|
|
8105
8115
|
let raw;
|
|
8106
8116
|
try {
|
|
8107
|
-
raw =
|
|
8117
|
+
raw = readFileSync10(path, "utf8");
|
|
8108
8118
|
} catch (err) {
|
|
8109
8119
|
const detail = err instanceof Error ? err.message : String(err);
|
|
8110
8120
|
throw new Error(`${cmdName}: failed to read ${flagName} (${path}): ${detail}`);
|
|
@@ -8159,13 +8169,6 @@ function parseRequestId(cmdName, raw) {
|
|
|
8159
8169
|
}
|
|
8160
8170
|
return raw;
|
|
8161
8171
|
}
|
|
8162
|
-
function requireUuidNormalised3(cmdName, raw, label) {
|
|
8163
|
-
requireUuid4(cmdName, raw, label);
|
|
8164
|
-
return raw.toLowerCase();
|
|
8165
|
-
}
|
|
8166
|
-
function requireUuid4(cmdName, raw, label) {
|
|
8167
|
-
requireUuid(cmdName, raw, label);
|
|
8168
|
-
}
|
|
8169
8172
|
|
|
8170
8173
|
// src/commands/work-list.ts
|
|
8171
8174
|
var import_sdk32 = require("@heyanon-arp/sdk");
|
|
@@ -8324,19 +8327,104 @@ function unknownOptionHint(program, err) {
|
|
|
8324
8327
|
}
|
|
8325
8328
|
}
|
|
8326
8329
|
|
|
8327
|
-
// src/
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
+
// src/version-gate.ts
|
|
8331
|
+
var import_node_fs10 = require("fs");
|
|
8332
|
+
var import_node_path7 = require("path");
|
|
8333
|
+
init_paths();
|
|
8334
|
+
var CHECK_INTERVAL_MS = 60 * 60 * 1e3;
|
|
8335
|
+
var REGISTRY_TIMEOUT_MS = 1500;
|
|
8336
|
+
var VERSION_GATE_BYPASS_ENV = "HEYARP_IGNORE_VERSION_GATE";
|
|
8337
|
+
var cacheFile = () => (0, import_node_path7.join)(arpHomeDir(), "update-check.json");
|
|
8338
|
+
function compareVersions(a, b) {
|
|
8339
|
+
const seg = (v, i) => Number.parseInt(v.split(".")[i] ?? "0", 10) || 0;
|
|
8340
|
+
for (let i = 0; i < 3; i++) {
|
|
8341
|
+
const d = seg(a, i) - seg(b, i);
|
|
8342
|
+
if (d !== 0) return d > 0 ? 1 : -1;
|
|
8343
|
+
}
|
|
8344
|
+
return 0;
|
|
8345
|
+
}
|
|
8346
|
+
function isClientBlocked(current, latest) {
|
|
8347
|
+
if (!latest) return false;
|
|
8348
|
+
return compareVersions(current, latest) < 0;
|
|
8349
|
+
}
|
|
8350
|
+
function isVersionGateExempt(argv) {
|
|
8351
|
+
if (argv.length === 0) return true;
|
|
8352
|
+
if (argv[0] === "help") return true;
|
|
8353
|
+
return argv.includes("--version") || argv.includes("-V") || argv.includes("--help") || argv.includes("-h");
|
|
8354
|
+
}
|
|
8355
|
+
async function fetchLatest(pkgName) {
|
|
8356
|
+
const ctrl = new AbortController();
|
|
8357
|
+
const timer = setTimeout(() => ctrl.abort(), REGISTRY_TIMEOUT_MS);
|
|
8330
8358
|
try {
|
|
8331
|
-
await (
|
|
8332
|
-
|
|
8333
|
-
|
|
8359
|
+
const res = await fetch(`https://registry.npmjs.org/-/package/${pkgName.replace("/", "%2F")}/dist-tags`, {
|
|
8360
|
+
signal: ctrl.signal,
|
|
8361
|
+
headers: { accept: "application/json" }
|
|
8334
8362
|
});
|
|
8363
|
+
if (!res.ok) return null;
|
|
8364
|
+
const body = await res.json();
|
|
8365
|
+
return typeof body.latest === "string" ? body.latest : null;
|
|
8335
8366
|
} catch {
|
|
8367
|
+
return null;
|
|
8368
|
+
} finally {
|
|
8369
|
+
clearTimeout(timer);
|
|
8336
8370
|
}
|
|
8337
8371
|
}
|
|
8372
|
+
async function resolveLatest(pkgName) {
|
|
8373
|
+
try {
|
|
8374
|
+
const cached = JSON.parse((0, import_node_fs10.readFileSync)(cacheFile(), "utf8"));
|
|
8375
|
+
if (typeof cached.latest === "string" && typeof cached.checkedAt === "number" && Date.now() - cached.checkedAt < CHECK_INTERVAL_MS) {
|
|
8376
|
+
return cached.latest;
|
|
8377
|
+
}
|
|
8378
|
+
} catch {
|
|
8379
|
+
}
|
|
8380
|
+
const fresh = await fetchLatest(pkgName);
|
|
8381
|
+
if (!fresh) return null;
|
|
8382
|
+
try {
|
|
8383
|
+
const file = cacheFile();
|
|
8384
|
+
(0, import_node_fs10.mkdirSync)((0, import_node_path7.dirname)(file), { recursive: true });
|
|
8385
|
+
(0, import_node_fs10.writeFileSync)(file, JSON.stringify({ checkedAt: Date.now(), latest: fresh }));
|
|
8386
|
+
} catch {
|
|
8387
|
+
}
|
|
8388
|
+
return fresh;
|
|
8389
|
+
}
|
|
8390
|
+
async function enforceMinClientVersion(pkg, argv) {
|
|
8391
|
+
if (process.env[VERSION_GATE_BYPASS_ENV]) return;
|
|
8392
|
+
if (isVersionGateExempt(argv)) return;
|
|
8393
|
+
let latest;
|
|
8394
|
+
try {
|
|
8395
|
+
latest = await resolveLatest(pkg.name);
|
|
8396
|
+
} catch {
|
|
8397
|
+
return;
|
|
8398
|
+
}
|
|
8399
|
+
if (!isClientBlocked(pkg.version, latest)) return;
|
|
8400
|
+
const update = `npm i -g ${pkg.name}@latest`;
|
|
8401
|
+
if (argv.includes("--json")) {
|
|
8402
|
+
console.error(
|
|
8403
|
+
JSON.stringify({
|
|
8404
|
+
code: "CLI_VERSION_TOO_OLD",
|
|
8405
|
+
message: `heyarp ${pkg.version} is out of date and blocked; the current release is ${latest}. Update with \`${update}\`, then re-run.`,
|
|
8406
|
+
details: { current: pkg.version, latest, update, bypassEnv: VERSION_GATE_BYPASS_ENV }
|
|
8407
|
+
})
|
|
8408
|
+
);
|
|
8409
|
+
} else {
|
|
8410
|
+
process.stderr.write(
|
|
8411
|
+
`
|
|
8412
|
+
heyarp ${pkg.version} is out of date and is blocked.
|
|
8413
|
+
The current release is ${latest}. Update, then re-run:
|
|
8414
|
+
|
|
8415
|
+
${update}
|
|
8416
|
+
|
|
8417
|
+
(emergency bypass: set ${VERSION_GATE_BYPASS_ENV}=1)
|
|
8418
|
+
|
|
8419
|
+
`
|
|
8420
|
+
);
|
|
8421
|
+
}
|
|
8422
|
+
process.exit(1);
|
|
8423
|
+
}
|
|
8424
|
+
|
|
8425
|
+
// src/cli.ts
|
|
8338
8426
|
async function main() {
|
|
8339
|
-
|
|
8427
|
+
await enforceMinClientVersion(package_default, process.argv.slice(2));
|
|
8340
8428
|
const program = new import_commander.Command();
|
|
8341
8429
|
program.name("heyarp").description("ARP \u2014 Agent Relationship Protocol CLI (talks to apps/arp-server)").version(package_default.version).usage("[global-options] <command> [options]").option("--trace", "Surface stack traces and error details on failure.", false);
|
|
8342
8430
|
program.exitOverride();
|