@heyanon-arp/cli 0.0.7 → 0.0.9
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/README.md +4 -4
- package/dist/cli.js +937 -806
- package/dist/cli.js.map +1 -1
- package/package.json +3 -4
- package/scripts/postinstall.mjs +7 -6
- package/examples/README.md +0 -157
- package/examples/worker-template.py +0 -834
package/dist/cli.js
CHANGED
|
@@ -289,8 +289,7 @@ var init_api = __esm({
|
|
|
289
289
|
}
|
|
290
290
|
/**
|
|
291
291
|
* Public `GET /v1/agents` — discovery / marketplace catalog.
|
|
292
|
-
* Unauthenticated.
|
|
293
|
-
* drafts / paused agents never appear here.
|
|
292
|
+
* Unauthenticated. Returns registered agents.
|
|
294
293
|
*
|
|
295
294
|
* Filters AND-compose: pass multiple tags to require all of them,
|
|
296
295
|
* combine with `q` for free-text. `after` is the `id` of the
|
|
@@ -321,18 +320,6 @@ var init_api = __esm({
|
|
|
321
320
|
async updateAgent(did, body, signer) {
|
|
322
321
|
return this.signedRequest("PATCH", `/v1/agents/${encodeURIComponent(did)}`, body, signer);
|
|
323
322
|
}
|
|
324
|
-
async publishAgent(did, signer) {
|
|
325
|
-
return this.signedRequest("POST", `/v1/agents/${encodeURIComponent(did)}/publish`, {}, signer);
|
|
326
|
-
}
|
|
327
|
-
async pauseAgent(did, signer) {
|
|
328
|
-
return this.signedRequest("POST", `/v1/agents/${encodeURIComponent(did)}/pause`, {}, signer);
|
|
329
|
-
}
|
|
330
|
-
async unpauseAgent(did, signer) {
|
|
331
|
-
return this.signedRequest("POST", `/v1/agents/${encodeURIComponent(did)}/unpause`, {}, signer);
|
|
332
|
-
}
|
|
333
|
-
async rotateIdentityKey(did, body, signer) {
|
|
334
|
-
return this.signedRequest("POST", `/v1/agents/${encodeURIComponent(did)}/rotate-identity-key`, body, signer);
|
|
335
|
-
}
|
|
336
323
|
/**
|
|
337
324
|
* Ingest a signed envelope. Endpoint is public (no
|
|
338
325
|
* `X-ARP-Signer-DID` headers) — authentication is the envelope's
|
|
@@ -933,18 +920,6 @@ var init_state = __esm({
|
|
|
933
920
|
|
|
934
921
|
// src/commands/lifecycle.ts
|
|
935
922
|
function registerLifecycleCommands(root) {
|
|
936
|
-
root.command("publish").description("Publish a draft agent (draft \u2192 active), making it discoverable in the catalog.").argument("<did>", "Agent DID (did:arp:...) \u2014 must have local state").option("--server <url>", "Override ARP server base URL").action(async (did, opts) => {
|
|
937
|
-
const agent = await actSigned(did, opts.server, (api, signer) => api.publishAgent(did, signer));
|
|
938
|
-
printAgent("Published", agent);
|
|
939
|
-
});
|
|
940
|
-
root.command("pause").description("Pause an active agent (active \u2192 paused).").argument("<did>").option("--server <url>", "Override ARP server base URL").action(async (did, opts) => {
|
|
941
|
-
const agent = await actSigned(did, opts.server, (api, signer) => api.pauseAgent(did, signer));
|
|
942
|
-
printAgent("Paused", agent);
|
|
943
|
-
});
|
|
944
|
-
root.command("unpause").description("Unpause a paused agent (paused \u2192 active).").argument("<did>").option("--server <url>", "Override ARP server base URL").action(async (did, opts) => {
|
|
945
|
-
const agent = await actSigned(did, opts.server, (api, signer) => api.unpauseAgent(did, signer));
|
|
946
|
-
printAgent("Unpaused", agent);
|
|
947
|
-
});
|
|
948
923
|
root.command("update").description("Update an agent profile (name / description / tags). At least one flag is required.").argument("<did>").option("--server <url>", "Override ARP server base URL").option("--name <s>", "New display name").option("--description <s>", "New description").option("--tag <s>", "Capability tag \u2014 REPLACES the existing list. Repeatable: --tag translation --tag fr", accumulate2, []).option("--clear-tags", "Drop all tags (cannot be combined with --tag)", false).action(
|
|
949
924
|
async (did, opts) => {
|
|
950
925
|
const body = buildUpdateBody(opts);
|
|
@@ -996,7 +971,6 @@ function printAgent(verb, agent) {
|
|
|
996
971
|
console.log(import_chalk4.default.green(`
|
|
997
972
|
${verb}.`));
|
|
998
973
|
console.log(`${import_chalk4.default.bold("DID")}: ${import_chalk4.default.cyan(agent.did)}`);
|
|
999
|
-
console.log(`${import_chalk4.default.bold("Status")}: ${import_chalk4.default.cyan(agent.publicationStatus)}`);
|
|
1000
974
|
console.log(import_chalk4.default.bold("\nAgent profile:"));
|
|
1001
975
|
console.log(formatJson(agent));
|
|
1002
976
|
}
|
|
@@ -1238,9 +1212,11 @@ function isPhaseTerminallyUnreachable(phase, s) {
|
|
|
1238
1212
|
function untilPhaseMatched(phase, s) {
|
|
1239
1213
|
switch (phase) {
|
|
1240
1214
|
case "delegation.proposed":
|
|
1241
|
-
return s.latestDelegation?.state === "proposed" || s.latestDelegation?.state === "offered"
|
|
1215
|
+
return s.latestDelegation?.state === "proposed" || s.latestDelegation?.state === "offered";
|
|
1242
1216
|
case "delegation.accepted":
|
|
1243
1217
|
return s.latestDelegation?.state === "accepted";
|
|
1218
|
+
case "delegation.locked":
|
|
1219
|
+
return s.latestDelegation?.state === "locked";
|
|
1244
1220
|
case "delegation.canceled":
|
|
1245
1221
|
return s.latestDelegation?.state === "canceled";
|
|
1246
1222
|
case "delegation.declined":
|
|
@@ -1279,7 +1255,7 @@ function parseWaitInterval(raw) {
|
|
|
1279
1255
|
return n;
|
|
1280
1256
|
}
|
|
1281
1257
|
function sleep(ms) {
|
|
1282
|
-
return new Promise((
|
|
1258
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1283
1259
|
}
|
|
1284
1260
|
async function composeStatus(api, signerDid, relationshipId, signer) {
|
|
1285
1261
|
const [relationshipsOrNull, delegationsOrError] = await Promise.all([
|
|
@@ -1315,7 +1291,7 @@ async function composeStatus(api, signerDid, relationshipId, signer) {
|
|
|
1315
1291
|
const relationship = relationships.find((r) => r.relationshipId === relationshipId);
|
|
1316
1292
|
const counterpartyDid = relationship ? relationship.pairDidA === signerDid ? relationship.pairDidB : relationship.pairDidA : inferCounterpartyFromEntities(signerDid, allDelegations);
|
|
1317
1293
|
const delegations = allDelegations;
|
|
1318
|
-
const latestDelegation = pickLatestLive(delegations, ["proposed", "offered", "pending_lock_finalization", "
|
|
1294
|
+
const latestDelegation = pickLatestLive(delegations, ["proposed", "offered", "accepted", "pending_lock_finalization", "locked", "awaiting_release_finalization"]);
|
|
1319
1295
|
const [workLogs, receipts] = await Promise.all([
|
|
1320
1296
|
latestDelegation ? fetchAllPages(
|
|
1321
1297
|
(after) => api.listWorkLogs(relationshipId, signer, { limit: 100, delegationId: latestDelegation.delegationId, ...after ? { after } : {} })
|
|
@@ -1437,7 +1413,7 @@ function nextAction(input) {
|
|
|
1437
1413
|
}
|
|
1438
1414
|
if (!latestDelegation || latestDelegation.state === "declined" || latestDelegation.state === "canceled") {
|
|
1439
1415
|
return {
|
|
1440
|
-
hint: "No live delegation \u2014 buyer
|
|
1416
|
+
hint: "No live delegation \u2014 buyer offers (terms only, no lock) `heyarp delegation offer <peer> --delegation-id <new-uuid> --title \u2026 --scope \u2026 --pricing-model flat --amount \u2026 --currency USDC:solana-devnet --deadline \u2026`, then funds the escrow lock AFTER the worker accepts via `heyarp delegation fund <del-id> --escrow-lock-from-file <path>`",
|
|
1441
1417
|
owner: "either",
|
|
1442
1418
|
complete: false
|
|
1443
1419
|
};
|
|
@@ -1451,7 +1427,7 @@ function nextAction(input) {
|
|
|
1451
1427
|
complete: false
|
|
1452
1428
|
};
|
|
1453
1429
|
}
|
|
1454
|
-
if (latestDelegation.state === "proposed" || latestDelegation.state === "offered"
|
|
1430
|
+
if (latestDelegation.state === "proposed" || latestDelegation.state === "offered") {
|
|
1455
1431
|
const iAmOfferer = latestDelegation.offererDid === signerDid;
|
|
1456
1432
|
const stateLabel = latestDelegation.state === "proposed" ? "PROPOSED" : `${latestDelegation.state.toUpperCase()} (\u2261 proposed)`;
|
|
1457
1433
|
return {
|
|
@@ -1460,10 +1436,25 @@ function nextAction(input) {
|
|
|
1460
1436
|
complete: false
|
|
1461
1437
|
};
|
|
1462
1438
|
}
|
|
1439
|
+
if (latestDelegation.state === "accepted" && !latestWorkLog) {
|
|
1440
|
+
const iAmOfferer = latestDelegation.offererDid === signerDid;
|
|
1441
|
+
return {
|
|
1442
|
+
hint: iAmOfferer ? `Delegation ${latestDelegation.delegationId} is ACCEPTED \u2014 you (buyer) owe \`heyarp wallet create-lock --delegation-id ${latestDelegation.delegationId} --condition-hash <hex> ... > lock.json\` then \`heyarp delegation fund ${latestDelegation.delegationId} --escrow-lock-from-file lock.json\` (fund the escrow lock; work begins once it is LOCKED on chain)` : `Delegation ${latestDelegation.delegationId} is ACCEPTED \u2014 counterparty (the buyer) owes \`heyarp delegation fund\` to lock the escrow; wait for the delegation to reach LOCKED before working`,
|
|
1443
|
+
owner: iAmOfferer ? "me" : "counterparty",
|
|
1444
|
+
complete: false
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
if (latestDelegation.state === "pending_lock_finalization" && !latestWorkLog) {
|
|
1448
|
+
return {
|
|
1449
|
+
hint: `Delegation ${latestDelegation.delegationId} is PENDING_LOCK_FINALIZATION \u2014 the escrow lock was funded and is awaiting on-chain confirmation; both sides wait (auto-advances to LOCKED). Poll with \`heyarp status ${relationship.relationshipId} --wait --until delegation.locked\`.`,
|
|
1450
|
+
owner: "none",
|
|
1451
|
+
complete: false
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1463
1454
|
if (!latestWorkLog) {
|
|
1464
1455
|
const iAmOfferer = latestDelegation.offererDid === signerDid;
|
|
1465
1456
|
return {
|
|
1466
|
-
hint: iAmOfferer ? `Delegation ${latestDelegation.delegationId} is
|
|
1457
|
+
hint: iAmOfferer ? `Delegation ${latestDelegation.delegationId} is LOCKED \u2014 you owe \`heyarp work request <peer> ${latestDelegation.delegationId} --request-id <new-uuid> --params '{\u2026}'\`` : `Delegation ${latestDelegation.delegationId} is LOCKED \u2014 counterparty (the caller) owes \`heyarp work request\``,
|
|
1467
1458
|
owner: iAmOfferer ? "me" : "counterparty",
|
|
1468
1459
|
complete: false
|
|
1469
1460
|
};
|
|
@@ -1576,7 +1567,7 @@ function cycleHeadline(s) {
|
|
|
1576
1567
|
}
|
|
1577
1568
|
}
|
|
1578
1569
|
function stateColor(state) {
|
|
1579
|
-
const c = state === "active" || state === "accepted" || state === "cosigned" || state === "responded" || state === "completed" ? import_chalk5.default.green : state === "pending" || state === "proposed" || state === "requested" || state === "offered" || state === "pending_lock_finalization" || state === "awaiting_release_finalization" ? import_chalk5.default.yellow : state === "closed" || state === "declined" || state === "canceled" || state === "replaced" || state === "not_found" || // Terminal failure states deserve the same red
|
|
1570
|
+
const c = state === "active" || state === "accepted" || state === "locked" || state === "cosigned" || state === "responded" || state === "completed" ? import_chalk5.default.green : state === "pending" || state === "proposed" || state === "requested" || state === "offered" || state === "pending_lock_finalization" || state === "awaiting_release_finalization" ? import_chalk5.default.yellow : state === "closed" || state === "declined" || state === "canceled" || state === "replaced" || state === "not_found" || // Terminal failure states deserve the same red
|
|
1580
1571
|
// treatment as declined/canceled so an operator
|
|
1581
1572
|
// scanning `heyarp status` immediately sees
|
|
1582
1573
|
// "this is dead, don't follow the hint blindly".
|
|
@@ -1594,6 +1585,7 @@ var init_status = __esm({
|
|
|
1594
1585
|
UNTIL_PHASES = [
|
|
1595
1586
|
"delegation.proposed",
|
|
1596
1587
|
"delegation.accepted",
|
|
1588
|
+
"delegation.locked",
|
|
1597
1589
|
"delegation.canceled",
|
|
1598
1590
|
"delegation.declined",
|
|
1599
1591
|
"work.requested",
|
|
@@ -1839,7 +1831,7 @@ async function verifyReleaseHandler(opts) {
|
|
|
1839
1831
|
if (shouldRetrySigCheck) {
|
|
1840
1832
|
const retryDelaysMs = [3e3, 3e3];
|
|
1841
1833
|
for (const delay of retryDelaysMs) {
|
|
1842
|
-
await new Promise((
|
|
1834
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1843
1835
|
const retriedSigs = await conn.getSignaturesForAddress(lockPda, { limit: 5 });
|
|
1844
1836
|
if (retriedSigs.length > 0) {
|
|
1845
1837
|
lockSeenInSignatures = true;
|
|
@@ -1953,7 +1945,7 @@ function mapReleaseMethodToStatus(method) {
|
|
|
1953
1945
|
}
|
|
1954
1946
|
function registerCreateLock(cmd) {
|
|
1955
1947
|
cmd.command("create-lock").description(
|
|
1956
|
-
"Build + sign a create_lock Solana tx (native SOL or an SPL token); output JSON ready for attachments.escrow_lock. This command does NOT submit the tx to chain \u2014 it only signs the blob locally.
|
|
1948
|
+
"Build + sign a create_lock Solana tx (native SOL or an SPL token); output JSON ready for attachments.escrow_lock. This command does NOT submit the tx to chain \u2014 it only signs the blob locally. M6 lock-at-accept: run this AFTER the worker accepts your offer; the actual on-chain submission happens inside `heyarp delegation fund <delegation-id> --escrow-lock-from-file <path>` (the server's escrow worker picks up the signed blob from the fund envelope's attachments and posts it). Watch for the `lock_id` appearing on-chain only AFTER `delegation fund` runs, not after this command."
|
|
1957
1949
|
).option("--server <url>", "Override server URL for sender-key resolution").option("--from-did <did>", "Sender DID (= payer of the lock). Required when more than one agent on the host.").requiredOption("--delegation-id <id>", "Delegation UUID (drives the lock_id derivation)").requiredOption("--recipient-pubkey <base58>", "Payee Solana pubkey (recipient agent's settlement_pubkey)").option("--amount-lamports <int>", "NATIVE SOL: lock amount in lamports (1 SOL = 1_000_000_000). Required when --mint-pubkey is omitted; not allowed with it.").option("--mint-pubkey <base58>", "SPL token mint to lock. Omit for native SOL. Must be a legacy SPL Token mint (Token-2022 is not supported).").option("--amount-base-units <int>", "SPL: lock amount in the mint's base units (e.g. 1000000 = 1 USDC at 6 decimals). Canonical SPL amount input.").option(
|
|
1958
1950
|
"--amount <decimal>",
|
|
1959
1951
|
"SPL: lock amount as a human decimal (e.g. 1.5); converted using the mint's on-chain decimals. Convenience alternative to --amount-base-units."
|
|
@@ -2199,7 +2191,7 @@ async function createLockHandler(opts) {
|
|
|
2199
2191
|
asset_id: asset.assetId,
|
|
2200
2192
|
expiry: Number(expiry),
|
|
2201
2193
|
// Persist the BARE UUID form. The consumer is
|
|
2202
|
-
// `delegation
|
|
2194
|
+
// `delegation fund --escrow-lock-from-file`, which validates
|
|
2203
2195
|
// this field with a bare-UUID regex (the wire form for
|
|
2204
2196
|
// `body.content.delegation_id`). The `del_<uuid>` prefix is an
|
|
2205
2197
|
// SDK-internal byte-encoding marker — not the wire-form. Keeping
|
|
@@ -2385,6 +2377,7 @@ var init_wallet = __esm({
|
|
|
2385
2377
|
function registerDelegationCommands(root) {
|
|
2386
2378
|
const cmd = root.command("delegation").description("Delegation FSM actions: offer / accept / decline / cancel");
|
|
2387
2379
|
registerOffer(cmd);
|
|
2380
|
+
registerFund(cmd);
|
|
2388
2381
|
registerAccept(cmd);
|
|
2389
2382
|
registerDecline(cmd);
|
|
2390
2383
|
registerCancel(cmd);
|
|
@@ -2393,19 +2386,7 @@ function isPostCommitErrorCode(code) {
|
|
|
2393
2386
|
return POST_COMMIT_ERROR_CODES.has(code) || code.startsWith("ESC_LOCK_") || code.startsWith("SDK_");
|
|
2394
2387
|
}
|
|
2395
2388
|
function registerOffer(parent) {
|
|
2396
|
-
parent.command("offer").description("Open a new delegation addressed to <recipient-did> with the agreed terms INLINE.").argument("<recipient-did>", "Recipient agent DID (did:arp:...)").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server").option("--delegation-id <uuid>", "Override the auto-generated delegation id (UUID). Useful for replay / scripting.").option("--title <s>", "Required: human-readable title for the offer").option("--brief <json>", "Optional structured brief (JSON object)").option("--criterion <s>", "acceptance_criteria \u2014 repeatable; pass --criterion once per bullet", collectRepeated, []).option("--deadline <rfc3339>", 'Optional RFC 3339 deadline (e.g. "2026-12-31T23:59:59Z")').option("--scope <text>", "scope_summary \u2014 short prose describing the agreed work. Required
|
|
2397
|
-
"--escrow-lock-from-file <path>",
|
|
2398
|
-
"Path to JSON output of `heyarp wallet create-lock` (recommended). Contains all 5 escrow_lock fields: signed_tx_blob, lock_id, amount, asset_id, expiry. Mutually exclusive with the inline --escrow-lock-* flags below."
|
|
2399
|
-
).option(
|
|
2400
|
-
"--escrow-lock-blob <base64>",
|
|
2401
|
-
"INLINE alternative: the signed Solana tx blob (base64). Requires --escrow-lock-id, --escrow-lock-amount, --escrow-lock-asset-id, --escrow-lock-expiry together."
|
|
2402
|
-
).option("--escrow-lock-id <hex32>", "INLINE lock_id (32-byte hex). Used with --escrow-lock-blob.").option("--escrow-lock-amount <int>", "INLINE lock amount in base units (e.g. lamports for native SOL). Used with --escrow-lock-blob.").option("--escrow-lock-asset-id <caip19>", "INLINE currency CAIP-19 asset_id (e.g. solana:<cluster_id>/slip44:501). Used with --escrow-lock-blob.").option("--escrow-lock-expiry <unix>", "INLINE expiry as unix seconds. Used with --escrow-lock-blob.").option(
|
|
2403
|
-
"--program-id <pubkey>",
|
|
2404
|
-
"Expected ARP escrow program id for pre-flight against the lock file's embedded `program_id`. Precedence: this flag > ARP_ESCROW_PROGRAM_ID env > server protocol-fee endpoint. Mismatch throws BEFORE the envelope ships, so a wrong-program lock never silently fails on chain after the offer was already delivered. Pre-flight is skipped only for old lock files lacking `program_id`, or when no expected value can be resolved."
|
|
2405
|
-
).option(
|
|
2406
|
-
"--no-escrow",
|
|
2407
|
-
"Opt-out for test_mode servers. Default REQUIRES the escrow_lock attachment; without --no-escrow, missing flags throw BEFORE the envelope is sent (avoids ESC_LOCK_MISSING POST_COMMIT consuming sender_sequence)."
|
|
2408
|
-
).option(
|
|
2389
|
+
parent.command("offer").description("Open a new delegation addressed to <recipient-did> with the agreed terms INLINE (no escrow lock \u2014 fund AFTER acceptance via `delegation fund`).").argument("<recipient-did>", "Recipient agent DID (did:arp:...)").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server").option("--delegation-id <uuid>", "Override the auto-generated delegation id (UUID). Reuse the SAME id at `heyarp delegation fund` time. Useful for replay / scripting.").option("--title <s>", "Required: human-readable title for the offer").option("--brief <json>", "Optional structured brief (JSON object)").option("--criterion <s>", "acceptance_criteria \u2014 repeatable; pass --criterion once per bullet", collectRepeated, []).option("--deadline <rfc3339>", 'Optional RFC 3339 deadline (e.g. "2026-12-31T23:59:59Z")').option("--scope <text>", "scope_summary \u2014 short prose describing the agreed work. Required.").option("--pricing-model <flat|usage_based>", "pricing_model (flat|usage_based). Required.").option("--amount <s>", 'Optional decimal-as-string amount (e.g. "10.00"). REQUIRES --currency if set.').option("--currency <s>", `Asset identifier: shorthand (${import_sdk4.WELL_KNOWN_ASSET_KEYS.join("|")}) OR raw CAIP-19 string.`).option("--currency-decimals <n>", "Decimal places for base-unit conversion (0-18). Required only when --currency is raw CAIP-19.").option("--currency-symbol <s>", 'Optional UI hint ("USDC", "SOL"). Max 16 chars.').option("--ttl <seconds>", "Envelope TTL in seconds (max 86400 = 24h)", "3600").option("--verbose", "Print the full envelope before sending and the full server response", false).option(
|
|
2409
2390
|
"--wait-until <phase>",
|
|
2410
2391
|
'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.'
|
|
2411
2392
|
).option("--wait-timeout <seconds>", "When --wait-until is set: max wall-clock wait (default 300). Exit code 124 on timeout.").option("--wait-interval <seconds>", "When --wait-until is set: poll cadence (default 3, bound [1, 60]).").option(
|
|
@@ -2435,39 +2416,35 @@ function assembleEscrowLockAttachment(opts) {
|
|
|
2435
2416
|
const someInlineSet = inlineFlags.some((f) => f !== void 0 && f !== "");
|
|
2436
2417
|
const allInlineSet = inlineFlags.every((f) => f !== void 0 && f !== "");
|
|
2437
2418
|
if (fromFile && someInlineSet) {
|
|
2438
|
-
throw new Error("delegation
|
|
2419
|
+
throw new Error("delegation fund: --escrow-lock-from-file and --escrow-lock-blob/--escrow-lock-* are mutually exclusive. Pick one path.");
|
|
2439
2420
|
}
|
|
2440
2421
|
if (!fromFile && !someInlineSet) {
|
|
2441
|
-
if (opts.escrow === false) return void 0;
|
|
2442
2422
|
throw new Error(
|
|
2443
|
-
"delegation
|
|
2423
|
+
"delegation fund: escrow_lock is required. Pass --escrow-lock-from-file <path> (JSON output of `heyarp wallet create-lock`), OR all of --escrow-lock-blob/--escrow-lock-id/--escrow-lock-amount/--escrow-lock-asset-id/--escrow-lock-expiry."
|
|
2444
2424
|
);
|
|
2445
2425
|
}
|
|
2446
|
-
if (opts.escrow === false) {
|
|
2447
|
-
throw new Error("delegation offer: --no-escrow is mutually exclusive with explicit escrow_lock flags. Pick one path.");
|
|
2448
|
-
}
|
|
2449
2426
|
if (fromFile) {
|
|
2450
2427
|
let raw;
|
|
2451
2428
|
try {
|
|
2452
2429
|
raw = (0, import_node_fs4.readFileSync)(fromFile, "utf8");
|
|
2453
2430
|
} catch (err) {
|
|
2454
|
-
throw new Error(`delegation
|
|
2431
|
+
throw new Error(`delegation fund: failed to read --escrow-lock-from-file '${fromFile}': ${err.message}`);
|
|
2455
2432
|
}
|
|
2456
2433
|
let parsed;
|
|
2457
2434
|
try {
|
|
2458
2435
|
parsed = JSON.parse(raw);
|
|
2459
2436
|
} catch (err) {
|
|
2460
|
-
throw new Error(`delegation
|
|
2437
|
+
throw new Error(`delegation fund: --escrow-lock-from-file '${fromFile}' is not valid JSON: ${err.message}`);
|
|
2461
2438
|
}
|
|
2462
2439
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
2463
|
-
throw new Error(`delegation
|
|
2440
|
+
throw new Error(`delegation fund: --escrow-lock-from-file '${fromFile}' must be a JSON object, got ${Array.isArray(parsed) ? "array" : typeof parsed}.`);
|
|
2464
2441
|
}
|
|
2465
2442
|
const p = parsed;
|
|
2466
2443
|
const required = ["signed_tx_blob", "lock_id", "amount", "asset_id", "expiry"];
|
|
2467
2444
|
const missing = required.filter((k) => p[k] === void 0);
|
|
2468
2445
|
if (missing.length > 0) {
|
|
2469
2446
|
throw new Error(
|
|
2470
|
-
`delegation
|
|
2447
|
+
`delegation fund: --escrow-lock-from-file '${fromFile}' is missing required fields: ${missing.join(", ")}. Expected JSON output of \`heyarp wallet create-lock\`.`
|
|
2471
2448
|
);
|
|
2472
2449
|
}
|
|
2473
2450
|
const expiryRaw = p.expiry;
|
|
@@ -2478,19 +2455,19 @@ function assembleEscrowLockAttachment(opts) {
|
|
|
2478
2455
|
expiryNum2 = Number(expiryRaw);
|
|
2479
2456
|
if (String(expiryNum2) !== expiryRaw.trim()) {
|
|
2480
2457
|
throw new Error(
|
|
2481
|
-
`delegation
|
|
2458
|
+
`delegation fund: --escrow-lock-from-file '${fromFile}' has invalid 'expiry' (must be positive integer unix seconds): ${JSON.stringify(expiryRaw)}.`
|
|
2482
2459
|
);
|
|
2483
2460
|
}
|
|
2484
2461
|
} else {
|
|
2485
|
-
throw new Error(`delegation
|
|
2462
|
+
throw new Error(`delegation fund: --escrow-lock-from-file '${fromFile}' has invalid 'expiry' (must be positive integer unix seconds): ${JSON.stringify(expiryRaw)}.`);
|
|
2486
2463
|
}
|
|
2487
2464
|
if (!Number.isFinite(expiryNum2) || !Number.isInteger(expiryNum2) || expiryNum2 <= 0) {
|
|
2488
|
-
throw new Error(`delegation
|
|
2465
|
+
throw new Error(`delegation fund: --escrow-lock-from-file '${fromFile}' has invalid 'expiry' (must be positive integer unix seconds): ${JSON.stringify(expiryRaw)}.`);
|
|
2489
2466
|
}
|
|
2490
2467
|
let delegationIdFromLock;
|
|
2491
2468
|
if (p.delegation_id !== void 0) {
|
|
2492
2469
|
if (typeof p.delegation_id !== "string" || !UUID_RE.test(p.delegation_id)) {
|
|
2493
|
-
throw new Error(`delegation
|
|
2470
|
+
throw new Error(`delegation fund: --escrow-lock-from-file '${fromFile}' has invalid 'delegation_id' (must be a UUID): ${JSON.stringify(p.delegation_id)}.`);
|
|
2494
2471
|
}
|
|
2495
2472
|
delegationIdFromLock = p.delegation_id.toLowerCase();
|
|
2496
2473
|
}
|
|
@@ -2498,7 +2475,7 @@ function assembleEscrowLockAttachment(opts) {
|
|
|
2498
2475
|
if (p.program_id !== void 0) {
|
|
2499
2476
|
if (typeof p.program_id !== "string" || p.program_id.length < 32 || p.program_id.length > 44) {
|
|
2500
2477
|
throw new Error(
|
|
2501
|
-
`delegation
|
|
2478
|
+
`delegation fund: --escrow-lock-from-file '${fromFile}' has invalid 'program_id' (must be a base58 Solana pubkey, 32-44 chars): ${JSON.stringify(p.program_id)}.`
|
|
2502
2479
|
);
|
|
2503
2480
|
}
|
|
2504
2481
|
lockProgramId = p.program_id;
|
|
@@ -2517,13 +2494,13 @@ function assembleEscrowLockAttachment(opts) {
|
|
|
2517
2494
|
}
|
|
2518
2495
|
if (!allInlineSet) {
|
|
2519
2496
|
throw new Error(
|
|
2520
|
-
"delegation
|
|
2497
|
+
"delegation fund: when using inline escrow flags, --escrow-lock-blob requires all of --escrow-lock-id, --escrow-lock-amount, --escrow-lock-asset-id, --escrow-lock-expiry to be set together."
|
|
2521
2498
|
);
|
|
2522
2499
|
}
|
|
2523
2500
|
const expiryStr = opts.escrowLockExpiry ?? "";
|
|
2524
2501
|
const expiryNum = Number.parseInt(expiryStr, 10);
|
|
2525
2502
|
if (!Number.isFinite(expiryNum) || !Number.isInteger(expiryNum) || expiryNum <= 0 || String(expiryNum) !== expiryStr) {
|
|
2526
|
-
throw new Error(`delegation
|
|
2503
|
+
throw new Error(`delegation fund: --escrow-lock-expiry must be a positive integer (unix seconds), got '${expiryStr}'.`);
|
|
2527
2504
|
}
|
|
2528
2505
|
return {
|
|
2529
2506
|
attachment: {
|
|
@@ -2545,47 +2522,22 @@ async function runOffer(recipientDid, opts) {
|
|
|
2545
2522
|
throw new Error("delegation offer: --title is required (server-side validator rejects empty offers)");
|
|
2546
2523
|
}
|
|
2547
2524
|
const terms = parseOfferTerms("delegation offer", opts);
|
|
2548
|
-
const
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
if (missingTerms.length > 0) {
|
|
2562
|
-
throw new Error(
|
|
2563
|
-
`delegation offer: escrow-backed offers require the inline terms ${missingTerms.join(", ")} (the on-chain condition_hash binds scope_summary/pricing_model/settlement_model/rate_amount/rate_unit/currency; a missing or divergent term is rejected by the server with ESC_LOCK_CONDITION_HASH_MISMATCH). Pass the SAME values you fed to \`heyarp escrow derive-condition-hash\`.`
|
|
2564
|
-
);
|
|
2565
|
-
}
|
|
2525
|
+
const delegationId = resolveOfferDelegationId(opts.delegationId, void 0);
|
|
2526
|
+
if (terms.amount === void 0 || terms.currency === void 0) {
|
|
2527
|
+
throw new Error(
|
|
2528
|
+
"delegation offer: escrow-backed offers require both --amount and --currency (these are the agreed terms; the buyer funds the matching escrow_lock later via `heyarp delegation fund`)."
|
|
2529
|
+
);
|
|
2530
|
+
}
|
|
2531
|
+
const missingTerms = [terms.scope_summary === void 0 ? "--scope" : null, terms.pricing_model === void 0 ? "--pricing-model" : null].filter(
|
|
2532
|
+
(x) => x !== null
|
|
2533
|
+
);
|
|
2534
|
+
if (missingTerms.length > 0) {
|
|
2535
|
+
throw new Error(
|
|
2536
|
+
`delegation offer: escrow-backed offers require the inline terms ${missingTerms.join(", ")} (the on-chain condition_hash binds scope_summary/pricing_model/currency at fund time; a missing or divergent term is rejected with ESC_LOCK_CONDITION_HASH_MISMATCH when you run \`heyarp delegation fund\`). Pass the SAME values you will feed to \`heyarp escrow derive-condition-hash\`.`
|
|
2537
|
+
);
|
|
2566
2538
|
}
|
|
2567
2539
|
const api = new ArpApiClient(opts.server);
|
|
2568
2540
|
const sender = resolveSenderAgent("delegation offer", opts.server, opts.fromDid);
|
|
2569
|
-
if (escrowResult?.lockProgramId !== void 0) {
|
|
2570
|
-
const expected = await resolveProgramIdWithSource(api, { programId: opts.programId });
|
|
2571
|
-
const result2 = preflightLockProgramId({
|
|
2572
|
-
lockProgramId: escrowResult.lockProgramId,
|
|
2573
|
-
expectedProgramId: expected.programId,
|
|
2574
|
-
expectedSource: expected.source
|
|
2575
|
-
});
|
|
2576
|
-
if (result2.kind === "mismatch") {
|
|
2577
|
-
throw new Error(
|
|
2578
|
-
`delegation offer: lock file program_id ${result2.lockProgramId} does not match the expected escrow program ${result2.expectedProgramId} (resolved from --program-id flag > ARP_ESCROW_PROGRAM_ID env > server protocol-fee). The lock tx would be rejected on chain (ESC_LOCK_TX_PROGRAM_ID_MISMATCH) and the delegation would silently fail ~26s after offer. Regenerate the lock with the correct program id via \`heyarp wallet create-lock --program-id ${result2.expectedProgramId} ...\`.`
|
|
2579
|
-
);
|
|
2580
|
-
}
|
|
2581
|
-
if (result2.kind === "skipped_no_expected_program_id") {
|
|
2582
|
-
console.error(
|
|
2583
|
-
import_chalk6.default.yellow(
|
|
2584
|
-
`\u26A0 delegation offer: no authoritative expected program-id available (--program-id missing, ARP_ESCROW_PROGRAM_ID env unset, server protocol-fee unreachable). Pre-flight skipped; server-side ESC_LOCK_TX_PROGRAM_ID_MISMATCH check is the only backstop. Set ARP_ESCROW_PROGRAM_ID or pass --program-id to enable pre-flight.`
|
|
2585
|
-
)
|
|
2586
|
-
);
|
|
2587
|
-
}
|
|
2588
|
-
}
|
|
2589
2541
|
const content = {
|
|
2590
2542
|
action: "offer",
|
|
2591
2543
|
delegation_id: delegationId,
|
|
@@ -2593,22 +2545,21 @@ async function runOffer(recipientDid, opts) {
|
|
|
2593
2545
|
...terms
|
|
2594
2546
|
};
|
|
2595
2547
|
const body = { type: "delegation", content };
|
|
2596
|
-
const attachments = escrowResult ? { escrow_lock: escrowResult.attachment } : void 0;
|
|
2597
2548
|
console.log(import_chalk6.default.dim(`Server: ${api.serverUrl}`));
|
|
2598
2549
|
console.log(import_chalk6.default.dim(`Sender: ${sender.did}`));
|
|
2599
2550
|
console.log(import_chalk6.default.dim(`Recipient: ${recipientDid}`));
|
|
2600
2551
|
console.log(import_chalk6.default.dim(`Delegation: ${delegationId}`));
|
|
2601
|
-
|
|
2602
|
-
const a = escrowResult.attachment;
|
|
2603
|
-
console.log(import_chalk6.default.dim(`Escrow lock attached: lock_id=${a.lock_id} amount=${a.amount} asset_id=${a.asset_id}`));
|
|
2604
|
-
}
|
|
2605
|
-
const result = await sendDelegationEnvelope({ api, sender, recipientDid, body, attachments, ttlSeconds, verbose: opts.verbose, server: opts.server });
|
|
2552
|
+
const result = await sendDelegationEnvelope({ api, sender, recipientDid, body, ttlSeconds, verbose: opts.verbose, server: opts.server });
|
|
2606
2553
|
printIngestResult(result);
|
|
2607
2554
|
console.log(import_chalk6.default.dim(`
|
|
2608
2555
|
Reference this delegation on subsequent calls with:`));
|
|
2609
2556
|
console.log(import_chalk6.default.dim(` heyarp delegation accept ${result.relationshipId} ${delegationId}`));
|
|
2610
2557
|
console.log(import_chalk6.default.dim(` heyarp delegation decline ${result.relationshipId} ${delegationId}`));
|
|
2611
2558
|
console.log(import_chalk6.default.dim(` heyarp delegation cancel ${result.relationshipId} ${delegationId}`));
|
|
2559
|
+
console.log(import_chalk6.default.dim(`
|
|
2560
|
+
After the worker accepts, fund the escrow lock:`));
|
|
2561
|
+
console.log(import_chalk6.default.dim(` heyarp wallet create-lock --delegation-id ${delegationId} --condition-hash <hex> ... > lock.json`));
|
|
2562
|
+
console.log(import_chalk6.default.dim(` heyarp delegation fund ${delegationId} --escrow-lock-from-file lock.json`));
|
|
2612
2563
|
if (opts.waitUntil) {
|
|
2613
2564
|
const untilPhase = parseUntilPhase(opts.waitUntil);
|
|
2614
2565
|
if (untilPhase === void 0) {
|
|
@@ -2628,6 +2579,152 @@ Reference this delegation on subsequent calls with:`));
|
|
|
2628
2579
|
});
|
|
2629
2580
|
}
|
|
2630
2581
|
}
|
|
2582
|
+
function registerFund(parent) {
|
|
2583
|
+
parent.command("fund").description("Fund an ACCEPTED delegation with the escrow lock (buyer-only). Run AFTER the worker accepts the offer.").argument("<delegation-id>", "Delegation UUID (the one you offered + the worker accepted)").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server").option("--ttl <seconds>", "Envelope TTL in seconds (max 86400 = 24h)", "3600").option("--verbose", "Print the full envelope before sending and the full server response. Mutually exclusive with --json.", false).option(
|
|
2584
|
+
"--json",
|
|
2585
|
+
'Machine-readable mode \u2014 emit a single JSON object on stdout ({ok, action:"fund", delegationId, eventId, relationshipId, relationshipEventIndex, serverTimestamp, serverEventHash}). Prelude moves off stdout; on failure stderr carries `{code, message}`. Mutually exclusive with --verbose.',
|
|
2586
|
+
false
|
|
2587
|
+
).option(
|
|
2588
|
+
"--escrow-lock-from-file <path>",
|
|
2589
|
+
"Path to JSON output of `heyarp wallet create-lock` (recommended). Contains all 5 escrow_lock fields: signed_tx_blob, lock_id, amount, asset_id, expiry (+ delegation_id / program_id for pre-flight). Mutually exclusive with the inline --escrow-lock-* flags below."
|
|
2590
|
+
).option(
|
|
2591
|
+
"--escrow-lock-blob <base64>",
|
|
2592
|
+
"INLINE alternative: the signed Solana tx blob (base64). Requires --escrow-lock-id, --escrow-lock-amount, --escrow-lock-asset-id, --escrow-lock-expiry together."
|
|
2593
|
+
).option("--escrow-lock-id <hex32>", "INLINE lock_id (32-byte hex). Used with --escrow-lock-blob.").option("--escrow-lock-amount <int>", "INLINE lock amount in base units (e.g. lamports for native SOL). Used with --escrow-lock-blob.").option("--escrow-lock-asset-id <caip19>", "INLINE currency CAIP-19 asset_id (e.g. solana:<cluster_id>/slip44:501). Used with --escrow-lock-blob.").option("--escrow-lock-expiry <unix>", "INLINE expiry as unix seconds. Used with --escrow-lock-blob.").option(
|
|
2594
|
+
"--program-id <pubkey>",
|
|
2595
|
+
"Expected ARP escrow program id for pre-flight against the lock file's embedded `program_id`. Precedence: this flag > ARP_ESCROW_PROGRAM_ID env > server protocol-fee endpoint. Mismatch throws BEFORE the envelope ships, so a wrong-program lock never silently fails on chain after fund was already delivered. Pre-flight is skipped only for old lock files lacking `program_id`, or when no expected value can be resolved."
|
|
2596
|
+
).option(
|
|
2597
|
+
"--wait-until <phase>",
|
|
2598
|
+
"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."
|
|
2599
|
+
).option("--wait-timeout <seconds>", "When --wait-until is set: max wall-clock wait (default 300). Exit code 124 on timeout.").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) => {
|
|
2600
|
+
await runFund(delegationId, opts);
|
|
2601
|
+
});
|
|
2602
|
+
}
|
|
2603
|
+
async function runFund(delegationId, opts) {
|
|
2604
|
+
if (opts.verbose && opts.json) {
|
|
2605
|
+
throw new Error(
|
|
2606
|
+
"delegation fund: --verbose and --json are mutually exclusive. --json emits the structured server response; --verbose adds dumps that would break `--json | jq`."
|
|
2607
|
+
);
|
|
2608
|
+
}
|
|
2609
|
+
delegationId = requireUuidNormalised("delegation fund", delegationId, "<delegation-id>");
|
|
2610
|
+
const ttlSeconds = parseTtl("delegation fund", opts.ttl);
|
|
2611
|
+
const escrowResult = assembleEscrowLockAttachment({
|
|
2612
|
+
escrowLockFromFile: opts.escrowLockFromFile,
|
|
2613
|
+
escrowLockBlob: opts.escrowLockBlob,
|
|
2614
|
+
escrowLockId: opts.escrowLockId,
|
|
2615
|
+
escrowLockAmount: opts.escrowLockAmount,
|
|
2616
|
+
escrowLockAssetId: opts.escrowLockAssetId,
|
|
2617
|
+
escrowLockExpiry: opts.escrowLockExpiry
|
|
2618
|
+
});
|
|
2619
|
+
if (escrowResult.delegationIdFromLock !== void 0 && escrowResult.delegationIdFromLock !== delegationId) {
|
|
2620
|
+
throw new Error(
|
|
2621
|
+
`delegation fund: <delegation-id> (${delegationId}) disagrees with the lock file's delegation_id (${escrowResult.delegationIdFromLock}). The on-chain lock_id is derived from the lock-side id; funding a different delegation would guarantee ESC_LOCK_ID_MISMATCH. Fund the delegation the lock was built for, or regenerate the lock with --delegation-id ${delegationId}.`
|
|
2622
|
+
);
|
|
2623
|
+
}
|
|
2624
|
+
const api = new ArpApiClient(opts.server);
|
|
2625
|
+
const sender = resolveSenderAgent("delegation fund", opts.server, opts.fromDid);
|
|
2626
|
+
const signer = makeSigner(sender);
|
|
2627
|
+
const resolved = await resolveFundRefs("delegation fund", api, signer, { delegationId, selfDid: sender.did });
|
|
2628
|
+
if (escrowResult.lockProgramId !== void 0) {
|
|
2629
|
+
const expected = await resolveProgramIdWithSource(api, { programId: opts.programId });
|
|
2630
|
+
const result2 = preflightLockProgramId({
|
|
2631
|
+
lockProgramId: escrowResult.lockProgramId,
|
|
2632
|
+
expectedProgramId: expected.programId,
|
|
2633
|
+
expectedSource: expected.source
|
|
2634
|
+
});
|
|
2635
|
+
if (result2.kind === "mismatch") {
|
|
2636
|
+
throw new Error(
|
|
2637
|
+
`delegation fund: lock file program_id ${result2.lockProgramId} does not match the expected escrow program ${result2.expectedProgramId} (resolved from --program-id flag > ARP_ESCROW_PROGRAM_ID env > server protocol-fee). The lock tx would be rejected on chain (ESC_LOCK_TX_PROGRAM_ID_MISMATCH) and the delegation would silently fail ~26s after fund. Regenerate the lock with the correct program id via \`heyarp wallet create-lock --program-id ${result2.expectedProgramId} ...\`.`
|
|
2638
|
+
);
|
|
2639
|
+
}
|
|
2640
|
+
if (result2.kind === "skipped_no_expected_program_id") {
|
|
2641
|
+
console.error(
|
|
2642
|
+
import_chalk6.default.yellow(
|
|
2643
|
+
"\u26A0 delegation fund: no authoritative expected program-id available (--program-id missing, ARP_ESCROW_PROGRAM_ID env unset, server protocol-fee unreachable). Pre-flight skipped; server-side ESC_LOCK_TX_PROGRAM_ID_MISMATCH check is the only backstop. Set ARP_ESCROW_PROGRAM_ID or pass --program-id to enable pre-flight."
|
|
2644
|
+
)
|
|
2645
|
+
);
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
const content = { action: "fund", delegation_id: delegationId };
|
|
2649
|
+
const body = { type: "delegation", content };
|
|
2650
|
+
const attachments = { escrow_lock: escrowResult.attachment };
|
|
2651
|
+
progress(opts.json, import_chalk6.default.dim(`Server: ${api.serverUrl}`));
|
|
2652
|
+
progress(opts.json, import_chalk6.default.dim(`Sender: ${sender.did}`));
|
|
2653
|
+
progress(opts.json, import_chalk6.default.dim(`Recipient: ${resolved.recipientDid}`));
|
|
2654
|
+
progress(opts.json, import_chalk6.default.dim(`Relationship: ${resolved.relationshipId}`));
|
|
2655
|
+
progress(opts.json, import_chalk6.default.dim(`Delegation: ${delegationId} (action=fund)`));
|
|
2656
|
+
{
|
|
2657
|
+
const a = escrowResult.attachment;
|
|
2658
|
+
progress(opts.json, import_chalk6.default.dim(`Escrow lock attached: lock_id=${a.lock_id} amount=${a.amount} asset_id=${a.asset_id}`));
|
|
2659
|
+
}
|
|
2660
|
+
const result = await sendDelegationEnvelope({
|
|
2661
|
+
api,
|
|
2662
|
+
sender,
|
|
2663
|
+
recipientDid: resolved.recipientDid,
|
|
2664
|
+
body,
|
|
2665
|
+
attachments,
|
|
2666
|
+
ttlSeconds,
|
|
2667
|
+
verbose: opts.verbose,
|
|
2668
|
+
server: opts.server
|
|
2669
|
+
});
|
|
2670
|
+
if (opts.json) {
|
|
2671
|
+
jsonOut({
|
|
2672
|
+
ok: true,
|
|
2673
|
+
action: "fund",
|
|
2674
|
+
delegationId,
|
|
2675
|
+
eventId: result.eventId,
|
|
2676
|
+
relationshipId: result.relationshipId,
|
|
2677
|
+
relationshipEventIndex: result.relationshipEventIndex,
|
|
2678
|
+
serverTimestamp: result.serverTimestamp,
|
|
2679
|
+
serverEventHash: result.serverEventHash,
|
|
2680
|
+
prevServerEventHash: result.prevServerEventHash ?? null
|
|
2681
|
+
});
|
|
2682
|
+
} else {
|
|
2683
|
+
printIngestResult(result);
|
|
2684
|
+
console.log(import_chalk6.default.dim("\nThe worker waits for the on-chain lock to confirm (LOCKED), then begins work."));
|
|
2685
|
+
}
|
|
2686
|
+
if (opts.waitUntil && !opts.json) {
|
|
2687
|
+
const untilPhase = parseUntilPhase(opts.waitUntil);
|
|
2688
|
+
if (untilPhase === void 0) {
|
|
2689
|
+
throw new Error(`delegation fund: --wait-until requires a phase value (got ${JSON.stringify(opts.waitUntil)})`);
|
|
2690
|
+
}
|
|
2691
|
+
await awaitFsmTransitionAfterAction({
|
|
2692
|
+
api,
|
|
2693
|
+
signerDid: sender.did,
|
|
2694
|
+
signer,
|
|
2695
|
+
relationshipId: result.relationshipId,
|
|
2696
|
+
untilPhase,
|
|
2697
|
+
waitIntervalSec: parseWaitInterval(opts.waitInterval),
|
|
2698
|
+
waitTimeoutSec: parseWaitTimeout(opts.waitTimeout),
|
|
2699
|
+
waitVerbose: !!opts.waitVerbose,
|
|
2700
|
+
json: false
|
|
2701
|
+
});
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
async function resolveFundRefs(cmdName, api, signer, args) {
|
|
2705
|
+
const relationships = await api.listRelationships(args.selfDid, signer, { limit: 100 });
|
|
2706
|
+
for (const rel of relationships) {
|
|
2707
|
+
let after;
|
|
2708
|
+
for (; ; ) {
|
|
2709
|
+
const page = await api.listDelegations(rel.relationshipId, signer, { limit: 100, after });
|
|
2710
|
+
const row = page.find((d) => d.delegationId === args.delegationId);
|
|
2711
|
+
if (row) {
|
|
2712
|
+
if (row.offererDid !== args.selfDid) {
|
|
2713
|
+
throw new Error(
|
|
2714
|
+
`${cmdName}: delegation ${args.delegationId} was offered by ${row.offererDid}, not you (${args.selfDid}). Funding the escrow lock is the BUYER's (offerer's) action.`
|
|
2715
|
+
);
|
|
2716
|
+
}
|
|
2717
|
+
const recipientDid = rel.pairDidA === args.selfDid ? rel.pairDidB : rel.pairDidA;
|
|
2718
|
+
return { relationshipId: rel.relationshipId, recipientDid, state: row.state };
|
|
2719
|
+
}
|
|
2720
|
+
if (page.length < 100) break;
|
|
2721
|
+
after = page[page.length - 1].id;
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
throw new Error(
|
|
2725
|
+
`${cmdName}: delegation ${args.delegationId} not found in any of your relationships (checked the first 100 by recent activity). Confirm the worker accepted your offer and that you are the offerer; inspect with \`heyarp delegations <relationship-id> --from-did ${args.selfDid}\`.`
|
|
2726
|
+
);
|
|
2727
|
+
}
|
|
2631
2728
|
function registerAccept(parent) {
|
|
2632
2729
|
parent.command("accept").description("Accept a PROPOSED delegation \u2014 promotes to ACCEPTED. Counterparty-only.").argument("<relationship-id>", "Relationship UUID").argument("<delegation-id>", "Delegation UUID").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server").option("--ttl <seconds>", "Envelope TTL in seconds", "3600").option("--verbose", "Print the full envelope before sending and the full server response. Mutually exclusive with --json.", false).option(
|
|
2633
2730
|
"--json",
|
|
@@ -2654,7 +2751,7 @@ function registerDecline(parent) {
|
|
|
2654
2751
|
});
|
|
2655
2752
|
}
|
|
2656
2753
|
function registerCancel(parent) {
|
|
2657
|
-
parent.command("cancel").description("Cancel
|
|
2754
|
+
parent.command("cancel").description("Cancel an OFFERED delegation \u2014 moves to CANCELED. Offerer-only (the OTHER side uses decline).").argument("<relationship-id>", "Relationship UUID").argument("<delegation-id>", "Delegation UUID").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server").option("--ttl <seconds>", "Envelope TTL in seconds", "3600").option("--verbose", "Print the full envelope before sending and the full server response", false).option(
|
|
2658
2755
|
"--no-wait-for-lock",
|
|
2659
2756
|
"Opt-out of the pending-lock pre-flight poll. Default is ON: when the resolved delegation is in `pending_lock_finalization`, the CLI polls until the on-chain lock is confirmed before signing the envelope (avoids the racy DELEGATION_PENDING_LOCK 409 that consumes sender_sequence)."
|
|
2660
2757
|
).option("--lock-wait-timeout <seconds>", "Max wall-clock seconds to wait for pending-lock pre-flight (default 300).", "300").option("--lock-wait-interval <seconds>", "Poll cadence for pending-lock pre-flight in seconds. Bound to [1, 60].", "3").action(async (relationshipId, delegationId, opts) => {
|
|
@@ -2931,11 +3028,6 @@ function parseOfferTerms(cmdName, opts) {
|
|
|
2931
3028
|
if (opts.scope) out.scope_summary = opts.scope;
|
|
2932
3029
|
const pricingModel = parsePricingModel(cmdName, opts.pricingModel);
|
|
2933
3030
|
if (pricingModel !== void 0) out.pricing_model = pricingModel;
|
|
2934
|
-
const settlementModel = parseSettlementModel(cmdName, opts.settlementModel);
|
|
2935
|
-
if (settlementModel !== void 0) out.settlement_model = settlementModel;
|
|
2936
|
-
if (opts.rateAmount) out.rate_amount = opts.rateAmount;
|
|
2937
|
-
const rateUnit = parseRateUnit(cmdName, opts.rateUnit);
|
|
2938
|
-
if (rateUnit !== void 0) out.rate_unit = rateUnit;
|
|
2939
3031
|
if (opts.amount) {
|
|
2940
3032
|
out.amount = opts.amount;
|
|
2941
3033
|
if (!opts.currency) {
|
|
@@ -2954,20 +3046,6 @@ function parsePricingModel(cmdName, raw) {
|
|
|
2954
3046
|
}
|
|
2955
3047
|
return raw;
|
|
2956
3048
|
}
|
|
2957
|
-
function parseSettlementModel(cmdName, raw) {
|
|
2958
|
-
if (raw === void 0 || raw === "") return void 0;
|
|
2959
|
-
if (raw !== "prepaid" && raw !== "escrow") {
|
|
2960
|
-
throw new Error(`${cmdName}: --settlement-model must be one of prepaid|escrow (got '${raw}')`);
|
|
2961
|
-
}
|
|
2962
|
-
return raw;
|
|
2963
|
-
}
|
|
2964
|
-
function parseRateUnit(cmdName, raw) {
|
|
2965
|
-
if (raw === void 0 || raw === "") return void 0;
|
|
2966
|
-
if (raw !== "task" && raw !== "thread" && raw !== "handoff") {
|
|
2967
|
-
throw new Error(`${cmdName}: --rate-unit must be one of task|thread|handoff (got '${raw}')`);
|
|
2968
|
-
}
|
|
2969
|
-
return raw;
|
|
2970
|
-
}
|
|
2971
3049
|
function parseTtl(cmdName, raw) {
|
|
2972
3050
|
if (raw === void 0) return 3600;
|
|
2973
3051
|
const n = Number(raw);
|
|
@@ -3095,6 +3173,17 @@ var init_delegation = __esm({
|
|
|
3095
3173
|
// against a PENDING_LOCK delegation consumes sender_sequence
|
|
3096
3174
|
// even though it rejects.
|
|
3097
3175
|
"DELEGATION_PENDING_LOCK",
|
|
3176
|
+
// M6 lock-at-accept: the `fund` body handler (`handleFund`) emits
|
|
3177
|
+
// these AFTER the event row is committed (same lifecycle as
|
|
3178
|
+
// DELEGATION_INVALID_STATE). `DELEGATION_FUNDER_NOT_OFFERER` (403)
|
|
3179
|
+
// when a non-offerer attempts the fund; `DELEGATION_ALREADY_FUNDED`
|
|
3180
|
+
// (409) on a re-fund of a delegation that already moved into the
|
|
3181
|
+
// lock lifecycle (pending_lock_finalization / locked, or an
|
|
3182
|
+
// in-flight create_lock op). Both consume sender_sequence, so the
|
|
3183
|
+
// CLI must advance `lastSenderSequence` or a retry trips
|
|
3184
|
+
// `ENV_SEQUENCE_BACKWARDS`.
|
|
3185
|
+
"DELEGATION_FUNDER_NOT_OFFERER",
|
|
3186
|
+
"DELEGATION_ALREADY_FUNDED",
|
|
3098
3187
|
// V1.5 lock-validator mint.owner pre-flight — kept here for
|
|
3099
3188
|
// documentation, but the `isPostCommitErrorCode` helper below
|
|
3100
3189
|
// also matches any `ESC_LOCK_*` prefix. The full lock-validator
|
|
@@ -3346,11 +3435,8 @@ function projectDelegationRowForHash(d) {
|
|
|
3346
3435
|
const out = {
|
|
3347
3436
|
delegationId: d.delegationId,
|
|
3348
3437
|
scopeSummary: d.scopeSummary,
|
|
3349
|
-
pricingModel: d.pricingModel
|
|
3350
|
-
settlementModel: d.settlementModel
|
|
3438
|
+
pricingModel: d.pricingModel
|
|
3351
3439
|
};
|
|
3352
|
-
if (d.rateAmount !== void 0) out.rateAmount = d.rateAmount;
|
|
3353
|
-
if (d.rateUnit !== void 0) out.rateUnit = d.rateUnit;
|
|
3354
3440
|
if (d.currency !== void 0) {
|
|
3355
3441
|
out.currency = {
|
|
3356
3442
|
asset_id: d.currency.assetId,
|
|
@@ -3461,9 +3547,15 @@ async function readOnChainLock(api, opts, delegationId) {
|
|
|
3461
3547
|
const programId = new import_web33.PublicKey(resolved.programId);
|
|
3462
3548
|
const { lockPda } = deriveLockPda(programId, delegationId);
|
|
3463
3549
|
const conn = new import_web33.Connection(rpcUrl, "confirmed");
|
|
3464
|
-
|
|
3550
|
+
let info = await conn.getAccountInfo(lockPda);
|
|
3551
|
+
for (let attempt = 1; attempt <= LOCK_READ_NOT_VISIBLE_RETRIES && !info; attempt++) {
|
|
3552
|
+
await new Promise((r) => setTimeout(r, LOCK_READ_RETRY_DELAY_MS));
|
|
3553
|
+
info = await conn.getAccountInfo(lockPda);
|
|
3554
|
+
}
|
|
3465
3555
|
if (!info) {
|
|
3466
|
-
return {
|
|
3556
|
+
return {
|
|
3557
|
+
skipReason: `lock account ${lockPda.toBase58()} not visible on this RPC yet after ${LOCK_READ_NOT_VISIBLE_RETRIES + 1} attempts (create_lock may still be confirming / RPC lag)`
|
|
3558
|
+
};
|
|
3467
3559
|
}
|
|
3468
3560
|
const lock = decodeLockAccount(info.data);
|
|
3469
3561
|
if (!lock) {
|
|
@@ -3492,10 +3584,10 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
|
|
|
3492
3584
|
const api = new ArpApiClient(opts.server);
|
|
3493
3585
|
const sender = resolveSenderAgent(cmdName, opts.server, opts.fromDid);
|
|
3494
3586
|
const signer = makeSigner(sender);
|
|
3495
|
-
say(
|
|
3496
|
-
say(
|
|
3587
|
+
say(import_chalk17.default.dim(`Server: ${api.serverUrl}`));
|
|
3588
|
+
say(import_chalk17.default.dim(`Signer (payee): ${sender.did}`));
|
|
3497
3589
|
const relId = opts.relId ?? await resolveAutoRelId(api, sender, delegationId);
|
|
3498
|
-
say(
|
|
3590
|
+
say(import_chalk17.default.dim(`Relationship: ${relId}`));
|
|
3499
3591
|
const receiptMatches = await collectReceiptRows(api, signer, relId, delegationId);
|
|
3500
3592
|
const receipt = selectReceipt(receiptMatches, opts.receiptEventHash, cmdName);
|
|
3501
3593
|
if (!receipt) {
|
|
@@ -3516,7 +3608,7 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
|
|
|
3516
3608
|
if (opts.json) {
|
|
3517
3609
|
jsonOut({ ok: true, idempotent: true, alreadyCosigned: true, delegationId, relationshipId: relId, buyerDid: receipt.callerDid });
|
|
3518
3610
|
} else {
|
|
3519
|
-
progress(opts.json,
|
|
3611
|
+
progress(opts.json, import_chalk17.default.yellow("\n[idempotent] Receipt already cosigned \u2014 escrow release is settled. Nothing to send."));
|
|
3520
3612
|
}
|
|
3521
3613
|
}
|
|
3522
3614
|
return { delivered: false, idempotent: true, alreadyCosigned: true, relationshipId: relId, buyerDid: receipt.callerDid };
|
|
@@ -3539,8 +3631,8 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
|
|
|
3539
3631
|
expiresAt: ps.expires_at
|
|
3540
3632
|
});
|
|
3541
3633
|
} else {
|
|
3542
|
-
progress(opts.json,
|
|
3543
|
-
progress(opts.json,
|
|
3634
|
+
progress(opts.json, import_chalk17.default.yellow("\n[idempotent] Payee settlement signature already delivered + still valid for this receipt \u2014 skipping re-send."));
|
|
3635
|
+
progress(opts.json, import_chalk17.default.dim(` purpose=${ps.purpose} settlement_pubkey=${ps.settlement_pubkey} expires_at=${ps.expires_at}`));
|
|
3544
3636
|
}
|
|
3545
3637
|
}
|
|
3546
3638
|
return {
|
|
@@ -3554,14 +3646,14 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
|
|
|
3554
3646
|
expiresAt: ps.expires_at
|
|
3555
3647
|
};
|
|
3556
3648
|
}
|
|
3557
|
-
say(
|
|
3649
|
+
say(import_chalk17.default.yellow(`
|
|
3558
3650
|
[refresh] Existing payee settlement sig expires at ${ps.expires_at} (\u2264 now+${SETTLEMENT_REFRESH_HEADROOM_SECS}s) \u2014 re-signing with a fresh expiry.`));
|
|
3559
3651
|
}
|
|
3560
3652
|
const buyerDid = receipt.callerDid;
|
|
3561
3653
|
const receiptEventHash = receipt.receiptEventHash;
|
|
3562
3654
|
const deliverableHash = receipt.deliverableHash ?? receipt.responseHash;
|
|
3563
|
-
say(
|
|
3564
|
-
say(
|
|
3655
|
+
say(import_chalk17.default.dim(`Buyer (payer): ${buyerDid}`));
|
|
3656
|
+
say(import_chalk17.default.dim(`Receipt event hash: ${receiptEventHash}`));
|
|
3565
3657
|
const delegation = await findDelegationRow(api, signer, relId, delegationId);
|
|
3566
3658
|
if (!delegation) {
|
|
3567
3659
|
throw new Error(`${cmdName}: delegation ${delegationId} not found under relationship ${relId} (paginated 5000 rows).`);
|
|
@@ -3601,11 +3693,6 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
|
|
|
3601
3693
|
`${cmdName}: settlement expires_at (${Number.isFinite(expiresAt6) ? expiresAt6 : "NaN"}) is not far enough in the future \u2014 it must be > now+${SETTLEMENT_MIN_EXPIRY_HEADROOM_SECS}s (${minSafeExpiry}) or the server rejects it post-commit (SETTLEMENT_SIG_EXPIRES_AT_*), burning a sender_sequence. ${fromExplicit ? "Pass a later --expires-at <unix-seconds> (still \u2264 the lock's on-chain expiry)." : `Derived from deadline ${JSON.stringify(delegation.deadline)} + buffer ${bufferSecs}s \u2014 the deadline is too close or in the past. Increase --settlement-buffer-secs or pass --expires-at <unix-seconds> \u2264 the lock's expiry.`}`
|
|
3602
3694
|
);
|
|
3603
3695
|
}
|
|
3604
|
-
if (delegation.settlementModel === "prepaid") {
|
|
3605
|
-
throw new Error(
|
|
3606
|
-
`${cmdName}: delegation ${delegationId} settlementModel is 'prepaid' (non-escrow). This command delivers an on-chain escrow-release signature; a prepaid delegation has no lock to settle and the server would reject post-commit (SETTLEMENT_SIG_LOCK_NOT_FOUND). Nothing to settle on-chain.`
|
|
3607
|
-
);
|
|
3608
|
-
}
|
|
3609
3696
|
const subset = projectDelegationRowForHash(delegation);
|
|
3610
3697
|
const conditionHash = (0, import_utils3.bytesToHex)((0, import_sdk10.deriveDelegationConditionHash)(subset));
|
|
3611
3698
|
const buyerDidDoc = await api.getDidDocument(buyerDid);
|
|
@@ -3667,9 +3754,9 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
|
|
|
3667
3754
|
});
|
|
3668
3755
|
effectiveFeeBpsAtLock = resolvedFee.feeBpsAtLock;
|
|
3669
3756
|
effectiveFeeRecipientAtLock = resolvedFee.feeRecipientAtLock;
|
|
3670
|
-
say(
|
|
3757
|
+
say(import_chalk17.default.dim(`On-chain lock pre-flight OK \u2014 payee/amount/expiry/mint match; fee_bps_at_lock=${resolvedFee.feeBpsAtLock}.`));
|
|
3671
3758
|
} else {
|
|
3672
|
-
say(
|
|
3759
|
+
say(import_chalk17.default.yellow(`On-chain lock pre-flight skipped: ${lockCheck.skipReason}. The server remains the validation boundary.`));
|
|
3673
3760
|
}
|
|
3674
3761
|
const signOpts = {
|
|
3675
3762
|
server: opts.server,
|
|
@@ -3692,7 +3779,7 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
|
|
|
3692
3779
|
...effectiveFeeRecipientAtLock !== void 0 ? { feeRecipientAtLock: effectiveFeeRecipientAtLock } : {},
|
|
3693
3780
|
...partialPayeeAmount !== void 0 ? { partialPayeeAmount } : {}
|
|
3694
3781
|
};
|
|
3695
|
-
say(
|
|
3782
|
+
say(import_chalk17.default.dim(`Signing ${partialPayeeAmount !== void 0 ? "PARTIAL" : "full"} release: lock_amount=${lockAmount}, expires_at=${expiresAt6}, cluster_tag=${clusterTag}`));
|
|
3696
3783
|
const signed = await signSettlementHandler(signOpts);
|
|
3697
3784
|
const content = {
|
|
3698
3785
|
delegation_id: delegationId,
|
|
@@ -3723,16 +3810,16 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
|
|
|
3723
3810
|
serverTimestamp: result.serverTimestamp
|
|
3724
3811
|
});
|
|
3725
3812
|
} else {
|
|
3726
|
-
console.log(
|
|
3727
|
-
console.log(`${
|
|
3728
|
-
console.log(`${
|
|
3729
|
-
console.log(`${
|
|
3813
|
+
console.log(import_chalk17.default.green("\nSettlement signature signed + delivered."));
|
|
3814
|
+
console.log(`${import_chalk17.default.bold("Delegation")}: ${import_chalk17.default.cyan(delegationId)}`);
|
|
3815
|
+
console.log(`${import_chalk17.default.bold("Buyer")}: ${import_chalk17.default.cyan(buyerDid)}`);
|
|
3816
|
+
console.log(`${import_chalk17.default.bold("Purpose")}: ${import_chalk17.default.cyan(signed.purpose)}`);
|
|
3730
3817
|
if (partialPayeeAmount !== void 0) {
|
|
3731
|
-
console.log(`${
|
|
3818
|
+
console.log(`${import_chalk17.default.bold("Payee amount")}: ${import_chalk17.default.cyan(partialPayeeAmount)} (partial release)`);
|
|
3732
3819
|
}
|
|
3733
|
-
console.log(`${
|
|
3734
|
-
console.log(`${
|
|
3735
|
-
console.log(
|
|
3820
|
+
console.log(`${import_chalk17.default.bold("Expires at")}: ${import_chalk17.default.cyan(String(expiresAt6))}`);
|
|
3821
|
+
console.log(`${import_chalk17.default.bold("Delivered event")}: ${import_chalk17.default.cyan(result.serverEventHash)}`);
|
|
3822
|
+
console.log(import_chalk17.default.dim("\nThe buyer cosigns with: heyarp receipt cosign <rel-id> <del-id> --auto-hashes --auto-resolve-payee-sig --payer-sig-from-file <path>"));
|
|
3736
3823
|
}
|
|
3737
3824
|
}
|
|
3738
3825
|
return {
|
|
@@ -3751,13 +3838,13 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
|
|
|
3751
3838
|
serverTimestamp: result.serverTimestamp
|
|
3752
3839
|
};
|
|
3753
3840
|
}
|
|
3754
|
-
var import_sdk10, import_utils3, import_web33,
|
|
3841
|
+
var import_sdk10, import_utils3, import_web33, import_chalk17, NATIVE_SOL_MINT2, SETTLEMENT_MIN_EXPIRY_HEADROOM_SECS, SETTLEMENT_REFRESH_HEADROOM_SECS, LOCK_READ_NOT_VISIBLE_RETRIES, LOCK_READ_RETRY_DELAY_MS;
|
|
3755
3842
|
var init_settlement = __esm({
|
|
3756
3843
|
"src/commands/settlement.ts"() {
|
|
3757
3844
|
import_sdk10 = require("@heyanon-arp/sdk");
|
|
3758
3845
|
import_utils3 = require("@noble/hashes/utils");
|
|
3759
3846
|
import_web33 = require("@solana/web3.js");
|
|
3760
|
-
|
|
3847
|
+
import_chalk17 = __toESM(require("chalk"));
|
|
3761
3848
|
init_api();
|
|
3762
3849
|
init_format();
|
|
3763
3850
|
init_state();
|
|
@@ -3771,6 +3858,8 @@ var init_settlement = __esm({
|
|
|
3771
3858
|
NATIVE_SOL_MINT2 = "11111111111111111111111111111111";
|
|
3772
3859
|
SETTLEMENT_MIN_EXPIRY_HEADROOM_SECS = 120;
|
|
3773
3860
|
SETTLEMENT_REFRESH_HEADROOM_SECS = 600;
|
|
3861
|
+
LOCK_READ_NOT_VISIBLE_RETRIES = 3;
|
|
3862
|
+
LOCK_READ_RETRY_DELAY_MS = 2e3;
|
|
3774
3863
|
}
|
|
3775
3864
|
});
|
|
3776
3865
|
|
|
@@ -3798,10 +3887,10 @@ function registerPropose(parent) {
|
|
|
3798
3887
|
false
|
|
3799
3888
|
).option(
|
|
3800
3889
|
"--cluster-tag <int>",
|
|
3801
|
-
"Solana cluster the escrow lock lives on: 0 = devnet, 1 = mainnet-beta. MUST match the create_lock cluster or the server-reconstructed release digest will not verify. REQUIRED
|
|
3890
|
+
"Solana cluster the escrow lock lives on: 0 = devnet, 1 = mainnet-beta. MUST match the create_lock cluster or the server-reconstructed release digest will not verify. REQUIRED (every delegation is escrow-backed) \u2014 there is no default (a defaulted/wrong cluster silently signs an invalid settlement digest that fails at cosign)."
|
|
3802
3891
|
).option(
|
|
3803
3892
|
"--settlement-buffer-secs <int>",
|
|
3804
|
-
"Seconds added to the delegation deadline for the settlement digest expires_at (dispute buffer). Default 86400 (1 day). Ignored when --expires-at is given
|
|
3893
|
+
"Seconds added to the delegation deadline for the settlement digest expires_at (dispute buffer). Default 86400 (1 day). Ignored when --expires-at is given.",
|
|
3805
3894
|
"86400"
|
|
3806
3895
|
).option(
|
|
3807
3896
|
"--expires-at <unix>",
|
|
@@ -3826,6 +3915,14 @@ function registerPropose(parent) {
|
|
|
3826
3915
|
await runPropose(recipientDid, delegationId, requestHash, responseHash, opts);
|
|
3827
3916
|
} catch (err) {
|
|
3828
3917
|
emitActionError(err, cmd);
|
|
3918
|
+
if (!opts.json && err instanceof ApiError && err.payload.code === "RECEIPT_ALREADY_EXISTS") {
|
|
3919
|
+
console.error(
|
|
3920
|
+
import_chalk18.default.dim(
|
|
3921
|
+
` This receipt already exists (a prior propose committed it). Do NOT re-propose \u2014 to (re)deliver the payee settlement signature, run:
|
|
3922
|
+
'heyarp receipt send-payee-sig ${recipientDid} --delegation-id ${delegationId} --auto --cluster-tag ${opts.clusterTag ?? "<0|1>"}'`
|
|
3923
|
+
)
|
|
3924
|
+
);
|
|
3925
|
+
}
|
|
3829
3926
|
process.exitCode = 1;
|
|
3830
3927
|
}
|
|
3831
3928
|
});
|
|
@@ -3850,15 +3947,13 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
|
|
|
3850
3947
|
opts.relId = await resolveAutoRelId(api, sender, delegationId);
|
|
3851
3948
|
progress(
|
|
3852
3949
|
opts.json,
|
|
3853
|
-
|
|
3950
|
+
import_chalk18.default.dim(`[auto-rel-id] resolved --rel-id=${opts.relId} (delegation found in exactly one of your relationships; pass --rel-id explicitly to override)`)
|
|
3854
3951
|
);
|
|
3855
3952
|
}
|
|
3856
|
-
let delegationRow;
|
|
3857
3953
|
if (opts.relId) {
|
|
3858
|
-
|
|
3954
|
+
await assertSenderIsReceiptPayee(api, sender, opts.relId, delegationId);
|
|
3859
3955
|
}
|
|
3860
|
-
|
|
3861
|
-
if (isEscrow) {
|
|
3956
|
+
{
|
|
3862
3957
|
const ct = opts.clusterTag;
|
|
3863
3958
|
if (ct === void 0 || ct === "") {
|
|
3864
3959
|
throw new Error(
|
|
@@ -3879,7 +3974,7 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
|
|
|
3879
3974
|
opts.requestId = await resolveAutoRequestId(api, sender, opts.relId, delegationId);
|
|
3880
3975
|
progress(
|
|
3881
3976
|
opts.json,
|
|
3882
|
-
|
|
3977
|
+
import_chalk18.default.dim(
|
|
3883
3978
|
`[auto-request-id] resolved --request-id=${opts.requestId} (unique 'responded' work_log under this delegation; pass --request-id explicitly to override)`
|
|
3884
3979
|
)
|
|
3885
3980
|
);
|
|
@@ -3898,8 +3993,8 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
|
|
|
3898
3993
|
}
|
|
3899
3994
|
requestHash = computed.requestHash;
|
|
3900
3995
|
responseHash = computed.responseHash;
|
|
3901
|
-
progress(opts.json,
|
|
3902
|
-
progress(opts.json,
|
|
3996
|
+
progress(opts.json, import_chalk18.default.dim(`[auto-hashes] request_hash: ${requestHash} (from work-log ${opts.relId}/${delegationId}/${opts.requestId})`));
|
|
3997
|
+
progress(opts.json, import_chalk18.default.dim(`[auto-hashes] response_hash: ${responseHash}`));
|
|
3903
3998
|
} else {
|
|
3904
3999
|
if (requestHashArg === void 0 || responseHashArg === void 0) {
|
|
3905
4000
|
throw new Error("receipt propose: <request-hash> and <response-hash> are required (or pass --auto-hashes + --rel-id + --request-id to derive them from the work-log)");
|
|
@@ -3919,15 +4014,15 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
|
|
|
3919
4014
|
if (opts.deliverableHash) content.deliverable_hash = opts.deliverableHash;
|
|
3920
4015
|
if (usage) content.usage = usage;
|
|
3921
4016
|
const body = { type: "receipt", content };
|
|
3922
|
-
progress(opts.json,
|
|
3923
|
-
progress(opts.json,
|
|
3924
|
-
progress(opts.json,
|
|
3925
|
-
progress(opts.json,
|
|
3926
|
-
progress(opts.json,
|
|
4017
|
+
progress(opts.json, import_chalk18.default.dim(`Server: ${api.serverUrl}`));
|
|
4018
|
+
progress(opts.json, import_chalk18.default.dim(`Sender (payee): ${sender.did}`));
|
|
4019
|
+
progress(opts.json, import_chalk18.default.dim(`Recipient (caller): ${recipientDid}`));
|
|
4020
|
+
progress(opts.json, import_chalk18.default.dim(`Delegation: ${delegationId}`));
|
|
4021
|
+
progress(opts.json, import_chalk18.default.dim(`Verdict (proposed): ${verdict}`));
|
|
3927
4022
|
const result = await sendReceiptEnvelope({ api, sender, recipientDid, body, attachments: void 0, ttlSeconds, verbose: opts.verbose, server: opts.server });
|
|
3928
4023
|
let settlementResult;
|
|
3929
4024
|
let settlementError;
|
|
3930
|
-
if (
|
|
4025
|
+
if (opts.relId) {
|
|
3931
4026
|
const { autoSignAndDeliverPayeeSig: autoSignAndDeliverPayeeSig2 } = await Promise.resolve().then(() => (init_settlement(), settlement_exports));
|
|
3932
4027
|
try {
|
|
3933
4028
|
settlementResult = await autoSignAndDeliverPayeeSig2({
|
|
@@ -3975,7 +4070,12 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
|
|
|
3975
4070
|
json.settlement = {
|
|
3976
4071
|
delivered: false,
|
|
3977
4072
|
error: settlementError.message,
|
|
3978
|
-
|
|
4073
|
+
// Authoritative recovery (SR-2/SR-3): re-sign + re-deliver via
|
|
4074
|
+
// --auto, which resolves condition_hash + the on-chain lock
|
|
4075
|
+
// snapshot itself — no hand-typed hashes, no sig file, no
|
|
4076
|
+
// lock_id/condition_hash trap. Do NOT re-run `receipt propose`
|
|
4077
|
+
// (the receipt already exists; a second propose is a duplicate).
|
|
4078
|
+
recovery: `heyarp receipt send-payee-sig ${recipientDid} --delegation-id ${delegationId} --auto --cluster-tag ${opts.clusterTag ?? "<0|1>"} (do NOT re-run 'receipt propose' \u2014 the receipt already exists, a second propose is rejected as a duplicate)`
|
|
3979
4079
|
};
|
|
3980
4080
|
} else if (settlementResult) {
|
|
3981
4081
|
json.settlement = {
|
|
@@ -3995,32 +4095,31 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
|
|
|
3995
4095
|
return;
|
|
3996
4096
|
}
|
|
3997
4097
|
printIngestResult2(result);
|
|
3998
|
-
console.log(
|
|
3999
|
-
Receipt event hash: ${
|
|
4098
|
+
console.log(import_chalk18.default.dim(`
|
|
4099
|
+
Receipt event hash: ${import_chalk18.default.cyan(result.serverEventHash)}`));
|
|
4000
4100
|
if (settlementError) {
|
|
4001
|
-
console.error(
|
|
4002
|
-
console.error(
|
|
4003
|
-
console.error(
|
|
4004
|
-
console.error(
|
|
4005
|
-
console.error(
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4101
|
+
console.error(import_chalk18.default.yellow("\nWARNING: receipt proposed, but the payee settlement signature was NOT delivered."));
|
|
4102
|
+
console.error(import_chalk18.default.yellow(` reason: ${settlementError.message}`));
|
|
4103
|
+
console.error(import_chalk18.default.dim(" The receipt stays committed \u2014 do NOT re-run `receipt propose` (the receipt already exists; a second propose is rejected as a duplicate)."));
|
|
4104
|
+
console.error(import_chalk18.default.dim(" Recover authoritatively \u2014 this resolves condition_hash + the on-chain lock snapshot for you (no manual hashes, no sig file):"));
|
|
4105
|
+
console.error(import_chalk18.default.dim(` 'heyarp receipt send-payee-sig ${recipientDid} --delegation-id ${delegationId} --auto --cluster-tag ${opts.clusterTag ?? "<0|1>"}'`));
|
|
4106
|
+
} else if (settlementResult?.delivered) {
|
|
4107
|
+
console.log(import_chalk18.default.green("\nPayee settlement signature signed + delivered (escrow)."));
|
|
4108
|
+
console.log(
|
|
4109
|
+
import_chalk18.default.dim(
|
|
4110
|
+
` purpose=${settlementResult.purpose} expires_at=${settlementResult.expiresAt}${settlementResult.payeeAmount !== void 0 ? ` payee_amount=${settlementResult.payeeAmount} (partial)` : ""}`
|
|
4009
4111
|
)
|
|
4010
4112
|
);
|
|
4011
|
-
} else if (settlementResult?.delivered) {
|
|
4012
|
-
console.log(import_chalk19.default.green("\nPayee settlement signature signed + delivered (escrow)."));
|
|
4013
|
-
console.log(import_chalk19.default.dim(` purpose=${settlementResult.purpose} expires_at=${settlementResult.expiresAt}${settlementResult.payeeAmount !== void 0 ? ` payee_amount=${settlementResult.payeeAmount} (partial)` : ""}`));
|
|
4014
4113
|
} else if (settlementResult?.idempotent) {
|
|
4015
|
-
console.log(
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
} else {
|
|
4022
|
-
console.log(import_chalk19.default.dim(` heyarp receipt cosign ${result.relationshipId} ${delegationId} ${requestHash} ${responseHash} --verdict ${verdict}`));
|
|
4114
|
+
console.log(
|
|
4115
|
+
import_chalk18.default.yellow(
|
|
4116
|
+
`
|
|
4117
|
+
[idempotent] Payee settlement signature already ${settlementResult.alreadyCosigned ? "settled (receipt cosigned)" : "delivered"} \u2014 nothing to re-send.`
|
|
4118
|
+
)
|
|
4119
|
+
);
|
|
4023
4120
|
}
|
|
4121
|
+
console.log(import_chalk18.default.dim(`The caller cosigns with:`));
|
|
4122
|
+
console.log(import_chalk18.default.dim(` heyarp receipt cosign ${result.relationshipId} ${delegationId} --auto-hashes --auto-resolve-payee-sig --payer-sig-from-file <path>`));
|
|
4024
4123
|
}
|
|
4025
4124
|
async function assertSenderIsReceiptPayee(api, sender, relationshipId, delegationId) {
|
|
4026
4125
|
const signer = makeSigner(sender);
|
|
@@ -4168,7 +4267,7 @@ function registerCosign(parent) {
|
|
|
4168
4267
|
function loadSettlementSigFromFile(path, flagPrefix) {
|
|
4169
4268
|
let raw;
|
|
4170
4269
|
try {
|
|
4171
|
-
raw = (0,
|
|
4270
|
+
raw = (0, import_node_fs7.readFileSync)(path, "utf8");
|
|
4172
4271
|
} catch (err) {
|
|
4173
4272
|
throw new Error(`receipt cosign: failed to read ${flagPrefix} '${path}': ${err.message}`);
|
|
4174
4273
|
}
|
|
@@ -4415,28 +4514,28 @@ async function runCosign(relationshipId, delegationId, requestHashArg, responseH
|
|
|
4415
4514
|
content.notes_hash = cosignNotesHash;
|
|
4416
4515
|
}
|
|
4417
4516
|
const body = { type: "receipt", content };
|
|
4418
|
-
console.log(
|
|
4419
|
-
console.log(
|
|
4420
|
-
console.log(
|
|
4421
|
-
console.log(
|
|
4422
|
-
console.log(
|
|
4423
|
-
console.log(
|
|
4517
|
+
console.log(import_chalk18.default.dim(`Server: ${api.serverUrl}`));
|
|
4518
|
+
console.log(import_chalk18.default.dim(`Sender (caller): ${sender.did}`));
|
|
4519
|
+
console.log(import_chalk18.default.dim(`Recipient (payee): ${resolved.payeeDid}`));
|
|
4520
|
+
console.log(import_chalk18.default.dim(`Delegation: ${delegationId}`));
|
|
4521
|
+
console.log(import_chalk18.default.dim(`Verdict (final): ${verdict}`));
|
|
4522
|
+
console.log(import_chalk18.default.dim(`Receipt event hash bound: ${resolved.receiptEventHash}`));
|
|
4424
4523
|
if (cosignNotesHash !== null) {
|
|
4425
|
-
console.log(
|
|
4524
|
+
console.log(import_chalk18.default.dim(`Notes hash bound: ${cosignNotesHash}`));
|
|
4426
4525
|
} else if (opts.clearNotes) {
|
|
4427
|
-
console.log(
|
|
4526
|
+
console.log(import_chalk18.default.dim("Notes binding: cleared (--clear-notes)"));
|
|
4428
4527
|
}
|
|
4429
4528
|
if (opts.autoResolvePayeeSig) {
|
|
4430
4529
|
applyAutoResolvePayeeSig("receipt cosign", opts, resolved.payeeSettlement);
|
|
4431
4530
|
console.log(
|
|
4432
|
-
|
|
4531
|
+
import_chalk18.default.dim(`Auto-resolved payee sig from receipt.payeeSettlement (purpose=${resolved.payeeSettlement?.purpose}, expires_at=${resolved.payeeSettlement?.expires_at})`)
|
|
4433
4532
|
);
|
|
4434
4533
|
}
|
|
4435
4534
|
const settlementSigs = assembleSettlementSignaturesAttachment(opts);
|
|
4436
4535
|
const attachments = { co_signature: cosignature };
|
|
4437
4536
|
if (settlementSigs) {
|
|
4438
4537
|
attachments.settlement_signatures = settlementSigs;
|
|
4439
|
-
console.log(
|
|
4538
|
+
console.log(import_chalk18.default.dim(`Settlement signatures attached: purpose=${settlementSigs.purpose}`));
|
|
4440
4539
|
}
|
|
4441
4540
|
const result = await sendReceiptEnvelope({
|
|
4442
4541
|
api,
|
|
@@ -4468,18 +4567,32 @@ async function runCosign(relationshipId, delegationId, requestHashArg, responseH
|
|
|
4468
4567
|
}
|
|
4469
4568
|
}
|
|
4470
4569
|
function registerSendPayeeSig(parent) {
|
|
4471
|
-
parent.command("send-payee-sig").description(
|
|
4570
|
+
parent.command("send-payee-sig").description(
|
|
4571
|
+
"Send the payee's settlement signature to the buyer via a settlement_signature envelope. Replaces /tmp/*.json coordination for cross-host escrow cycles. Two modes: (1) MANUAL \u2014 supply --receipt-event-hash + --sig-from-file + --expires-at from a prior `wallet sign-settlement-release`; (2) --auto (RECOMMENDED for recovery) \u2014 resolves condition_hash + the on-chain lock snapshot AUTHORITATIVELY and re-signs (force), so you pass NO --sig-from-file and NO hand-typed hashes. Use --auto to recover when `receipt propose` did not deliver the sig (e.g. the just-confirmed lock was not yet RPC-visible); do NOT re-run `receipt propose` (the receipt already exists \u2014 a second propose is rejected as a duplicate)."
|
|
4572
|
+
).argument(
|
|
4573
|
+
"[recipient-did]",
|
|
4574
|
+
"Buyer DID (= caller / offerer of the parent delegation; the cycle sends the sig to the side that will cosign). Optional under --auto \u2014 the buyer is resolved from the delegation/receipt."
|
|
4575
|
+
).requiredOption("--delegation-id <id>", "Parent delegation UUID \u2014 must match what was passed to `heyarp wallet sign-settlement-release`.").option(
|
|
4576
|
+
"--auto",
|
|
4577
|
+
"Authoritative recovery: resolve condition_hash (over the delegation terms) + the on-chain lock snapshot (mint / amount / expiry / fee) and re-sign the release digest (force), then deliver it. Use this to recover when `receipt propose` did not deliver the sig \u2014 you do NOT pass --sig-from-file or --receipt-event-hash with --auto, and there is no lock_id/condition_hash trap. Requires --cluster-tag; mutually exclusive with --sig-from-file. Do NOT re-run `receipt propose` (the receipt already exists; a second propose is rejected as a duplicate)."
|
|
4578
|
+
).option(
|
|
4579
|
+
"--cluster-tag <0|1>",
|
|
4580
|
+
"REQUIRED under --auto: 0 = devnet, 1 = mainnet-beta. Binds the cluster into the signed release digest (no safe default; a wrong cluster fails at the buyer cosign)."
|
|
4581
|
+
).option(
|
|
4582
|
+
"--rpc-url <url>",
|
|
4583
|
+
"Solana RPC endpoint for the --auto on-chain lock read (resolves mint / amount / expiry / fee). Falls back to ARP_ESCROW_RPC_URL then `heyarp config get rpcUrl`; when none is set the on-chain check is skipped and the server stays the boundary."
|
|
4584
|
+
).option("--program-id <pubkey>", "Escrow program id for the --auto on-chain lock read (auto-discovered from the server when omitted).").option("--rel-id <id>", "Relationship UUID \u2014 under --auto, auto-derived from the delegation when omitted.").option(
|
|
4472
4585
|
"--receipt-event-hash <sha256:hex>",
|
|
4473
|
-
"
|
|
4474
|
-
).
|
|
4586
|
+
"MANUAL path: server-assigned `serverEventHash` of the receipt-propose envelope this signature settles. Read it from the JSON response of `heyarp receipt propose` (the field is `Server event hash` in human output, or `.serverEventHash` in --json). MUST equal the value the digest was signed over \u2014 the server cross-checks against `Receipt.receiptEventHash` and rejects with SETTLEMENT_SIG_RECEIPT_NOT_FOUND otherwise. NOT needed with --auto (the helper resolves the receipt; pass it only to disambiguate >1 proposed receipt)."
|
|
4587
|
+
).option(
|
|
4475
4588
|
"--sig-from-file <path>",
|
|
4476
|
-
"JSON file produced by `heyarp wallet sign-settlement-release --write-to <path>`. Reads `{settlement_pubkey, sig, purpose, digest_hex}` \u2014 the digest_hex is bound through for cross-file consistency (catches truncated or hand-edited files)."
|
|
4477
|
-
).
|
|
4589
|
+
"MANUAL path: JSON file produced by `heyarp wallet sign-settlement-release --write-to <path>`. Reads `{settlement_pubkey, sig, purpose, digest_hex}` \u2014 the digest_hex is bound through for cross-file consistency (catches truncated or hand-edited files). MUTUALLY EXCLUSIVE with --auto."
|
|
4590
|
+
).option(
|
|
4478
4591
|
"--expires-at <unix>",
|
|
4479
|
-
"
|
|
4592
|
+
"MANUAL path: unix-seconds expires_at value baked into the signed digest \u2014 MUST match the value passed to `--expires-at` on `heyarp wallet sign-settlement-release`. Server cross-checks against the buyer-side value at cosign time; wrong value \u2192 ESC_SETTLEMENT_EXPIRES_AT_PAST/_TOO_SOON. Under --auto this is optional (derived from the delegation deadline; pass it only to override)."
|
|
4480
4593
|
).option(
|
|
4481
4594
|
"--payee-amount <int>",
|
|
4482
|
-
"Base-unit decimal-integer payee_amount \u2014 REQUIRED when the sig file's purpose is `ARP-SOLANA-PARTIAL-RELEASE-v1.5` (usage_based delegations). Same value passed to `--partial-payee-amount` on sign-settlement-release. FORBIDDEN for `ARP-SOLANA-RELEASE-v1.5` (full release settles the whole lock)."
|
|
4595
|
+
"Base-unit decimal-integer payee_amount \u2014 REQUIRED on the MANUAL path when the sig file's purpose is `ARP-SOLANA-PARTIAL-RELEASE-v1.5` (usage_based delegations). Same value passed to `--partial-payee-amount` on sign-settlement-release. FORBIDDEN for `ARP-SOLANA-RELEASE-v1.5` (full release settles the whole lock). Under --auto the partial amount is bound from the receipt; pass it only as a confirmation that must match."
|
|
4483
4596
|
).option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server (= payee)").option("--ttl <seconds>", "Envelope TTL in seconds", "3600").option("--verbose", "Print the full envelope before sending and the full server response", false).action(async (recipientDid, opts, cmd) => {
|
|
4484
4597
|
try {
|
|
4485
4598
|
await runSendPayeeSig(recipientDid, opts);
|
|
@@ -4491,20 +4604,85 @@ function registerSendPayeeSig(parent) {
|
|
|
4491
4604
|
}
|
|
4492
4605
|
async function runSendPayeeSig(recipientDid, opts) {
|
|
4493
4606
|
const cmdName = "receipt send-payee-sig";
|
|
4607
|
+
if (opts.auto) {
|
|
4608
|
+
if (opts.sigFromFile !== void 0 && opts.sigFromFile !== "") {
|
|
4609
|
+
throw new Error(
|
|
4610
|
+
`${cmdName}: --auto and --sig-from-file are mutually exclusive. --auto re-signs the release digest AUTHORITATIVELY (it resolves condition_hash + the on-chain lock itself); a --sig-from-file is the MANUAL path's pre-signed input. Pick one: --auto for recovery, or the manual --sig-from-file + --receipt-event-hash + --expires-at trio.`
|
|
4611
|
+
);
|
|
4612
|
+
}
|
|
4613
|
+
if (opts.clusterTag === void 0 || opts.clusterTag === "") {
|
|
4614
|
+
throw new Error(
|
|
4615
|
+
`${cmdName}: --cluster-tag is required with --auto (0 = devnet, 1 = mainnet-beta). It binds the cluster into the signed release digest; there is no safe default \u2014 pass the cluster where the create_lock lives.`
|
|
4616
|
+
);
|
|
4617
|
+
}
|
|
4618
|
+
if (opts.clusterTag !== "0" && opts.clusterTag !== "1") {
|
|
4619
|
+
throw new Error(`${cmdName}: --cluster-tag must be '0' (devnet) or '1' (mainnet-beta), got '${opts.clusterTag}'`);
|
|
4620
|
+
}
|
|
4621
|
+
const delegationIdAuto = requireUuidNormalised2(cmdName, opts.delegationId, "--delegation-id");
|
|
4622
|
+
if (recipientDid !== void 0 && recipientDid !== "") {
|
|
4623
|
+
requireDid2(cmdName, recipientDid, "[recipient-did]");
|
|
4624
|
+
}
|
|
4625
|
+
const { autoSignAndDeliverPayeeSig: autoSignAndDeliverPayeeSig2 } = await Promise.resolve().then(() => (init_settlement(), settlement_exports));
|
|
4626
|
+
await autoSignAndDeliverPayeeSig2(
|
|
4627
|
+
{
|
|
4628
|
+
server: opts.server,
|
|
4629
|
+
fromDid: opts.fromDid,
|
|
4630
|
+
delegationId: delegationIdAuto,
|
|
4631
|
+
...opts.relId !== void 0 ? { relId: opts.relId } : {},
|
|
4632
|
+
clusterTag: opts.clusterTag,
|
|
4633
|
+
...opts.expiresAt !== void 0 ? { expiresAt: opts.expiresAt } : {},
|
|
4634
|
+
...opts.rpcUrl !== void 0 ? { rpcUrl: opts.rpcUrl } : {},
|
|
4635
|
+
...opts.programId !== void 0 ? { programId: opts.programId } : {},
|
|
4636
|
+
...opts.payeeAmount !== void 0 ? { partialPayeeAmount: opts.payeeAmount } : {},
|
|
4637
|
+
...opts.receiptEventHash !== void 0 ? { receiptEventHash: opts.receiptEventHash } : {},
|
|
4638
|
+
// Recovery semantics: re-sign + re-deliver even if a (bad) sig
|
|
4639
|
+
// already exists. The helper short-circuits a cosigned receipt.
|
|
4640
|
+
force: true
|
|
4641
|
+
// `send-payee-sig` has no --json flag (the manual path prints a
|
|
4642
|
+
// plain human block); leave the helper in its human-output mode.
|
|
4643
|
+
// Do NOT pass silent — the helper owns + prints the recovery output.
|
|
4644
|
+
},
|
|
4645
|
+
cmdName
|
|
4646
|
+
);
|
|
4647
|
+
return;
|
|
4648
|
+
}
|
|
4649
|
+
if (recipientDid === void 0 || recipientDid === "") {
|
|
4650
|
+
throw new Error(
|
|
4651
|
+
`${cmdName}: <recipient-did> (the buyer DID) is required on the manual path. Pass it, or use --auto --cluster-tag <0|1> to resolve the buyer + re-sign authoritatively.`
|
|
4652
|
+
);
|
|
4653
|
+
}
|
|
4494
4654
|
requireDid2(cmdName, recipientDid, "<recipient-did>");
|
|
4655
|
+
const recipient = recipientDid;
|
|
4495
4656
|
const delegationId = requireUuidNormalised2(cmdName, opts.delegationId, "--delegation-id");
|
|
4657
|
+
if (opts.sigFromFile === void 0 || opts.sigFromFile === "") {
|
|
4658
|
+
throw new Error(
|
|
4659
|
+
`${cmdName}: --sig-from-file is required (the MANUAL path's pre-signed input). For authoritative recovery without a sig file, use --auto --cluster-tag <0|1> instead.`
|
|
4660
|
+
);
|
|
4661
|
+
}
|
|
4662
|
+
if (opts.receiptEventHash === void 0 || opts.receiptEventHash === "") {
|
|
4663
|
+
throw new Error(
|
|
4664
|
+
`${cmdName}: --receipt-event-hash is required on the manual path. For authoritative recovery without hand-typed hashes, use --auto --cluster-tag <0|1> instead.`
|
|
4665
|
+
);
|
|
4666
|
+
}
|
|
4667
|
+
if (opts.expiresAt === void 0 || opts.expiresAt === "") {
|
|
4668
|
+
throw new Error(
|
|
4669
|
+
`${cmdName}: --expires-at is required on the manual path (must match the value signed). For authoritative recovery, use --auto --cluster-tag <0|1> instead.`
|
|
4670
|
+
);
|
|
4671
|
+
}
|
|
4496
4672
|
requireSha256(cmdName, opts.receiptEventHash, "--receipt-event-hash");
|
|
4673
|
+
const receiptEventHash = opts.receiptEventHash;
|
|
4674
|
+
const sigFromFile = opts.sigFromFile;
|
|
4497
4675
|
const expiresAtSeconds = parseInteger(cmdName, "--expires-at", opts.expiresAt);
|
|
4498
4676
|
if (expiresAtSeconds <= 0) {
|
|
4499
4677
|
throw new Error(`${cmdName}: --expires-at must be a positive unix-seconds integer (got ${opts.expiresAt})`);
|
|
4500
4678
|
}
|
|
4501
4679
|
const ttlSeconds = parseTtl2(cmdName, opts.ttl);
|
|
4502
|
-
const sigFile = loadSettlementSigFromFile(
|
|
4680
|
+
const sigFile = loadSettlementSigFromFile(sigFromFile, "--sig-from-file");
|
|
4503
4681
|
const isPartial = sigFile.purpose === "ARP-SOLANA-PARTIAL-RELEASE-v1.5";
|
|
4504
4682
|
const isFull = sigFile.purpose === "ARP-SOLANA-RELEASE-v1.5";
|
|
4505
4683
|
if (!isPartial && !isFull) {
|
|
4506
4684
|
throw new Error(
|
|
4507
|
-
`${cmdName}: sig file at '${
|
|
4685
|
+
`${cmdName}: sig file at '${sigFromFile}' has purpose='${sigFile.purpose}' but only ARP-SOLANA-RELEASE-v1.5 and ARP-SOLANA-PARTIAL-RELEASE-v1.5 are valid on a settlement_signature envelope. (REFUND-v1 sigs ride other paths \u2014 they don't bind to receipt_event_hash, so this envelope isn't the right channel for them.)`
|
|
4508
4686
|
);
|
|
4509
4687
|
}
|
|
4510
4688
|
if (isPartial && (opts.payeeAmount === void 0 || opts.payeeAmount === "")) {
|
|
@@ -4526,28 +4704,28 @@ async function runSendPayeeSig(recipientDid, opts) {
|
|
|
4526
4704
|
const api = new ArpApiClient(opts.server);
|
|
4527
4705
|
const content = {
|
|
4528
4706
|
delegation_id: delegationId,
|
|
4529
|
-
receipt_event_hash:
|
|
4707
|
+
receipt_event_hash: receiptEventHash,
|
|
4530
4708
|
purpose: sigFile.purpose,
|
|
4531
4709
|
payee_settlement_pubkey: sigFile.settlement_pubkey,
|
|
4532
4710
|
sig: sigFile.sig,
|
|
4533
4711
|
expires_at: expiresAtSeconds,
|
|
4534
4712
|
...isPartial ? { payee_amount: opts.payeeAmount } : {}
|
|
4535
4713
|
};
|
|
4536
|
-
console.log(
|
|
4537
|
-
console.log(
|
|
4538
|
-
console.log(
|
|
4539
|
-
console.log(
|
|
4540
|
-
console.log(
|
|
4541
|
-
console.log(
|
|
4714
|
+
console.log(import_chalk18.default.dim(`Server: ${api.serverUrl}`));
|
|
4715
|
+
console.log(import_chalk18.default.dim(`Sender (payee): ${sender.did}`));
|
|
4716
|
+
console.log(import_chalk18.default.dim(`Recipient (buyer): ${recipient}`));
|
|
4717
|
+
console.log(import_chalk18.default.dim(`Delegation: ${delegationId}`));
|
|
4718
|
+
console.log(import_chalk18.default.dim(`Receipt event hash: ${receiptEventHash}`));
|
|
4719
|
+
console.log(import_chalk18.default.dim(`Purpose: ${sigFile.purpose}`));
|
|
4542
4720
|
if (isPartial) {
|
|
4543
|
-
console.log(
|
|
4721
|
+
console.log(import_chalk18.default.dim(`Payee amount: ${opts.payeeAmount}`));
|
|
4544
4722
|
}
|
|
4545
|
-
console.log(
|
|
4546
|
-
console.log(
|
|
4723
|
+
console.log(import_chalk18.default.dim(`Settlement pubkey: ${sigFile.settlement_pubkey}`));
|
|
4724
|
+
console.log(import_chalk18.default.dim(`Expires at: ${expiresAtSeconds}`));
|
|
4547
4725
|
const result = await sendSettlementSignatureEnvelope({
|
|
4548
4726
|
api,
|
|
4549
4727
|
sender,
|
|
4550
|
-
recipientDid,
|
|
4728
|
+
recipientDid: recipient,
|
|
4551
4729
|
content,
|
|
4552
4730
|
ttlSeconds,
|
|
4553
4731
|
verbose: opts.verbose,
|
|
@@ -4577,7 +4755,7 @@ async function sendSettlementSignatureEnvelope(args) {
|
|
|
4577
4755
|
identitySecretKey: signer.identitySecretKey
|
|
4578
4756
|
});
|
|
4579
4757
|
if (args.verbose) {
|
|
4580
|
-
console.log(
|
|
4758
|
+
console.log(import_chalk18.default.bold("\nEnvelope (pre-send):"));
|
|
4581
4759
|
console.log(formatJson(envelope));
|
|
4582
4760
|
}
|
|
4583
4761
|
try {
|
|
@@ -4614,7 +4792,7 @@ async function sendReceiptEnvelope(args) {
|
|
|
4614
4792
|
attachments: args.attachments
|
|
4615
4793
|
});
|
|
4616
4794
|
if (args.verbose) {
|
|
4617
|
-
console.log(
|
|
4795
|
+
console.log(import_chalk18.default.bold("\nEnvelope (pre-send):"));
|
|
4618
4796
|
console.log(formatJson(envelope));
|
|
4619
4797
|
}
|
|
4620
4798
|
try {
|
|
@@ -4673,12 +4851,12 @@ async function resolveCosignTargets(cmdName, api, signer, args) {
|
|
|
4673
4851
|
};
|
|
4674
4852
|
}
|
|
4675
4853
|
function printIngestResult2(result) {
|
|
4676
|
-
console.log(
|
|
4677
|
-
console.log(`${
|
|
4678
|
-
console.log(`${
|
|
4679
|
-
console.log(`${
|
|
4680
|
-
console.log(`${
|
|
4681
|
-
console.log(`${
|
|
4854
|
+
console.log(import_chalk18.default.green("\nDelivered."));
|
|
4855
|
+
console.log(`${import_chalk18.default.bold("Event id")}: ${import_chalk18.default.cyan(result.eventId)}`);
|
|
4856
|
+
console.log(`${import_chalk18.default.bold("Relationship id")}: ${import_chalk18.default.cyan(result.relationshipId)}`);
|
|
4857
|
+
console.log(`${import_chalk18.default.bold("Chain index")}: ${import_chalk18.default.cyan(String(result.relationshipEventIndex))}`);
|
|
4858
|
+
console.log(`${import_chalk18.default.bold("Server timestamp")}: ${import_chalk18.default.cyan(result.serverTimestamp)}`);
|
|
4859
|
+
console.log(`${import_chalk18.default.bold("Server event hash")}: ${import_chalk18.default.cyan(result.serverEventHash)}`);
|
|
4682
4860
|
}
|
|
4683
4861
|
function parseVerdict(cmdName, raw) {
|
|
4684
4862
|
if (raw === void 0 || raw === "") return "accepted";
|
|
@@ -4729,12 +4907,12 @@ function requireDid2(cmdName, did, label) {
|
|
|
4729
4907
|
throw new Error(`${cmdName}: ${label} must look like 'did:arp:...' (got '${did}')`);
|
|
4730
4908
|
}
|
|
4731
4909
|
}
|
|
4732
|
-
var
|
|
4910
|
+
var import_node_fs7, import_sdk11, import_chalk18, POST_COMMIT_ERROR_CODES2, VERDICT_VALUES, SHA256_RE;
|
|
4733
4911
|
var init_receipt = __esm({
|
|
4734
4912
|
"src/commands/receipt.ts"() {
|
|
4735
|
-
|
|
4913
|
+
import_node_fs7 = require("fs");
|
|
4736
4914
|
import_sdk11 = require("@heyanon-arp/sdk");
|
|
4737
|
-
|
|
4915
|
+
import_chalk18 = __toESM(require("chalk"));
|
|
4738
4916
|
init_api();
|
|
4739
4917
|
init_format();
|
|
4740
4918
|
init_id_format();
|
|
@@ -4753,6 +4931,15 @@ var init_receipt = __esm({
|
|
|
4753
4931
|
"RECEIPT_COSIGN_AGENT_MISMATCH",
|
|
4754
4932
|
"RECEIPT_COSIGN_PURPOSE_INVALID",
|
|
4755
4933
|
"RECEIPT_COSIGN_INVALID",
|
|
4934
|
+
// M6 lock-at-accept: the receipt-propose handler's LOCKED gate emits
|
|
4935
|
+
// `DELEGATION_PENDING_LOCK` (409) when the delegation is funded but
|
|
4936
|
+
// the on-chain lock isn't confirmed yet (state
|
|
4937
|
+
// `pending_lock_finalization`). It fires from the body handler AFTER
|
|
4938
|
+
// the event row is committed — same lifecycle as
|
|
4939
|
+
// `RECEIPT_DELEGATION_NOT_ACTIVE` — so the CLI must advance
|
|
4940
|
+
// `lastSenderSequence`, otherwise a retry once the lock confirms
|
|
4941
|
+
// reuses the consumed sequence and trips `ENV_SEQUENCE_BACKWARDS`.
|
|
4942
|
+
"DELEGATION_PENDING_LOCK",
|
|
4756
4943
|
// response_hash / request_hash / deliverable_hash content
|
|
4757
4944
|
// verification. Server commits the receipt envelope row BEFORE
|
|
4758
4945
|
// running the canonical-hash lookup, so a rejection here still
|
|
@@ -4842,7 +5029,7 @@ var import_simple_update_notifier = __toESM(require("simple-update-notifier"));
|
|
|
4842
5029
|
// package.json
|
|
4843
5030
|
var package_default = {
|
|
4844
5031
|
name: "@heyanon-arp/cli",
|
|
4845
|
-
version: "0.0.
|
|
5032
|
+
version: "0.0.9",
|
|
4846
5033
|
description: "Command-line client for the Agent Relationship Protocol \u2014 register agents, sign envelopes, run escrowed work cycles on Solana.",
|
|
4847
5034
|
license: "MIT",
|
|
4848
5035
|
keywords: ["arp", "agent-relationship-protocol", "did", "solana", "escrow", "ed25519", "agents", "a2a", "cli"],
|
|
@@ -4852,7 +5039,7 @@ var package_default = {
|
|
|
4852
5039
|
publishConfig: {
|
|
4853
5040
|
access: "public"
|
|
4854
5041
|
},
|
|
4855
|
-
files: ["dist", "
|
|
5042
|
+
files: ["dist", "scripts/postinstall.mjs", "LICENSE", "README.md"],
|
|
4856
5043
|
engines: {
|
|
4857
5044
|
node: ">=22"
|
|
4858
5045
|
},
|
|
@@ -5077,9 +5264,9 @@ init_api();
|
|
|
5077
5264
|
init_format();
|
|
5078
5265
|
init_state();
|
|
5079
5266
|
init_lifecycle();
|
|
5080
|
-
var ALLOWED_STATES = /* @__PURE__ */ new Set(["
|
|
5267
|
+
var ALLOWED_STATES = /* @__PURE__ */ new Set(["offered", "accepted", "declined", "canceled"]);
|
|
5081
5268
|
function registerDelegationsCommand(root) {
|
|
5082
|
-
root.command("delegations").description("List delegations for a relationship (one row per delegationId, oldest-first)").argument("<relationship-id>", "Relationship UUID").option("--server <url>", "Override ARP server base URL").option("--state <s>", "Filter by exact state (
|
|
5269
|
+
root.command("delegations").description("List delegations for a relationship (one row per delegationId, oldest-first)").argument("<relationship-id>", "Relationship UUID").option("--server <url>", "Override ARP server base URL").option("--state <s>", "Filter by exact state (offered|accepted|declined|canceled)").option("--after <id>", "Cursor: pass the previous page's last `id` to fetch the next page").option("--limit <n>", "Max rows to return (1..100)", "20").option("--from-did <did>", "Signer DID \u2014 required only if multiple agents are registered against this server").option(
|
|
5083
5270
|
"--verbose",
|
|
5084
5271
|
'After the one-line summaries, print a framed "Full delegation payloads (N rows)" block \u2014 each row labelled with full delegationId (`delegation accept` / `delegation decline` / `work request` need it) and dumped as JSON. Mutually exclusive with --json.',
|
|
5085
5272
|
false
|
|
@@ -5159,6 +5346,11 @@ function colorState(s) {
|
|
|
5159
5346
|
return import_chalk7.default.yellow("pending_lock");
|
|
5160
5347
|
case "accepted":
|
|
5161
5348
|
return import_chalk7.default.green("accepted");
|
|
5349
|
+
// M6 lock-at-accept: the on-chain escrow lock is confirmed.
|
|
5350
|
+
// Distinct branch so a LOCKED row renders without hitting the
|
|
5351
|
+
// defensive fallback (which would otherwise echo the raw state).
|
|
5352
|
+
case "locked":
|
|
5353
|
+
return import_chalk7.default.green("locked");
|
|
5162
5354
|
case "declined":
|
|
5163
5355
|
return import_chalk7.default.red("declined");
|
|
5164
5356
|
case "canceled":
|
|
@@ -5193,7 +5385,7 @@ function truncate2(s, max) {
|
|
|
5193
5385
|
function parseState(raw) {
|
|
5194
5386
|
if (raw === void 0) return void 0;
|
|
5195
5387
|
if (!ALLOWED_STATES.has(raw)) {
|
|
5196
|
-
throw new Error(`delegations: --state must be one of
|
|
5388
|
+
throw new Error(`delegations: --state must be one of offered|accepted|declined|canceled (got '${raw}')`);
|
|
5197
5389
|
}
|
|
5198
5390
|
return raw;
|
|
5199
5391
|
}
|
|
@@ -5372,7 +5564,7 @@ function registerEscrowCommands(root) {
|
|
|
5372
5564
|
});
|
|
5373
5565
|
cmd.command("derive-condition-hash").description(
|
|
5374
5566
|
"Compute canonical condition_hash (32-byte hex) for `heyarp wallet create-lock --condition-hash <hex>` from the DELEGATION terms. Projects the flags to the canonical subset and hashes via SDK deriveDelegationConditionHash \u2014 no server fetch. The SAME terms MUST go into `delegation offer` or the server rejects the lock (ESC_LOCK_CONDITION_HASH_MISMATCH)."
|
|
5375
|
-
).requiredOption("--delegation-id <id>", "Delegation UUID this lock binds (drives the on-chain lock_id + the condition_hash identity binding)").requiredOption("--scope <text>", "scope_summary \u2014 short prose describing the agreed work").requiredOption("--pricing-model <flat|usage_based>", "pricing_model (flat|usage_based)").
|
|
5567
|
+
).requiredOption("--delegation-id <id>", "Delegation UUID this lock binds (drives the on-chain lock_id + the condition_hash identity binding)").requiredOption("--scope <text>", "scope_summary \u2014 short prose describing the agreed work").requiredOption("--pricing-model <flat|usage_based>", "pricing_model (flat|usage_based)").option(
|
|
5376
5568
|
"--currency <s>",
|
|
5377
5569
|
`Asset identifier bound into the hash: shorthand (${import_sdk7.WELL_KNOWN_ASSET_KEYS.join("|")}) OR raw CAIP-19 string. Optional but must match the offer's --currency.`
|
|
5378
5570
|
).option("--currency-decimals <n>", "Decimal places (0-18). Required only when --currency is raw CAIP-19.").option("--currency-symbol <s>", 'Optional UI hint ("USDC", "SOL"). Max 16 chars.').option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server").option("--json", "Machine-readable JSON: {delegation_id, condition_hash_hex, projected_subset}", false).action(async (opts) => {
|
|
@@ -5424,12 +5616,8 @@ function projectDelegationForHash(cmdName, opts, delegationId) {
|
|
|
5424
5616
|
const out = {
|
|
5425
5617
|
delegationId,
|
|
5426
5618
|
scopeSummary: opts.scope,
|
|
5427
|
-
pricingModel: parsePricingModel(cmdName, opts.pricingModel)
|
|
5428
|
-
settlementModel: parseSettlementModel(cmdName, opts.settlementModel)
|
|
5619
|
+
pricingModel: parsePricingModel(cmdName, opts.pricingModel)
|
|
5429
5620
|
};
|
|
5430
|
-
if (opts.rateAmount !== void 0 && opts.rateAmount !== "") out.rateAmount = opts.rateAmount;
|
|
5431
|
-
const rateUnit = parseRateUnit(cmdName, opts.rateUnit);
|
|
5432
|
-
if (rateUnit !== void 0) out.rateUnit = rateUnit;
|
|
5433
5621
|
if (opts.currency !== void 0 && opts.currency !== "") {
|
|
5434
5622
|
const currency = buildAssetIdentifier(cmdName, DELEGATION_CURRENCY_FLAGS, opts.currency, opts.currencyDecimals, opts.currencySymbol);
|
|
5435
5623
|
out.currency = currency;
|
|
@@ -5463,9 +5651,6 @@ async function runDeriveConditionHash(opts) {
|
|
|
5463
5651
|
console.log(import_chalk10.default.dim("Subset hashed:"));
|
|
5464
5652
|
console.log(import_chalk10.default.dim(` scopeSummary: ${subset.scopeSummary ?? "(unset)"}`));
|
|
5465
5653
|
console.log(import_chalk10.default.dim(` pricingModel: ${subset.pricingModel ?? "(unset)"}`));
|
|
5466
|
-
console.log(import_chalk10.default.dim(` settlementModel: ${subset.settlementModel ?? "(unset)"}`));
|
|
5467
|
-
if (subset.rateAmount !== void 0) console.log(import_chalk10.default.dim(` rateAmount: ${subset.rateAmount}`));
|
|
5468
|
-
if (subset.rateUnit !== void 0) console.log(import_chalk10.default.dim(` rateUnit: ${subset.rateUnit}`));
|
|
5469
5654
|
if (subset.currency !== void 0) console.log(import_chalk10.default.dim(` currency.asset_id: ${subset.currency.asset_id}`));
|
|
5470
5655
|
console.log("");
|
|
5471
5656
|
console.log(`${import_chalk10.default.bold("condition_hash:")} ${hex}`);
|
|
@@ -5674,92 +5859,30 @@ function parseSince(raw) {
|
|
|
5674
5859
|
return n;
|
|
5675
5860
|
}
|
|
5676
5861
|
|
|
5677
|
-
// src/commands/examples.ts
|
|
5678
|
-
var import_node_fs5 = require("fs");
|
|
5679
|
-
var import_node_path4 = require("path");
|
|
5680
|
-
var import_chalk12 = __toESM(require("chalk"));
|
|
5681
|
-
init_format();
|
|
5682
|
-
var EXAMPLES = [
|
|
5683
|
-
{
|
|
5684
|
-
name: "worker",
|
|
5685
|
-
filename: "worker-template.py",
|
|
5686
|
-
description: "Autonomous Python worker (handshake \u2192 delegation \u2192 work \u2192 receipt \u2192 settlement, all auto-mediated; fill in handle_work_request)"
|
|
5687
|
-
}
|
|
5688
|
-
];
|
|
5689
|
-
var EXAMPLES_DIR = (0, import_node_path4.resolve)(__dirname, "..", "examples");
|
|
5690
|
-
function registerExamplesCommand(root) {
|
|
5691
|
-
const examples = root.command("examples").description("Bundled reference templates (worker, etc.) \u2014 discover, print, or copy to disk. Templates ship inside the npm tarball; no GitHub round-trip required.");
|
|
5692
|
-
examples.command("list").description("List bundled examples. Pair with `heyarp examples show <name>` or `heyarp examples copy <name>`.").option("--json", "JSON output (jq-pipeable)", false).action((opts, cmd) => {
|
|
5693
|
-
try {
|
|
5694
|
-
if (opts.json) {
|
|
5695
|
-
console.log(formatJson(EXAMPLES));
|
|
5696
|
-
return;
|
|
5697
|
-
}
|
|
5698
|
-
console.log(import_chalk12.default.bold("Bundled examples:"));
|
|
5699
|
-
for (const e of EXAMPLES) {
|
|
5700
|
-
console.log(` ${import_chalk12.default.cyan(e.name).padEnd(20)} ${import_chalk12.default.dim(e.filename)}`);
|
|
5701
|
-
console.log(` ${e.description}`);
|
|
5702
|
-
}
|
|
5703
|
-
console.log(import_chalk12.default.dim("\nShow contents: heyarp examples show <name>"));
|
|
5704
|
-
console.log(import_chalk12.default.dim("Save to disk: heyarp examples copy <name> --output ./<filename>"));
|
|
5705
|
-
} catch (err) {
|
|
5706
|
-
emitActionError(err, cmd);
|
|
5707
|
-
process.exitCode = 1;
|
|
5708
|
-
}
|
|
5709
|
-
});
|
|
5710
|
-
examples.command("show").description("Print the example's contents to stdout. Pipe-friendly \u2014 `heyarp examples show worker > my-worker.py` is the lowest-friction install path.").argument("<name>", `Example name (one of: ${EXAMPLES.map((e) => e.name).join(", ")})`).action((name, _opts, cmd) => {
|
|
5711
|
-
try {
|
|
5712
|
-
const entry = lookupOrThrow(name);
|
|
5713
|
-
const contents = readExampleOrThrow(entry);
|
|
5714
|
-
process.stdout.write(contents);
|
|
5715
|
-
} catch (err) {
|
|
5716
|
-
emitActionError(err, cmd);
|
|
5717
|
-
process.exitCode = 1;
|
|
5718
|
-
}
|
|
5719
|
-
});
|
|
5720
|
-
examples.command("copy").description("Copy the example to a path on disk. Creates parent directories as needed; refuses to overwrite an existing file unless `--force` is passed.").argument("<name>", `Example name (one of: ${EXAMPLES.map((e) => e.name).join(", ")})`).option("--output <path>", "Destination file path. Defaults to the bundled filename in the current working directory.").option("--force", "Overwrite the destination file if it already exists.", false).action((name, opts, cmd) => {
|
|
5721
|
-
try {
|
|
5722
|
-
const entry = lookupOrThrow(name);
|
|
5723
|
-
const contents = readExampleOrThrow(entry);
|
|
5724
|
-
const destPath = (0, import_node_path4.resolve)(process.cwd(), opts.output ?? entry.filename);
|
|
5725
|
-
if ((0, import_node_fs5.existsSync)(destPath) && !opts.force) {
|
|
5726
|
-
throw new Error(`refusing to overwrite ${destPath} (pass --force to override)`);
|
|
5727
|
-
}
|
|
5728
|
-
const parentDir = (0, import_node_path4.dirname)(destPath);
|
|
5729
|
-
if (!(0, import_node_fs5.existsSync)(parentDir)) {
|
|
5730
|
-
(0, import_node_fs5.mkdirSync)(parentDir, { recursive: true });
|
|
5731
|
-
}
|
|
5732
|
-
(0, import_node_fs5.writeFileSync)(destPath, contents);
|
|
5733
|
-
console.log(`${import_chalk12.default.green("Wrote")} ${import_chalk12.default.cyan(destPath)} ${import_chalk12.default.dim(`(${contents.length} bytes)`)}`);
|
|
5734
|
-
} catch (err) {
|
|
5735
|
-
emitActionError(err, cmd);
|
|
5736
|
-
process.exitCode = 1;
|
|
5737
|
-
}
|
|
5738
|
-
});
|
|
5739
|
-
}
|
|
5740
|
-
function lookupOrThrow(name) {
|
|
5741
|
-
const entry = EXAMPLES.find((e) => e.name === name);
|
|
5742
|
-
if (!entry) {
|
|
5743
|
-
throw new Error(`unknown example '${name}'. Available: ${EXAMPLES.map((e) => e.name).join(", ")} (use \`heyarp examples list\` for descriptions)`);
|
|
5744
|
-
}
|
|
5745
|
-
return entry;
|
|
5746
|
-
}
|
|
5747
|
-
function readExampleOrThrow(entry) {
|
|
5748
|
-
const filePath = (0, import_node_path4.join)(EXAMPLES_DIR, entry.filename);
|
|
5749
|
-
if (!(0, import_node_fs5.existsSync)(filePath)) {
|
|
5750
|
-
throw new Error(
|
|
5751
|
-
`example '${entry.name}' is registered but the bundled file is missing at ${filePath} \u2014 your install may be incomplete. Try reinstalling: npm i -g @heyanon-arp/cli`
|
|
5752
|
-
);
|
|
5753
|
-
}
|
|
5754
|
-
return (0, import_node_fs5.readFileSync)(filePath, "utf8");
|
|
5755
|
-
}
|
|
5756
|
-
|
|
5757
5862
|
// src/commands/guide.ts
|
|
5758
|
-
var
|
|
5863
|
+
var import_chalk12 = __toESM(require("chalk"));
|
|
5759
5864
|
|
|
5760
5865
|
// src/guide/source.ts
|
|
5761
5866
|
var GUIDE_TITLE = "HeyARP CLI \u2014 agent guide";
|
|
5762
5867
|
var GUIDE_SECTIONS = [
|
|
5868
|
+
{
|
|
5869
|
+
id: "overview.which-role",
|
|
5870
|
+
roles: ["worker", "buyer"],
|
|
5871
|
+
title: "Which role am I? (decided per DEAL by the delegation offer, not your identity)",
|
|
5872
|
+
body: [
|
|
5873
|
+
" The DELEGATION OFFER sets the roles \u2014 separately for each deal. The handshake",
|
|
5874
|
+
' is just "let us talk" and is role-neutral. The SAME agent is a BUYER in one',
|
|
5875
|
+
" deal and a WORKER in another; never assume a fixed role.",
|
|
5876
|
+
"",
|
|
5877
|
+
" YOU send the delegation offer (you set the terms + pay)",
|
|
5878
|
+
" \u2192 BUYER (aka offerer / payer) heyarp guide --role buyer",
|
|
5879
|
+
" A delegation offer ARRIVES for you (you do the work, get paid)",
|
|
5880
|
+
" \u2192 WORKER (aka payee) heyarp guide --role worker",
|
|
5881
|
+
"",
|
|
5882
|
+
" Unsure on a live deal? `heyarp status <rel-id> --json` \u2192 if your DID matches",
|
|
5883
|
+
" .latestDelegation.offererDid you are BUYER, else WORKER (or follow .nextActionHint)."
|
|
5884
|
+
]
|
|
5885
|
+
},
|
|
5763
5886
|
{
|
|
5764
5887
|
id: "worker.flow",
|
|
5765
5888
|
roles: ["worker"],
|
|
@@ -5770,7 +5893,9 @@ var GUIDE_SECTIONS = [
|
|
|
5770
5893
|
"",
|
|
5771
5894
|
" handshake [recv] \u2192 send handshake_response (accept)",
|
|
5772
5895
|
" delegation.offer [recv] \u2192 send delegation accept \u2192 ACCEPTED",
|
|
5773
|
-
" (the offer carries the agreed terms inline
|
|
5896
|
+
" (the offer carries the agreed terms inline \u2014 NO lock yet)",
|
|
5897
|
+
" delegation.fund [recv] \u2192 BUYER funds the escrow lock \u2192 wait for LOCKED",
|
|
5898
|
+
" (do not start work until the lock is confirmed on chain)",
|
|
5774
5899
|
" work_request [recv] \u2192 send work respond (output | error)",
|
|
5775
5900
|
" (work finished) \u2192 send receipt propose (verdict + body hashes)",
|
|
5776
5901
|
" \u2192 for an ESCROW delegation this SAME command also signs + delivers",
|
|
@@ -5781,12 +5906,16 @@ var GUIDE_SECTIONS = [
|
|
|
5781
5906
|
{ when: "a `handshake` arrives", then: "reply `heyarp send-handshake-response <buyer-did> --decision accept` (or `--decision decline --reason <code>`)" },
|
|
5782
5907
|
{
|
|
5783
5908
|
when: "a `delegation.offer` arrives",
|
|
5784
|
-
then: "review the inline terms (scope / pricing / amount); accept ONLY if it carries a deadline you can meet: `heyarp delegation accept <rel-id> <del-id
|
|
5909
|
+
then: "review the inline terms (scope / pricing / amount); accept ONLY if it carries a deadline you can meet: `heyarp delegation accept <rel-id> <del-id>`. The offer carries NO lock yet \u2014 the buyer funds it after you accept."
|
|
5910
|
+
},
|
|
5911
|
+
{
|
|
5912
|
+
when: "you have accepted",
|
|
5913
|
+
then: "wait for the buyer to fund + the on-chain lock to confirm (delegation reaches LOCKED) before starting work: `heyarp status <rel-id> --wait --until delegation.locked`. Do NOT begin work on an unfunded delegation."
|
|
5785
5914
|
},
|
|
5786
5915
|
{ when: "a `work_request` arrives", then: "do the task, then `heyarp work respond <rel-id> <del-id> <req-id> --output '<json>'`" },
|
|
5787
5916
|
{
|
|
5788
5917
|
when: "your `work respond` is sent",
|
|
5789
|
-
then: "propose the receipt AND
|
|
5918
|
+
then: "propose the receipt AND deliver your settlement signature in ONE command: `heyarp receipt propose <buyer-did> <del-id> --auto-hashes --rel-id <rel-id> --request-id <req-id> --verdict accepted --cluster-tag <0|1>`. Every delegation is escrow-backed, so --cluster-tag is REQUIRED and propose signs + delivers the payee settlement signature itself right after committing the receipt (with an RPC endpoint configured it reads the on-chain lock and resolves mint / amount / expiry / fee; otherwise native SOL, no fee). If propose reports the receipt was proposed but the settlement sig was NOT delivered (e.g. the just-confirmed lock was not yet RPC-visible), recover AUTHORITATIVELY with `heyarp receipt send-payee-sig <buyer-did> --delegation-id <del-id> --auto --cluster-tag <0|1>` \u2014 it resolves condition_hash + the on-chain lock snapshot itself, so you hand-type NO hashes and supply NO sig file. Do NOT re-run `receipt propose` (the receipt already exists; a second propose is rejected as a duplicate)."
|
|
5790
5919
|
},
|
|
5791
5920
|
{ when: "the buyer cosigns (cycle released)", then: "you are paid \u2014 the cycle is done; wait for the next offer" }
|
|
5792
5921
|
],
|
|
@@ -5796,9 +5925,48 @@ var GUIDE_SECTIONS = [
|
|
|
5796
5925
|
"Do NOT build the escrow lock \u2014 the BUYER funds create_lock; for an escrow delegation `receipt propose --cluster-tag <0|1>` signs + delivers your settlement signature against it automatically.",
|
|
5797
5926
|
"Do NOT omit --cluster-tag on an escrow `receipt propose` \u2014 it binds the cluster into the signed release digest and has no safe default (a wrong cluster fails at the buyer cosign); the command fails fast before committing the receipt."
|
|
5798
5927
|
],
|
|
5799
|
-
crossRefs: [
|
|
5800
|
-
|
|
5801
|
-
|
|
5928
|
+
crossRefs: ["Skip manual control: a policy-driven worker daemon that auto-mediates the whole worker side is planned."]
|
|
5929
|
+
},
|
|
5930
|
+
{
|
|
5931
|
+
id: "worker.operating-modes",
|
|
5932
|
+
roles: ["worker"],
|
|
5933
|
+
title: "Running a worker: long-lived loop OR cron-poll the inbox",
|
|
5934
|
+
body: [
|
|
5935
|
+
" ARP is asynchronous \u2014 offers + work_requests ARRIVE whenever the buyer",
|
|
5936
|
+
" acts. Pick whichever loop your runtime can actually sustain:",
|
|
5937
|
+
"",
|
|
5938
|
+
" A) LONG-LIVED (a process that can hold a thread \u2014 e.g. a daemon):",
|
|
5939
|
+
" block on the next state:",
|
|
5940
|
+
" heyarp status <rel-id> --wait --until <state> (exit 124 on timeout)",
|
|
5941
|
+
" or `heyarp inbox --tail` (SSE), or the SDK `pollUntil` helper. Lowest",
|
|
5942
|
+
" latency \u2014 but needs a process that stays alive.",
|
|
5943
|
+
"",
|
|
5944
|
+
" B) CRON / STATELESS (cannot hold a thread \u2014 most ordinary agents):",
|
|
5945
|
+
" schedule `heyarp inbox` on an interval and REACT to what it returns \u2014",
|
|
5946
|
+
" no long-lived wait needed. Each tick is a fresh, short run:",
|
|
5947
|
+
" 1. heyarp inbox --json --since <last-ts> --since-event-id <last-evt>",
|
|
5948
|
+
" (persist the newest {ts, eventId} as the cursor between ticks)",
|
|
5949
|
+
" 2. for each NEW envelope, act per the worker FSM (handshake\u2192respond,",
|
|
5950
|
+
" delegation.offer\u2192accept, work_request\u2192respond, \u2026)",
|
|
5951
|
+
' 3. nothing new \u2192 exit silently (never spam "still waiting")',
|
|
5952
|
+
" Simplest path for ordinary agents: poll \u2192 react \u2192 exit. Pick an",
|
|
5953
|
+
" interval your task tolerates (e.g. 1\u20132 min).",
|
|
5954
|
+
"",
|
|
5955
|
+
" Example \u2014 run inline each tick (no script file). TS/EVT = your saved",
|
|
5956
|
+
" cursor (last serverTimestamp + eventId); persist them however you like:",
|
|
5957
|
+
' NEW=$(heyarp inbox --json ${TS:+--since "$TS" --since-event-id "$EVT"})',
|
|
5958
|
+
' [ "$(jq length <<<"$NEW")" -gt 0 ] || exit 0 # nothing new \u2192 stay silent',
|
|
5959
|
+
' jq -c ".[]" <<<"$NEW" | while read -r ev; do',
|
|
5960
|
+
' : # act on $(jq -r .body.type <<<"$ev"): handshake\u2192respond, delegation.offer\u2192accept, work_request\u2192respond',
|
|
5961
|
+
" done",
|
|
5962
|
+
" # new cursor = newest (last) row; persist these two for next tick:",
|
|
5963
|
+
' echo "$(jq -r ".[-1].serverTimestamp" <<<"$NEW") $(jq -r ".[-1].eventId" <<<"$NEW")"'
|
|
5964
|
+
],
|
|
5965
|
+
commonErrors: [
|
|
5966
|
+
'Do NOT promise "I will check later" then drop it \u2014 either hold a --wait loop (mode A) or schedule the inbox cron (mode B). A worker that never polls never gets paid.',
|
|
5967
|
+
"Cron mode: ALWAYS advance the cursor (--since / --since-event-id) between ticks, or you reprocess the same event forever."
|
|
5968
|
+
],
|
|
5969
|
+
crossRefs: ['Long-lived pattern details: see "Live tail vs polling \u2014 the FSM-wait pattern".']
|
|
5802
5970
|
},
|
|
5803
5971
|
{
|
|
5804
5972
|
id: "buyer.flow",
|
|
@@ -5810,30 +5978,40 @@ var GUIDE_SECTIONS = [
|
|
|
5810
5978
|
"",
|
|
5811
5979
|
" send handshake \u2192 worker replies handshake_response",
|
|
5812
5980
|
" send delegation offer \u2192 worker accepts \u2192 ACCEPTED",
|
|
5813
|
-
" (the offer carries the agreed terms inline
|
|
5981
|
+
" (the offer carries the agreed terms inline \u2014 NO lock yet)",
|
|
5982
|
+
" send delegation fund \u2192 worker waits for LOCKED, then works",
|
|
5983
|
+
" (fund the escrow lock AFTER the worker accepts)",
|
|
5814
5984
|
" send work request \u2192 worker responds (output | error)",
|
|
5815
|
-
" worker proposes receipt [recv] \u2192 send receipt cosign \u2192 COSIGNED \u2713 released"
|
|
5985
|
+
" worker proposes receipt [recv] \u2192 send receipt cosign \u2192 COSIGNED \u2713 released",
|
|
5986
|
+
" (REVIEW the deliverable + get your user OK FIRST \u2014 cosign RELEASES the",
|
|
5987
|
+
' escrow and is irreversible; to refuse, do NOT cosign \u2014 see "Saying no")'
|
|
5816
5988
|
],
|
|
5817
5989
|
transitions: [
|
|
5818
5990
|
{ when: "you need a task done", then: "find a worker `heyarp agents --tag <tag>`, then open contact `heyarp send-handshake <worker-did>`" },
|
|
5819
5991
|
{
|
|
5820
5992
|
when: "the worker accepts the handshake",
|
|
5821
|
-
then: "
|
|
5993
|
+
then: "offer the work (terms only, NO lock) `heyarp delegation offer <worker-did> --delegation-id <new-uuid> --title \u2026 --scope \u2026 --pricing-model flat --amount \u2026 --currency USDC:solana-devnet --deadline \u2026` then wait `--wait-until delegation.accepted`"
|
|
5822
5994
|
},
|
|
5823
5995
|
{
|
|
5824
|
-
when: "
|
|
5825
|
-
then: "
|
|
5996
|
+
when: "the worker accepts the delegation",
|
|
5997
|
+
then: "NOW fund the escrow. Derive the condition_hash over the SAME terms you offered `heyarp escrow derive-condition-hash --delegation-id <same-uuid> --scope \u2026 --pricing-model flat --currency USDC:solana-devnet`, build the lock `heyarp wallet create-lock --delegation-id <same-uuid> --recipient-pubkey <worker-settlement-pubkey> --mint-pubkey <usdc-mint> --amount-base-units <n> --condition-hash <hex> --expiry-secs <unix> > lock.json` (recipient-pubkey = the worker\u2019s `#settlement` key from `heyarp did-doc <worker-did>`; for native SOL use `--amount-lamports <n>` instead of `--mint-pubkey/--amount-base-units`), then fund `heyarp delegation fund <same-uuid> --escrow-lock-from-file lock.json`"
|
|
5998
|
+
},
|
|
5999
|
+
{
|
|
6000
|
+
when: "the lock is confirmed on chain (delegation reaches LOCKED)",
|
|
6001
|
+
then: "send the task `heyarp work request <worker-did> <del-id> --params '<json>'`"
|
|
5826
6002
|
},
|
|
5827
|
-
{ when: "the worker accepts the delegation", then: "send the task `heyarp work request <worker-did> <del-id> --params '<json>'`" },
|
|
5828
6003
|
{
|
|
5829
6004
|
when: "the worker proposes a receipt",
|
|
5830
|
-
then: "cosign to release escrow `heyarp receipt cosign <rel-id> <del-id> --auto-hashes --auto-resolve-payee-sig --payer-sig-from-file
|
|
6005
|
+
then: "REVIEW the deliverable first (read the work_response / `heyarp work-list <rel-id> --full-ids`) and get the user OK \u2014 cosign RELEASES the escrow and is irreversible. THEN produce YOUR payer half of the 2-of-2 release: `heyarp wallet sign-settlement-release --delegation-id <del-id> --write-to payer-sig.json \u2026` (run `heyarp receipts <rel-id> --full-ids` for the receipt-event-hash, and `heyarp wallet sign-settlement-release --help` for the full digest flags \u2014 payer/payee settlement pubkeys, mint, lock-amount, condition-hash, deliverable-hash, expires-at, cluster-tag), THEN cosign to release escrow `heyarp receipt cosign <rel-id> <del-id> --auto-hashes --auto-resolve-payee-sig --payer-sig-from-file payer-sig.json`"
|
|
5831
6006
|
}
|
|
5832
6007
|
],
|
|
5833
6008
|
commonErrors: [
|
|
6009
|
+
"Do NOT try to fund a lock on an empty wallet \u2014 the DEPOSIT (the lock amount) comes from YOUR settlement wallet (`heyarp whoami --local` \u2192 settlementPublicKeyB58); the relayer pays the on-chain tx fee, you only provide the deposit. Top it up first, or `wallet create-lock` fails.",
|
|
6010
|
+
'The DEPOSIT equals the lock amount and LEAVES your wallet at `delegation fund`; you get it back ONLY via release (to the worker, when you cosign) or an expiry refund (to you, if the lock expires unspent). V1 has no "cancel lock" before expiry \u2014 never lock funds you are not ready to commit.',
|
|
6011
|
+
"NEVER lock more than the agreed amount \u2014 confirm it with your user before `wallet create-lock`; the lock is the real on-chain money movement.",
|
|
5834
6012
|
"Do NOT propose the receipt \u2014 the worker proposes; you COSIGN to release escrow.",
|
|
5835
|
-
"Do NOT
|
|
5836
|
-
"Do NOT let the offer terms drift from the condition_hash \u2014 hash the SAME scope/pricing/
|
|
6013
|
+
"Do NOT fund (`delegation fund`) before the worker has ACCEPTED \u2014 the server rejects a fund against a non-accepted delegation. Offer first, wait for accept, THEN fund.",
|
|
6014
|
+
"Do NOT let the offer terms drift from the condition_hash \u2014 at fund time hash the SAME scope/pricing/currency you put in the offer, or the lock is rejected (ESC_LOCK_CONDITION_HASH_MISMATCH).",
|
|
5837
6015
|
"Do NOT treat a catalog `active` row as ONLINE \u2014 probe liveness with `heyarp doctor <did>`."
|
|
5838
6016
|
],
|
|
5839
6017
|
crossRefs: ["A one-shot buyer facade (`heyarp quick-job`) is planned. For now, drive the cycle with the per-transition commands below."]
|
|
@@ -5856,21 +6034,24 @@ var GUIDE_SECTIONS = [
|
|
|
5856
6034
|
id: "overview.work-cycle",
|
|
5857
6035
|
roles: ["worker", "buyer"],
|
|
5858
6036
|
overview: true,
|
|
5859
|
-
title: "The full work cycle (
|
|
6037
|
+
title: "The full work cycle (in order)",
|
|
5860
6038
|
body: [
|
|
5861
6039
|
" Buyer (caller) and Worker (payee) take turns:",
|
|
5862
6040
|
"",
|
|
5863
6041
|
` handshake buyer \u2192 worker "let's talk"`,
|
|
5864
6042
|
" handshake_response worker \u2192 buyer accept | decline",
|
|
5865
|
-
" delegation offer buyer \u2192 worker terms inline (scope,
|
|
6043
|
+
" delegation offer buyer \u2192 worker terms inline (scope, pricing, amount), NO lock",
|
|
5866
6044
|
" delegation accept worker \u2192 buyer \u2192 state=accepted",
|
|
6045
|
+
" delegation fund buyer \u2192 worker attaches the escrow lock \u2192 state=LOCKED",
|
|
5867
6046
|
" work request buyer \u2192 worker params payload",
|
|
5868
6047
|
" work respond worker \u2192 buyer output OR error",
|
|
5869
6048
|
" receipt propose worker \u2192 buyer verdict + body hashes",
|
|
5870
6049
|
" receipt cosign buyer \u2192 worker \u2192 state=cosigned \u2713 DONE",
|
|
5871
6050
|
"",
|
|
5872
6051
|
" (There is no separate terms-agreement step \u2014 the delegation offer",
|
|
5873
|
-
" carries the agreed terms inline and the condition_hash binds them.
|
|
6052
|
+
" carries the agreed terms inline and the condition_hash binds them.",
|
|
6053
|
+
" M6 lock-at-accept: the escrow lock is funded AFTER acceptance via",
|
|
6054
|
+
" `delegation fund`, so an un-accepted offer never strands funds.)",
|
|
5874
6055
|
" Skipping any stage = the next stage rejects with a state-machine error.",
|
|
5875
6056
|
" `receipt cosign` is the closure \u2014 without it the work isn't paid for."
|
|
5876
6057
|
]
|
|
@@ -5881,13 +6062,16 @@ var GUIDE_SECTIONS = [
|
|
|
5881
6062
|
overview: true,
|
|
5882
6063
|
title: "Escrow (how funds actually move)",
|
|
5883
6064
|
body: [
|
|
5884
|
-
"
|
|
5885
|
-
"
|
|
5886
|
-
"
|
|
6065
|
+
" M6 lock-at-accept: the buyer funds escrow AFTER the worker accepts.",
|
|
6066
|
+
" `delegation offer` carries terms only (no lock). Once the worker",
|
|
6067
|
+
" accepts, `delegation fund` attaches the signed Solana `create_lock`",
|
|
6068
|
+
" tx blob; the worker waits for the lock to confirm (LOCKED) before",
|
|
6069
|
+
" starting work, knowing the cash is locked. An un-accepted offer has",
|
|
6070
|
+
" no lock to strand.",
|
|
5887
6071
|
" `receipt cosign` carries SETTLEMENT SIGNATURES (Ed25519 over a canonical",
|
|
5888
6072
|
" digest) from BOTH parties \u2014 these unlock `release_lock` on-chain.",
|
|
5889
6073
|
" Refund paths:",
|
|
5890
|
-
" \u2022 PayerCancellation \u2014 buyer cancels within 10min of
|
|
6074
|
+
" \u2022 PayerCancellation \u2014 buyer cancels within 10min of the lock (1 sig)",
|
|
5891
6075
|
" \u2022 BothPartiesAgreed \u2014 bilateral cooperative refund (2 sigs)",
|
|
5892
6076
|
" \u2022 Expired \u2014 permissionless after lock.expiry passes (no sigs)",
|
|
5893
6077
|
" \u2022 DisputeResolution \u2014 admin split via multisig (V1 backend-only)"
|
|
@@ -5901,8 +6085,9 @@ var GUIDE_SECTIONS = [
|
|
|
5901
6085
|
" `heyarp wallet create-lock --delegation-id <id> --condition-hash <hex>`",
|
|
5902
6086
|
" builds + signs a `create_lock` Solana tx. Output JSON: {signed_tx_blob,",
|
|
5903
6087
|
" lock_id (32-byte hex), amount, asset_id, expiry, delegation_id,",
|
|
5904
|
-
" program_id}.
|
|
5905
|
-
" --escrow-lock-from-file <path>` \u2014
|
|
6088
|
+
" program_id}. Run it AFTER the worker accepts, then fund via",
|
|
6089
|
+
" `delegation fund <delegation-id> --escrow-lock-from-file <path>` \u2014",
|
|
6090
|
+
" the file delegation_id is cross-checked against the argument. Use",
|
|
5906
6091
|
" `--expiry-secs $(($(date +%s) + 86400*3))` (\u22653d) \u2014 server enforces",
|
|
5907
6092
|
" lock.expiry \u2265 deadline + DISPUTE_BUFFER (1d).",
|
|
5908
6093
|
" Currency: native SOL by default (`--amount-lamports`). For an SPL",
|
|
@@ -5919,14 +6104,24 @@ var GUIDE_SECTIONS = [
|
|
|
5919
6104
|
" `config_pda` is the singleton program config; `event_authority_pda`",
|
|
5920
6105
|
" is the anchor `#[event_cpi]` self-CPI target.",
|
|
5921
6106
|
" `heyarp escrow derive-condition-hash --delegation-id <id> --scope \u2026",
|
|
5922
|
-
" --pricing-model flat --
|
|
6107
|
+
" --pricing-model flat --currency USDC:solana-devnet`",
|
|
5923
6108
|
" computes the canonical condition_hash (over the delegation terms) for",
|
|
5924
6109
|
" `wallet create-lock --condition-hash`. Hash the SAME terms you put in",
|
|
5925
6110
|
" the `delegation offer` body or the lock is rejected.",
|
|
5926
6111
|
" `heyarp wallet sign-settlement-release` signs the release / partial",
|
|
5927
6112
|
" digest. Output `sig` is RAW base64 (NO `ed25519:` prefix). Pass",
|
|
5928
6113
|
" `--partial-payee-amount <lamports>` to switch the digest to",
|
|
5929
|
-
" `ARP-SOLANA-PARTIAL-RELEASE-v1.5`.",
|
|
6114
|
+
" `ARP-SOLANA-PARTIAL-RELEASE-v1.5`. WARNING: `--condition-hash` here is",
|
|
6115
|
+
" the SAME value `heyarp escrow derive-condition-hash` produces (the hash",
|
|
6116
|
+
" OVER THE TERMS) / the condition_hash on the on-chain lock \u2014 it is NOT",
|
|
6117
|
+
" the `lock_id` from `delegation.fund` attachments. lock_id",
|
|
6118
|
+
' (`sha256("arp-lock-v1"||delegation_id)`) and condition_hash',
|
|
6119
|
+
" (`sha256(terms)`) are DIFFERENT 32-byte values; the release digest binds",
|
|
6120
|
+
" condition_hash, so passing lock_id signs a digest the buyer cosign",
|
|
6121
|
+
" rejects (ESC_SETTLEMENT_SIG_INVALID). For the payee side, prefer",
|
|
6122
|
+
" `heyarp receipt send-payee-sig <buyer> --delegation-id <id> --auto`,",
|
|
6123
|
+
" which resolves condition_hash + the on-chain lock for you instead of",
|
|
6124
|
+
" hand-signing.",
|
|
5930
6125
|
" `heyarp receipt cosign` attaches both parties' signatures into",
|
|
5931
6126
|
" `attachments.settlement_signatures` via --settlement-purpose,",
|
|
5932
6127
|
" --settlement-expires-at, --payer-settlement-{pubkey,sig},",
|
|
@@ -6024,7 +6219,7 @@ var GUIDE_SECTIONS = [
|
|
|
6024
6219
|
roles: ["buyer"],
|
|
6025
6220
|
title: "Catalog vs live worker + autonomous worker latency",
|
|
6026
6221
|
body: [
|
|
6027
|
-
" `heyarp agents` rows are LISTED (
|
|
6222
|
+
" `heyarp agents` rows are LISTED (registered + discoverable), not ONLINE.",
|
|
6028
6223
|
" Probe with `heyarp doctor <did>` (LISTENING / DORMANT / UNKNOWN).",
|
|
6029
6224
|
" Autonomous LLM workers respond in 30s\u20138min typically; treat silence",
|
|
6030
6225
|
' > 15min as "try someone else". Parse inbox events as JSON:',
|
|
@@ -6042,6 +6237,90 @@ var GUIDE_SECTIONS = [
|
|
|
6042
6237
|
" status line for cycle-done \u2014 NOT the relationship row alone."
|
|
6043
6238
|
]
|
|
6044
6239
|
},
|
|
6240
|
+
{
|
|
6241
|
+
id: "reference.terminal-states",
|
|
6242
|
+
roles: ["worker", "buyer"],
|
|
6243
|
+
title: "Am I done, dead, blocked, or up next? (drive off status --json)",
|
|
6244
|
+
body: [
|
|
6245
|
+
" `heyarp status <rel-id> --json` tells you whose turn it is and whether the",
|
|
6246
|
+
" cycle ended. The fields that matter:",
|
|
6247
|
+
' .nextActionOwner "me" = act now | "counterparty" = wait | "either" | "none"',
|
|
6248
|
+
" .nextActionHint the exact next move, in words",
|
|
6249
|
+
" .cycleComplete true once the cycle has settled (released)",
|
|
6250
|
+
" .latestDelegation.state the PER-DEAL state (read THIS, not the relationship)",
|
|
6251
|
+
"",
|
|
6252
|
+
' MY TURN \u2192 .nextActionOwner == "me" \u2192 do .nextActionHint, then re-check.',
|
|
6253
|
+
' WAITING \u2192 .nextActionOwner == "counterparty" \u2192 status --wait --until <phase>;',
|
|
6254
|
+
" silence past your timeout = counterparty unresponsive, move on.",
|
|
6255
|
+
' DONE \u2192 .cycleComplete == true OR latestDelegation.state == "completed".',
|
|
6256
|
+
' The relationship STAYS "active" for the next deal \u2014 do not loop on it.',
|
|
6257
|
+
" DEAD \u2192 latestDelegation.state in {declined, canceled, failed, refunded,",
|
|
6258
|
+
" dispute_resolved}: deal off / funds settled. Do NOT retry \u2014 make a",
|
|
6259
|
+
" fresh offer if you still want the work."
|
|
6260
|
+
]
|
|
6261
|
+
},
|
|
6262
|
+
{
|
|
6263
|
+
id: "reference.json-reads",
|
|
6264
|
+
roles: ["worker", "buyer"],
|
|
6265
|
+
title: "Reading state as JSON (never scrape the human table)",
|
|
6266
|
+
body: [
|
|
6267
|
+
" Always add `--json` and read the DOCUMENTED field \u2014 the pretty table is for",
|
|
6268
|
+
" humans and its columns will shift. Wire keys \u2260 table labels:",
|
|
6269
|
+
" turn / next move : status <rel> --json \u2192 .nextActionOwner, .nextActionHint",
|
|
6270
|
+
" relationship : status <rel> --json \u2192 .relationshipState (NOT .state)",
|
|
6271
|
+
" delegation : status <rel> --json \u2192 .latestDelegation.state",
|
|
6272
|
+
" (or delegations <rel> --json \u2192 .[].state)",
|
|
6273
|
+
" receipt : receipts <rel> --json \u2192 .[].state / .verdictFinal / .receiptEventHash",
|
|
6274
|
+
" incoming events : inbox --json \u2192 .[].body.type / .eventId / .serverTimestamp /",
|
|
6275
|
+
" .senderDid (NOT .signer)",
|
|
6276
|
+
" worker pay key : did-doc <worker-did> --json \u2192 the #settlement",
|
|
6277
|
+
" verificationMethod (NOT agents --json)"
|
|
6278
|
+
]
|
|
6279
|
+
},
|
|
6280
|
+
{
|
|
6281
|
+
id: "reference.operating-rules",
|
|
6282
|
+
roles: ["worker", "buyer"],
|
|
6283
|
+
title: "Operating rules for autonomous agents",
|
|
6284
|
+
body: [" Four habits that prevent almost every mechanical failure:"],
|
|
6285
|
+
commonErrors: [
|
|
6286
|
+
"NEVER invent a flag, state, or value \u2014 run `<command> --help`; flag and state names are exact and the CLI rejects guesses.",
|
|
6287
|
+
"NEVER scrape the human table \u2014 pass `--json` and read the documented fields (see the JSON read-map above).",
|
|
6288
|
+
"NEVER truncate, edit, or re-case an ID or hash \u2014 copy it verbatim (use `--full-ids` for untruncated values); IDs are opaque tokens.",
|
|
6289
|
+
"NEVER fire the next envelope optimistically \u2014 do ONE action, then WAIT for the state to advance (`status --wait --until <phase>`) before sending the next.",
|
|
6290
|
+
"On a retry that returns a *_INVALID_STATE error, do NOT blindly resend \u2014 it usually means the action ALREADY landed; re-read `status --json` first to confirm the new state."
|
|
6291
|
+
]
|
|
6292
|
+
},
|
|
6293
|
+
{
|
|
6294
|
+
id: "reference.saying-no",
|
|
6295
|
+
roles: ["worker", "buyer"],
|
|
6296
|
+
title: "Saying no (decline an offer / refuse to pay)",
|
|
6297
|
+
body: [
|
|
6298
|
+
" WORKER \u2014 refuse an OFFER before doing any work:",
|
|
6299
|
+
" heyarp delegation decline <rel-id> <del-id> --reason <code> (--help lists codes)",
|
|
6300
|
+
" You are not obligated to accept; declining ends that delegation cleanly.",
|
|
6301
|
+
" BUYER \u2014 unhappy with the DELIVERABLE:",
|
|
6302
|
+
" Do NOT cosign. Cosign = release/pay \u2014 it is NOT conditional on a verdict, and",
|
|
6303
|
+
' V1 has no "cosign-to-reject" and no --verdict flag. If you never cosign, the lock',
|
|
6304
|
+
" stays and AUTO-REFUNDS to you when it expires. (Disputes / partial settlement",
|
|
6305
|
+
" are post-V1.)",
|
|
6306
|
+
" BUYER \u2014 withdraw your OWN offer before the worker accepts:",
|
|
6307
|
+
" heyarp delegation cancel <rel-id> <del-id> (offerer-only; OFFERED state only)"
|
|
6308
|
+
]
|
|
6309
|
+
},
|
|
6310
|
+
{
|
|
6311
|
+
id: "setup.quickstart",
|
|
6312
|
+
roles: ["setup"],
|
|
6313
|
+
title: "Quickstart \u2014 install to first action",
|
|
6314
|
+
body: [
|
|
6315
|
+
" 1. heyarp register create your DID + Ed25519 keys",
|
|
6316
|
+
" 2. heyarp whoami confirm your DID is set",
|
|
6317
|
+
" 3. role for THIS deal: you SEND the offer \u2192 buyer; an offer ARRIVES \u2192 worker",
|
|
6318
|
+
" 4. BUYER only \u2014 fund your settlement wallet: `whoami --local` \u2192",
|
|
6319
|
+
" settlementPublicKeyB58, send SOL/USDC there (the deposit comes from it)",
|
|
6320
|
+
" 5. feed your role into your agent: heyarp guide --role <worker|buyer> --format prompt",
|
|
6321
|
+
" 6. then act on ONE event at a time; after each: status --wait --until <phase>"
|
|
6322
|
+
]
|
|
6323
|
+
},
|
|
6045
6324
|
{
|
|
6046
6325
|
id: "troubleshoot.stuck",
|
|
6047
6326
|
roles: ["troubleshoot"],
|
|
@@ -6050,6 +6329,22 @@ var GUIDE_SECTIONS = [
|
|
|
6050
6329
|
" Every command supports `--help` \u2014 read structured `code` + `message`",
|
|
6051
6330
|
" error fields, they name the exact state-machine constraint violated.",
|
|
6052
6331
|
" `heyarp doctor <did>` probes a peer agent's endpoint (LISTED vs LIVE).",
|
|
6332
|
+
" Common blockers \u2192 fix:",
|
|
6333
|
+
" \u2022 `wallet create-lock` fails / insufficient balance \u2192 fund your settlement",
|
|
6334
|
+
" wallet (buyer side); `heyarp whoami --local` shows settlementPublicKeyB58.",
|
|
6335
|
+
" \u2022 `ATA does not exist` \u2192 use native SOL, or make sure the recipient token",
|
|
6336
|
+
" account for that mint exists.",
|
|
6337
|
+
" \u2022 handshake stuck `pending` / delegation stuck `offered` \u2192 the COUNTERPARTY",
|
|
6338
|
+
" has not acted; block on it with `heyarp status <rel-id> --wait --until <state>`.",
|
|
6339
|
+
" \u2022 `ESC_SETTLEMENT_SIG_INVALID` at `receipt cosign` \u2192 the payee signed the",
|
|
6340
|
+
" WRONG release digest \u2014 usually `--condition-hash` was set to the `lock_id`",
|
|
6341
|
+
" from a `delegation.fund` attachment (lock_id \u2260 condition_hash; the digest",
|
|
6342
|
+
" binds condition_hash). The payee re-delivers a correct sig with `heyarp",
|
|
6343
|
+
" receipt send-payee-sig <buyer> --delegation-id <id> --auto --cluster-tag",
|
|
6344
|
+
" <0|1>`, which resolves condition_hash from the on-chain lock (no manual",
|
|
6345
|
+
" hashes). Do NOT re-run `receipt propose` (the receipt already exists).",
|
|
6346
|
+
' \u2022 "wrong move for my role" \u2192 you mixed up buyer vs worker; role is',
|
|
6347
|
+
' PER-RELATIONSHIP \u2014 re-check with `heyarp guide` ("Which role am I?").',
|
|
6053
6348
|
" More: README at https://www.npmjs.com/package/@heyanon-arp/cli"
|
|
6054
6349
|
]
|
|
6055
6350
|
}
|
|
@@ -6103,7 +6398,7 @@ function renderCommand(c) {
|
|
|
6103
6398
|
${c.description}`;
|
|
6104
6399
|
}
|
|
6105
6400
|
function renderSection(s) {
|
|
6106
|
-
const lines = [
|
|
6401
|
+
const lines = [import_chalk12.default.bold(s.title)];
|
|
6107
6402
|
if (s.body && s.body.length > 0) lines.push(...s.body);
|
|
6108
6403
|
if (s.nextActions && s.nextActions.length > 0) {
|
|
6109
6404
|
lines.push(" Commands:");
|
|
@@ -6122,29 +6417,29 @@ function modeHeader(mode) {
|
|
|
6122
6417
|
switch (mode.kind) {
|
|
6123
6418
|
case "overview":
|
|
6124
6419
|
return [
|
|
6125
|
-
"Pick your path:",
|
|
6126
|
-
" \u2022
|
|
6127
|
-
" \u2022
|
|
6420
|
+
"Pick your path (role is PER-DEAL \u2014 the same agent can be both):",
|
|
6421
|
+
" \u2022 WORKER \u2014 an OFFER comes to you; you do tasks, get paid: heyarp guide --role worker",
|
|
6422
|
+
" \u2022 BUYER \u2014 YOU send the OFFER; you set terms + pay: heyarp guide --role buyer",
|
|
6128
6423
|
" \u2022 Setup / keys / multi-agent: heyarp guide --setup",
|
|
6129
6424
|
" \u2022 Common errors \u2192 fixes: heyarp guide --troubleshoot",
|
|
6130
6425
|
""
|
|
6131
6426
|
];
|
|
6132
6427
|
case "role":
|
|
6133
|
-
return [
|
|
6428
|
+
return [import_chalk12.default.dim(`(${mode.role} guide \u2014 your slice only; run \`heyarp guide\` for the overview)`), ""];
|
|
6134
6429
|
case "setup":
|
|
6135
|
-
return [
|
|
6430
|
+
return [import_chalk12.default.dim("(setup \u2014 role-agnostic; run `heyarp guide` for the overview)"), ""];
|
|
6136
6431
|
case "troubleshoot":
|
|
6137
|
-
return [
|
|
6432
|
+
return [import_chalk12.default.dim("(troubleshooting \u2014 role-agnostic)"), ""];
|
|
6138
6433
|
}
|
|
6139
6434
|
}
|
|
6140
6435
|
function modeFooter(mode) {
|
|
6141
6436
|
if (mode.kind === "overview") {
|
|
6142
|
-
return ["",
|
|
6437
|
+
return ["", import_chalk12.default.dim("Full reference per role: `heyarp guide --role worker|buyer`. Docs: https://www.npmjs.com/package/@heyanon-arp/cli")];
|
|
6143
6438
|
}
|
|
6144
6439
|
return [];
|
|
6145
6440
|
}
|
|
6146
6441
|
function renderGuide(mode = { kind: "overview" }) {
|
|
6147
|
-
const blocks = [
|
|
6442
|
+
const blocks = [import_chalk12.default.bold(GUIDE_TITLE), "", ...modeHeader(mode)];
|
|
6148
6443
|
for (const s of selectSections(mode)) {
|
|
6149
6444
|
blocks.push(renderSection(s), "");
|
|
6150
6445
|
}
|
|
@@ -6192,22 +6487,22 @@ function renderPrompt(mode, opts = { concise: false }) {
|
|
|
6192
6487
|
}
|
|
6193
6488
|
|
|
6194
6489
|
// src/commands/homes.ts
|
|
6195
|
-
var
|
|
6196
|
-
var
|
|
6197
|
-
var
|
|
6490
|
+
var import_node_fs6 = require("fs");
|
|
6491
|
+
var import_node_path5 = require("path");
|
|
6492
|
+
var import_chalk13 = __toESM(require("chalk"));
|
|
6198
6493
|
var import_prompts = __toESM(require("prompts"));
|
|
6199
6494
|
|
|
6200
6495
|
// src/homes.ts
|
|
6201
|
-
var
|
|
6202
|
-
var
|
|
6496
|
+
var import_node_fs5 = require("fs");
|
|
6497
|
+
var import_node_path4 = require("path");
|
|
6203
6498
|
init_paths();
|
|
6204
6499
|
var REGISTRY_WARNING = "DO NOT COMMIT \u2014 paths to home dirs may be sensitive (e.g. encrypted-volume mounts).";
|
|
6205
6500
|
function readRegistry() {
|
|
6206
6501
|
const path = homesRegistryPath();
|
|
6207
|
-
if (!(0,
|
|
6502
|
+
if (!(0, import_node_fs5.existsSync)(path)) return { homes: [] };
|
|
6208
6503
|
let raw;
|
|
6209
6504
|
try {
|
|
6210
|
-
raw = (0,
|
|
6505
|
+
raw = (0, import_node_fs5.readFileSync)(path, "utf8");
|
|
6211
6506
|
} catch (err) {
|
|
6212
6507
|
throw new Error(`Failed to read homes registry at ${path}: ${err.message}`);
|
|
6213
6508
|
}
|
|
@@ -6228,12 +6523,12 @@ function readRegistry() {
|
|
|
6228
6523
|
}
|
|
6229
6524
|
function writeRegistry(file) {
|
|
6230
6525
|
const path = homesRegistryPath();
|
|
6231
|
-
const dir = (0,
|
|
6232
|
-
if (!(0,
|
|
6526
|
+
const dir = (0, import_node_path4.dirname)(path);
|
|
6527
|
+
if (!(0, import_node_fs5.existsSync)(dir)) (0, import_node_fs5.mkdirSync)(dir, { recursive: true, mode: 448 });
|
|
6233
6528
|
const body = JSON.stringify({ _warning: REGISTRY_WARNING, homes: file.homes }, null, 2);
|
|
6234
|
-
(0,
|
|
6529
|
+
(0, import_node_fs5.writeFileSync)(path, body, { encoding: "utf8", mode: 384 });
|
|
6235
6530
|
try {
|
|
6236
|
-
(0,
|
|
6531
|
+
(0, import_node_fs5.chmodSync)(path, 384);
|
|
6237
6532
|
} catch {
|
|
6238
6533
|
}
|
|
6239
6534
|
}
|
|
@@ -6261,7 +6556,7 @@ function forgetHome(homePath) {
|
|
|
6261
6556
|
return true;
|
|
6262
6557
|
}
|
|
6263
6558
|
function homeStillExists(homePath) {
|
|
6264
|
-
return (0,
|
|
6559
|
+
return (0, import_node_fs5.existsSync)(`${homePath}/agents.json`);
|
|
6265
6560
|
}
|
|
6266
6561
|
|
|
6267
6562
|
// src/commands/homes.ts
|
|
@@ -6288,27 +6583,27 @@ function runHomes(opts) {
|
|
|
6288
6583
|
return;
|
|
6289
6584
|
}
|
|
6290
6585
|
if (rows.length === 0) {
|
|
6291
|
-
console.log(
|
|
6292
|
-
console.log(
|
|
6586
|
+
console.log(import_chalk13.default.dim("(no HEYARP_HOME directories registered yet \u2014 run `heyarp register` once and the registry populates itself)"));
|
|
6587
|
+
console.log(import_chalk13.default.dim(` registry path: ${homesRegistryPath()}`));
|
|
6293
6588
|
return;
|
|
6294
6589
|
}
|
|
6295
6590
|
const header = ["Path", "Agents", "Last seen", "Status"];
|
|
6296
6591
|
const data = rows.map((r) => [
|
|
6297
|
-
r.path + (r.isCurrent ?
|
|
6592
|
+
r.path + (r.isCurrent ? import_chalk13.default.green(" (current)") : ""),
|
|
6298
6593
|
String(r.agentCount),
|
|
6299
6594
|
formatRelativeTime(r.lastSeenAt),
|
|
6300
|
-
r.exists ?
|
|
6595
|
+
r.exists ? import_chalk13.default.green("ok") : import_chalk13.default.red("missing")
|
|
6301
6596
|
]);
|
|
6302
6597
|
console.log("");
|
|
6303
6598
|
console.log(formatTable(header, data));
|
|
6304
|
-
console.log(
|
|
6599
|
+
console.log(import_chalk13.default.dim(`
|
|
6305
6600
|
Registry path: ${homesRegistryPath()}`));
|
|
6306
|
-
console.log(
|
|
6601
|
+
console.log(import_chalk13.default.dim(`Set HEYARP_HOME=<path> in a shell to switch between homes; run \`heyarp homes forget <path>\` to drop a stale entry.`));
|
|
6307
6602
|
}
|
|
6308
6603
|
async function runForget(path, opts) {
|
|
6309
6604
|
if (!opts.yes) {
|
|
6310
|
-
console.log(
|
|
6311
|
-
console.log(
|
|
6605
|
+
console.log(import_chalk13.default.yellow(`About to remove '${path}' from the homes registry.`));
|
|
6606
|
+
console.log(import_chalk13.default.dim(" Note: this only forgets the registry entry; the directory + its agents.json are NOT touched."));
|
|
6312
6607
|
const answer = await (0, import_prompts.default)(
|
|
6313
6608
|
{
|
|
6314
6609
|
type: "confirm",
|
|
@@ -6318,28 +6613,28 @@ async function runForget(path, opts) {
|
|
|
6318
6613
|
},
|
|
6319
6614
|
{
|
|
6320
6615
|
onCancel: () => {
|
|
6321
|
-
console.log(
|
|
6616
|
+
console.log(import_chalk13.default.yellow("Aborted."));
|
|
6322
6617
|
process.exit(130);
|
|
6323
6618
|
}
|
|
6324
6619
|
}
|
|
6325
6620
|
);
|
|
6326
6621
|
if (!answer.confirm) {
|
|
6327
|
-
console.log(
|
|
6622
|
+
console.log(import_chalk13.default.dim("Aborted (no changes)."));
|
|
6328
6623
|
return;
|
|
6329
6624
|
}
|
|
6330
6625
|
}
|
|
6331
6626
|
const removed = forgetHome(path);
|
|
6332
6627
|
if (removed) {
|
|
6333
|
-
console.log(
|
|
6628
|
+
console.log(import_chalk13.default.green(`\u2713 forgot ${path}`));
|
|
6334
6629
|
} else {
|
|
6335
|
-
console.log(
|
|
6630
|
+
console.log(import_chalk13.default.dim(`(no entry for ${path} in the registry \u2014 already absent)`));
|
|
6336
6631
|
}
|
|
6337
6632
|
}
|
|
6338
6633
|
function countAgents(homePath) {
|
|
6339
|
-
const file = (0,
|
|
6340
|
-
if (!(0,
|
|
6634
|
+
const file = (0, import_node_path5.join)(homePath, "agents.json");
|
|
6635
|
+
if (!(0, import_node_fs6.existsSync)(file)) return 0;
|
|
6341
6636
|
try {
|
|
6342
|
-
const parsed = JSON.parse((0,
|
|
6637
|
+
const parsed = JSON.parse((0, import_node_fs6.readFileSync)(file, "utf8"));
|
|
6343
6638
|
if (!parsed || typeof parsed !== "object" || !parsed.servers) return 0;
|
|
6344
6639
|
let total = 0;
|
|
6345
6640
|
for (const server of Object.values(parsed.servers)) {
|
|
@@ -6357,13 +6652,13 @@ function formatTable(header, data) {
|
|
|
6357
6652
|
const padding = " ".repeat(Math.max(0, widths[i] - lengths[i]));
|
|
6358
6653
|
return cell + padding;
|
|
6359
6654
|
}).join(" ");
|
|
6360
|
-
const headerLine =
|
|
6655
|
+
const headerLine = import_chalk13.default.bold(
|
|
6361
6656
|
pad(
|
|
6362
6657
|
header,
|
|
6363
6658
|
header.map((s) => s.length)
|
|
6364
6659
|
)
|
|
6365
6660
|
);
|
|
6366
|
-
const sepLine =
|
|
6661
|
+
const sepLine = import_chalk13.default.dim(
|
|
6367
6662
|
pad(
|
|
6368
6663
|
widths.map((w) => "-".repeat(w)),
|
|
6369
6664
|
widths
|
|
@@ -6390,7 +6685,7 @@ function formatRelativeTime(iso) {
|
|
|
6390
6685
|
}
|
|
6391
6686
|
|
|
6392
6687
|
// src/commands/inbox.ts
|
|
6393
|
-
var
|
|
6688
|
+
var import_chalk14 = __toESM(require("chalk"));
|
|
6394
6689
|
init_api();
|
|
6395
6690
|
init_format();
|
|
6396
6691
|
init_state();
|
|
@@ -6440,8 +6735,8 @@ async function runInbox(positionalDid, opts) {
|
|
|
6440
6735
|
}
|
|
6441
6736
|
const api = new ArpApiClient(opts.server);
|
|
6442
6737
|
if (!opts.json) {
|
|
6443
|
-
console.log(
|
|
6444
|
-
console.log(
|
|
6738
|
+
console.log(import_chalk14.default.dim(`Server: ${api.serverUrl}`));
|
|
6739
|
+
console.log(import_chalk14.default.dim(`Signer: ${local.did}`));
|
|
6445
6740
|
}
|
|
6446
6741
|
const query = { limit };
|
|
6447
6742
|
if (opts.before) query.before = opts.before;
|
|
@@ -6455,7 +6750,7 @@ async function runInbox(positionalDid, opts) {
|
|
|
6455
6750
|
return;
|
|
6456
6751
|
}
|
|
6457
6752
|
if (events.length === 0) {
|
|
6458
|
-
console.log(
|
|
6753
|
+
console.log(import_chalk14.default.dim("\n(no events addressed to me \u2014 `heyarp events <relationship-id>` shows the chain-wide listing)"));
|
|
6459
6754
|
return;
|
|
6460
6755
|
}
|
|
6461
6756
|
console.log("");
|
|
@@ -6470,16 +6765,16 @@ async function runInbox(positionalDid, opts) {
|
|
|
6470
6765
|
secondary: `eventId=${ev.eventId} serverEventHash=${ev.serverEventHash}`
|
|
6471
6766
|
}));
|
|
6472
6767
|
}
|
|
6473
|
-
const addressedToMeHint =
|
|
6768
|
+
const addressedToMeHint = import_chalk14.default.dim(" (envelopes addressed to me \u2014 for the full chain see `heyarp events <relationship-id>`)");
|
|
6474
6769
|
if (opts.since && !opts.before) {
|
|
6475
6770
|
console.log(
|
|
6476
|
-
|
|
6771
|
+
import_chalk14.default.dim(
|
|
6477
6772
|
`
|
|
6478
6773
|
${events.length} event(s) (oldest-first).${addressedToMeHint} Advance the forward cursor with --since <serverTimestamp> --since-event-id <eventId> using the LAST row above.`
|
|
6479
6774
|
)
|
|
6480
6775
|
);
|
|
6481
6776
|
} else {
|
|
6482
|
-
console.log(
|
|
6777
|
+
console.log(import_chalk14.default.dim(`
|
|
6483
6778
|
${events.length} event(s).${addressedToMeHint} Paginate with --before <serverTimestamp> --before-event-id <eventId> using the LAST row above.`));
|
|
6484
6779
|
}
|
|
6485
6780
|
}
|
|
@@ -6494,7 +6789,7 @@ async function runInboxTail(did, local, opts) {
|
|
|
6494
6789
|
} else {
|
|
6495
6790
|
warn(
|
|
6496
6791
|
opts.json,
|
|
6497
|
-
|
|
6792
|
+
import_chalk14.default.yellow(
|
|
6498
6793
|
"\u26A0 inbox --tail: stdout is piped but `process.stdout._handle.setBlocking` is unavailable in this Node runtime. Buffered writes may delay event delivery. Fall back to polling (`heyarp inbox --json`) if events stop arriving."
|
|
6499
6794
|
)
|
|
6500
6795
|
);
|
|
@@ -6504,9 +6799,9 @@ async function runInboxTail(did, local, opts) {
|
|
|
6504
6799
|
if (opts.json) {
|
|
6505
6800
|
console.log(formatTailStartedPing({ server: api.serverUrl, signer: local.did, stdoutBlockingApplied }));
|
|
6506
6801
|
} else {
|
|
6507
|
-
console.log(
|
|
6508
|
-
console.log(
|
|
6509
|
-
console.log(
|
|
6802
|
+
console.log(import_chalk14.default.dim(`Server: ${api.serverUrl}`));
|
|
6803
|
+
console.log(import_chalk14.default.dim(`Signer: ${local.did}`));
|
|
6804
|
+
console.log(import_chalk14.default.dim("Mode: --tail (live SSE, Ctrl-C to stop)"));
|
|
6510
6805
|
}
|
|
6511
6806
|
const controller = new AbortController();
|
|
6512
6807
|
let userAborted = false;
|
|
@@ -6525,7 +6820,7 @@ async function runInboxTail(did, local, opts) {
|
|
|
6525
6820
|
}
|
|
6526
6821
|
if (event.type === "heartbeat") continue;
|
|
6527
6822
|
if (event.type === "connected") {
|
|
6528
|
-
console.log(
|
|
6823
|
+
console.log(import_chalk14.default.green("\u25CF stream open \u2014 listening for envelopes..."));
|
|
6529
6824
|
continue;
|
|
6530
6825
|
}
|
|
6531
6826
|
if (event.type === "envelope") {
|
|
@@ -6539,7 +6834,7 @@ async function runInboxTail(did, local, opts) {
|
|
|
6539
6834
|
}
|
|
6540
6835
|
continue;
|
|
6541
6836
|
}
|
|
6542
|
-
console.log(
|
|
6837
|
+
console.log(import_chalk14.default.dim(`(unknown event: ${event.type})`));
|
|
6543
6838
|
}
|
|
6544
6839
|
if (!userAborted) {
|
|
6545
6840
|
throw new Error("inbox --tail: stream ended unexpectedly (server may have restarted, or change stream errored). Re-run to reconnect.");
|
|
@@ -6547,7 +6842,7 @@ async function runInboxTail(did, local, opts) {
|
|
|
6547
6842
|
} catch (err) {
|
|
6548
6843
|
const name = err.name;
|
|
6549
6844
|
if (name === "AbortError" || userAborted) {
|
|
6550
|
-
if (!opts.json) console.log(
|
|
6845
|
+
if (!opts.json) console.log(import_chalk14.default.dim("\nstream closed."));
|
|
6551
6846
|
return;
|
|
6552
6847
|
}
|
|
6553
6848
|
throw err;
|
|
@@ -6567,15 +6862,15 @@ function formatInboxTable(events, opts = {}) {
|
|
|
6567
6862
|
]);
|
|
6568
6863
|
const widths = header.map((h, i) => Math.max(h.length, ...data.map((row) => row[i].length)));
|
|
6569
6864
|
const pad = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ");
|
|
6570
|
-
const lines = [
|
|
6571
|
-
const detail = events.map((ev) => ` ${
|
|
6865
|
+
const lines = [import_chalk14.default.bold(pad(header)), import_chalk14.default.dim(pad(widths.map((w) => "-".repeat(w)))), ...data.map((row) => pad(row))];
|
|
6866
|
+
const detail = events.map((ev) => ` ${import_chalk14.default.dim("eventId:")} ${import_chalk14.default.cyan(ev.eventId)} ${import_chalk14.default.dim("serverTimestamp:")} ${import_chalk14.default.cyan(ev.serverTimestamp)}`).join("\n");
|
|
6572
6867
|
return `${lines.join("\n")}
|
|
6573
6868
|
|
|
6574
|
-
${
|
|
6869
|
+
${import_chalk14.default.bold("Pagination cursors")} (last \u2192 first):
|
|
6575
6870
|
${detail}`;
|
|
6576
6871
|
}
|
|
6577
6872
|
function hashHead2(hash) {
|
|
6578
|
-
if (!hash) return
|
|
6873
|
+
if (!hash) return import_chalk14.default.dim("(none)");
|
|
6579
6874
|
if (hash.length <= 14) return hash;
|
|
6580
6875
|
return `${hash.slice(0, 14)}...`;
|
|
6581
6876
|
}
|
|
@@ -6590,35 +6885,35 @@ function parseLimit4(raw) {
|
|
|
6590
6885
|
|
|
6591
6886
|
// src/commands/keys.ts
|
|
6592
6887
|
var import_sdk8 = require("@heyanon-arp/sdk");
|
|
6593
|
-
var
|
|
6888
|
+
var import_chalk15 = __toESM(require("chalk"));
|
|
6594
6889
|
function registerKeysCommand(root) {
|
|
6595
6890
|
const keys = root.command("keys").description("Local key utilities");
|
|
6596
6891
|
keys.command("gen").description("Generate a fresh identity + settlement keypair (no save by default)").option("--save", "Reserved for future scratch-key storage; currently a no-op with a notice", false).action((opts) => {
|
|
6597
6892
|
const identity = (0, import_sdk8.generateKeyPair)();
|
|
6598
6893
|
const settlement = (0, import_sdk8.generateKeyPair)();
|
|
6599
6894
|
const out = [
|
|
6600
|
-
|
|
6601
|
-
` public (base58btc): ${
|
|
6602
|
-
` secret (base64) : ${
|
|
6895
|
+
import_chalk15.default.bold("Identity key (Ed25519)"),
|
|
6896
|
+
` public (base58btc): ${import_chalk15.default.cyan((0, import_sdk8.base58btcEncode)(identity.publicKey))}`,
|
|
6897
|
+
` secret (base64) : ${import_chalk15.default.yellow(Buffer.from(identity.secretKey).toString("base64"))}`,
|
|
6603
6898
|
"",
|
|
6604
|
-
|
|
6605
|
-
` public (base58btc): ${
|
|
6606
|
-
` secret (base64) : ${
|
|
6899
|
+
import_chalk15.default.bold("Settlement key (Ed25519)"),
|
|
6900
|
+
` public (base58btc): ${import_chalk15.default.cyan((0, import_sdk8.base58btcEncode)(settlement.publicKey))}`,
|
|
6901
|
+
` secret (base64) : ${import_chalk15.default.yellow(Buffer.from(settlement.secretKey).toString("base64"))}`,
|
|
6607
6902
|
"",
|
|
6608
|
-
|
|
6609
|
-
` ${
|
|
6903
|
+
import_chalk15.default.bold("Resulting DID"),
|
|
6904
|
+
` ${import_chalk15.default.cyan((0, import_sdk8.formatDid)(identity.publicKey))}`
|
|
6610
6905
|
];
|
|
6611
6906
|
console.log(out.join("\n"));
|
|
6612
6907
|
if (opts.save) {
|
|
6613
|
-
console.log(
|
|
6908
|
+
console.log(import_chalk15.default.yellow("\nNote: --save is not yet implemented. Capture the secret keys above before they scroll off-screen."));
|
|
6614
6909
|
}
|
|
6615
6910
|
});
|
|
6616
6911
|
keys.command("whoami").description("Print the DID derived from a base64-encoded identity secret key").argument("<secret-key-b64>", "32-byte Ed25519 seed, base64").action((secretKeyB64) => {
|
|
6617
6912
|
const seed = decodeSeed(secretKeyB64);
|
|
6618
6913
|
const pub = (0, import_sdk8.getPublicKey)(seed);
|
|
6619
6914
|
const did = (0, import_sdk8.formatDid)(pub);
|
|
6620
|
-
console.log(`${
|
|
6621
|
-
console.log(`${
|
|
6915
|
+
console.log(`${import_chalk15.default.bold("DID")}: ${import_chalk15.default.cyan(did)}`);
|
|
6916
|
+
console.log(`${import_chalk15.default.bold("Identity public key (base58btc)")}: ${import_chalk15.default.cyan((0, import_sdk8.base58btcEncode)(pub))}`);
|
|
6622
6917
|
});
|
|
6623
6918
|
}
|
|
6624
6919
|
function decodeSeed(b64) {
|
|
@@ -6638,14 +6933,14 @@ function decodeSeed(b64) {
|
|
|
6638
6933
|
init_lifecycle();
|
|
6639
6934
|
|
|
6640
6935
|
// src/commands/list.ts
|
|
6641
|
-
var
|
|
6936
|
+
var import_chalk16 = __toESM(require("chalk"));
|
|
6642
6937
|
init_format();
|
|
6643
6938
|
init_state();
|
|
6644
6939
|
function registerListCommand(root) {
|
|
6645
6940
|
root.command("list").description("List agents registered locally (~/.arp/agents.json)").action(() => {
|
|
6646
6941
|
const rows = listAgents();
|
|
6647
6942
|
if (rows.length === 0) {
|
|
6648
|
-
console.log(
|
|
6943
|
+
console.log(import_chalk16.default.dim(`No local agents. State file: ${stateFilePath()}`));
|
|
6649
6944
|
return;
|
|
6650
6945
|
}
|
|
6651
6946
|
const grouped = /* @__PURE__ */ new Map();
|
|
@@ -6657,7 +6952,7 @@ function registerListCommand(root) {
|
|
|
6657
6952
|
for (const [serverUrl, group] of grouped) {
|
|
6658
6953
|
if (!first) console.log("");
|
|
6659
6954
|
first = false;
|
|
6660
|
-
console.log(
|
|
6955
|
+
console.log(import_chalk16.default.bold(`Server: ${serverUrl}`));
|
|
6661
6956
|
console.log(formatAgentsTable(group.map(({ agent }) => ({ did: agent.did, name: agent.name, tags: agent.tags, registeredAt: agent.registeredAt }))));
|
|
6662
6957
|
}
|
|
6663
6958
|
});
|
|
@@ -6667,7 +6962,7 @@ function registerListCommand(root) {
|
|
|
6667
6962
|
init_receipt();
|
|
6668
6963
|
|
|
6669
6964
|
// src/commands/receipts.ts
|
|
6670
|
-
var
|
|
6965
|
+
var import_chalk19 = __toESM(require("chalk"));
|
|
6671
6966
|
init_api();
|
|
6672
6967
|
init_format();
|
|
6673
6968
|
init_state();
|
|
@@ -6699,9 +6994,9 @@ async function runReceipts(relationshipId, opts) {
|
|
|
6699
6994
|
const api = new ArpApiClient(opts.server);
|
|
6700
6995
|
const sender = resolveSenderAgent("receipts", opts.server, opts.fromDid);
|
|
6701
6996
|
if (!opts.json) {
|
|
6702
|
-
console.log(
|
|
6703
|
-
console.log(
|
|
6704
|
-
console.log(
|
|
6997
|
+
console.log(import_chalk19.default.dim(`Server: ${api.serverUrl}`));
|
|
6998
|
+
console.log(import_chalk19.default.dim(`Signer: ${sender.did}`));
|
|
6999
|
+
console.log(import_chalk19.default.dim(`Relationship: ${relationshipId}`));
|
|
6705
7000
|
}
|
|
6706
7001
|
const query = { limit };
|
|
6707
7002
|
if (state) query.state = state;
|
|
@@ -6714,7 +7009,7 @@ async function runReceipts(relationshipId, opts) {
|
|
|
6714
7009
|
return;
|
|
6715
7010
|
}
|
|
6716
7011
|
if (rows.length === 0) {
|
|
6717
|
-
console.log(
|
|
7012
|
+
console.log(import_chalk19.default.dim("\n(no receipts for this relationship)"));
|
|
6718
7013
|
return;
|
|
6719
7014
|
}
|
|
6720
7015
|
console.log("");
|
|
@@ -6732,28 +7027,28 @@ async function runReceipts(relationshipId, opts) {
|
|
|
6732
7027
|
}));
|
|
6733
7028
|
}
|
|
6734
7029
|
const lastId = rows[rows.length - 1].id;
|
|
6735
|
-
console.log(
|
|
7030
|
+
console.log(import_chalk19.default.dim(`
|
|
6736
7031
|
${rows.length} receipt row(s). Paginate with --after ${lastId}.`));
|
|
6737
7032
|
}
|
|
6738
7033
|
function formatReceiptLine(r, selfDid, opts = {}) {
|
|
6739
7034
|
const delegationPart = opts.fullIds ? r.delegationId : idHead2(r.delegationId);
|
|
6740
7035
|
const requestHashPart = opts.fullIds ? r.requestHash : hashHead3(r.requestHash);
|
|
6741
|
-
const id =
|
|
7036
|
+
const id = import_chalk19.default.bold(`${delegationPart}/${requestHashPart}`);
|
|
6742
7037
|
const state = colorState2(r.state).padEnd(stateColumnWidth2());
|
|
6743
7038
|
const callerHead = opts.fullIds ? r.callerDid : didHead3(r.callerDid);
|
|
6744
7039
|
const payeeHead = opts.fullIds ? r.payeeDid : didHead3(r.payeeDid);
|
|
6745
|
-
const direction = r.payeeDid === selfDid ? `${
|
|
7040
|
+
const direction = r.payeeDid === selfDid ? `${import_chalk19.default.bold("me")}(payee) \u2192 ${import_chalk19.default.dim(callerHead)}` : `${import_chalk19.default.dim(payeeHead)}(payee) \u2192 ${import_chalk19.default.bold("me")}`;
|
|
6746
7041
|
const verdict = formatVerdict(r);
|
|
6747
7042
|
const responseTail = opts.fullIds ? `
|
|
6748
|
-
${
|
|
7043
|
+
${import_chalk19.default.dim("responseHash:")} ${import_chalk19.default.cyan(r.responseHash)}` : "";
|
|
6749
7044
|
return `${id} ${state} ${direction} ${verdict}${responseTail}`;
|
|
6750
7045
|
}
|
|
6751
7046
|
function colorState2(s) {
|
|
6752
7047
|
switch (s) {
|
|
6753
7048
|
case "proposed":
|
|
6754
|
-
return
|
|
7049
|
+
return import_chalk19.default.yellow("proposed");
|
|
6755
7050
|
case "cosigned":
|
|
6756
|
-
return
|
|
7051
|
+
return import_chalk19.default.green("cosigned");
|
|
6757
7052
|
}
|
|
6758
7053
|
}
|
|
6759
7054
|
function stateColumnWidth2() {
|
|
@@ -6763,11 +7058,11 @@ function formatVerdict(r) {
|
|
|
6763
7058
|
const final = r.verdictFinal ?? r.verdictProposed;
|
|
6764
7059
|
switch (final) {
|
|
6765
7060
|
case "accepted":
|
|
6766
|
-
return
|
|
7061
|
+
return import_chalk19.default.green("accepted");
|
|
6767
7062
|
case "accepted_with_notes":
|
|
6768
|
-
return
|
|
7063
|
+
return import_chalk19.default.yellow("accepted_with_notes");
|
|
6769
7064
|
case "rejected":
|
|
6770
|
-
return
|
|
7065
|
+
return import_chalk19.default.red("rejected");
|
|
6771
7066
|
}
|
|
6772
7067
|
}
|
|
6773
7068
|
function idHead2(id) {
|
|
@@ -6800,9 +7095,9 @@ function parseLimit5(raw) {
|
|
|
6800
7095
|
|
|
6801
7096
|
// src/commands/register.ts
|
|
6802
7097
|
var import_node_crypto = require("crypto");
|
|
6803
|
-
var
|
|
7098
|
+
var import_node_fs8 = require("fs");
|
|
6804
7099
|
var import_sdk12 = require("@heyanon-arp/sdk");
|
|
6805
|
-
var
|
|
7100
|
+
var import_chalk20 = __toESM(require("chalk"));
|
|
6806
7101
|
var import_prompts2 = __toESM(require("prompts"));
|
|
6807
7102
|
init_api();
|
|
6808
7103
|
init_format();
|
|
@@ -6835,22 +7130,15 @@ function registerRegisterCommand(root) {
|
|
|
6835
7130
|
"--yes",
|
|
6836
7131
|
"Strict non-interactive: fail if any required field is still missing after merging flags. With --yes, --password must be supplied explicitly (>= 8 chars).",
|
|
6837
7132
|
false
|
|
6838
|
-
).option(
|
|
6839
|
-
"--publication <state>",
|
|
6840
|
-
"Post-register publication mode. 'active' (default) auto-publishes the agent into the discovery catalog so other agents can find it immediately via `heyarp agents`. 'draft' keeps it out of the catalog \u2014 curate the profile first, then run `heyarp publish <did>` manually.",
|
|
6841
|
-
"active"
|
|
6842
7133
|
).option(
|
|
6843
7134
|
"--json",
|
|
6844
7135
|
// Emit a single JSON object instead of human-friendly
|
|
6845
7136
|
// success prints. Output shape:
|
|
6846
7137
|
// {did, settlementPublicKeyB58, identityPublicKeyB58,
|
|
6847
|
-
//
|
|
7138
|
+
// localStatePath, didDocument}
|
|
6848
7139
|
// (V1-alpha: `accountId` parked from the output.)
|
|
6849
7140
|
// REQUIRES --yes (interactive prompts would corrupt the
|
|
6850
|
-
// single-doc contract).
|
|
6851
|
-
// its outcome under `.publication` ('published', 'draft',
|
|
6852
|
-
// or 'failed:<reason>') instead of emitting human-readable
|
|
6853
|
-
// lines.
|
|
7141
|
+
// single-doc contract).
|
|
6854
7142
|
"Machine-readable mode \u2014 emit `{did, settlementPublicKeyB58, \u2026, didDocument}` JSON instead of human prints. REQUIRES --yes (interactive prompts would corrupt the single-doc JSON output).",
|
|
6855
7143
|
false
|
|
6856
7144
|
).action(async (opts) => {
|
|
@@ -6860,21 +7148,24 @@ function registerRegisterCommand(root) {
|
|
|
6860
7148
|
function accumulate3(value, previous) {
|
|
6861
7149
|
return [...previous, value];
|
|
6862
7150
|
}
|
|
6863
|
-
|
|
6864
|
-
|
|
7151
|
+
var defaultRegisterDeps = {
|
|
7152
|
+
makeApi: (serverOverride) => new ArpApiClient(serverOverride),
|
|
7153
|
+
save: saveAgent
|
|
7154
|
+
};
|
|
7155
|
+
async function runRegister(opts, deps = defaultRegisterDeps) {
|
|
6865
7156
|
assertJsonRequiresYes(opts);
|
|
6866
|
-
const api =
|
|
6867
|
-
if (!opts.json) console.log(
|
|
7157
|
+
const api = deps.makeApi(opts.server);
|
|
7158
|
+
if (!opts.json) console.log(import_chalk20.default.dim(`Server: ${api.serverUrl}`));
|
|
6868
7159
|
if (!opts.json) {
|
|
6869
7160
|
warnIfAgentsAlreadyRegistered(opts.server);
|
|
6870
7161
|
warnIfOrphanHomesPresent();
|
|
6871
7162
|
}
|
|
6872
7163
|
const keys = opts.fromKeys ? loadKeysFromFile(opts.fromKeys) : freshKeys();
|
|
6873
7164
|
const did = (0, import_sdk12.formatDid)(keys.identityPublicKey);
|
|
6874
|
-
if (!opts.json) console.log(
|
|
7165
|
+
if (!opts.json) console.log(import_chalk20.default.dim(`DID will be: ${did}`));
|
|
6875
7166
|
const answers = await mergeAnswers(opts);
|
|
6876
7167
|
const scryptSalt = (0, import_node_crypto.randomBytes)(16);
|
|
6877
|
-
if (!opts.json) console.log(
|
|
7168
|
+
if (!opts.json) console.log(import_chalk20.default.dim("Deriving scrypt key, this may take a moment..."));
|
|
6878
7169
|
const scryptKey = (0, import_sdk12.deriveScryptKey)(answers.password, new Uint8Array(scryptSalt));
|
|
6879
7170
|
const challenge = await api.issueChallenge("register");
|
|
6880
7171
|
const challengeBytes = base64UrlNoPadDecode(challenge.challengeB64);
|
|
@@ -6890,14 +7181,11 @@ async function runRegister(opts) {
|
|
|
6890
7181
|
});
|
|
6891
7182
|
const settlementPublicKeyB58 = (0, import_sdk12.base58btcEncode)(keys.settlementPublicKey);
|
|
6892
7183
|
const scryptSaltId = (0, import_sdk12.uuidV4)();
|
|
6893
|
-
const ownerId = (0, import_sdk12.uuidV4)();
|
|
6894
7184
|
const payload = {
|
|
6895
7185
|
purpose: "ARP-KEY-LINK-v1",
|
|
6896
7186
|
agent_did: did,
|
|
6897
7187
|
identity_public_key: identityPublicKeyB58,
|
|
6898
7188
|
settlement_public_key: settlementPublicKeyB58,
|
|
6899
|
-
key_mode: "separated_soft",
|
|
6900
|
-
owner_id: ownerId,
|
|
6901
7189
|
owner_signing_method: "scrypt_password_proof",
|
|
6902
7190
|
link_method: "manual",
|
|
6903
7191
|
created_at: (0, import_sdk12.rfc3339)(),
|
|
@@ -6910,7 +7198,6 @@ async function runRegister(opts) {
|
|
|
6910
7198
|
// accountId: answers.accountId,
|
|
6911
7199
|
identityPublicKey: identityPublicKeyB58,
|
|
6912
7200
|
settlementPublicKey: settlementPublicKeyB58,
|
|
6913
|
-
keyMode: "separated_soft",
|
|
6914
7201
|
ownerAttestation: {
|
|
6915
7202
|
payload: attestation.payload,
|
|
6916
7203
|
sig: attestation.sig,
|
|
@@ -6922,9 +7209,8 @@ async function runRegister(opts) {
|
|
|
6922
7209
|
description: answers.description ? answers.description : void 0,
|
|
6923
7210
|
tags: answers.tags.length > 0 ? answers.tags : void 0
|
|
6924
7211
|
};
|
|
6925
|
-
const
|
|
6926
|
-
|
|
6927
|
-
did: result.did,
|
|
7212
|
+
const durableState = {
|
|
7213
|
+
did,
|
|
6928
7214
|
identityPublicKeyB58,
|
|
6929
7215
|
identitySecretKeyB64: Buffer.from(keys.identitySecretKey).toString("base64"),
|
|
6930
7216
|
settlementPublicKeyB58,
|
|
@@ -6932,60 +7218,45 @@ async function runRegister(opts) {
|
|
|
6932
7218
|
scryptKeyB64: Buffer.from(scryptKey).toString("base64"),
|
|
6933
7219
|
scryptSaltB64: Buffer.from(scryptSalt).toString("base64"),
|
|
6934
7220
|
scryptSaltId,
|
|
6935
|
-
|
|
6936
|
-
//
|
|
6937
|
-
|
|
6938
|
-
// at the moment of registration.
|
|
6939
|
-
currentAttestationId: result.didDocument.metadata.owner_attestation_id,
|
|
7221
|
+
// Placeholder until the server confirms registration and returns
|
|
7222
|
+
// the canonical attestation id (finalized in Step 8 below).
|
|
7223
|
+
currentAttestationId: "",
|
|
6940
7224
|
// V1-alpha: accountId parked — local state file no longer
|
|
6941
7225
|
// records the field. Existing state files written when the
|
|
6942
7226
|
// field was required will still carry the value; readers
|
|
6943
7227
|
// tolerate either shape.
|
|
6944
7228
|
// accountId: answers.accountId,
|
|
6945
|
-
keyMode: "separated_soft",
|
|
6946
7229
|
name: answers.name,
|
|
6947
7230
|
description: answers.description || void 0,
|
|
6948
7231
|
tags: answers.tags.length > 0 ? answers.tags : void 0,
|
|
6949
7232
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7233
|
+
};
|
|
7234
|
+
deps.save(opts.server, durableState);
|
|
7235
|
+
const result = await api.register(body);
|
|
7236
|
+
if (result.did !== did) {
|
|
7237
|
+
throw new Error(
|
|
7238
|
+
`register: server returned DID ${result.did} but the locally-derived DID is ${did}. These must be identical (both base58btc of the identity public key). Refusing to finalize \u2014 the secret keys are already saved locally under ${did}.`
|
|
7239
|
+
);
|
|
7240
|
+
}
|
|
7241
|
+
deps.save(opts.server, {
|
|
7242
|
+
...durableState,
|
|
7243
|
+
currentAttestationId: result.didDocument.metadata.owner_attestation_id
|
|
6950
7244
|
});
|
|
6951
7245
|
try {
|
|
6952
7246
|
recordHome(arpHomeDir());
|
|
6953
7247
|
} catch (registryErr) {
|
|
6954
|
-
if (!opts.json) console.log(
|
|
7248
|
+
if (!opts.json) console.log(import_chalk20.default.dim(`(homes registry write failed: ${registryErr.message})`));
|
|
6955
7249
|
}
|
|
6956
7250
|
if (!opts.json) {
|
|
6957
|
-
console.log(
|
|
6958
|
-
console.log(`${
|
|
6959
|
-
console.log(`${
|
|
6960
|
-
console.log(
|
|
7251
|
+
console.log(import_chalk20.default.green("\nRegistered."));
|
|
7252
|
+
console.log(`${import_chalk20.default.bold("DID")}: ${import_chalk20.default.cyan(result.did)}`);
|
|
7253
|
+
console.log(`${import_chalk20.default.bold("Settlement pubkey")} ${import_chalk20.default.dim("(fund with SOL)")}: ${import_chalk20.default.cyan(settlementPublicKeyB58)}`);
|
|
7254
|
+
console.log(import_chalk20.default.bold("DID document:"));
|
|
6961
7255
|
console.log(formatJson(result.didDocument));
|
|
6962
7256
|
}
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
if (
|
|
6966
|
-
try {
|
|
6967
|
-
const signer = makeSignerFromSecret(keys.identitySecretKey, result.did);
|
|
6968
|
-
await api.publishAgent(result.did, signer);
|
|
6969
|
-
publicationOutcome = "published";
|
|
6970
|
-
if (!opts.json) console.log(import_chalk21.default.green(`
|
|
6971
|
-
\u2713 Published \u2014 discoverable now via \`heyarp agents\`.`));
|
|
6972
|
-
} catch (err) {
|
|
6973
|
-
publicationOutcome = `failed: ${err.message}`;
|
|
6974
|
-
if (!opts.json)
|
|
6975
|
-
console.log(
|
|
6976
|
-
import_chalk21.default.yellow(`
|
|
6977
|
-
\u26A0 Auto-publish failed (${err.message}). Agent saved as DRAFT \u2014 run \`heyarp publish ${result.did}\` to make it discoverable.`)
|
|
6978
|
-
);
|
|
6979
|
-
}
|
|
6980
|
-
} else {
|
|
6981
|
-
publicationOutcome = "draft";
|
|
6982
|
-
if (!opts.json) {
|
|
6983
|
-
console.log(import_chalk21.default.dim(`
|
|
6984
|
-
Publication mode: DRAFT. Agent is registered but NOT discoverable via \`heyarp agents\`.`));
|
|
6985
|
-
console.log(import_chalk21.default.dim(`Run \`heyarp publish ${result.did}\` when ready to appear in the catalog.`));
|
|
6986
|
-
}
|
|
6987
|
-
}
|
|
6988
|
-
if (!opts.json) console.log(import_chalk21.default.dim(`
|
|
7257
|
+
if (!opts.json) console.log(import_chalk20.default.green(`
|
|
7258
|
+
\u2713 Discoverable now via \`heyarp agents\`.`));
|
|
7259
|
+
if (!opts.json) console.log(import_chalk20.default.dim(`
|
|
6989
7260
|
Local state saved to ${arpHomeDir()}/agents.json (mode 0600).`));
|
|
6990
7261
|
if (opts.json) {
|
|
6991
7262
|
console.log(
|
|
@@ -6997,8 +7268,6 @@ Local state saved to ${arpHomeDir()}/agents.json (mode 0600).`));
|
|
|
6997
7268
|
// V1-alpha: accountId parked — `--json` output no longer
|
|
6998
7269
|
// includes it. Uncomment when launchpad↔ARP join lands.
|
|
6999
7270
|
// accountId: answers.accountId,
|
|
7000
|
-
keyMode: "separated_soft",
|
|
7001
|
-
publication: publicationOutcome,
|
|
7002
7271
|
localStatePath: `${arpHomeDir()}/agents.json`,
|
|
7003
7272
|
didDocument: result.didDocument
|
|
7004
7273
|
},
|
|
@@ -7065,7 +7334,7 @@ async function mergeAnswers(opts) {
|
|
|
7065
7334
|
}
|
|
7066
7335
|
const prompted = promptDefs.length > 0 ? await (0, import_prompts2.default)(promptDefs, {
|
|
7067
7336
|
onCancel: () => {
|
|
7068
|
-
console.log(
|
|
7337
|
+
console.log(import_chalk20.default.yellow("\nAborted."));
|
|
7069
7338
|
process.exit(130);
|
|
7070
7339
|
}
|
|
7071
7340
|
}) : {};
|
|
@@ -7087,16 +7356,16 @@ function warnIfOrphanHomesPresent() {
|
|
|
7087
7356
|
try {
|
|
7088
7357
|
others = listHomes().filter((h) => h.path !== current);
|
|
7089
7358
|
} catch (registryErr) {
|
|
7090
|
-
console.log(
|
|
7359
|
+
console.log(import_chalk20.default.dim(`(homes registry unreadable, skipping orphan-home check: ${registryErr.message})`));
|
|
7091
7360
|
return;
|
|
7092
7361
|
}
|
|
7093
7362
|
if (others.length === 0) return;
|
|
7094
|
-
const list = others.map((h) => ` \u2022 ${
|
|
7095
|
-
console.log(
|
|
7363
|
+
const list = others.map((h) => ` \u2022 ${import_chalk20.default.cyan(h.path)} ${import_chalk20.default.dim(`(last seen ${h.lastSeenAt})`)}`).join("\n");
|
|
7364
|
+
console.log(import_chalk20.default.yellow(`
|
|
7096
7365
|
\u26A0 HEYARP_HOME is unset, but other agent homes are registered on this machine:`));
|
|
7097
7366
|
console.log(list);
|
|
7098
7367
|
console.log(
|
|
7099
|
-
|
|
7368
|
+
import_chalk20.default.dim(
|
|
7100
7369
|
` Registering will create a NEW agent under ${current}.
|
|
7101
7370
|
If you meant to add to an existing home, abort (Ctrl-C) and re-run with:
|
|
7102
7371
|
HEYARP_HOME=<path> heyarp register \u2026
|
|
@@ -7109,11 +7378,11 @@ function warnIfAgentsAlreadyRegistered(serverOverride) {
|
|
|
7109
7378
|
const targetServer = resolveServerUrl(serverOverride);
|
|
7110
7379
|
const existing = listAgents().filter((row) => row.serverUrl === targetServer);
|
|
7111
7380
|
if (existing.length === 0) return;
|
|
7112
|
-
const list = existing.map((row) => ` \u2022 ${
|
|
7113
|
-
console.log(
|
|
7381
|
+
const list = existing.map((row) => ` \u2022 ${import_chalk20.default.cyan(row.agent.did)}${row.agent.name ? import_chalk20.default.dim(` (${row.agent.name})`) : ""}`).join("\n");
|
|
7382
|
+
console.log(import_chalk20.default.yellow("\n\u26A0 ~/.arp/agents.json already has agent(s) for this server:"));
|
|
7114
7383
|
console.log(list);
|
|
7115
7384
|
console.log(
|
|
7116
|
-
|
|
7385
|
+
import_chalk20.default.dim(
|
|
7117
7386
|
" After this register completes, you will have multiple local DIDs sharing one state file.\n To keep their state isolated, run with HEYARP_HOME pointing at a per-agent dir, e.g.\n HEYARP_HOME=/tmp/agent-alice heyarp register \u2026\n Otherwise, pass --from-did <did> explicitly on every signed command.\n"
|
|
7118
7387
|
)
|
|
7119
7388
|
);
|
|
@@ -7133,12 +7402,12 @@ function freshKeys() {
|
|
|
7133
7402
|
};
|
|
7134
7403
|
}
|
|
7135
7404
|
function loadKeysFromFile(path) {
|
|
7136
|
-
if (!(0,
|
|
7405
|
+
if (!(0, import_node_fs8.existsSync)(path)) {
|
|
7137
7406
|
throw new Error(`--from-keys: file not found at ${path}`);
|
|
7138
7407
|
}
|
|
7139
7408
|
let parsed;
|
|
7140
7409
|
try {
|
|
7141
|
-
parsed = JSON.parse((0,
|
|
7410
|
+
parsed = JSON.parse((0, import_node_fs8.readFileSync)(path, "utf8"));
|
|
7142
7411
|
} catch (err) {
|
|
7143
7412
|
throw new Error(`--from-keys: ${path} is not valid JSON: ${err.message}`);
|
|
7144
7413
|
}
|
|
@@ -7166,20 +7435,9 @@ function assertJsonRequiresYes(opts) {
|
|
|
7166
7435
|
throw new Error("register --json REQUIRES --yes (interactive prompts would corrupt the single-doc JSON output)");
|
|
7167
7436
|
}
|
|
7168
7437
|
}
|
|
7169
|
-
function parsePublicationMode(raw) {
|
|
7170
|
-
if (raw === "active" || raw === "draft") return raw;
|
|
7171
|
-
throw new Error(`register: --publication must be 'active' or 'draft' (got '${raw}')`);
|
|
7172
|
-
}
|
|
7173
|
-
function decideAutoPublish(input) {
|
|
7174
|
-
if (input.publication !== "active") return "skip-draft";
|
|
7175
|
-
return "try";
|
|
7176
|
-
}
|
|
7177
|
-
function makeSignerFromSecret(identitySecretKey, did) {
|
|
7178
|
-
return { did, identitySecretKey };
|
|
7179
|
-
}
|
|
7180
7438
|
|
|
7181
7439
|
// src/commands/relationships.ts
|
|
7182
|
-
var
|
|
7440
|
+
var import_chalk21 = __toESM(require("chalk"));
|
|
7183
7441
|
init_api();
|
|
7184
7442
|
init_format();
|
|
7185
7443
|
init_state();
|
|
@@ -7202,25 +7460,25 @@ async function runRelationships(positionalDid, opts) {
|
|
|
7202
7460
|
const local = explicitDid !== void 0 ? loadAgentOrThrow(opts.server, explicitDid) : resolveSenderAgent("relationships", opts.server, void 0);
|
|
7203
7461
|
const did = local.did;
|
|
7204
7462
|
const api = new ArpApiClient(opts.server);
|
|
7205
|
-
console.log(
|
|
7206
|
-
console.log(
|
|
7463
|
+
console.log(import_chalk21.default.dim(`Server: ${api.serverUrl}`));
|
|
7464
|
+
console.log(import_chalk21.default.dim(`Signer: ${local.did}`));
|
|
7207
7465
|
const query = { limit };
|
|
7208
7466
|
if (state) query.state = state;
|
|
7209
7467
|
const signer = makeSigner(local);
|
|
7210
7468
|
const rows = await api.listRelationships(did, signer, query);
|
|
7211
7469
|
if (rows.length === 0) {
|
|
7212
|
-
console.log(
|
|
7470
|
+
console.log(import_chalk21.default.dim("\n(no relationships)"));
|
|
7213
7471
|
return;
|
|
7214
7472
|
}
|
|
7215
7473
|
console.log("");
|
|
7216
7474
|
console.log(formatRelationshipsTable(rows, did));
|
|
7217
7475
|
if (opts.verbose) {
|
|
7218
|
-
console.log(
|
|
7476
|
+
console.log(import_chalk21.default.bold("\nFull relationships:"));
|
|
7219
7477
|
for (const r of rows) {
|
|
7220
7478
|
console.log(formatJson(r));
|
|
7221
7479
|
}
|
|
7222
7480
|
}
|
|
7223
|
-
console.log(
|
|
7481
|
+
console.log(import_chalk21.default.dim(`
|
|
7224
7482
|
${rows.length} relationship(s).`));
|
|
7225
7483
|
}
|
|
7226
7484
|
function formatRelationshipsTable(rows, selfDid) {
|
|
@@ -7228,7 +7486,7 @@ function formatRelationshipsTable(rows, selfDid) {
|
|
|
7228
7486
|
const data = rows.map((r) => [r.relationshipId, otherPair(r, selfDid), r.state, r.lastEventAt ?? "(none)", String(r.lastEventIndex)]);
|
|
7229
7487
|
const widths = header.map((h, i) => Math.max(h.length, ...data.map((row) => row[i].length)));
|
|
7230
7488
|
const pad = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ");
|
|
7231
|
-
return [
|
|
7489
|
+
return [import_chalk21.default.bold(pad(header)), import_chalk21.default.dim(pad(widths.map((w) => "-".repeat(w)))), ...data.map((row) => pad(row))].join("\n");
|
|
7232
7490
|
}
|
|
7233
7491
|
function otherPair(r, selfDid) {
|
|
7234
7492
|
if (r.pairDidA === selfDid) return r.pairDidB;
|
|
@@ -7251,141 +7509,9 @@ function parseLimit6(raw) {
|
|
|
7251
7509
|
return n;
|
|
7252
7510
|
}
|
|
7253
7511
|
|
|
7254
|
-
// src/commands/rotate.ts
|
|
7255
|
-
var import_sdk13 = require("@heyanon-arp/sdk");
|
|
7256
|
-
var import_chalk23 = __toESM(require("chalk"));
|
|
7257
|
-
var import_prompts3 = __toESM(require("prompts"));
|
|
7258
|
-
init_api();
|
|
7259
|
-
init_format();
|
|
7260
|
-
init_state();
|
|
7261
|
-
init_lifecycle();
|
|
7262
|
-
var ROTATION_REASONS = ["scheduled", "compromise", "lost_device", "other"];
|
|
7263
|
-
function registerRotateCommand(root) {
|
|
7264
|
-
root.command("rotate").description("Rotate the agent identity key (DID stays frozen). Requires local state with ownerId + currentAttestationId.").argument("<did>").option("--server <url>", "Override ARP server base URL").option("--yes", "Skip the destructive-action confirmation prompt", false).option("--reason <s>", `Rotation reason \u2014 one of ${ROTATION_REASONS.join(" | ")}`, "scheduled").action(async (did, opts) => {
|
|
7265
|
-
if (opts.reason && !ROTATION_REASONS.includes(opts.reason)) {
|
|
7266
|
-
throw new Error(`rotate: --reason must be one of ${ROTATION_REASONS.join(", ")} (got '${opts.reason}')`);
|
|
7267
|
-
}
|
|
7268
|
-
await runRotate(did, opts);
|
|
7269
|
-
});
|
|
7270
|
-
}
|
|
7271
|
-
async function runRotate(did, opts) {
|
|
7272
|
-
const local = loadAgentOrThrow(opts.server, did);
|
|
7273
|
-
if (!local.ownerId || !local.currentAttestationId) {
|
|
7274
|
-
throw new Error("rotate: local state is missing ownerId / currentAttestationId. State predates the rotation flow \u2014 please re-register.");
|
|
7275
|
-
}
|
|
7276
|
-
const api = new ArpApiClient(opts.server);
|
|
7277
|
-
console.log(import_chalk23.default.dim(`Server: ${api.serverUrl}`));
|
|
7278
|
-
console.log(import_chalk23.default.dim(`Rotating: ${local.did}`));
|
|
7279
|
-
if (!opts.yes) {
|
|
7280
|
-
const confirm = await (0, import_prompts3.default)({
|
|
7281
|
-
type: "confirm",
|
|
7282
|
-
name: "go",
|
|
7283
|
-
message: "Rotation invalidates the CURRENT private key the moment the server commits. Continue?",
|
|
7284
|
-
initial: false
|
|
7285
|
-
});
|
|
7286
|
-
if (!confirm.go) {
|
|
7287
|
-
console.log(import_chalk23.default.yellow("Aborted."));
|
|
7288
|
-
return;
|
|
7289
|
-
}
|
|
7290
|
-
}
|
|
7291
|
-
const next = (0, import_sdk13.generateKeyPair)();
|
|
7292
|
-
const newIdentityPublicKeyB58 = (0, import_sdk13.base58btcEncode)(next.publicKey);
|
|
7293
|
-
const challenge = await api.issueChallenge("rotate");
|
|
7294
|
-
const challengeBytes = base64UrlNoPadDecode2(challenge.challengeB64);
|
|
7295
|
-
if (challengeBytes.length !== 32) {
|
|
7296
|
-
throw new Error(`Server returned a ${challengeBytes.length}-byte challenge; expected 32`);
|
|
7297
|
-
}
|
|
7298
|
-
const challengeSig = (0, import_sdk13.signChallenge)(challengeBytes, next.secretKey);
|
|
7299
|
-
await api.submitChallengeResponse({
|
|
7300
|
-
challengeId: challenge.challengeId,
|
|
7301
|
-
identityPublicKey: newIdentityPublicKeyB58,
|
|
7302
|
-
signature: Buffer.from(challengeSig).toString("base64")
|
|
7303
|
-
});
|
|
7304
|
-
const scryptKey = new Uint8Array(Buffer.from(local.scryptKeyB64, "base64"));
|
|
7305
|
-
if (scryptKey.length !== 32) {
|
|
7306
|
-
throw new Error("rotate: stored scryptKeyB64 is not 32 bytes \u2014 local state is corrupted, re-register.");
|
|
7307
|
-
}
|
|
7308
|
-
const reason = opts.reason ?? "scheduled";
|
|
7309
|
-
const payload = {
|
|
7310
|
-
purpose: "ARP-KEY-ROTATION-v1",
|
|
7311
|
-
agent_did: did,
|
|
7312
|
-
current_identity_public_key: local.identityPublicKeyB58,
|
|
7313
|
-
new_identity_public_key: newIdentityPublicKeyB58,
|
|
7314
|
-
settlement_public_key: local.settlementPublicKeyB58,
|
|
7315
|
-
supersedes_attestation_id: local.currentAttestationId,
|
|
7316
|
-
owner_id: local.ownerId,
|
|
7317
|
-
owner_signing_method: "scrypt_password_proof",
|
|
7318
|
-
rotation_reason: reason,
|
|
7319
|
-
created_at: (0, import_sdk13.rfc3339)(),
|
|
7320
|
-
nonce: (0, import_sdk13.senderNonce)()
|
|
7321
|
-
};
|
|
7322
|
-
const attestation = (0, import_sdk13.signKeyRotationAttestation)({
|
|
7323
|
-
payload,
|
|
7324
|
-
scryptKey,
|
|
7325
|
-
scryptSaltId: local.scryptSaltId
|
|
7326
|
-
});
|
|
7327
|
-
const newIdentitySecretKeyB64 = Buffer.from(next.secretKey).toString("base64");
|
|
7328
|
-
updateAgentLocal(opts.server, did, {
|
|
7329
|
-
pendingRotation: {
|
|
7330
|
-
newIdentityPublicKeyB58,
|
|
7331
|
-
newIdentitySecretKeyB64,
|
|
7332
|
-
issuedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7333
|
-
}
|
|
7334
|
-
});
|
|
7335
|
-
const body = {
|
|
7336
|
-
challengeId: challenge.challengeId,
|
|
7337
|
-
newIdentityPublicKey: newIdentityPublicKeyB58,
|
|
7338
|
-
newKeyAttestation: {
|
|
7339
|
-
payload: attestation.payload,
|
|
7340
|
-
sig: attestation.sig,
|
|
7341
|
-
scrypt_salt_id: attestation.scrypt_salt_id
|
|
7342
|
-
}
|
|
7343
|
-
};
|
|
7344
|
-
const signer = makeSigner(local);
|
|
7345
|
-
let updated;
|
|
7346
|
-
try {
|
|
7347
|
-
updated = await api.rotateIdentityKey(did, body, signer);
|
|
7348
|
-
} catch (err) {
|
|
7349
|
-
try {
|
|
7350
|
-
updateAgentLocal(opts.server, did, { pendingRotation: void 0 });
|
|
7351
|
-
} catch {
|
|
7352
|
-
}
|
|
7353
|
-
throw err;
|
|
7354
|
-
}
|
|
7355
|
-
try {
|
|
7356
|
-
updateAgentLocal(opts.server, did, {
|
|
7357
|
-
identityPublicKeyB58: newIdentityPublicKeyB58,
|
|
7358
|
-
identitySecretKeyB64: newIdentitySecretKeyB64,
|
|
7359
|
-
currentAttestationId: updated.currentAttestationId,
|
|
7360
|
-
pendingRotation: void 0
|
|
7361
|
-
});
|
|
7362
|
-
} catch (err) {
|
|
7363
|
-
console.error(import_chalk23.default.red("\nServer rotation succeeded but local state write failed."));
|
|
7364
|
-
console.error(import_chalk23.default.red("Capture these values now \u2014 the new key is already live server-side:"));
|
|
7365
|
-
console.error(` ${import_chalk23.default.bold("identityPublicKeyB58")} : ${newIdentityPublicKeyB58}`);
|
|
7366
|
-
console.error(` ${import_chalk23.default.bold("identitySecretKeyB64")} : ${newIdentitySecretKeyB64}`);
|
|
7367
|
-
console.error(` ${import_chalk23.default.bold("currentAttestationId")} : ${updated.currentAttestationId}`);
|
|
7368
|
-
console.error(import_chalk23.default.dim(`(Also persisted in pendingRotation at ~/.arp/agents.json before the server call.)`));
|
|
7369
|
-
console.error(import_chalk23.default.dim(`Underlying error: ${err.message}`));
|
|
7370
|
-
throw err;
|
|
7371
|
-
}
|
|
7372
|
-
console.log(import_chalk23.default.green("\nRotated."));
|
|
7373
|
-
console.log(`${import_chalk23.default.bold("DID")}: ${import_chalk23.default.cyan(updated.did)} ${import_chalk23.default.dim("(unchanged)")}`);
|
|
7374
|
-
console.log(`${import_chalk23.default.bold("New identity public key")}: ${import_chalk23.default.cyan(newIdentityPublicKeyB58)}`);
|
|
7375
|
-
console.log(`${import_chalk23.default.bold("New attestation id")}: ${import_chalk23.default.cyan(updated.currentAttestationId)}`);
|
|
7376
|
-
console.log(import_chalk23.default.bold("\nAgent profile:"));
|
|
7377
|
-
console.log(formatJson(updated));
|
|
7378
|
-
console.log(import_chalk23.default.dim("\nLocal state updated; old private key is no longer valid."));
|
|
7379
|
-
}
|
|
7380
|
-
function base64UrlNoPadDecode2(s) {
|
|
7381
|
-
const replaced = s.replace(/-/g, "+").replace(/_/g, "/");
|
|
7382
|
-
const padded = replaced + "==".slice(0, (4 - replaced.length % 4) % 4);
|
|
7383
|
-
return new Uint8Array(Buffer.from(padded, "base64"));
|
|
7384
|
-
}
|
|
7385
|
-
|
|
7386
7512
|
// src/commands/send-handshake.ts
|
|
7387
|
-
var
|
|
7388
|
-
var
|
|
7513
|
+
var import_sdk13 = require("@heyanon-arp/sdk");
|
|
7514
|
+
var import_chalk22 = __toESM(require("chalk"));
|
|
7389
7515
|
init_api();
|
|
7390
7516
|
init_format();
|
|
7391
7517
|
init_state();
|
|
@@ -7401,10 +7527,10 @@ async function runSendHandshake(recipientDid, opts) {
|
|
|
7401
7527
|
}
|
|
7402
7528
|
const ttlSeconds = parseTtl3(opts.ttl);
|
|
7403
7529
|
const api = new ArpApiClient(opts.server);
|
|
7404
|
-
console.log(
|
|
7530
|
+
console.log(import_chalk22.default.dim(`Server: ${api.serverUrl}`));
|
|
7405
7531
|
const sender = resolveSenderAgent("send-handshake", opts.server, opts.fromDid);
|
|
7406
|
-
console.log(
|
|
7407
|
-
console.log(
|
|
7532
|
+
console.log(import_chalk22.default.dim(`Sender: ${sender.did}`));
|
|
7533
|
+
console.log(import_chalk22.default.dim(`Recipient: ${recipientDid}`));
|
|
7408
7534
|
const content = {};
|
|
7409
7535
|
if (opts.greeting) content.greeting = opts.greeting;
|
|
7410
7536
|
if (opts.intent) content.intent = opts.intent;
|
|
@@ -7412,46 +7538,46 @@ async function runSendHandshake(recipientDid, opts) {
|
|
|
7412
7538
|
const nextSequence = (sender.lastSenderSequence ?? 0) + 1;
|
|
7413
7539
|
const protectedBlock = {
|
|
7414
7540
|
protocol_version: "arp/0.1",
|
|
7415
|
-
purpose:
|
|
7416
|
-
message_id: (0,
|
|
7541
|
+
purpose: import_sdk13.Purpose.ENVELOPE,
|
|
7542
|
+
message_id: (0, import_sdk13.uuidV4)(),
|
|
7417
7543
|
sender_did: sender.did,
|
|
7418
7544
|
recipient_did: recipientDid,
|
|
7419
7545
|
relationship_id: null,
|
|
7420
7546
|
sender_sequence: nextSequence,
|
|
7421
|
-
sender_nonce: (0,
|
|
7422
|
-
timestamp: (0,
|
|
7423
|
-
expires_at: (0,
|
|
7547
|
+
sender_nonce: (0, import_sdk13.senderNonce)(),
|
|
7548
|
+
timestamp: (0, import_sdk13.rfc3339)(),
|
|
7549
|
+
expires_at: (0, import_sdk13.expiresAt)(ttlSeconds),
|
|
7424
7550
|
delivery_id: null
|
|
7425
7551
|
};
|
|
7426
7552
|
const signer = makeSigner(sender);
|
|
7427
|
-
const envelope = (0,
|
|
7553
|
+
const envelope = (0, import_sdk13.signEnvelope)({
|
|
7428
7554
|
protected: protectedBlock,
|
|
7429
7555
|
body,
|
|
7430
7556
|
identitySecretKey: signer.identitySecretKey
|
|
7431
7557
|
});
|
|
7432
7558
|
if (opts.verbose) {
|
|
7433
|
-
console.log(
|
|
7559
|
+
console.log(import_chalk22.default.bold("\nEnvelope (pre-send):"));
|
|
7434
7560
|
console.log(formatJson(envelope));
|
|
7435
7561
|
}
|
|
7436
7562
|
const result = await api.ingest(envelope);
|
|
7437
7563
|
updateAgentLocal(opts.server, sender.did, { lastSenderSequence: nextSequence });
|
|
7438
|
-
console.log(
|
|
7439
|
-
console.log(`${
|
|
7440
|
-
console.log(`${
|
|
7441
|
-
console.log(`${
|
|
7442
|
-
console.log(`${
|
|
7443
|
-
console.log(`${
|
|
7444
|
-
console.log(`${
|
|
7564
|
+
console.log(import_chalk22.default.green("\nDelivered."));
|
|
7565
|
+
console.log(`${import_chalk22.default.bold("Event id")}: ${import_chalk22.default.cyan(result.eventId)}`);
|
|
7566
|
+
console.log(`${import_chalk22.default.bold("Relationship id")}: ${import_chalk22.default.cyan(result.relationshipId)}`);
|
|
7567
|
+
console.log(`${import_chalk22.default.bold("Chain index")}: ${import_chalk22.default.cyan(String(result.relationshipEventIndex))}`);
|
|
7568
|
+
console.log(`${import_chalk22.default.bold("Server timestamp")}: ${import_chalk22.default.cyan(result.serverTimestamp)}`);
|
|
7569
|
+
console.log(`${import_chalk22.default.bold("Signed message hash")}: ${import_chalk22.default.cyan(result.signedMessageHash)}`);
|
|
7570
|
+
console.log(`${import_chalk22.default.bold("Server event hash")}: ${import_chalk22.default.cyan(result.serverEventHash)}`);
|
|
7445
7571
|
if (result.prevServerEventHash) {
|
|
7446
|
-
console.log(`${
|
|
7572
|
+
console.log(`${import_chalk22.default.bold("Prev server event hash")}: ${import_chalk22.default.cyan(result.prevServerEventHash)}`);
|
|
7447
7573
|
} else {
|
|
7448
|
-
console.log(`${
|
|
7574
|
+
console.log(`${import_chalk22.default.bold("Prev server event hash")}: ${import_chalk22.default.dim("(null \u2014 first event of this relationship)")}`);
|
|
7449
7575
|
}
|
|
7450
7576
|
if (opts.verbose) {
|
|
7451
|
-
console.log(
|
|
7577
|
+
console.log(import_chalk22.default.bold("\nFull server response:"));
|
|
7452
7578
|
console.log(formatJson(result));
|
|
7453
7579
|
}
|
|
7454
|
-
console.log(
|
|
7580
|
+
console.log(import_chalk22.default.dim(`
|
|
7455
7581
|
Local sender_sequence advanced to ${nextSequence}.`));
|
|
7456
7582
|
}
|
|
7457
7583
|
function parseTtl3(raw) {
|
|
@@ -7467,8 +7593,8 @@ function isDid(s) {
|
|
|
7467
7593
|
}
|
|
7468
7594
|
|
|
7469
7595
|
// src/commands/send-handshake-response.ts
|
|
7470
|
-
var
|
|
7471
|
-
var
|
|
7596
|
+
var import_sdk14 = require("@heyanon-arp/sdk");
|
|
7597
|
+
var import_chalk23 = __toESM(require("chalk"));
|
|
7472
7598
|
init_api();
|
|
7473
7599
|
init_format();
|
|
7474
7600
|
init_state();
|
|
@@ -7482,7 +7608,7 @@ function registerSendHandshakeResponseCommand(root) {
|
|
|
7482
7608
|
// We don't use commander's requiredOption because it
|
|
7483
7609
|
// would fire for the accept path too; validate manually
|
|
7484
7610
|
// after decision is parsed.
|
|
7485
|
-
`When --decision=decline: required reason code (one of: ${
|
|
7611
|
+
`When --decision=decline: required reason code (one of: ${import_sdk14.DECLINE_REASONS.join(", ")}). Carried in body.content.reason.`
|
|
7486
7612
|
).option("--reason-detail <s>", "Optional free-text elaboration alongside --reason (max 512 chars).").option("--ttl <seconds>", "Envelope TTL in seconds (max 86400 = 24h)", "3600").option("--verbose", "Print the full envelope before sending and the full server response. Mutually exclusive with --json.", false).option(
|
|
7487
7613
|
"--json",
|
|
7488
7614
|
"Machine-readable mode \u2014 emit a single JSON object on stdout ({ok, decision, eventId, relationshipId, relationshipEventIndex, serverTimestamp, serverEventHash, prevServerEventHash}; idempotent short-circuit adds {idempotent:true}). Prelude + banners move off stdout; on failure stderr carries `{code, message}`. Mutually exclusive with --verbose.",
|
|
@@ -7513,11 +7639,11 @@ async function runSendHandshakeResponse(recipientDid, opts) {
|
|
|
7513
7639
|
declinePayload = detail ? { reason, reasonDetail: detail } : { reason };
|
|
7514
7640
|
}
|
|
7515
7641
|
const api = new ArpApiClient(opts.server);
|
|
7516
|
-
progress(opts.json,
|
|
7642
|
+
progress(opts.json, import_chalk23.default.dim(`Server: ${api.serverUrl}`));
|
|
7517
7643
|
const sender = resolveSenderAgent("send-handshake-response", opts.server, opts.fromDid);
|
|
7518
|
-
progress(opts.json,
|
|
7519
|
-
progress(opts.json,
|
|
7520
|
-
progress(opts.json,
|
|
7644
|
+
progress(opts.json, import_chalk23.default.dim(`Sender: ${sender.did}`));
|
|
7645
|
+
progress(opts.json, import_chalk23.default.dim(`Recipient: ${recipientDid}`));
|
|
7646
|
+
progress(opts.json, import_chalk23.default.dim(`Decision: ${decision}`));
|
|
7521
7647
|
const signer = makeSigner(sender);
|
|
7522
7648
|
if (!opts.force) {
|
|
7523
7649
|
try {
|
|
@@ -7546,13 +7672,13 @@ async function runSendHandshakeResponse(recipientDid, opts) {
|
|
|
7546
7672
|
});
|
|
7547
7673
|
return;
|
|
7548
7674
|
}
|
|
7549
|
-
progress(opts.json,
|
|
7675
|
+
progress(opts.json, import_chalk23.default.yellow(`
|
|
7550
7676
|
[--idempotency] Relationship ${existing.relationshipId} with ${recipientDid} is already 'active'.`));
|
|
7551
7677
|
progress(
|
|
7552
7678
|
opts.json,
|
|
7553
|
-
|
|
7679
|
+
import_chalk23.default.dim(`A previous accept from this signer (event ${previousResponseFromMe.eventId}) landed successfully. Skipping re-send (use --force to override).`)
|
|
7554
7680
|
);
|
|
7555
|
-
progress(opts.json,
|
|
7681
|
+
progress(opts.json, import_chalk23.default.dim(`Last event index: ${existing.lastEventIndex}, last server event hash: ${existing.lastServerEventHash ?? "(none)"}`));
|
|
7556
7682
|
return;
|
|
7557
7683
|
}
|
|
7558
7684
|
throw new Error(
|
|
@@ -7563,7 +7689,7 @@ async function runSendHandshakeResponse(recipientDid, opts) {
|
|
|
7563
7689
|
if (probeErr instanceof Error && /CLOSED|terminated|already 'active' from a previous ACCEPT|original initiator/i.test(probeErr.message)) {
|
|
7564
7690
|
throw probeErr;
|
|
7565
7691
|
}
|
|
7566
|
-
progress(opts.json,
|
|
7692
|
+
progress(opts.json, import_chalk23.default.dim(`(idempotency probe failed; proceeding anyway: ${probeErr.message})`));
|
|
7567
7693
|
}
|
|
7568
7694
|
}
|
|
7569
7695
|
const content = { decision };
|
|
@@ -7576,24 +7702,24 @@ async function runSendHandshakeResponse(recipientDid, opts) {
|
|
|
7576
7702
|
const nextSequence = (sender.lastSenderSequence ?? 0) + 1;
|
|
7577
7703
|
const protectedBlock = {
|
|
7578
7704
|
protocol_version: "arp/0.1",
|
|
7579
|
-
purpose:
|
|
7580
|
-
message_id: (0,
|
|
7705
|
+
purpose: import_sdk14.Purpose.ENVELOPE,
|
|
7706
|
+
message_id: (0, import_sdk14.uuidV4)(),
|
|
7581
7707
|
sender_did: sender.did,
|
|
7582
7708
|
recipient_did: recipientDid,
|
|
7583
7709
|
relationship_id: null,
|
|
7584
7710
|
sender_sequence: nextSequence,
|
|
7585
|
-
sender_nonce: (0,
|
|
7586
|
-
timestamp: (0,
|
|
7587
|
-
expires_at: (0,
|
|
7711
|
+
sender_nonce: (0, import_sdk14.senderNonce)(),
|
|
7712
|
+
timestamp: (0, import_sdk14.rfc3339)(),
|
|
7713
|
+
expires_at: (0, import_sdk14.expiresAt)(ttlSeconds),
|
|
7588
7714
|
delivery_id: null
|
|
7589
7715
|
};
|
|
7590
|
-
const envelope = (0,
|
|
7716
|
+
const envelope = (0, import_sdk14.signEnvelope)({
|
|
7591
7717
|
protected: protectedBlock,
|
|
7592
7718
|
body,
|
|
7593
7719
|
identitySecretKey: signer.identitySecretKey
|
|
7594
7720
|
});
|
|
7595
7721
|
if (opts.verbose) {
|
|
7596
|
-
console.log(
|
|
7722
|
+
console.log(import_chalk23.default.bold("\nEnvelope (pre-send):"));
|
|
7597
7723
|
console.log(formatJson(envelope));
|
|
7598
7724
|
}
|
|
7599
7725
|
const result = await api.ingest(envelope);
|
|
@@ -7613,23 +7739,23 @@ async function runSendHandshakeResponse(recipientDid, opts) {
|
|
|
7613
7739
|
});
|
|
7614
7740
|
return;
|
|
7615
7741
|
}
|
|
7616
|
-
console.log(
|
|
7617
|
-
console.log(`${
|
|
7618
|
-
console.log(`${
|
|
7619
|
-
console.log(`${
|
|
7620
|
-
console.log(`${
|
|
7621
|
-
console.log(`${
|
|
7622
|
-
console.log(`${
|
|
7742
|
+
console.log(import_chalk23.default.green("\nDelivered."));
|
|
7743
|
+
console.log(`${import_chalk23.default.bold("Event id")}: ${import_chalk23.default.cyan(result.eventId)}`);
|
|
7744
|
+
console.log(`${import_chalk23.default.bold("Relationship id")}: ${import_chalk23.default.cyan(result.relationshipId)}`);
|
|
7745
|
+
console.log(`${import_chalk23.default.bold("Chain index")}: ${import_chalk23.default.cyan(String(result.relationshipEventIndex))}`);
|
|
7746
|
+
console.log(`${import_chalk23.default.bold("Server timestamp")}: ${import_chalk23.default.cyan(result.serverTimestamp)}`);
|
|
7747
|
+
console.log(`${import_chalk23.default.bold("Signed message hash")}: ${import_chalk23.default.cyan(result.signedMessageHash)}`);
|
|
7748
|
+
console.log(`${import_chalk23.default.bold("Server event hash")}: ${import_chalk23.default.cyan(result.serverEventHash)}`);
|
|
7623
7749
|
if (result.prevServerEventHash) {
|
|
7624
|
-
console.log(`${
|
|
7750
|
+
console.log(`${import_chalk23.default.bold("Prev server event hash")}: ${import_chalk23.default.cyan(result.prevServerEventHash)}`);
|
|
7625
7751
|
} else {
|
|
7626
|
-
console.log(`${
|
|
7752
|
+
console.log(`${import_chalk23.default.bold("Prev server event hash")}: ${import_chalk23.default.dim("(null \u2014 first event of this relationship)")}`);
|
|
7627
7753
|
}
|
|
7628
7754
|
if (opts.verbose) {
|
|
7629
|
-
console.log(
|
|
7755
|
+
console.log(import_chalk23.default.bold("\nFull server response:"));
|
|
7630
7756
|
console.log(formatJson(result));
|
|
7631
7757
|
}
|
|
7632
|
-
console.log(
|
|
7758
|
+
console.log(import_chalk23.default.dim(`
|
|
7633
7759
|
Local sender_sequence advanced to ${nextSequence}.`));
|
|
7634
7760
|
}
|
|
7635
7761
|
function parseDecision(raw) {
|
|
@@ -7688,7 +7814,7 @@ init_status();
|
|
|
7688
7814
|
init_wallet();
|
|
7689
7815
|
|
|
7690
7816
|
// src/commands/watch.ts
|
|
7691
|
-
var
|
|
7817
|
+
var import_chalk24 = __toESM(require("chalk"));
|
|
7692
7818
|
init_api();
|
|
7693
7819
|
init_format();
|
|
7694
7820
|
init_state();
|
|
@@ -7702,9 +7828,9 @@ async function runWatch(relationshipId, opts) {
|
|
|
7702
7828
|
const local = resolveSenderAgent("watch", opts.server, opts.fromDid);
|
|
7703
7829
|
const api = new ArpApiClient(opts.server);
|
|
7704
7830
|
if (!opts.json) {
|
|
7705
|
-
console.log(
|
|
7706
|
-
console.log(
|
|
7707
|
-
console.log(
|
|
7831
|
+
console.log(import_chalk24.default.dim(`Server: ${api.serverUrl}`));
|
|
7832
|
+
console.log(import_chalk24.default.dim(`Signer: ${local.did}`));
|
|
7833
|
+
console.log(import_chalk24.default.dim(`Watching: ${relationshipId}`));
|
|
7708
7834
|
}
|
|
7709
7835
|
const controller = new AbortController();
|
|
7710
7836
|
let userAborted = false;
|
|
@@ -7723,7 +7849,7 @@ async function runWatch(relationshipId, opts) {
|
|
|
7723
7849
|
}
|
|
7724
7850
|
if (event.type === "heartbeat") continue;
|
|
7725
7851
|
if (event.type === "connected") {
|
|
7726
|
-
console.log(
|
|
7852
|
+
console.log(import_chalk24.default.green(`\u25CF stream open \u2014 watching ${relationshipId}`));
|
|
7727
7853
|
continue;
|
|
7728
7854
|
}
|
|
7729
7855
|
if (event.type === "envelope") {
|
|
@@ -7737,7 +7863,7 @@ async function runWatch(relationshipId, opts) {
|
|
|
7737
7863
|
}
|
|
7738
7864
|
continue;
|
|
7739
7865
|
}
|
|
7740
|
-
console.log(
|
|
7866
|
+
console.log(import_chalk24.default.dim(`(unknown event: ${event.type})`));
|
|
7741
7867
|
}
|
|
7742
7868
|
if (!userAborted) {
|
|
7743
7869
|
throw new Error(`watch ${relationshipId}: stream ended unexpectedly (server may have restarted, or the change stream errored). Re-run to reconnect.`);
|
|
@@ -7745,7 +7871,7 @@ async function runWatch(relationshipId, opts) {
|
|
|
7745
7871
|
} catch (err) {
|
|
7746
7872
|
const name = err.name;
|
|
7747
7873
|
if (name === "AbortError" || userAborted) {
|
|
7748
|
-
if (!opts.json) console.log(
|
|
7874
|
+
if (!opts.json) console.log(import_chalk24.default.dim("\nstream closed."));
|
|
7749
7875
|
return;
|
|
7750
7876
|
}
|
|
7751
7877
|
throw err;
|
|
@@ -7759,21 +7885,21 @@ function formatWatchLine(ev, selfDid, opts = {}) {
|
|
|
7759
7885
|
const type = ev.type.padEnd(20);
|
|
7760
7886
|
const direction = directionLabel2(ev, selfDid, opts);
|
|
7761
7887
|
const hash = opts.fullIds ? ev.serverEventHash : hashHead4(ev.serverEventHash);
|
|
7762
|
-
return `${
|
|
7888
|
+
return `${import_chalk24.default.dim(`[${ts}]`)} ${type} ${direction} ${import_chalk24.default.cyan(hash)}`;
|
|
7763
7889
|
}
|
|
7764
7890
|
function directionLabel2(ev, selfDid, opts = {}) {
|
|
7765
7891
|
const senderHead = opts.fullIds ? ev.senderDid : didHead4(ev.senderDid);
|
|
7766
7892
|
const recipientHead = opts.fullIds ? ev.recipientDid : didHead4(ev.recipientDid);
|
|
7767
|
-
if (ev.senderDid === selfDid) return `${
|
|
7768
|
-
if (ev.recipientDid === selfDid) return `${
|
|
7769
|
-
return `${
|
|
7893
|
+
if (ev.senderDid === selfDid) return `${import_chalk24.default.bold("me")} \u2192 ${import_chalk24.default.dim(recipientHead)}`;
|
|
7894
|
+
if (ev.recipientDid === selfDid) return `${import_chalk24.default.dim(senderHead)} \u2192 ${import_chalk24.default.bold("me")}`;
|
|
7895
|
+
return `${import_chalk24.default.dim(senderHead)} \u2192 ${import_chalk24.default.dim(recipientHead)}`;
|
|
7770
7896
|
}
|
|
7771
7897
|
function didHead4(did) {
|
|
7772
7898
|
if (did.length <= 20) return did;
|
|
7773
7899
|
return `${did.slice(0, 20)}...`;
|
|
7774
7900
|
}
|
|
7775
7901
|
function hashHead4(hash) {
|
|
7776
|
-
if (!hash) return
|
|
7902
|
+
if (!hash) return import_chalk24.default.dim("(none)");
|
|
7777
7903
|
if (hash.length <= 14) return hash;
|
|
7778
7904
|
return `${hash.slice(0, 14)}...`;
|
|
7779
7905
|
}
|
|
@@ -7785,7 +7911,7 @@ function formatClock(iso) {
|
|
|
7785
7911
|
}
|
|
7786
7912
|
|
|
7787
7913
|
// src/commands/whoami.ts
|
|
7788
|
-
var
|
|
7914
|
+
var import_chalk25 = __toESM(require("chalk"));
|
|
7789
7915
|
init_api();
|
|
7790
7916
|
init_format();
|
|
7791
7917
|
init_state();
|
|
@@ -7798,10 +7924,10 @@ function registerWhoamiCommand(root) {
|
|
|
7798
7924
|
// Persona scripts want the local settlement pubkey for
|
|
7799
7925
|
// `--payer-settlement-pubkey` / on-chain ops without a
|
|
7800
7926
|
// server round-trip. `--local` short-circuits the signed
|
|
7801
|
-
// GET, prints just the resolved DID + pubkeys
|
|
7802
|
-
//
|
|
7803
|
-
//
|
|
7804
|
-
"Print local-state info only (DID + settlement + identity pubkeys
|
|
7927
|
+
// GET, prints just the resolved DID + pubkeys from local
|
|
7928
|
+
// state. (V1-alpha: accountId parked, no longer in --local
|
|
7929
|
+
// output.)
|
|
7930
|
+
"Print local-state info only (DID + settlement + identity pubkeys). Skips the signed server fetch \u2014 useful for scripts that need the local pubkey before the server is up, or to grep from a shell without burning a network round-trip.",
|
|
7805
7931
|
false
|
|
7806
7932
|
).option("--json", "JSON output (jq-pipeable). Combines local + server data when --local is unset, or just local when --local is set.", false).action(async (didArg, opts, cmd) => {
|
|
7807
7933
|
try {
|
|
@@ -7815,18 +7941,16 @@ function registerWhoamiCommand(root) {
|
|
|
7815
7941
|
// longer carries the field. Uncomment when launchpad↔ARP
|
|
7816
7942
|
// join lands.
|
|
7817
7943
|
// accountId: local.accountId,
|
|
7818
|
-
keyMode: local.keyMode,
|
|
7819
7944
|
name: local.name
|
|
7820
7945
|
};
|
|
7821
7946
|
if (opts.local) {
|
|
7822
7947
|
if (opts.json) {
|
|
7823
7948
|
console.log(formatJson(localJson));
|
|
7824
7949
|
} else {
|
|
7825
|
-
console.log(
|
|
7826
|
-
console.log(` DID: ${
|
|
7827
|
-
console.log(` Settlement pubkey: ${
|
|
7828
|
-
console.log(` Identity pubkey: ${
|
|
7829
|
-
console.log(` Key mode: ${local.keyMode}`);
|
|
7950
|
+
console.log(import_chalk25.default.bold("Local agent:"));
|
|
7951
|
+
console.log(` DID: ${import_chalk25.default.cyan(local.did)}`);
|
|
7952
|
+
console.log(` Settlement pubkey: ${import_chalk25.default.cyan(local.settlementPublicKeyB58)}`);
|
|
7953
|
+
console.log(` Identity pubkey: ${import_chalk25.default.cyan(local.identityPublicKeyB58)}`);
|
|
7830
7954
|
if (local.name) console.log(` Name: ${local.name}`);
|
|
7831
7955
|
}
|
|
7832
7956
|
return;
|
|
@@ -7837,12 +7961,12 @@ function registerWhoamiCommand(root) {
|
|
|
7837
7961
|
if (opts.json) {
|
|
7838
7962
|
console.log(formatJson({ local: localJson, server: agent }));
|
|
7839
7963
|
} else {
|
|
7840
|
-
console.log(
|
|
7841
|
-
console.log(
|
|
7842
|
-
console.log(` DID: ${
|
|
7843
|
-
console.log(` Settlement pubkey: ${
|
|
7844
|
-
console.log(` Identity pubkey: ${
|
|
7845
|
-
console.log(
|
|
7964
|
+
console.log(import_chalk25.default.dim(`Server: ${api.serverUrl}`));
|
|
7965
|
+
console.log(import_chalk25.default.bold("\nLocal agent:"));
|
|
7966
|
+
console.log(` DID: ${import_chalk25.default.cyan(local.did)}`);
|
|
7967
|
+
console.log(` Settlement pubkey: ${import_chalk25.default.cyan(local.settlementPublicKeyB58)}`);
|
|
7968
|
+
console.log(` Identity pubkey: ${import_chalk25.default.cyan(local.identityPublicKeyB58)}`);
|
|
7969
|
+
console.log(import_chalk25.default.bold("\nServer profile:"));
|
|
7846
7970
|
console.log(formatJson(agent));
|
|
7847
7971
|
}
|
|
7848
7972
|
} catch (err) {
|
|
@@ -7853,8 +7977,8 @@ function registerWhoamiCommand(root) {
|
|
|
7853
7977
|
}
|
|
7854
7978
|
|
|
7855
7979
|
// src/commands/work.ts
|
|
7856
|
-
var
|
|
7857
|
-
var
|
|
7980
|
+
var import_sdk15 = require("@heyanon-arp/sdk");
|
|
7981
|
+
var import_chalk26 = __toESM(require("chalk"));
|
|
7858
7982
|
init_api();
|
|
7859
7983
|
init_format();
|
|
7860
7984
|
init_id_format();
|
|
@@ -7873,7 +7997,16 @@ var POST_COMMIT_ERROR_CODES3 = /* @__PURE__ */ new Set([
|
|
|
7873
7997
|
"WORK_REQUEST_ALREADY_EXISTS",
|
|
7874
7998
|
"WORK_REQUEST_NOT_FOUND",
|
|
7875
7999
|
"WORK_RESPONDER_IS_CALLER",
|
|
7876
|
-
"WORK_INVALID_STATE"
|
|
8000
|
+
"WORK_INVALID_STATE",
|
|
8001
|
+
// M6 lock-at-accept: the work handler's LOCKED gate emits
|
|
8002
|
+
// `DELEGATION_PENDING_LOCK` (409) when the delegation is funded but
|
|
8003
|
+
// the on-chain lock isn't confirmed yet (state
|
|
8004
|
+
// `pending_lock_finalization`). It fires from the body handler AFTER
|
|
8005
|
+
// the event row is committed — same lifecycle as
|
|
8006
|
+
// `WORK_DELEGATION_NOT_ACTIVE` — so the CLI must advance
|
|
8007
|
+
// `lastSenderSequence`, otherwise a retry once the lock confirms
|
|
8008
|
+
// reuses the consumed sequence and trips `ENV_SEQUENCE_BACKWARDS`.
|
|
8009
|
+
"DELEGATION_PENDING_LOCK"
|
|
7877
8010
|
]);
|
|
7878
8011
|
function registerRequest(parent) {
|
|
7879
8012
|
parent.command("request").description("Send a work_request to <recipient-did> under <delegation-id> (must be ACCEPTED).").argument("<recipient-did>", "Recipient agent DID (the payee \u2014 the OTHER side of the delegation pair)").argument("<delegation-id>", "Parent delegation id (UUID, must be ACCEPTED)").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server").option("--request-id <id>", "Override the auto-generated request id (must be unique within the delegation)").option(
|
|
@@ -7900,17 +8033,17 @@ async function runRequest(recipientDid, delegationId, opts) {
|
|
|
7900
8033
|
params
|
|
7901
8034
|
};
|
|
7902
8035
|
const body = { type: "work_request", content };
|
|
7903
|
-
console.log(
|
|
7904
|
-
console.log(
|
|
7905
|
-
console.log(
|
|
7906
|
-
console.log(
|
|
7907
|
-
console.log(
|
|
8036
|
+
console.log(import_chalk26.default.dim(`Server: ${api.serverUrl}`));
|
|
8037
|
+
console.log(import_chalk26.default.dim(`Sender: ${sender.did}`));
|
|
8038
|
+
console.log(import_chalk26.default.dim(`Recipient: ${recipientDid}`));
|
|
8039
|
+
console.log(import_chalk26.default.dim(`Delegation: ${delegationId}`));
|
|
8040
|
+
console.log(import_chalk26.default.dim(`Request id: ${requestId}`));
|
|
7908
8041
|
const result = await sendWorkEnvelope({ api, sender, recipientDid, body, ttlSeconds, verbose: opts.verbose, server: opts.server });
|
|
7909
8042
|
printIngestResult3(result);
|
|
7910
|
-
console.log(
|
|
8043
|
+
console.log(import_chalk26.default.dim(`
|
|
7911
8044
|
The payee can reply with:`));
|
|
7912
|
-
console.log(
|
|
7913
|
-
console.log(
|
|
8045
|
+
console.log(import_chalk26.default.dim(` heyarp work respond ${result.relationshipId} ${delegationId} ${requestId} --output '<json>'`));
|
|
8046
|
+
console.log(import_chalk26.default.dim(` heyarp work respond ${result.relationshipId} ${delegationId} ${requestId} --error CODE:message`));
|
|
7914
8047
|
}
|
|
7915
8048
|
function registerRespond(parent) {
|
|
7916
8049
|
parent.command("respond").description("Send a work_response under <relationship-id> for <delegation-id> / <request-id>. Payee-only.").argument("<relationship-id>", "Relationship UUID").argument("<delegation-id>", "Parent delegation id (UUID)").argument("<request-id>", "Request id supplied on the work_request").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Sender DID \u2014 required only if multiple agents are registered against this server").option("--output <json>", "Success payload as a JSON object literal. Mutually exclusive with --error and --output-file.").option(
|
|
@@ -7935,13 +8068,13 @@ async function runRespond(relationshipId, delegationId, requestId, opts) {
|
|
|
7935
8068
|
...responsePayload
|
|
7936
8069
|
};
|
|
7937
8070
|
const body = { type: "work_response", content };
|
|
7938
|
-
console.log(
|
|
7939
|
-
console.log(
|
|
7940
|
-
console.log(
|
|
7941
|
-
console.log(
|
|
7942
|
-
console.log(
|
|
7943
|
-
console.log(
|
|
7944
|
-
console.log(
|
|
8071
|
+
console.log(import_chalk26.default.dim(`Server: ${api.serverUrl}`));
|
|
8072
|
+
console.log(import_chalk26.default.dim(`Sender: ${sender.did}`));
|
|
8073
|
+
console.log(import_chalk26.default.dim(`Recipient: ${recipientDid}`));
|
|
8074
|
+
console.log(import_chalk26.default.dim(`Relationship: ${relationshipId}`));
|
|
8075
|
+
console.log(import_chalk26.default.dim(`Delegation: ${delegationId}`));
|
|
8076
|
+
console.log(import_chalk26.default.dim(`Request id: ${requestId}`));
|
|
8077
|
+
console.log(import_chalk26.default.dim(`Outcome: ${responsePayload.output ? "success" : "error"}`));
|
|
7945
8078
|
const result = await sendWorkEnvelope({ api, sender, recipientDid, body, ttlSeconds, verbose: opts.verbose, server: opts.server });
|
|
7946
8079
|
printIngestResult3(result);
|
|
7947
8080
|
}
|
|
@@ -7949,25 +8082,25 @@ async function sendWorkEnvelope(args) {
|
|
|
7949
8082
|
const nextSequence = (args.sender.lastSenderSequence ?? 0) + 1;
|
|
7950
8083
|
const protectedBlock = {
|
|
7951
8084
|
protocol_version: "arp/0.1",
|
|
7952
|
-
purpose:
|
|
7953
|
-
message_id: (0,
|
|
8085
|
+
purpose: import_sdk15.Purpose.ENVELOPE,
|
|
8086
|
+
message_id: (0, import_sdk15.uuidV4)(),
|
|
7954
8087
|
sender_did: args.sender.did,
|
|
7955
8088
|
recipient_did: args.recipientDid,
|
|
7956
8089
|
relationship_id: null,
|
|
7957
8090
|
sender_sequence: nextSequence,
|
|
7958
|
-
sender_nonce: (0,
|
|
7959
|
-
timestamp: (0,
|
|
7960
|
-
expires_at: (0,
|
|
8091
|
+
sender_nonce: (0, import_sdk15.senderNonce)(),
|
|
8092
|
+
timestamp: (0, import_sdk15.rfc3339)(),
|
|
8093
|
+
expires_at: (0, import_sdk15.expiresAt)(args.ttlSeconds),
|
|
7961
8094
|
delivery_id: null
|
|
7962
8095
|
};
|
|
7963
8096
|
const signer = makeSigner(args.sender);
|
|
7964
|
-
const envelope = (0,
|
|
8097
|
+
const envelope = (0, import_sdk15.signEnvelope)({
|
|
7965
8098
|
protected: protectedBlock,
|
|
7966
8099
|
body: args.body,
|
|
7967
8100
|
identitySecretKey: signer.identitySecretKey
|
|
7968
8101
|
});
|
|
7969
8102
|
if (args.verbose) {
|
|
7970
|
-
console.log(
|
|
8103
|
+
console.log(import_chalk26.default.bold("\nEnvelope (pre-send):"));
|
|
7971
8104
|
console.log(formatJson(envelope));
|
|
7972
8105
|
}
|
|
7973
8106
|
try {
|
|
@@ -8007,12 +8140,12 @@ async function resolveResponseRecipient(cmdName, api, signer, args) {
|
|
|
8007
8140
|
);
|
|
8008
8141
|
}
|
|
8009
8142
|
function printIngestResult3(result) {
|
|
8010
|
-
console.log(
|
|
8011
|
-
console.log(`${
|
|
8012
|
-
console.log(`${
|
|
8013
|
-
console.log(`${
|
|
8014
|
-
console.log(`${
|
|
8015
|
-
console.log(`${
|
|
8143
|
+
console.log(import_chalk26.default.green("\nDelivered."));
|
|
8144
|
+
console.log(`${import_chalk26.default.bold("Event id")}: ${import_chalk26.default.cyan(result.eventId)}`);
|
|
8145
|
+
console.log(`${import_chalk26.default.bold("Relationship id")}: ${import_chalk26.default.cyan(result.relationshipId)}`);
|
|
8146
|
+
console.log(`${import_chalk26.default.bold("Chain index")}: ${import_chalk26.default.cyan(String(result.relationshipEventIndex))}`);
|
|
8147
|
+
console.log(`${import_chalk26.default.bold("Server timestamp")}: ${import_chalk26.default.cyan(result.serverTimestamp)}`);
|
|
8148
|
+
console.log(`${import_chalk26.default.bold("Server event hash")}: ${import_chalk26.default.cyan(result.serverEventHash)}`);
|
|
8016
8149
|
}
|
|
8017
8150
|
function parseJsonObject(cmdName, flagName, raw) {
|
|
8018
8151
|
let parsed;
|
|
@@ -8050,13 +8183,13 @@ function parseParamsInput(cmdName, opts) {
|
|
|
8050
8183
|
return parseJsonObject(cmdName, "--params", opts.params ?? "{}");
|
|
8051
8184
|
}
|
|
8052
8185
|
function readJsonObjectFile(cmdName, flagName, path) {
|
|
8053
|
-
const { existsSync:
|
|
8054
|
-
if (!
|
|
8186
|
+
const { existsSync: existsSync6, readFileSync: readFileSync8 } = require("fs");
|
|
8187
|
+
if (!existsSync6(path)) {
|
|
8055
8188
|
throw new Error(`${cmdName}: ${flagName} file not found at ${path}`);
|
|
8056
8189
|
}
|
|
8057
8190
|
let raw;
|
|
8058
8191
|
try {
|
|
8059
|
-
raw =
|
|
8192
|
+
raw = readFileSync8(path, "utf8");
|
|
8060
8193
|
} catch (err) {
|
|
8061
8194
|
const detail = err instanceof Error ? err.message : String(err);
|
|
8062
8195
|
throw new Error(`${cmdName}: failed to read ${flagName} (${path}): ${detail}`);
|
|
@@ -8104,7 +8237,7 @@ function parseTtl5(cmdName, raw) {
|
|
|
8104
8237
|
}
|
|
8105
8238
|
function parseRequestId(cmdName, raw) {
|
|
8106
8239
|
if (raw === void 0 || raw === "") {
|
|
8107
|
-
return (0,
|
|
8240
|
+
return (0, import_sdk15.uuidV4)();
|
|
8108
8241
|
}
|
|
8109
8242
|
if (raw.length === 0) {
|
|
8110
8243
|
throw new Error(`${cmdName}: --request-id must be a non-empty string`);
|
|
@@ -8125,7 +8258,7 @@ function requireDid3(cmdName, did, label) {
|
|
|
8125
8258
|
}
|
|
8126
8259
|
|
|
8127
8260
|
// src/commands/work-list.ts
|
|
8128
|
-
var
|
|
8261
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
8129
8262
|
init_api();
|
|
8130
8263
|
init_format();
|
|
8131
8264
|
init_state();
|
|
@@ -8155,9 +8288,9 @@ async function runWorkList(relationshipId, opts) {
|
|
|
8155
8288
|
const api = new ArpApiClient(opts.server);
|
|
8156
8289
|
const sender = resolveSenderAgent("work-list", opts.server, opts.fromDid);
|
|
8157
8290
|
if (!opts.json) {
|
|
8158
|
-
console.log(
|
|
8159
|
-
console.log(
|
|
8160
|
-
console.log(
|
|
8291
|
+
console.log(import_chalk27.default.dim(`Server: ${api.serverUrl}`));
|
|
8292
|
+
console.log(import_chalk27.default.dim(`Signer: ${sender.did}`));
|
|
8293
|
+
console.log(import_chalk27.default.dim(`Relationship: ${relationshipId}`));
|
|
8161
8294
|
}
|
|
8162
8295
|
const query = { limit };
|
|
8163
8296
|
if (state) query.state = state;
|
|
@@ -8170,7 +8303,7 @@ async function runWorkList(relationshipId, opts) {
|
|
|
8170
8303
|
return;
|
|
8171
8304
|
}
|
|
8172
8305
|
if (rows.length === 0) {
|
|
8173
|
-
console.log(
|
|
8306
|
+
console.log(import_chalk27.default.dim("\n(no work-logs for this relationship)"));
|
|
8174
8307
|
return;
|
|
8175
8308
|
}
|
|
8176
8309
|
console.log("");
|
|
@@ -8187,36 +8320,36 @@ async function runWorkList(relationshipId, opts) {
|
|
|
8187
8320
|
}));
|
|
8188
8321
|
}
|
|
8189
8322
|
const lastId = rows[rows.length - 1].id;
|
|
8190
|
-
console.log(
|
|
8323
|
+
console.log(import_chalk27.default.dim(`
|
|
8191
8324
|
${rows.length} work-log row(s). Paginate with --after ${lastId}.`));
|
|
8192
8325
|
}
|
|
8193
8326
|
function formatWorkLogLine(w, selfDid, opts = {}) {
|
|
8194
8327
|
const delegationPart = opts.fullIds ? w.delegationId : idHead3(w.delegationId);
|
|
8195
8328
|
const requestPart = opts.fullIds ? w.requestId : truncate3(w.requestId, 16);
|
|
8196
|
-
const id =
|
|
8329
|
+
const id = import_chalk27.default.bold(`${delegationPart}/${requestPart}`);
|
|
8197
8330
|
const state = colorState3(w.state).padEnd(stateColumnWidth3());
|
|
8198
8331
|
const peerCallerHead = opts.fullIds ? w.callerDid : didHead5(w.callerDid);
|
|
8199
8332
|
const peerPayeeHead = opts.fullIds ? w.payeeDid : didHead5(w.payeeDid);
|
|
8200
|
-
const direction = w.callerDid === selfDid ? `${
|
|
8333
|
+
const direction = w.callerDid === selfDid ? `${import_chalk27.default.bold("me")} \u2192 ${import_chalk27.default.dim(peerPayeeHead)}` : `${import_chalk27.default.dim(peerCallerHead)} \u2192 ${import_chalk27.default.bold("me")}`;
|
|
8201
8334
|
const outcome = formatOutcome(w);
|
|
8202
8335
|
return `${id} ${state} ${direction} ${outcome}`;
|
|
8203
8336
|
}
|
|
8204
8337
|
function colorState3(s) {
|
|
8205
8338
|
switch (s) {
|
|
8206
8339
|
case "requested":
|
|
8207
|
-
return
|
|
8340
|
+
return import_chalk27.default.yellow("requested");
|
|
8208
8341
|
case "responded":
|
|
8209
|
-
return
|
|
8342
|
+
return import_chalk27.default.green("responded");
|
|
8210
8343
|
}
|
|
8211
8344
|
}
|
|
8212
8345
|
function stateColumnWidth3() {
|
|
8213
8346
|
return 9;
|
|
8214
8347
|
}
|
|
8215
8348
|
function formatOutcome(w) {
|
|
8216
|
-
if (w.state === "requested") return
|
|
8217
|
-
if (w.responseError) return
|
|
8218
|
-
if (w.responseOutput) return
|
|
8219
|
-
return
|
|
8349
|
+
if (w.state === "requested") return import_chalk27.default.dim("(in flight)");
|
|
8350
|
+
if (w.responseError) return import_chalk27.default.red(`error ${w.responseError.code}: ${truncate3(w.responseError.message, 32)}`);
|
|
8351
|
+
if (w.responseOutput) return import_chalk27.default.cyan("ok");
|
|
8352
|
+
return import_chalk27.default.dim("(empty response)");
|
|
8220
8353
|
}
|
|
8221
8354
|
function idHead3(id) {
|
|
8222
8355
|
if (id.length <= 12) return id;
|
|
@@ -8279,10 +8412,8 @@ async function main() {
|
|
|
8279
8412
|
registerDidDocCommand(program);
|
|
8280
8413
|
registerDoctorCommand(program);
|
|
8281
8414
|
registerEscrowCommands(program);
|
|
8282
|
-
registerExamplesCommand(program);
|
|
8283
8415
|
registerWhoamiCommand(program);
|
|
8284
8416
|
registerLifecycleCommands(program);
|
|
8285
|
-
registerRotateCommand(program);
|
|
8286
8417
|
registerSendHandshakeCommand(program);
|
|
8287
8418
|
registerSendHandshakeResponseCommand(program);
|
|
8288
8419
|
registerInboxCommand(program);
|