@heyanon-arp/cli 0.0.7 → 0.0.8
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 +806 -784
- 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,
|
|
@@ -3492,10 +3578,10 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
|
|
|
3492
3578
|
const api = new ArpApiClient(opts.server);
|
|
3493
3579
|
const sender = resolveSenderAgent(cmdName, opts.server, opts.fromDid);
|
|
3494
3580
|
const signer = makeSigner(sender);
|
|
3495
|
-
say(
|
|
3496
|
-
say(
|
|
3581
|
+
say(import_chalk17.default.dim(`Server: ${api.serverUrl}`));
|
|
3582
|
+
say(import_chalk17.default.dim(`Signer (payee): ${sender.did}`));
|
|
3497
3583
|
const relId = opts.relId ?? await resolveAutoRelId(api, sender, delegationId);
|
|
3498
|
-
say(
|
|
3584
|
+
say(import_chalk17.default.dim(`Relationship: ${relId}`));
|
|
3499
3585
|
const receiptMatches = await collectReceiptRows(api, signer, relId, delegationId);
|
|
3500
3586
|
const receipt = selectReceipt(receiptMatches, opts.receiptEventHash, cmdName);
|
|
3501
3587
|
if (!receipt) {
|
|
@@ -3516,7 +3602,7 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
|
|
|
3516
3602
|
if (opts.json) {
|
|
3517
3603
|
jsonOut({ ok: true, idempotent: true, alreadyCosigned: true, delegationId, relationshipId: relId, buyerDid: receipt.callerDid });
|
|
3518
3604
|
} else {
|
|
3519
|
-
progress(opts.json,
|
|
3605
|
+
progress(opts.json, import_chalk17.default.yellow("\n[idempotent] Receipt already cosigned \u2014 escrow release is settled. Nothing to send."));
|
|
3520
3606
|
}
|
|
3521
3607
|
}
|
|
3522
3608
|
return { delivered: false, idempotent: true, alreadyCosigned: true, relationshipId: relId, buyerDid: receipt.callerDid };
|
|
@@ -3539,8 +3625,8 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
|
|
|
3539
3625
|
expiresAt: ps.expires_at
|
|
3540
3626
|
});
|
|
3541
3627
|
} else {
|
|
3542
|
-
progress(opts.json,
|
|
3543
|
-
progress(opts.json,
|
|
3628
|
+
progress(opts.json, import_chalk17.default.yellow("\n[idempotent] Payee settlement signature already delivered + still valid for this receipt \u2014 skipping re-send."));
|
|
3629
|
+
progress(opts.json, import_chalk17.default.dim(` purpose=${ps.purpose} settlement_pubkey=${ps.settlement_pubkey} expires_at=${ps.expires_at}`));
|
|
3544
3630
|
}
|
|
3545
3631
|
}
|
|
3546
3632
|
return {
|
|
@@ -3554,14 +3640,14 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
|
|
|
3554
3640
|
expiresAt: ps.expires_at
|
|
3555
3641
|
};
|
|
3556
3642
|
}
|
|
3557
|
-
say(
|
|
3643
|
+
say(import_chalk17.default.yellow(`
|
|
3558
3644
|
[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
3645
|
}
|
|
3560
3646
|
const buyerDid = receipt.callerDid;
|
|
3561
3647
|
const receiptEventHash = receipt.receiptEventHash;
|
|
3562
3648
|
const deliverableHash = receipt.deliverableHash ?? receipt.responseHash;
|
|
3563
|
-
say(
|
|
3564
|
-
say(
|
|
3649
|
+
say(import_chalk17.default.dim(`Buyer (payer): ${buyerDid}`));
|
|
3650
|
+
say(import_chalk17.default.dim(`Receipt event hash: ${receiptEventHash}`));
|
|
3565
3651
|
const delegation = await findDelegationRow(api, signer, relId, delegationId);
|
|
3566
3652
|
if (!delegation) {
|
|
3567
3653
|
throw new Error(`${cmdName}: delegation ${delegationId} not found under relationship ${relId} (paginated 5000 rows).`);
|
|
@@ -3601,11 +3687,6 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
|
|
|
3601
3687
|
`${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
3688
|
);
|
|
3603
3689
|
}
|
|
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
3690
|
const subset = projectDelegationRowForHash(delegation);
|
|
3610
3691
|
const conditionHash = (0, import_utils3.bytesToHex)((0, import_sdk10.deriveDelegationConditionHash)(subset));
|
|
3611
3692
|
const buyerDidDoc = await api.getDidDocument(buyerDid);
|
|
@@ -3667,9 +3748,9 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
|
|
|
3667
3748
|
});
|
|
3668
3749
|
effectiveFeeBpsAtLock = resolvedFee.feeBpsAtLock;
|
|
3669
3750
|
effectiveFeeRecipientAtLock = resolvedFee.feeRecipientAtLock;
|
|
3670
|
-
say(
|
|
3751
|
+
say(import_chalk17.default.dim(`On-chain lock pre-flight OK \u2014 payee/amount/expiry/mint match; fee_bps_at_lock=${resolvedFee.feeBpsAtLock}.`));
|
|
3671
3752
|
} else {
|
|
3672
|
-
say(
|
|
3753
|
+
say(import_chalk17.default.yellow(`On-chain lock pre-flight skipped: ${lockCheck.skipReason}. The server remains the validation boundary.`));
|
|
3673
3754
|
}
|
|
3674
3755
|
const signOpts = {
|
|
3675
3756
|
server: opts.server,
|
|
@@ -3692,7 +3773,7 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
|
|
|
3692
3773
|
...effectiveFeeRecipientAtLock !== void 0 ? { feeRecipientAtLock: effectiveFeeRecipientAtLock } : {},
|
|
3693
3774
|
...partialPayeeAmount !== void 0 ? { partialPayeeAmount } : {}
|
|
3694
3775
|
};
|
|
3695
|
-
say(
|
|
3776
|
+
say(import_chalk17.default.dim(`Signing ${partialPayeeAmount !== void 0 ? "PARTIAL" : "full"} release: lock_amount=${lockAmount}, expires_at=${expiresAt6}, cluster_tag=${clusterTag}`));
|
|
3696
3777
|
const signed = await signSettlementHandler(signOpts);
|
|
3697
3778
|
const content = {
|
|
3698
3779
|
delegation_id: delegationId,
|
|
@@ -3723,16 +3804,16 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
|
|
|
3723
3804
|
serverTimestamp: result.serverTimestamp
|
|
3724
3805
|
});
|
|
3725
3806
|
} else {
|
|
3726
|
-
console.log(
|
|
3727
|
-
console.log(`${
|
|
3728
|
-
console.log(`${
|
|
3729
|
-
console.log(`${
|
|
3807
|
+
console.log(import_chalk17.default.green("\nSettlement signature signed + delivered."));
|
|
3808
|
+
console.log(`${import_chalk17.default.bold("Delegation")}: ${import_chalk17.default.cyan(delegationId)}`);
|
|
3809
|
+
console.log(`${import_chalk17.default.bold("Buyer")}: ${import_chalk17.default.cyan(buyerDid)}`);
|
|
3810
|
+
console.log(`${import_chalk17.default.bold("Purpose")}: ${import_chalk17.default.cyan(signed.purpose)}`);
|
|
3730
3811
|
if (partialPayeeAmount !== void 0) {
|
|
3731
|
-
console.log(`${
|
|
3812
|
+
console.log(`${import_chalk17.default.bold("Payee amount")}: ${import_chalk17.default.cyan(partialPayeeAmount)} (partial release)`);
|
|
3732
3813
|
}
|
|
3733
|
-
console.log(`${
|
|
3734
|
-
console.log(`${
|
|
3735
|
-
console.log(
|
|
3814
|
+
console.log(`${import_chalk17.default.bold("Expires at")}: ${import_chalk17.default.cyan(String(expiresAt6))}`);
|
|
3815
|
+
console.log(`${import_chalk17.default.bold("Delivered event")}: ${import_chalk17.default.cyan(result.serverEventHash)}`);
|
|
3816
|
+
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
3817
|
}
|
|
3737
3818
|
}
|
|
3738
3819
|
return {
|
|
@@ -3751,13 +3832,13 @@ async function autoSignAndDeliverPayeeSig(opts, cmdName = "receipt propose") {
|
|
|
3751
3832
|
serverTimestamp: result.serverTimestamp
|
|
3752
3833
|
};
|
|
3753
3834
|
}
|
|
3754
|
-
var import_sdk10, import_utils3, import_web33,
|
|
3835
|
+
var import_sdk10, import_utils3, import_web33, import_chalk17, NATIVE_SOL_MINT2, SETTLEMENT_MIN_EXPIRY_HEADROOM_SECS, SETTLEMENT_REFRESH_HEADROOM_SECS;
|
|
3755
3836
|
var init_settlement = __esm({
|
|
3756
3837
|
"src/commands/settlement.ts"() {
|
|
3757
3838
|
import_sdk10 = require("@heyanon-arp/sdk");
|
|
3758
3839
|
import_utils3 = require("@noble/hashes/utils");
|
|
3759
3840
|
import_web33 = require("@solana/web3.js");
|
|
3760
|
-
|
|
3841
|
+
import_chalk17 = __toESM(require("chalk"));
|
|
3761
3842
|
init_api();
|
|
3762
3843
|
init_format();
|
|
3763
3844
|
init_state();
|
|
@@ -3798,10 +3879,10 @@ function registerPropose(parent) {
|
|
|
3798
3879
|
false
|
|
3799
3880
|
).option(
|
|
3800
3881
|
"--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
|
|
3882
|
+
"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
3883
|
).option(
|
|
3803
3884
|
"--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
|
|
3885
|
+
"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
3886
|
"86400"
|
|
3806
3887
|
).option(
|
|
3807
3888
|
"--expires-at <unix>",
|
|
@@ -3850,15 +3931,13 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
|
|
|
3850
3931
|
opts.relId = await resolveAutoRelId(api, sender, delegationId);
|
|
3851
3932
|
progress(
|
|
3852
3933
|
opts.json,
|
|
3853
|
-
|
|
3934
|
+
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
3935
|
);
|
|
3855
3936
|
}
|
|
3856
|
-
let delegationRow;
|
|
3857
3937
|
if (opts.relId) {
|
|
3858
|
-
|
|
3938
|
+
await assertSenderIsReceiptPayee(api, sender, opts.relId, delegationId);
|
|
3859
3939
|
}
|
|
3860
|
-
|
|
3861
|
-
if (isEscrow) {
|
|
3940
|
+
{
|
|
3862
3941
|
const ct = opts.clusterTag;
|
|
3863
3942
|
if (ct === void 0 || ct === "") {
|
|
3864
3943
|
throw new Error(
|
|
@@ -3879,7 +3958,7 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
|
|
|
3879
3958
|
opts.requestId = await resolveAutoRequestId(api, sender, opts.relId, delegationId);
|
|
3880
3959
|
progress(
|
|
3881
3960
|
opts.json,
|
|
3882
|
-
|
|
3961
|
+
import_chalk18.default.dim(
|
|
3883
3962
|
`[auto-request-id] resolved --request-id=${opts.requestId} (unique 'responded' work_log under this delegation; pass --request-id explicitly to override)`
|
|
3884
3963
|
)
|
|
3885
3964
|
);
|
|
@@ -3898,8 +3977,8 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
|
|
|
3898
3977
|
}
|
|
3899
3978
|
requestHash = computed.requestHash;
|
|
3900
3979
|
responseHash = computed.responseHash;
|
|
3901
|
-
progress(opts.json,
|
|
3902
|
-
progress(opts.json,
|
|
3980
|
+
progress(opts.json, import_chalk18.default.dim(`[auto-hashes] request_hash: ${requestHash} (from work-log ${opts.relId}/${delegationId}/${opts.requestId})`));
|
|
3981
|
+
progress(opts.json, import_chalk18.default.dim(`[auto-hashes] response_hash: ${responseHash}`));
|
|
3903
3982
|
} else {
|
|
3904
3983
|
if (requestHashArg === void 0 || responseHashArg === void 0) {
|
|
3905
3984
|
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 +3998,15 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
|
|
|
3919
3998
|
if (opts.deliverableHash) content.deliverable_hash = opts.deliverableHash;
|
|
3920
3999
|
if (usage) content.usage = usage;
|
|
3921
4000
|
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,
|
|
4001
|
+
progress(opts.json, import_chalk18.default.dim(`Server: ${api.serverUrl}`));
|
|
4002
|
+
progress(opts.json, import_chalk18.default.dim(`Sender (payee): ${sender.did}`));
|
|
4003
|
+
progress(opts.json, import_chalk18.default.dim(`Recipient (caller): ${recipientDid}`));
|
|
4004
|
+
progress(opts.json, import_chalk18.default.dim(`Delegation: ${delegationId}`));
|
|
4005
|
+
progress(opts.json, import_chalk18.default.dim(`Verdict (proposed): ${verdict}`));
|
|
3927
4006
|
const result = await sendReceiptEnvelope({ api, sender, recipientDid, body, attachments: void 0, ttlSeconds, verbose: opts.verbose, server: opts.server });
|
|
3928
4007
|
let settlementResult;
|
|
3929
4008
|
let settlementError;
|
|
3930
|
-
if (
|
|
4009
|
+
if (opts.relId) {
|
|
3931
4010
|
const { autoSignAndDeliverPayeeSig: autoSignAndDeliverPayeeSig2 } = await Promise.resolve().then(() => (init_settlement(), settlement_exports));
|
|
3932
4011
|
try {
|
|
3933
4012
|
settlementResult = await autoSignAndDeliverPayeeSig2({
|
|
@@ -3995,32 +4074,36 @@ async function runPropose(recipientDid, delegationId, requestHashArg, responseHa
|
|
|
3995
4074
|
return;
|
|
3996
4075
|
}
|
|
3997
4076
|
printIngestResult2(result);
|
|
3998
|
-
console.log(
|
|
3999
|
-
Receipt event hash: ${
|
|
4077
|
+
console.log(import_chalk18.default.dim(`
|
|
4078
|
+
Receipt event hash: ${import_chalk18.default.cyan(result.serverEventHash)}`));
|
|
4000
4079
|
if (settlementError) {
|
|
4001
|
-
console.error(
|
|
4002
|
-
console.error(
|
|
4003
|
-
console.error(
|
|
4004
|
-
console.error(
|
|
4080
|
+
console.error(import_chalk18.default.yellow("\nWARNING: receipt proposed, but the payee settlement signature was NOT delivered."));
|
|
4081
|
+
console.error(import_chalk18.default.yellow(` reason: ${settlementError.message}`));
|
|
4082
|
+
console.error(import_chalk18.default.dim(" The receipt stays committed \u2014 re-running `receipt propose` will NOT help (it hits RECEIPT_ALREADY_EXISTS)."));
|
|
4083
|
+
console.error(import_chalk18.default.dim(" Recover by signing + delivering the payee sig directly, once the on-chain lock / RPC is reachable:"));
|
|
4005
4084
|
console.error(
|
|
4006
|
-
|
|
4085
|
+
import_chalk18.default.dim(
|
|
4007
4086
|
` 'heyarp wallet sign-settlement-release ... --write-to <path>' then
|
|
4008
4087
|
'heyarp receipt send-payee-sig ${recipientDid} --delegation-id ${delegationId} --receipt-event-hash ${result.serverEventHash} --sig-from-file <path> --expires-at <unix>'`
|
|
4009
4088
|
)
|
|
4010
4089
|
);
|
|
4011
4090
|
} else if (settlementResult?.delivered) {
|
|
4012
|
-
console.log(
|
|
4013
|
-
console.log(
|
|
4091
|
+
console.log(import_chalk18.default.green("\nPayee settlement signature signed + delivered (escrow)."));
|
|
4092
|
+
console.log(
|
|
4093
|
+
import_chalk18.default.dim(
|
|
4094
|
+
` purpose=${settlementResult.purpose} expires_at=${settlementResult.expiresAt}${settlementResult.payeeAmount !== void 0 ? ` payee_amount=${settlementResult.payeeAmount} (partial)` : ""}`
|
|
4095
|
+
)
|
|
4096
|
+
);
|
|
4014
4097
|
} 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}`));
|
|
4098
|
+
console.log(
|
|
4099
|
+
import_chalk18.default.yellow(
|
|
4100
|
+
`
|
|
4101
|
+
[idempotent] Payee settlement signature already ${settlementResult.alreadyCosigned ? "settled (receipt cosigned)" : "delivered"} \u2014 nothing to re-send.`
|
|
4102
|
+
)
|
|
4103
|
+
);
|
|
4023
4104
|
}
|
|
4105
|
+
console.log(import_chalk18.default.dim(`The caller cosigns with:`));
|
|
4106
|
+
console.log(import_chalk18.default.dim(` heyarp receipt cosign ${result.relationshipId} ${delegationId} --auto-hashes --auto-resolve-payee-sig --payer-sig-from-file <path>`));
|
|
4024
4107
|
}
|
|
4025
4108
|
async function assertSenderIsReceiptPayee(api, sender, relationshipId, delegationId) {
|
|
4026
4109
|
const signer = makeSigner(sender);
|
|
@@ -4168,7 +4251,7 @@ function registerCosign(parent) {
|
|
|
4168
4251
|
function loadSettlementSigFromFile(path, flagPrefix) {
|
|
4169
4252
|
let raw;
|
|
4170
4253
|
try {
|
|
4171
|
-
raw = (0,
|
|
4254
|
+
raw = (0, import_node_fs7.readFileSync)(path, "utf8");
|
|
4172
4255
|
} catch (err) {
|
|
4173
4256
|
throw new Error(`receipt cosign: failed to read ${flagPrefix} '${path}': ${err.message}`);
|
|
4174
4257
|
}
|
|
@@ -4415,28 +4498,28 @@ async function runCosign(relationshipId, delegationId, requestHashArg, responseH
|
|
|
4415
4498
|
content.notes_hash = cosignNotesHash;
|
|
4416
4499
|
}
|
|
4417
4500
|
const body = { type: "receipt", content };
|
|
4418
|
-
console.log(
|
|
4419
|
-
console.log(
|
|
4420
|
-
console.log(
|
|
4421
|
-
console.log(
|
|
4422
|
-
console.log(
|
|
4423
|
-
console.log(
|
|
4501
|
+
console.log(import_chalk18.default.dim(`Server: ${api.serverUrl}`));
|
|
4502
|
+
console.log(import_chalk18.default.dim(`Sender (caller): ${sender.did}`));
|
|
4503
|
+
console.log(import_chalk18.default.dim(`Recipient (payee): ${resolved.payeeDid}`));
|
|
4504
|
+
console.log(import_chalk18.default.dim(`Delegation: ${delegationId}`));
|
|
4505
|
+
console.log(import_chalk18.default.dim(`Verdict (final): ${verdict}`));
|
|
4506
|
+
console.log(import_chalk18.default.dim(`Receipt event hash bound: ${resolved.receiptEventHash}`));
|
|
4424
4507
|
if (cosignNotesHash !== null) {
|
|
4425
|
-
console.log(
|
|
4508
|
+
console.log(import_chalk18.default.dim(`Notes hash bound: ${cosignNotesHash}`));
|
|
4426
4509
|
} else if (opts.clearNotes) {
|
|
4427
|
-
console.log(
|
|
4510
|
+
console.log(import_chalk18.default.dim("Notes binding: cleared (--clear-notes)"));
|
|
4428
4511
|
}
|
|
4429
4512
|
if (opts.autoResolvePayeeSig) {
|
|
4430
4513
|
applyAutoResolvePayeeSig("receipt cosign", opts, resolved.payeeSettlement);
|
|
4431
4514
|
console.log(
|
|
4432
|
-
|
|
4515
|
+
import_chalk18.default.dim(`Auto-resolved payee sig from receipt.payeeSettlement (purpose=${resolved.payeeSettlement?.purpose}, expires_at=${resolved.payeeSettlement?.expires_at})`)
|
|
4433
4516
|
);
|
|
4434
4517
|
}
|
|
4435
4518
|
const settlementSigs = assembleSettlementSignaturesAttachment(opts);
|
|
4436
4519
|
const attachments = { co_signature: cosignature };
|
|
4437
4520
|
if (settlementSigs) {
|
|
4438
4521
|
attachments.settlement_signatures = settlementSigs;
|
|
4439
|
-
console.log(
|
|
4522
|
+
console.log(import_chalk18.default.dim(`Settlement signatures attached: purpose=${settlementSigs.purpose}`));
|
|
4440
4523
|
}
|
|
4441
4524
|
const result = await sendReceiptEnvelope({
|
|
4442
4525
|
api,
|
|
@@ -4533,17 +4616,17 @@ async function runSendPayeeSig(recipientDid, opts) {
|
|
|
4533
4616
|
expires_at: expiresAtSeconds,
|
|
4534
4617
|
...isPartial ? { payee_amount: opts.payeeAmount } : {}
|
|
4535
4618
|
};
|
|
4536
|
-
console.log(
|
|
4537
|
-
console.log(
|
|
4538
|
-
console.log(
|
|
4539
|
-
console.log(
|
|
4540
|
-
console.log(
|
|
4541
|
-
console.log(
|
|
4619
|
+
console.log(import_chalk18.default.dim(`Server: ${api.serverUrl}`));
|
|
4620
|
+
console.log(import_chalk18.default.dim(`Sender (payee): ${sender.did}`));
|
|
4621
|
+
console.log(import_chalk18.default.dim(`Recipient (buyer): ${recipientDid}`));
|
|
4622
|
+
console.log(import_chalk18.default.dim(`Delegation: ${delegationId}`));
|
|
4623
|
+
console.log(import_chalk18.default.dim(`Receipt event hash: ${opts.receiptEventHash}`));
|
|
4624
|
+
console.log(import_chalk18.default.dim(`Purpose: ${sigFile.purpose}`));
|
|
4542
4625
|
if (isPartial) {
|
|
4543
|
-
console.log(
|
|
4626
|
+
console.log(import_chalk18.default.dim(`Payee amount: ${opts.payeeAmount}`));
|
|
4544
4627
|
}
|
|
4545
|
-
console.log(
|
|
4546
|
-
console.log(
|
|
4628
|
+
console.log(import_chalk18.default.dim(`Settlement pubkey: ${sigFile.settlement_pubkey}`));
|
|
4629
|
+
console.log(import_chalk18.default.dim(`Expires at: ${expiresAtSeconds}`));
|
|
4547
4630
|
const result = await sendSettlementSignatureEnvelope({
|
|
4548
4631
|
api,
|
|
4549
4632
|
sender,
|
|
@@ -4577,7 +4660,7 @@ async function sendSettlementSignatureEnvelope(args) {
|
|
|
4577
4660
|
identitySecretKey: signer.identitySecretKey
|
|
4578
4661
|
});
|
|
4579
4662
|
if (args.verbose) {
|
|
4580
|
-
console.log(
|
|
4663
|
+
console.log(import_chalk18.default.bold("\nEnvelope (pre-send):"));
|
|
4581
4664
|
console.log(formatJson(envelope));
|
|
4582
4665
|
}
|
|
4583
4666
|
try {
|
|
@@ -4614,7 +4697,7 @@ async function sendReceiptEnvelope(args) {
|
|
|
4614
4697
|
attachments: args.attachments
|
|
4615
4698
|
});
|
|
4616
4699
|
if (args.verbose) {
|
|
4617
|
-
console.log(
|
|
4700
|
+
console.log(import_chalk18.default.bold("\nEnvelope (pre-send):"));
|
|
4618
4701
|
console.log(formatJson(envelope));
|
|
4619
4702
|
}
|
|
4620
4703
|
try {
|
|
@@ -4673,12 +4756,12 @@ async function resolveCosignTargets(cmdName, api, signer, args) {
|
|
|
4673
4756
|
};
|
|
4674
4757
|
}
|
|
4675
4758
|
function printIngestResult2(result) {
|
|
4676
|
-
console.log(
|
|
4677
|
-
console.log(`${
|
|
4678
|
-
console.log(`${
|
|
4679
|
-
console.log(`${
|
|
4680
|
-
console.log(`${
|
|
4681
|
-
console.log(`${
|
|
4759
|
+
console.log(import_chalk18.default.green("\nDelivered."));
|
|
4760
|
+
console.log(`${import_chalk18.default.bold("Event id")}: ${import_chalk18.default.cyan(result.eventId)}`);
|
|
4761
|
+
console.log(`${import_chalk18.default.bold("Relationship id")}: ${import_chalk18.default.cyan(result.relationshipId)}`);
|
|
4762
|
+
console.log(`${import_chalk18.default.bold("Chain index")}: ${import_chalk18.default.cyan(String(result.relationshipEventIndex))}`);
|
|
4763
|
+
console.log(`${import_chalk18.default.bold("Server timestamp")}: ${import_chalk18.default.cyan(result.serverTimestamp)}`);
|
|
4764
|
+
console.log(`${import_chalk18.default.bold("Server event hash")}: ${import_chalk18.default.cyan(result.serverEventHash)}`);
|
|
4682
4765
|
}
|
|
4683
4766
|
function parseVerdict(cmdName, raw) {
|
|
4684
4767
|
if (raw === void 0 || raw === "") return "accepted";
|
|
@@ -4729,12 +4812,12 @@ function requireDid2(cmdName, did, label) {
|
|
|
4729
4812
|
throw new Error(`${cmdName}: ${label} must look like 'did:arp:...' (got '${did}')`);
|
|
4730
4813
|
}
|
|
4731
4814
|
}
|
|
4732
|
-
var
|
|
4815
|
+
var import_node_fs7, import_sdk11, import_chalk18, POST_COMMIT_ERROR_CODES2, VERDICT_VALUES, SHA256_RE;
|
|
4733
4816
|
var init_receipt = __esm({
|
|
4734
4817
|
"src/commands/receipt.ts"() {
|
|
4735
|
-
|
|
4818
|
+
import_node_fs7 = require("fs");
|
|
4736
4819
|
import_sdk11 = require("@heyanon-arp/sdk");
|
|
4737
|
-
|
|
4820
|
+
import_chalk18 = __toESM(require("chalk"));
|
|
4738
4821
|
init_api();
|
|
4739
4822
|
init_format();
|
|
4740
4823
|
init_id_format();
|
|
@@ -4753,6 +4836,15 @@ var init_receipt = __esm({
|
|
|
4753
4836
|
"RECEIPT_COSIGN_AGENT_MISMATCH",
|
|
4754
4837
|
"RECEIPT_COSIGN_PURPOSE_INVALID",
|
|
4755
4838
|
"RECEIPT_COSIGN_INVALID",
|
|
4839
|
+
// M6 lock-at-accept: the receipt-propose handler's LOCKED gate emits
|
|
4840
|
+
// `DELEGATION_PENDING_LOCK` (409) when the delegation is funded but
|
|
4841
|
+
// the on-chain lock isn't confirmed yet (state
|
|
4842
|
+
// `pending_lock_finalization`). It fires from the body handler AFTER
|
|
4843
|
+
// the event row is committed — same lifecycle as
|
|
4844
|
+
// `RECEIPT_DELEGATION_NOT_ACTIVE` — so the CLI must advance
|
|
4845
|
+
// `lastSenderSequence`, otherwise a retry once the lock confirms
|
|
4846
|
+
// reuses the consumed sequence and trips `ENV_SEQUENCE_BACKWARDS`.
|
|
4847
|
+
"DELEGATION_PENDING_LOCK",
|
|
4756
4848
|
// response_hash / request_hash / deliverable_hash content
|
|
4757
4849
|
// verification. Server commits the receipt envelope row BEFORE
|
|
4758
4850
|
// running the canonical-hash lookup, so a rejection here still
|
|
@@ -4842,7 +4934,7 @@ var import_simple_update_notifier = __toESM(require("simple-update-notifier"));
|
|
|
4842
4934
|
// package.json
|
|
4843
4935
|
var package_default = {
|
|
4844
4936
|
name: "@heyanon-arp/cli",
|
|
4845
|
-
version: "0.0.
|
|
4937
|
+
version: "0.0.8",
|
|
4846
4938
|
description: "Command-line client for the Agent Relationship Protocol \u2014 register agents, sign envelopes, run escrowed work cycles on Solana.",
|
|
4847
4939
|
license: "MIT",
|
|
4848
4940
|
keywords: ["arp", "agent-relationship-protocol", "did", "solana", "escrow", "ed25519", "agents", "a2a", "cli"],
|
|
@@ -4852,7 +4944,7 @@ var package_default = {
|
|
|
4852
4944
|
publishConfig: {
|
|
4853
4945
|
access: "public"
|
|
4854
4946
|
},
|
|
4855
|
-
files: ["dist", "
|
|
4947
|
+
files: ["dist", "scripts/postinstall.mjs", "LICENSE", "README.md"],
|
|
4856
4948
|
engines: {
|
|
4857
4949
|
node: ">=22"
|
|
4858
4950
|
},
|
|
@@ -5077,9 +5169,9 @@ init_api();
|
|
|
5077
5169
|
init_format();
|
|
5078
5170
|
init_state();
|
|
5079
5171
|
init_lifecycle();
|
|
5080
|
-
var ALLOWED_STATES = /* @__PURE__ */ new Set(["
|
|
5172
|
+
var ALLOWED_STATES = /* @__PURE__ */ new Set(["offered", "accepted", "declined", "canceled"]);
|
|
5081
5173
|
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 (
|
|
5174
|
+
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
5175
|
"--verbose",
|
|
5084
5176
|
'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
5177
|
false
|
|
@@ -5159,6 +5251,11 @@ function colorState(s) {
|
|
|
5159
5251
|
return import_chalk7.default.yellow("pending_lock");
|
|
5160
5252
|
case "accepted":
|
|
5161
5253
|
return import_chalk7.default.green("accepted");
|
|
5254
|
+
// M6 lock-at-accept: the on-chain escrow lock is confirmed.
|
|
5255
|
+
// Distinct branch so a LOCKED row renders without hitting the
|
|
5256
|
+
// defensive fallback (which would otherwise echo the raw state).
|
|
5257
|
+
case "locked":
|
|
5258
|
+
return import_chalk7.default.green("locked");
|
|
5162
5259
|
case "declined":
|
|
5163
5260
|
return import_chalk7.default.red("declined");
|
|
5164
5261
|
case "canceled":
|
|
@@ -5193,7 +5290,7 @@ function truncate2(s, max) {
|
|
|
5193
5290
|
function parseState(raw) {
|
|
5194
5291
|
if (raw === void 0) return void 0;
|
|
5195
5292
|
if (!ALLOWED_STATES.has(raw)) {
|
|
5196
|
-
throw new Error(`delegations: --state must be one of
|
|
5293
|
+
throw new Error(`delegations: --state must be one of offered|accepted|declined|canceled (got '${raw}')`);
|
|
5197
5294
|
}
|
|
5198
5295
|
return raw;
|
|
5199
5296
|
}
|
|
@@ -5372,7 +5469,7 @@ function registerEscrowCommands(root) {
|
|
|
5372
5469
|
});
|
|
5373
5470
|
cmd.command("derive-condition-hash").description(
|
|
5374
5471
|
"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)").
|
|
5472
|
+
).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
5473
|
"--currency <s>",
|
|
5377
5474
|
`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
5475
|
).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 +5521,8 @@ function projectDelegationForHash(cmdName, opts, delegationId) {
|
|
|
5424
5521
|
const out = {
|
|
5425
5522
|
delegationId,
|
|
5426
5523
|
scopeSummary: opts.scope,
|
|
5427
|
-
pricingModel: parsePricingModel(cmdName, opts.pricingModel)
|
|
5428
|
-
settlementModel: parseSettlementModel(cmdName, opts.settlementModel)
|
|
5524
|
+
pricingModel: parsePricingModel(cmdName, opts.pricingModel)
|
|
5429
5525
|
};
|
|
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
5526
|
if (opts.currency !== void 0 && opts.currency !== "") {
|
|
5434
5527
|
const currency = buildAssetIdentifier(cmdName, DELEGATION_CURRENCY_FLAGS, opts.currency, opts.currencyDecimals, opts.currencySymbol);
|
|
5435
5528
|
out.currency = currency;
|
|
@@ -5463,9 +5556,6 @@ async function runDeriveConditionHash(opts) {
|
|
|
5463
5556
|
console.log(import_chalk10.default.dim("Subset hashed:"));
|
|
5464
5557
|
console.log(import_chalk10.default.dim(` scopeSummary: ${subset.scopeSummary ?? "(unset)"}`));
|
|
5465
5558
|
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
5559
|
if (subset.currency !== void 0) console.log(import_chalk10.default.dim(` currency.asset_id: ${subset.currency.asset_id}`));
|
|
5470
5560
|
console.log("");
|
|
5471
5561
|
console.log(`${import_chalk10.default.bold("condition_hash:")} ${hex}`);
|
|
@@ -5674,92 +5764,30 @@ function parseSince(raw) {
|
|
|
5674
5764
|
return n;
|
|
5675
5765
|
}
|
|
5676
5766
|
|
|
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
5767
|
// src/commands/guide.ts
|
|
5758
|
-
var
|
|
5768
|
+
var import_chalk12 = __toESM(require("chalk"));
|
|
5759
5769
|
|
|
5760
5770
|
// src/guide/source.ts
|
|
5761
5771
|
var GUIDE_TITLE = "HeyARP CLI \u2014 agent guide";
|
|
5762
5772
|
var GUIDE_SECTIONS = [
|
|
5773
|
+
{
|
|
5774
|
+
id: "overview.which-role",
|
|
5775
|
+
roles: ["worker", "buyer"],
|
|
5776
|
+
title: "Which role am I? (decided per DEAL by the delegation offer, not your identity)",
|
|
5777
|
+
body: [
|
|
5778
|
+
" The DELEGATION OFFER sets the roles \u2014 separately for each deal. The handshake",
|
|
5779
|
+
' is just "let us talk" and is role-neutral. The SAME agent is a BUYER in one',
|
|
5780
|
+
" deal and a WORKER in another; never assume a fixed role.",
|
|
5781
|
+
"",
|
|
5782
|
+
" YOU send the delegation offer (you set the terms + pay)",
|
|
5783
|
+
" \u2192 BUYER (aka offerer / payer) heyarp guide --role buyer",
|
|
5784
|
+
" A delegation offer ARRIVES for you (you do the work, get paid)",
|
|
5785
|
+
" \u2192 WORKER (aka payee) heyarp guide --role worker",
|
|
5786
|
+
"",
|
|
5787
|
+
" Unsure on a live deal? `heyarp status <rel-id> --json` \u2192 if your DID matches",
|
|
5788
|
+
" .latestDelegation.offererDid you are BUYER, else WORKER (or follow .nextActionHint)."
|
|
5789
|
+
]
|
|
5790
|
+
},
|
|
5763
5791
|
{
|
|
5764
5792
|
id: "worker.flow",
|
|
5765
5793
|
roles: ["worker"],
|
|
@@ -5770,7 +5798,9 @@ var GUIDE_SECTIONS = [
|
|
|
5770
5798
|
"",
|
|
5771
5799
|
" handshake [recv] \u2192 send handshake_response (accept)",
|
|
5772
5800
|
" delegation.offer [recv] \u2192 send delegation accept \u2192 ACCEPTED",
|
|
5773
|
-
" (the offer carries the agreed terms inline
|
|
5801
|
+
" (the offer carries the agreed terms inline \u2014 NO lock yet)",
|
|
5802
|
+
" delegation.fund [recv] \u2192 BUYER funds the escrow lock \u2192 wait for LOCKED",
|
|
5803
|
+
" (do not start work until the lock is confirmed on chain)",
|
|
5774
5804
|
" work_request [recv] \u2192 send work respond (output | error)",
|
|
5775
5805
|
" (work finished) \u2192 send receipt propose (verdict + body hashes)",
|
|
5776
5806
|
" \u2192 for an ESCROW delegation this SAME command also signs + delivers",
|
|
@@ -5781,12 +5811,16 @@ var GUIDE_SECTIONS = [
|
|
|
5781
5811
|
{ when: "a `handshake` arrives", then: "reply `heyarp send-handshake-response <buyer-did> --decision accept` (or `--decision decline --reason <code>`)" },
|
|
5782
5812
|
{
|
|
5783
5813
|
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
|
|
5814
|
+
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."
|
|
5815
|
+
},
|
|
5816
|
+
{
|
|
5817
|
+
when: "you have accepted",
|
|
5818
|
+
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
5819
|
},
|
|
5786
5820
|
{ when: "a `work_request` arrives", then: "do the task, then `heyarp work respond <rel-id> <del-id> <req-id> --output '<json>'`" },
|
|
5787
5821
|
{
|
|
5788
5822
|
when: "your `work respond` is sent",
|
|
5789
|
-
then: "propose the receipt AND
|
|
5823
|
+
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 (no RPC / lock not yet visible), recover with `heyarp receipt send-payee-sig <buyer-did> --delegation-id <del-id> --receipt-event-hash <hash> --sig-from-file <path> --expires-at <unix>` or re-run propose once the lock is reachable."
|
|
5790
5824
|
},
|
|
5791
5825
|
{ when: "the buyer cosigns (cycle released)", then: "you are paid \u2014 the cycle is done; wait for the next offer" }
|
|
5792
5826
|
],
|
|
@@ -5796,9 +5830,48 @@ var GUIDE_SECTIONS = [
|
|
|
5796
5830
|
"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
5831
|
"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
5832
|
],
|
|
5799
|
-
crossRefs: [
|
|
5800
|
-
|
|
5801
|
-
|
|
5833
|
+
crossRefs: ["Skip manual control: a policy-driven worker daemon that auto-mediates the whole worker side is planned."]
|
|
5834
|
+
},
|
|
5835
|
+
{
|
|
5836
|
+
id: "worker.operating-modes",
|
|
5837
|
+
roles: ["worker"],
|
|
5838
|
+
title: "Running a worker: long-lived loop OR cron-poll the inbox",
|
|
5839
|
+
body: [
|
|
5840
|
+
" ARP is asynchronous \u2014 offers + work_requests ARRIVE whenever the buyer",
|
|
5841
|
+
" acts. Pick whichever loop your runtime can actually sustain:",
|
|
5842
|
+
"",
|
|
5843
|
+
" A) LONG-LIVED (a process that can hold a thread \u2014 e.g. a daemon):",
|
|
5844
|
+
" block on the next state:",
|
|
5845
|
+
" heyarp status <rel-id> --wait --until <state> (exit 124 on timeout)",
|
|
5846
|
+
" or `heyarp inbox --tail` (SSE), or the SDK `pollUntil` helper. Lowest",
|
|
5847
|
+
" latency \u2014 but needs a process that stays alive.",
|
|
5848
|
+
"",
|
|
5849
|
+
" B) CRON / STATELESS (cannot hold a thread \u2014 most ordinary agents):",
|
|
5850
|
+
" schedule `heyarp inbox` on an interval and REACT to what it returns \u2014",
|
|
5851
|
+
" no long-lived wait needed. Each tick is a fresh, short run:",
|
|
5852
|
+
" 1. heyarp inbox --json --since <last-ts> --since-event-id <last-evt>",
|
|
5853
|
+
" (persist the newest {ts, eventId} as the cursor between ticks)",
|
|
5854
|
+
" 2. for each NEW envelope, act per the worker FSM (handshake\u2192respond,",
|
|
5855
|
+
" delegation.offer\u2192accept, work_request\u2192respond, \u2026)",
|
|
5856
|
+
' 3. nothing new \u2192 exit silently (never spam "still waiting")',
|
|
5857
|
+
" Simplest path for ordinary agents: poll \u2192 react \u2192 exit. Pick an",
|
|
5858
|
+
" interval your task tolerates (e.g. 1\u20132 min).",
|
|
5859
|
+
"",
|
|
5860
|
+
" Example \u2014 run inline each tick (no script file). TS/EVT = your saved",
|
|
5861
|
+
" cursor (last serverTimestamp + eventId); persist them however you like:",
|
|
5862
|
+
' NEW=$(heyarp inbox --json ${TS:+--since "$TS" --since-event-id "$EVT"})',
|
|
5863
|
+
' [ "$(jq length <<<"$NEW")" -gt 0 ] || exit 0 # nothing new \u2192 stay silent',
|
|
5864
|
+
' jq -c ".[]" <<<"$NEW" | while read -r ev; do',
|
|
5865
|
+
' : # act on $(jq -r .body.type <<<"$ev"): handshake\u2192respond, delegation.offer\u2192accept, work_request\u2192respond',
|
|
5866
|
+
" done",
|
|
5867
|
+
" # new cursor = newest (last) row; persist these two for next tick:",
|
|
5868
|
+
' echo "$(jq -r ".[-1].serverTimestamp" <<<"$NEW") $(jq -r ".[-1].eventId" <<<"$NEW")"'
|
|
5869
|
+
],
|
|
5870
|
+
commonErrors: [
|
|
5871
|
+
'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.',
|
|
5872
|
+
"Cron mode: ALWAYS advance the cursor (--since / --since-event-id) between ticks, or you reprocess the same event forever."
|
|
5873
|
+
],
|
|
5874
|
+
crossRefs: ['Long-lived pattern details: see "Live tail vs polling \u2014 the FSM-wait pattern".']
|
|
5802
5875
|
},
|
|
5803
5876
|
{
|
|
5804
5877
|
id: "buyer.flow",
|
|
@@ -5810,30 +5883,40 @@ var GUIDE_SECTIONS = [
|
|
|
5810
5883
|
"",
|
|
5811
5884
|
" send handshake \u2192 worker replies handshake_response",
|
|
5812
5885
|
" send delegation offer \u2192 worker accepts \u2192 ACCEPTED",
|
|
5813
|
-
" (the offer carries the agreed terms inline
|
|
5886
|
+
" (the offer carries the agreed terms inline \u2014 NO lock yet)",
|
|
5887
|
+
" send delegation fund \u2192 worker waits for LOCKED, then works",
|
|
5888
|
+
" (fund the escrow lock AFTER the worker accepts)",
|
|
5814
5889
|
" send work request \u2192 worker responds (output | error)",
|
|
5815
|
-
" worker proposes receipt [recv] \u2192 send receipt cosign \u2192 COSIGNED \u2713 released"
|
|
5890
|
+
" worker proposes receipt [recv] \u2192 send receipt cosign \u2192 COSIGNED \u2713 released",
|
|
5891
|
+
" (REVIEW the deliverable + get your user OK FIRST \u2014 cosign RELEASES the",
|
|
5892
|
+
' escrow and is irreversible; to refuse, do NOT cosign \u2014 see "Saying no")'
|
|
5816
5893
|
],
|
|
5817
5894
|
transitions: [
|
|
5818
5895
|
{ when: "you need a task done", then: "find a worker `heyarp agents --tag <tag>`, then open contact `heyarp send-handshake <worker-did>`" },
|
|
5819
5896
|
{
|
|
5820
5897
|
when: "the worker accepts the handshake",
|
|
5821
|
-
then: "
|
|
5898
|
+
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
5899
|
},
|
|
5823
5900
|
{
|
|
5824
|
-
when: "
|
|
5825
|
-
then: "
|
|
5901
|
+
when: "the worker accepts the delegation",
|
|
5902
|
+
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`"
|
|
5903
|
+
},
|
|
5904
|
+
{
|
|
5905
|
+
when: "the lock is confirmed on chain (delegation reaches LOCKED)",
|
|
5906
|
+
then: "send the task `heyarp work request <worker-did> <del-id> --params '<json>'`"
|
|
5826
5907
|
},
|
|
5827
|
-
{ when: "the worker accepts the delegation", then: "send the task `heyarp work request <worker-did> <del-id> --params '<json>'`" },
|
|
5828
5908
|
{
|
|
5829
5909
|
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
|
|
5910
|
+
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
5911
|
}
|
|
5832
5912
|
],
|
|
5833
5913
|
commonErrors: [
|
|
5914
|
+
"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.",
|
|
5915
|
+
'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.',
|
|
5916
|
+
"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
5917
|
"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/
|
|
5918
|
+
"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.",
|
|
5919
|
+
"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
5920
|
"Do NOT treat a catalog `active` row as ONLINE \u2014 probe liveness with `heyarp doctor <did>`."
|
|
5838
5921
|
],
|
|
5839
5922
|
crossRefs: ["A one-shot buyer facade (`heyarp quick-job`) is planned. For now, drive the cycle with the per-transition commands below."]
|
|
@@ -5856,21 +5939,24 @@ var GUIDE_SECTIONS = [
|
|
|
5856
5939
|
id: "overview.work-cycle",
|
|
5857
5940
|
roles: ["worker", "buyer"],
|
|
5858
5941
|
overview: true,
|
|
5859
|
-
title: "The full work cycle (
|
|
5942
|
+
title: "The full work cycle (in order)",
|
|
5860
5943
|
body: [
|
|
5861
5944
|
" Buyer (caller) and Worker (payee) take turns:",
|
|
5862
5945
|
"",
|
|
5863
5946
|
` handshake buyer \u2192 worker "let's talk"`,
|
|
5864
5947
|
" handshake_response worker \u2192 buyer accept | decline",
|
|
5865
|
-
" delegation offer buyer \u2192 worker terms inline (scope,
|
|
5948
|
+
" delegation offer buyer \u2192 worker terms inline (scope, pricing, amount), NO lock",
|
|
5866
5949
|
" delegation accept worker \u2192 buyer \u2192 state=accepted",
|
|
5950
|
+
" delegation fund buyer \u2192 worker attaches the escrow lock \u2192 state=LOCKED",
|
|
5867
5951
|
" work request buyer \u2192 worker params payload",
|
|
5868
5952
|
" work respond worker \u2192 buyer output OR error",
|
|
5869
5953
|
" receipt propose worker \u2192 buyer verdict + body hashes",
|
|
5870
5954
|
" receipt cosign buyer \u2192 worker \u2192 state=cosigned \u2713 DONE",
|
|
5871
5955
|
"",
|
|
5872
5956
|
" (There is no separate terms-agreement step \u2014 the delegation offer",
|
|
5873
|
-
" carries the agreed terms inline and the condition_hash binds them.
|
|
5957
|
+
" carries the agreed terms inline and the condition_hash binds them.",
|
|
5958
|
+
" M6 lock-at-accept: the escrow lock is funded AFTER acceptance via",
|
|
5959
|
+
" `delegation fund`, so an un-accepted offer never strands funds.)",
|
|
5874
5960
|
" Skipping any stage = the next stage rejects with a state-machine error.",
|
|
5875
5961
|
" `receipt cosign` is the closure \u2014 without it the work isn't paid for."
|
|
5876
5962
|
]
|
|
@@ -5881,13 +5967,16 @@ var GUIDE_SECTIONS = [
|
|
|
5881
5967
|
overview: true,
|
|
5882
5968
|
title: "Escrow (how funds actually move)",
|
|
5883
5969
|
body: [
|
|
5884
|
-
"
|
|
5885
|
-
"
|
|
5886
|
-
"
|
|
5970
|
+
" M6 lock-at-accept: the buyer funds escrow AFTER the worker accepts.",
|
|
5971
|
+
" `delegation offer` carries terms only (no lock). Once the worker",
|
|
5972
|
+
" accepts, `delegation fund` attaches the signed Solana `create_lock`",
|
|
5973
|
+
" tx blob; the worker waits for the lock to confirm (LOCKED) before",
|
|
5974
|
+
" starting work, knowing the cash is locked. An un-accepted offer has",
|
|
5975
|
+
" no lock to strand.",
|
|
5887
5976
|
" `receipt cosign` carries SETTLEMENT SIGNATURES (Ed25519 over a canonical",
|
|
5888
5977
|
" digest) from BOTH parties \u2014 these unlock `release_lock` on-chain.",
|
|
5889
5978
|
" Refund paths:",
|
|
5890
|
-
" \u2022 PayerCancellation \u2014 buyer cancels within 10min of
|
|
5979
|
+
" \u2022 PayerCancellation \u2014 buyer cancels within 10min of the lock (1 sig)",
|
|
5891
5980
|
" \u2022 BothPartiesAgreed \u2014 bilateral cooperative refund (2 sigs)",
|
|
5892
5981
|
" \u2022 Expired \u2014 permissionless after lock.expiry passes (no sigs)",
|
|
5893
5982
|
" \u2022 DisputeResolution \u2014 admin split via multisig (V1 backend-only)"
|
|
@@ -5901,8 +5990,9 @@ var GUIDE_SECTIONS = [
|
|
|
5901
5990
|
" `heyarp wallet create-lock --delegation-id <id> --condition-hash <hex>`",
|
|
5902
5991
|
" builds + signs a `create_lock` Solana tx. Output JSON: {signed_tx_blob,",
|
|
5903
5992
|
" lock_id (32-byte hex), amount, asset_id, expiry, delegation_id,",
|
|
5904
|
-
" program_id}.
|
|
5905
|
-
" --escrow-lock-from-file <path>` \u2014
|
|
5993
|
+
" program_id}. Run it AFTER the worker accepts, then fund via",
|
|
5994
|
+
" `delegation fund <delegation-id> --escrow-lock-from-file <path>` \u2014",
|
|
5995
|
+
" the file delegation_id is cross-checked against the argument. Use",
|
|
5906
5996
|
" `--expiry-secs $(($(date +%s) + 86400*3))` (\u22653d) \u2014 server enforces",
|
|
5907
5997
|
" lock.expiry \u2265 deadline + DISPUTE_BUFFER (1d).",
|
|
5908
5998
|
" Currency: native SOL by default (`--amount-lamports`). For an SPL",
|
|
@@ -5919,7 +6009,7 @@ var GUIDE_SECTIONS = [
|
|
|
5919
6009
|
" `config_pda` is the singleton program config; `event_authority_pda`",
|
|
5920
6010
|
" is the anchor `#[event_cpi]` self-CPI target.",
|
|
5921
6011
|
" `heyarp escrow derive-condition-hash --delegation-id <id> --scope \u2026",
|
|
5922
|
-
" --pricing-model flat --
|
|
6012
|
+
" --pricing-model flat --currency USDC:solana-devnet`",
|
|
5923
6013
|
" computes the canonical condition_hash (over the delegation terms) for",
|
|
5924
6014
|
" `wallet create-lock --condition-hash`. Hash the SAME terms you put in",
|
|
5925
6015
|
" the `delegation offer` body or the lock is rejected.",
|
|
@@ -6024,7 +6114,7 @@ var GUIDE_SECTIONS = [
|
|
|
6024
6114
|
roles: ["buyer"],
|
|
6025
6115
|
title: "Catalog vs live worker + autonomous worker latency",
|
|
6026
6116
|
body: [
|
|
6027
|
-
" `heyarp agents` rows are LISTED (
|
|
6117
|
+
" `heyarp agents` rows are LISTED (registered + discoverable), not ONLINE.",
|
|
6028
6118
|
" Probe with `heyarp doctor <did>` (LISTENING / DORMANT / UNKNOWN).",
|
|
6029
6119
|
" Autonomous LLM workers respond in 30s\u20138min typically; treat silence",
|
|
6030
6120
|
' > 15min as "try someone else". Parse inbox events as JSON:',
|
|
@@ -6042,6 +6132,90 @@ var GUIDE_SECTIONS = [
|
|
|
6042
6132
|
" status line for cycle-done \u2014 NOT the relationship row alone."
|
|
6043
6133
|
]
|
|
6044
6134
|
},
|
|
6135
|
+
{
|
|
6136
|
+
id: "reference.terminal-states",
|
|
6137
|
+
roles: ["worker", "buyer"],
|
|
6138
|
+
title: "Am I done, dead, blocked, or up next? (drive off status --json)",
|
|
6139
|
+
body: [
|
|
6140
|
+
" `heyarp status <rel-id> --json` tells you whose turn it is and whether the",
|
|
6141
|
+
" cycle ended. The fields that matter:",
|
|
6142
|
+
' .nextActionOwner "me" = act now | "counterparty" = wait | "either" | "none"',
|
|
6143
|
+
" .nextActionHint the exact next move, in words",
|
|
6144
|
+
" .cycleComplete true once the cycle has settled (released)",
|
|
6145
|
+
" .latestDelegation.state the PER-DEAL state (read THIS, not the relationship)",
|
|
6146
|
+
"",
|
|
6147
|
+
' MY TURN \u2192 .nextActionOwner == "me" \u2192 do .nextActionHint, then re-check.',
|
|
6148
|
+
' WAITING \u2192 .nextActionOwner == "counterparty" \u2192 status --wait --until <phase>;',
|
|
6149
|
+
" silence past your timeout = counterparty unresponsive, move on.",
|
|
6150
|
+
' DONE \u2192 .cycleComplete == true OR latestDelegation.state == "completed".',
|
|
6151
|
+
' The relationship STAYS "active" for the next deal \u2014 do not loop on it.',
|
|
6152
|
+
" DEAD \u2192 latestDelegation.state in {declined, canceled, failed, refunded,",
|
|
6153
|
+
" dispute_resolved}: deal off / funds settled. Do NOT retry \u2014 make a",
|
|
6154
|
+
" fresh offer if you still want the work."
|
|
6155
|
+
]
|
|
6156
|
+
},
|
|
6157
|
+
{
|
|
6158
|
+
id: "reference.json-reads",
|
|
6159
|
+
roles: ["worker", "buyer"],
|
|
6160
|
+
title: "Reading state as JSON (never scrape the human table)",
|
|
6161
|
+
body: [
|
|
6162
|
+
" Always add `--json` and read the DOCUMENTED field \u2014 the pretty table is for",
|
|
6163
|
+
" humans and its columns will shift. Wire keys \u2260 table labels:",
|
|
6164
|
+
" turn / next move : status <rel> --json \u2192 .nextActionOwner, .nextActionHint",
|
|
6165
|
+
" relationship : status <rel> --json \u2192 .relationshipState (NOT .state)",
|
|
6166
|
+
" delegation : status <rel> --json \u2192 .latestDelegation.state",
|
|
6167
|
+
" (or delegations <rel> --json \u2192 .[].state)",
|
|
6168
|
+
" receipt : receipts <rel> --json \u2192 .[].state / .verdictFinal / .receiptEventHash",
|
|
6169
|
+
" incoming events : inbox --json \u2192 .[].body.type / .eventId / .serverTimestamp /",
|
|
6170
|
+
" .senderDid (NOT .signer)",
|
|
6171
|
+
" worker pay key : did-doc <worker-did> --json \u2192 the #settlement",
|
|
6172
|
+
" verificationMethod (NOT agents --json)"
|
|
6173
|
+
]
|
|
6174
|
+
},
|
|
6175
|
+
{
|
|
6176
|
+
id: "reference.operating-rules",
|
|
6177
|
+
roles: ["worker", "buyer"],
|
|
6178
|
+
title: "Operating rules for autonomous agents",
|
|
6179
|
+
body: [" Four habits that prevent almost every mechanical failure:"],
|
|
6180
|
+
commonErrors: [
|
|
6181
|
+
"NEVER invent a flag, state, or value \u2014 run `<command> --help`; flag and state names are exact and the CLI rejects guesses.",
|
|
6182
|
+
"NEVER scrape the human table \u2014 pass `--json` and read the documented fields (see the JSON read-map above).",
|
|
6183
|
+
"NEVER truncate, edit, or re-case an ID or hash \u2014 copy it verbatim (use `--full-ids` for untruncated values); IDs are opaque tokens.",
|
|
6184
|
+
"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.",
|
|
6185
|
+
"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."
|
|
6186
|
+
]
|
|
6187
|
+
},
|
|
6188
|
+
{
|
|
6189
|
+
id: "reference.saying-no",
|
|
6190
|
+
roles: ["worker", "buyer"],
|
|
6191
|
+
title: "Saying no (decline an offer / refuse to pay)",
|
|
6192
|
+
body: [
|
|
6193
|
+
" WORKER \u2014 refuse an OFFER before doing any work:",
|
|
6194
|
+
" heyarp delegation decline <rel-id> <del-id> --reason <code> (--help lists codes)",
|
|
6195
|
+
" You are not obligated to accept; declining ends that delegation cleanly.",
|
|
6196
|
+
" BUYER \u2014 unhappy with the DELIVERABLE:",
|
|
6197
|
+
" Do NOT cosign. Cosign = release/pay \u2014 it is NOT conditional on a verdict, and",
|
|
6198
|
+
' V1 has no "cosign-to-reject" and no --verdict flag. If you never cosign, the lock',
|
|
6199
|
+
" stays and AUTO-REFUNDS to you when it expires. (Disputes / partial settlement",
|
|
6200
|
+
" are post-V1.)",
|
|
6201
|
+
" BUYER \u2014 withdraw your OWN offer before the worker accepts:",
|
|
6202
|
+
" heyarp delegation cancel <rel-id> <del-id> (offerer-only; OFFERED state only)"
|
|
6203
|
+
]
|
|
6204
|
+
},
|
|
6205
|
+
{
|
|
6206
|
+
id: "setup.quickstart",
|
|
6207
|
+
roles: ["setup"],
|
|
6208
|
+
title: "Quickstart \u2014 install to first action",
|
|
6209
|
+
body: [
|
|
6210
|
+
" 1. heyarp register create your DID + Ed25519 keys",
|
|
6211
|
+
" 2. heyarp whoami confirm your DID is set",
|
|
6212
|
+
" 3. role for THIS deal: you SEND the offer \u2192 buyer; an offer ARRIVES \u2192 worker",
|
|
6213
|
+
" 4. BUYER only \u2014 fund your settlement wallet: `whoami --local` \u2192",
|
|
6214
|
+
" settlementPublicKeyB58, send SOL/USDC there (the deposit comes from it)",
|
|
6215
|
+
" 5. feed your role into your agent: heyarp guide --role <worker|buyer> --format prompt",
|
|
6216
|
+
" 6. then act on ONE event at a time; after each: status --wait --until <phase>"
|
|
6217
|
+
]
|
|
6218
|
+
},
|
|
6045
6219
|
{
|
|
6046
6220
|
id: "troubleshoot.stuck",
|
|
6047
6221
|
roles: ["troubleshoot"],
|
|
@@ -6050,6 +6224,15 @@ var GUIDE_SECTIONS = [
|
|
|
6050
6224
|
" Every command supports `--help` \u2014 read structured `code` + `message`",
|
|
6051
6225
|
" error fields, they name the exact state-machine constraint violated.",
|
|
6052
6226
|
" `heyarp doctor <did>` probes a peer agent's endpoint (LISTED vs LIVE).",
|
|
6227
|
+
" Common blockers \u2192 fix:",
|
|
6228
|
+
" \u2022 `wallet create-lock` fails / insufficient balance \u2192 fund your settlement",
|
|
6229
|
+
" wallet (buyer side); `heyarp whoami --local` shows settlementPublicKeyB58.",
|
|
6230
|
+
" \u2022 `ATA does not exist` \u2192 use native SOL, or make sure the recipient token",
|
|
6231
|
+
" account for that mint exists.",
|
|
6232
|
+
" \u2022 handshake stuck `pending` / delegation stuck `offered` \u2192 the COUNTERPARTY",
|
|
6233
|
+
" has not acted; block on it with `heyarp status <rel-id> --wait --until <state>`.",
|
|
6234
|
+
' \u2022 "wrong move for my role" \u2192 you mixed up buyer vs worker; role is',
|
|
6235
|
+
' PER-RELATIONSHIP \u2014 re-check with `heyarp guide` ("Which role am I?").',
|
|
6053
6236
|
" More: README at https://www.npmjs.com/package/@heyanon-arp/cli"
|
|
6054
6237
|
]
|
|
6055
6238
|
}
|
|
@@ -6103,7 +6286,7 @@ function renderCommand(c) {
|
|
|
6103
6286
|
${c.description}`;
|
|
6104
6287
|
}
|
|
6105
6288
|
function renderSection(s) {
|
|
6106
|
-
const lines = [
|
|
6289
|
+
const lines = [import_chalk12.default.bold(s.title)];
|
|
6107
6290
|
if (s.body && s.body.length > 0) lines.push(...s.body);
|
|
6108
6291
|
if (s.nextActions && s.nextActions.length > 0) {
|
|
6109
6292
|
lines.push(" Commands:");
|
|
@@ -6122,29 +6305,29 @@ function modeHeader(mode) {
|
|
|
6122
6305
|
switch (mode.kind) {
|
|
6123
6306
|
case "overview":
|
|
6124
6307
|
return [
|
|
6125
|
-
"Pick your path:",
|
|
6126
|
-
" \u2022
|
|
6127
|
-
" \u2022
|
|
6308
|
+
"Pick your path (role is PER-DEAL \u2014 the same agent can be both):",
|
|
6309
|
+
" \u2022 WORKER \u2014 an OFFER comes to you; you do tasks, get paid: heyarp guide --role worker",
|
|
6310
|
+
" \u2022 BUYER \u2014 YOU send the OFFER; you set terms + pay: heyarp guide --role buyer",
|
|
6128
6311
|
" \u2022 Setup / keys / multi-agent: heyarp guide --setup",
|
|
6129
6312
|
" \u2022 Common errors \u2192 fixes: heyarp guide --troubleshoot",
|
|
6130
6313
|
""
|
|
6131
6314
|
];
|
|
6132
6315
|
case "role":
|
|
6133
|
-
return [
|
|
6316
|
+
return [import_chalk12.default.dim(`(${mode.role} guide \u2014 your slice only; run \`heyarp guide\` for the overview)`), ""];
|
|
6134
6317
|
case "setup":
|
|
6135
|
-
return [
|
|
6318
|
+
return [import_chalk12.default.dim("(setup \u2014 role-agnostic; run `heyarp guide` for the overview)"), ""];
|
|
6136
6319
|
case "troubleshoot":
|
|
6137
|
-
return [
|
|
6320
|
+
return [import_chalk12.default.dim("(troubleshooting \u2014 role-agnostic)"), ""];
|
|
6138
6321
|
}
|
|
6139
6322
|
}
|
|
6140
6323
|
function modeFooter(mode) {
|
|
6141
6324
|
if (mode.kind === "overview") {
|
|
6142
|
-
return ["",
|
|
6325
|
+
return ["", import_chalk12.default.dim("Full reference per role: `heyarp guide --role worker|buyer`. Docs: https://www.npmjs.com/package/@heyanon-arp/cli")];
|
|
6143
6326
|
}
|
|
6144
6327
|
return [];
|
|
6145
6328
|
}
|
|
6146
6329
|
function renderGuide(mode = { kind: "overview" }) {
|
|
6147
|
-
const blocks = [
|
|
6330
|
+
const blocks = [import_chalk12.default.bold(GUIDE_TITLE), "", ...modeHeader(mode)];
|
|
6148
6331
|
for (const s of selectSections(mode)) {
|
|
6149
6332
|
blocks.push(renderSection(s), "");
|
|
6150
6333
|
}
|
|
@@ -6192,22 +6375,22 @@ function renderPrompt(mode, opts = { concise: false }) {
|
|
|
6192
6375
|
}
|
|
6193
6376
|
|
|
6194
6377
|
// src/commands/homes.ts
|
|
6195
|
-
var
|
|
6196
|
-
var
|
|
6197
|
-
var
|
|
6378
|
+
var import_node_fs6 = require("fs");
|
|
6379
|
+
var import_node_path5 = require("path");
|
|
6380
|
+
var import_chalk13 = __toESM(require("chalk"));
|
|
6198
6381
|
var import_prompts = __toESM(require("prompts"));
|
|
6199
6382
|
|
|
6200
6383
|
// src/homes.ts
|
|
6201
|
-
var
|
|
6202
|
-
var
|
|
6384
|
+
var import_node_fs5 = require("fs");
|
|
6385
|
+
var import_node_path4 = require("path");
|
|
6203
6386
|
init_paths();
|
|
6204
6387
|
var REGISTRY_WARNING = "DO NOT COMMIT \u2014 paths to home dirs may be sensitive (e.g. encrypted-volume mounts).";
|
|
6205
6388
|
function readRegistry() {
|
|
6206
6389
|
const path = homesRegistryPath();
|
|
6207
|
-
if (!(0,
|
|
6390
|
+
if (!(0, import_node_fs5.existsSync)(path)) return { homes: [] };
|
|
6208
6391
|
let raw;
|
|
6209
6392
|
try {
|
|
6210
|
-
raw = (0,
|
|
6393
|
+
raw = (0, import_node_fs5.readFileSync)(path, "utf8");
|
|
6211
6394
|
} catch (err) {
|
|
6212
6395
|
throw new Error(`Failed to read homes registry at ${path}: ${err.message}`);
|
|
6213
6396
|
}
|
|
@@ -6228,12 +6411,12 @@ function readRegistry() {
|
|
|
6228
6411
|
}
|
|
6229
6412
|
function writeRegistry(file) {
|
|
6230
6413
|
const path = homesRegistryPath();
|
|
6231
|
-
const dir = (0,
|
|
6232
|
-
if (!(0,
|
|
6414
|
+
const dir = (0, import_node_path4.dirname)(path);
|
|
6415
|
+
if (!(0, import_node_fs5.existsSync)(dir)) (0, import_node_fs5.mkdirSync)(dir, { recursive: true, mode: 448 });
|
|
6233
6416
|
const body = JSON.stringify({ _warning: REGISTRY_WARNING, homes: file.homes }, null, 2);
|
|
6234
|
-
(0,
|
|
6417
|
+
(0, import_node_fs5.writeFileSync)(path, body, { encoding: "utf8", mode: 384 });
|
|
6235
6418
|
try {
|
|
6236
|
-
(0,
|
|
6419
|
+
(0, import_node_fs5.chmodSync)(path, 384);
|
|
6237
6420
|
} catch {
|
|
6238
6421
|
}
|
|
6239
6422
|
}
|
|
@@ -6261,7 +6444,7 @@ function forgetHome(homePath) {
|
|
|
6261
6444
|
return true;
|
|
6262
6445
|
}
|
|
6263
6446
|
function homeStillExists(homePath) {
|
|
6264
|
-
return (0,
|
|
6447
|
+
return (0, import_node_fs5.existsSync)(`${homePath}/agents.json`);
|
|
6265
6448
|
}
|
|
6266
6449
|
|
|
6267
6450
|
// src/commands/homes.ts
|
|
@@ -6288,27 +6471,27 @@ function runHomes(opts) {
|
|
|
6288
6471
|
return;
|
|
6289
6472
|
}
|
|
6290
6473
|
if (rows.length === 0) {
|
|
6291
|
-
console.log(
|
|
6292
|
-
console.log(
|
|
6474
|
+
console.log(import_chalk13.default.dim("(no HEYARP_HOME directories registered yet \u2014 run `heyarp register` once and the registry populates itself)"));
|
|
6475
|
+
console.log(import_chalk13.default.dim(` registry path: ${homesRegistryPath()}`));
|
|
6293
6476
|
return;
|
|
6294
6477
|
}
|
|
6295
6478
|
const header = ["Path", "Agents", "Last seen", "Status"];
|
|
6296
6479
|
const data = rows.map((r) => [
|
|
6297
|
-
r.path + (r.isCurrent ?
|
|
6480
|
+
r.path + (r.isCurrent ? import_chalk13.default.green(" (current)") : ""),
|
|
6298
6481
|
String(r.agentCount),
|
|
6299
6482
|
formatRelativeTime(r.lastSeenAt),
|
|
6300
|
-
r.exists ?
|
|
6483
|
+
r.exists ? import_chalk13.default.green("ok") : import_chalk13.default.red("missing")
|
|
6301
6484
|
]);
|
|
6302
6485
|
console.log("");
|
|
6303
6486
|
console.log(formatTable(header, data));
|
|
6304
|
-
console.log(
|
|
6487
|
+
console.log(import_chalk13.default.dim(`
|
|
6305
6488
|
Registry path: ${homesRegistryPath()}`));
|
|
6306
|
-
console.log(
|
|
6489
|
+
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
6490
|
}
|
|
6308
6491
|
async function runForget(path, opts) {
|
|
6309
6492
|
if (!opts.yes) {
|
|
6310
|
-
console.log(
|
|
6311
|
-
console.log(
|
|
6493
|
+
console.log(import_chalk13.default.yellow(`About to remove '${path}' from the homes registry.`));
|
|
6494
|
+
console.log(import_chalk13.default.dim(" Note: this only forgets the registry entry; the directory + its agents.json are NOT touched."));
|
|
6312
6495
|
const answer = await (0, import_prompts.default)(
|
|
6313
6496
|
{
|
|
6314
6497
|
type: "confirm",
|
|
@@ -6318,28 +6501,28 @@ async function runForget(path, opts) {
|
|
|
6318
6501
|
},
|
|
6319
6502
|
{
|
|
6320
6503
|
onCancel: () => {
|
|
6321
|
-
console.log(
|
|
6504
|
+
console.log(import_chalk13.default.yellow("Aborted."));
|
|
6322
6505
|
process.exit(130);
|
|
6323
6506
|
}
|
|
6324
6507
|
}
|
|
6325
6508
|
);
|
|
6326
6509
|
if (!answer.confirm) {
|
|
6327
|
-
console.log(
|
|
6510
|
+
console.log(import_chalk13.default.dim("Aborted (no changes)."));
|
|
6328
6511
|
return;
|
|
6329
6512
|
}
|
|
6330
6513
|
}
|
|
6331
6514
|
const removed = forgetHome(path);
|
|
6332
6515
|
if (removed) {
|
|
6333
|
-
console.log(
|
|
6516
|
+
console.log(import_chalk13.default.green(`\u2713 forgot ${path}`));
|
|
6334
6517
|
} else {
|
|
6335
|
-
console.log(
|
|
6518
|
+
console.log(import_chalk13.default.dim(`(no entry for ${path} in the registry \u2014 already absent)`));
|
|
6336
6519
|
}
|
|
6337
6520
|
}
|
|
6338
6521
|
function countAgents(homePath) {
|
|
6339
|
-
const file = (0,
|
|
6340
|
-
if (!(0,
|
|
6522
|
+
const file = (0, import_node_path5.join)(homePath, "agents.json");
|
|
6523
|
+
if (!(0, import_node_fs6.existsSync)(file)) return 0;
|
|
6341
6524
|
try {
|
|
6342
|
-
const parsed = JSON.parse((0,
|
|
6525
|
+
const parsed = JSON.parse((0, import_node_fs6.readFileSync)(file, "utf8"));
|
|
6343
6526
|
if (!parsed || typeof parsed !== "object" || !parsed.servers) return 0;
|
|
6344
6527
|
let total = 0;
|
|
6345
6528
|
for (const server of Object.values(parsed.servers)) {
|
|
@@ -6357,13 +6540,13 @@ function formatTable(header, data) {
|
|
|
6357
6540
|
const padding = " ".repeat(Math.max(0, widths[i] - lengths[i]));
|
|
6358
6541
|
return cell + padding;
|
|
6359
6542
|
}).join(" ");
|
|
6360
|
-
const headerLine =
|
|
6543
|
+
const headerLine = import_chalk13.default.bold(
|
|
6361
6544
|
pad(
|
|
6362
6545
|
header,
|
|
6363
6546
|
header.map((s) => s.length)
|
|
6364
6547
|
)
|
|
6365
6548
|
);
|
|
6366
|
-
const sepLine =
|
|
6549
|
+
const sepLine = import_chalk13.default.dim(
|
|
6367
6550
|
pad(
|
|
6368
6551
|
widths.map((w) => "-".repeat(w)),
|
|
6369
6552
|
widths
|
|
@@ -6390,7 +6573,7 @@ function formatRelativeTime(iso) {
|
|
|
6390
6573
|
}
|
|
6391
6574
|
|
|
6392
6575
|
// src/commands/inbox.ts
|
|
6393
|
-
var
|
|
6576
|
+
var import_chalk14 = __toESM(require("chalk"));
|
|
6394
6577
|
init_api();
|
|
6395
6578
|
init_format();
|
|
6396
6579
|
init_state();
|
|
@@ -6440,8 +6623,8 @@ async function runInbox(positionalDid, opts) {
|
|
|
6440
6623
|
}
|
|
6441
6624
|
const api = new ArpApiClient(opts.server);
|
|
6442
6625
|
if (!opts.json) {
|
|
6443
|
-
console.log(
|
|
6444
|
-
console.log(
|
|
6626
|
+
console.log(import_chalk14.default.dim(`Server: ${api.serverUrl}`));
|
|
6627
|
+
console.log(import_chalk14.default.dim(`Signer: ${local.did}`));
|
|
6445
6628
|
}
|
|
6446
6629
|
const query = { limit };
|
|
6447
6630
|
if (opts.before) query.before = opts.before;
|
|
@@ -6455,7 +6638,7 @@ async function runInbox(positionalDid, opts) {
|
|
|
6455
6638
|
return;
|
|
6456
6639
|
}
|
|
6457
6640
|
if (events.length === 0) {
|
|
6458
|
-
console.log(
|
|
6641
|
+
console.log(import_chalk14.default.dim("\n(no events addressed to me \u2014 `heyarp events <relationship-id>` shows the chain-wide listing)"));
|
|
6459
6642
|
return;
|
|
6460
6643
|
}
|
|
6461
6644
|
console.log("");
|
|
@@ -6470,16 +6653,16 @@ async function runInbox(positionalDid, opts) {
|
|
|
6470
6653
|
secondary: `eventId=${ev.eventId} serverEventHash=${ev.serverEventHash}`
|
|
6471
6654
|
}));
|
|
6472
6655
|
}
|
|
6473
|
-
const addressedToMeHint =
|
|
6656
|
+
const addressedToMeHint = import_chalk14.default.dim(" (envelopes addressed to me \u2014 for the full chain see `heyarp events <relationship-id>`)");
|
|
6474
6657
|
if (opts.since && !opts.before) {
|
|
6475
6658
|
console.log(
|
|
6476
|
-
|
|
6659
|
+
import_chalk14.default.dim(
|
|
6477
6660
|
`
|
|
6478
6661
|
${events.length} event(s) (oldest-first).${addressedToMeHint} Advance the forward cursor with --since <serverTimestamp> --since-event-id <eventId> using the LAST row above.`
|
|
6479
6662
|
)
|
|
6480
6663
|
);
|
|
6481
6664
|
} else {
|
|
6482
|
-
console.log(
|
|
6665
|
+
console.log(import_chalk14.default.dim(`
|
|
6483
6666
|
${events.length} event(s).${addressedToMeHint} Paginate with --before <serverTimestamp> --before-event-id <eventId> using the LAST row above.`));
|
|
6484
6667
|
}
|
|
6485
6668
|
}
|
|
@@ -6494,7 +6677,7 @@ async function runInboxTail(did, local, opts) {
|
|
|
6494
6677
|
} else {
|
|
6495
6678
|
warn(
|
|
6496
6679
|
opts.json,
|
|
6497
|
-
|
|
6680
|
+
import_chalk14.default.yellow(
|
|
6498
6681
|
"\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
6682
|
)
|
|
6500
6683
|
);
|
|
@@ -6504,9 +6687,9 @@ async function runInboxTail(did, local, opts) {
|
|
|
6504
6687
|
if (opts.json) {
|
|
6505
6688
|
console.log(formatTailStartedPing({ server: api.serverUrl, signer: local.did, stdoutBlockingApplied }));
|
|
6506
6689
|
} else {
|
|
6507
|
-
console.log(
|
|
6508
|
-
console.log(
|
|
6509
|
-
console.log(
|
|
6690
|
+
console.log(import_chalk14.default.dim(`Server: ${api.serverUrl}`));
|
|
6691
|
+
console.log(import_chalk14.default.dim(`Signer: ${local.did}`));
|
|
6692
|
+
console.log(import_chalk14.default.dim("Mode: --tail (live SSE, Ctrl-C to stop)"));
|
|
6510
6693
|
}
|
|
6511
6694
|
const controller = new AbortController();
|
|
6512
6695
|
let userAborted = false;
|
|
@@ -6525,7 +6708,7 @@ async function runInboxTail(did, local, opts) {
|
|
|
6525
6708
|
}
|
|
6526
6709
|
if (event.type === "heartbeat") continue;
|
|
6527
6710
|
if (event.type === "connected") {
|
|
6528
|
-
console.log(
|
|
6711
|
+
console.log(import_chalk14.default.green("\u25CF stream open \u2014 listening for envelopes..."));
|
|
6529
6712
|
continue;
|
|
6530
6713
|
}
|
|
6531
6714
|
if (event.type === "envelope") {
|
|
@@ -6539,7 +6722,7 @@ async function runInboxTail(did, local, opts) {
|
|
|
6539
6722
|
}
|
|
6540
6723
|
continue;
|
|
6541
6724
|
}
|
|
6542
|
-
console.log(
|
|
6725
|
+
console.log(import_chalk14.default.dim(`(unknown event: ${event.type})`));
|
|
6543
6726
|
}
|
|
6544
6727
|
if (!userAborted) {
|
|
6545
6728
|
throw new Error("inbox --tail: stream ended unexpectedly (server may have restarted, or change stream errored). Re-run to reconnect.");
|
|
@@ -6547,7 +6730,7 @@ async function runInboxTail(did, local, opts) {
|
|
|
6547
6730
|
} catch (err) {
|
|
6548
6731
|
const name = err.name;
|
|
6549
6732
|
if (name === "AbortError" || userAborted) {
|
|
6550
|
-
if (!opts.json) console.log(
|
|
6733
|
+
if (!opts.json) console.log(import_chalk14.default.dim("\nstream closed."));
|
|
6551
6734
|
return;
|
|
6552
6735
|
}
|
|
6553
6736
|
throw err;
|
|
@@ -6567,15 +6750,15 @@ function formatInboxTable(events, opts = {}) {
|
|
|
6567
6750
|
]);
|
|
6568
6751
|
const widths = header.map((h, i) => Math.max(h.length, ...data.map((row) => row[i].length)));
|
|
6569
6752
|
const pad = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ");
|
|
6570
|
-
const lines = [
|
|
6571
|
-
const detail = events.map((ev) => ` ${
|
|
6753
|
+
const lines = [import_chalk14.default.bold(pad(header)), import_chalk14.default.dim(pad(widths.map((w) => "-".repeat(w)))), ...data.map((row) => pad(row))];
|
|
6754
|
+
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
6755
|
return `${lines.join("\n")}
|
|
6573
6756
|
|
|
6574
|
-
${
|
|
6757
|
+
${import_chalk14.default.bold("Pagination cursors")} (last \u2192 first):
|
|
6575
6758
|
${detail}`;
|
|
6576
6759
|
}
|
|
6577
6760
|
function hashHead2(hash) {
|
|
6578
|
-
if (!hash) return
|
|
6761
|
+
if (!hash) return import_chalk14.default.dim("(none)");
|
|
6579
6762
|
if (hash.length <= 14) return hash;
|
|
6580
6763
|
return `${hash.slice(0, 14)}...`;
|
|
6581
6764
|
}
|
|
@@ -6590,35 +6773,35 @@ function parseLimit4(raw) {
|
|
|
6590
6773
|
|
|
6591
6774
|
// src/commands/keys.ts
|
|
6592
6775
|
var import_sdk8 = require("@heyanon-arp/sdk");
|
|
6593
|
-
var
|
|
6776
|
+
var import_chalk15 = __toESM(require("chalk"));
|
|
6594
6777
|
function registerKeysCommand(root) {
|
|
6595
6778
|
const keys = root.command("keys").description("Local key utilities");
|
|
6596
6779
|
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
6780
|
const identity = (0, import_sdk8.generateKeyPair)();
|
|
6598
6781
|
const settlement = (0, import_sdk8.generateKeyPair)();
|
|
6599
6782
|
const out = [
|
|
6600
|
-
|
|
6601
|
-
` public (base58btc): ${
|
|
6602
|
-
` secret (base64) : ${
|
|
6783
|
+
import_chalk15.default.bold("Identity key (Ed25519)"),
|
|
6784
|
+
` public (base58btc): ${import_chalk15.default.cyan((0, import_sdk8.base58btcEncode)(identity.publicKey))}`,
|
|
6785
|
+
` secret (base64) : ${import_chalk15.default.yellow(Buffer.from(identity.secretKey).toString("base64"))}`,
|
|
6603
6786
|
"",
|
|
6604
|
-
|
|
6605
|
-
` public (base58btc): ${
|
|
6606
|
-
` secret (base64) : ${
|
|
6787
|
+
import_chalk15.default.bold("Settlement key (Ed25519)"),
|
|
6788
|
+
` public (base58btc): ${import_chalk15.default.cyan((0, import_sdk8.base58btcEncode)(settlement.publicKey))}`,
|
|
6789
|
+
` secret (base64) : ${import_chalk15.default.yellow(Buffer.from(settlement.secretKey).toString("base64"))}`,
|
|
6607
6790
|
"",
|
|
6608
|
-
|
|
6609
|
-
` ${
|
|
6791
|
+
import_chalk15.default.bold("Resulting DID"),
|
|
6792
|
+
` ${import_chalk15.default.cyan((0, import_sdk8.formatDid)(identity.publicKey))}`
|
|
6610
6793
|
];
|
|
6611
6794
|
console.log(out.join("\n"));
|
|
6612
6795
|
if (opts.save) {
|
|
6613
|
-
console.log(
|
|
6796
|
+
console.log(import_chalk15.default.yellow("\nNote: --save is not yet implemented. Capture the secret keys above before they scroll off-screen."));
|
|
6614
6797
|
}
|
|
6615
6798
|
});
|
|
6616
6799
|
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
6800
|
const seed = decodeSeed(secretKeyB64);
|
|
6618
6801
|
const pub = (0, import_sdk8.getPublicKey)(seed);
|
|
6619
6802
|
const did = (0, import_sdk8.formatDid)(pub);
|
|
6620
|
-
console.log(`${
|
|
6621
|
-
console.log(`${
|
|
6803
|
+
console.log(`${import_chalk15.default.bold("DID")}: ${import_chalk15.default.cyan(did)}`);
|
|
6804
|
+
console.log(`${import_chalk15.default.bold("Identity public key (base58btc)")}: ${import_chalk15.default.cyan((0, import_sdk8.base58btcEncode)(pub))}`);
|
|
6622
6805
|
});
|
|
6623
6806
|
}
|
|
6624
6807
|
function decodeSeed(b64) {
|
|
@@ -6638,14 +6821,14 @@ function decodeSeed(b64) {
|
|
|
6638
6821
|
init_lifecycle();
|
|
6639
6822
|
|
|
6640
6823
|
// src/commands/list.ts
|
|
6641
|
-
var
|
|
6824
|
+
var import_chalk16 = __toESM(require("chalk"));
|
|
6642
6825
|
init_format();
|
|
6643
6826
|
init_state();
|
|
6644
6827
|
function registerListCommand(root) {
|
|
6645
6828
|
root.command("list").description("List agents registered locally (~/.arp/agents.json)").action(() => {
|
|
6646
6829
|
const rows = listAgents();
|
|
6647
6830
|
if (rows.length === 0) {
|
|
6648
|
-
console.log(
|
|
6831
|
+
console.log(import_chalk16.default.dim(`No local agents. State file: ${stateFilePath()}`));
|
|
6649
6832
|
return;
|
|
6650
6833
|
}
|
|
6651
6834
|
const grouped = /* @__PURE__ */ new Map();
|
|
@@ -6657,7 +6840,7 @@ function registerListCommand(root) {
|
|
|
6657
6840
|
for (const [serverUrl, group] of grouped) {
|
|
6658
6841
|
if (!first) console.log("");
|
|
6659
6842
|
first = false;
|
|
6660
|
-
console.log(
|
|
6843
|
+
console.log(import_chalk16.default.bold(`Server: ${serverUrl}`));
|
|
6661
6844
|
console.log(formatAgentsTable(group.map(({ agent }) => ({ did: agent.did, name: agent.name, tags: agent.tags, registeredAt: agent.registeredAt }))));
|
|
6662
6845
|
}
|
|
6663
6846
|
});
|
|
@@ -6667,7 +6850,7 @@ function registerListCommand(root) {
|
|
|
6667
6850
|
init_receipt();
|
|
6668
6851
|
|
|
6669
6852
|
// src/commands/receipts.ts
|
|
6670
|
-
var
|
|
6853
|
+
var import_chalk19 = __toESM(require("chalk"));
|
|
6671
6854
|
init_api();
|
|
6672
6855
|
init_format();
|
|
6673
6856
|
init_state();
|
|
@@ -6699,9 +6882,9 @@ async function runReceipts(relationshipId, opts) {
|
|
|
6699
6882
|
const api = new ArpApiClient(opts.server);
|
|
6700
6883
|
const sender = resolveSenderAgent("receipts", opts.server, opts.fromDid);
|
|
6701
6884
|
if (!opts.json) {
|
|
6702
|
-
console.log(
|
|
6703
|
-
console.log(
|
|
6704
|
-
console.log(
|
|
6885
|
+
console.log(import_chalk19.default.dim(`Server: ${api.serverUrl}`));
|
|
6886
|
+
console.log(import_chalk19.default.dim(`Signer: ${sender.did}`));
|
|
6887
|
+
console.log(import_chalk19.default.dim(`Relationship: ${relationshipId}`));
|
|
6705
6888
|
}
|
|
6706
6889
|
const query = { limit };
|
|
6707
6890
|
if (state) query.state = state;
|
|
@@ -6714,7 +6897,7 @@ async function runReceipts(relationshipId, opts) {
|
|
|
6714
6897
|
return;
|
|
6715
6898
|
}
|
|
6716
6899
|
if (rows.length === 0) {
|
|
6717
|
-
console.log(
|
|
6900
|
+
console.log(import_chalk19.default.dim("\n(no receipts for this relationship)"));
|
|
6718
6901
|
return;
|
|
6719
6902
|
}
|
|
6720
6903
|
console.log("");
|
|
@@ -6732,28 +6915,28 @@ async function runReceipts(relationshipId, opts) {
|
|
|
6732
6915
|
}));
|
|
6733
6916
|
}
|
|
6734
6917
|
const lastId = rows[rows.length - 1].id;
|
|
6735
|
-
console.log(
|
|
6918
|
+
console.log(import_chalk19.default.dim(`
|
|
6736
6919
|
${rows.length} receipt row(s). Paginate with --after ${lastId}.`));
|
|
6737
6920
|
}
|
|
6738
6921
|
function formatReceiptLine(r, selfDid, opts = {}) {
|
|
6739
6922
|
const delegationPart = opts.fullIds ? r.delegationId : idHead2(r.delegationId);
|
|
6740
6923
|
const requestHashPart = opts.fullIds ? r.requestHash : hashHead3(r.requestHash);
|
|
6741
|
-
const id =
|
|
6924
|
+
const id = import_chalk19.default.bold(`${delegationPart}/${requestHashPart}`);
|
|
6742
6925
|
const state = colorState2(r.state).padEnd(stateColumnWidth2());
|
|
6743
6926
|
const callerHead = opts.fullIds ? r.callerDid : didHead3(r.callerDid);
|
|
6744
6927
|
const payeeHead = opts.fullIds ? r.payeeDid : didHead3(r.payeeDid);
|
|
6745
|
-
const direction = r.payeeDid === selfDid ? `${
|
|
6928
|
+
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
6929
|
const verdict = formatVerdict(r);
|
|
6747
6930
|
const responseTail = opts.fullIds ? `
|
|
6748
|
-
${
|
|
6931
|
+
${import_chalk19.default.dim("responseHash:")} ${import_chalk19.default.cyan(r.responseHash)}` : "";
|
|
6749
6932
|
return `${id} ${state} ${direction} ${verdict}${responseTail}`;
|
|
6750
6933
|
}
|
|
6751
6934
|
function colorState2(s) {
|
|
6752
6935
|
switch (s) {
|
|
6753
6936
|
case "proposed":
|
|
6754
|
-
return
|
|
6937
|
+
return import_chalk19.default.yellow("proposed");
|
|
6755
6938
|
case "cosigned":
|
|
6756
|
-
return
|
|
6939
|
+
return import_chalk19.default.green("cosigned");
|
|
6757
6940
|
}
|
|
6758
6941
|
}
|
|
6759
6942
|
function stateColumnWidth2() {
|
|
@@ -6763,11 +6946,11 @@ function formatVerdict(r) {
|
|
|
6763
6946
|
const final = r.verdictFinal ?? r.verdictProposed;
|
|
6764
6947
|
switch (final) {
|
|
6765
6948
|
case "accepted":
|
|
6766
|
-
return
|
|
6949
|
+
return import_chalk19.default.green("accepted");
|
|
6767
6950
|
case "accepted_with_notes":
|
|
6768
|
-
return
|
|
6951
|
+
return import_chalk19.default.yellow("accepted_with_notes");
|
|
6769
6952
|
case "rejected":
|
|
6770
|
-
return
|
|
6953
|
+
return import_chalk19.default.red("rejected");
|
|
6771
6954
|
}
|
|
6772
6955
|
}
|
|
6773
6956
|
function idHead2(id) {
|
|
@@ -6800,9 +6983,9 @@ function parseLimit5(raw) {
|
|
|
6800
6983
|
|
|
6801
6984
|
// src/commands/register.ts
|
|
6802
6985
|
var import_node_crypto = require("crypto");
|
|
6803
|
-
var
|
|
6986
|
+
var import_node_fs8 = require("fs");
|
|
6804
6987
|
var import_sdk12 = require("@heyanon-arp/sdk");
|
|
6805
|
-
var
|
|
6988
|
+
var import_chalk20 = __toESM(require("chalk"));
|
|
6806
6989
|
var import_prompts2 = __toESM(require("prompts"));
|
|
6807
6990
|
init_api();
|
|
6808
6991
|
init_format();
|
|
@@ -6835,22 +7018,15 @@ function registerRegisterCommand(root) {
|
|
|
6835
7018
|
"--yes",
|
|
6836
7019
|
"Strict non-interactive: fail if any required field is still missing after merging flags. With --yes, --password must be supplied explicitly (>= 8 chars).",
|
|
6837
7020
|
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
7021
|
).option(
|
|
6843
7022
|
"--json",
|
|
6844
7023
|
// Emit a single JSON object instead of human-friendly
|
|
6845
7024
|
// success prints. Output shape:
|
|
6846
7025
|
// {did, settlementPublicKeyB58, identityPublicKeyB58,
|
|
6847
|
-
//
|
|
7026
|
+
// localStatePath, didDocument}
|
|
6848
7027
|
// (V1-alpha: `accountId` parked from the output.)
|
|
6849
7028
|
// 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.
|
|
7029
|
+
// single-doc contract).
|
|
6854
7030
|
"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
7031
|
false
|
|
6856
7032
|
).action(async (opts) => {
|
|
@@ -6860,21 +7036,24 @@ function registerRegisterCommand(root) {
|
|
|
6860
7036
|
function accumulate3(value, previous) {
|
|
6861
7037
|
return [...previous, value];
|
|
6862
7038
|
}
|
|
6863
|
-
|
|
6864
|
-
|
|
7039
|
+
var defaultRegisterDeps = {
|
|
7040
|
+
makeApi: (serverOverride) => new ArpApiClient(serverOverride),
|
|
7041
|
+
save: saveAgent
|
|
7042
|
+
};
|
|
7043
|
+
async function runRegister(opts, deps = defaultRegisterDeps) {
|
|
6865
7044
|
assertJsonRequiresYes(opts);
|
|
6866
|
-
const api =
|
|
6867
|
-
if (!opts.json) console.log(
|
|
7045
|
+
const api = deps.makeApi(opts.server);
|
|
7046
|
+
if (!opts.json) console.log(import_chalk20.default.dim(`Server: ${api.serverUrl}`));
|
|
6868
7047
|
if (!opts.json) {
|
|
6869
7048
|
warnIfAgentsAlreadyRegistered(opts.server);
|
|
6870
7049
|
warnIfOrphanHomesPresent();
|
|
6871
7050
|
}
|
|
6872
7051
|
const keys = opts.fromKeys ? loadKeysFromFile(opts.fromKeys) : freshKeys();
|
|
6873
7052
|
const did = (0, import_sdk12.formatDid)(keys.identityPublicKey);
|
|
6874
|
-
if (!opts.json) console.log(
|
|
7053
|
+
if (!opts.json) console.log(import_chalk20.default.dim(`DID will be: ${did}`));
|
|
6875
7054
|
const answers = await mergeAnswers(opts);
|
|
6876
7055
|
const scryptSalt = (0, import_node_crypto.randomBytes)(16);
|
|
6877
|
-
if (!opts.json) console.log(
|
|
7056
|
+
if (!opts.json) console.log(import_chalk20.default.dim("Deriving scrypt key, this may take a moment..."));
|
|
6878
7057
|
const scryptKey = (0, import_sdk12.deriveScryptKey)(answers.password, new Uint8Array(scryptSalt));
|
|
6879
7058
|
const challenge = await api.issueChallenge("register");
|
|
6880
7059
|
const challengeBytes = base64UrlNoPadDecode(challenge.challengeB64);
|
|
@@ -6896,7 +7075,6 @@ async function runRegister(opts) {
|
|
|
6896
7075
|
agent_did: did,
|
|
6897
7076
|
identity_public_key: identityPublicKeyB58,
|
|
6898
7077
|
settlement_public_key: settlementPublicKeyB58,
|
|
6899
|
-
key_mode: "separated_soft",
|
|
6900
7078
|
owner_id: ownerId,
|
|
6901
7079
|
owner_signing_method: "scrypt_password_proof",
|
|
6902
7080
|
link_method: "manual",
|
|
@@ -6910,7 +7088,6 @@ async function runRegister(opts) {
|
|
|
6910
7088
|
// accountId: answers.accountId,
|
|
6911
7089
|
identityPublicKey: identityPublicKeyB58,
|
|
6912
7090
|
settlementPublicKey: settlementPublicKeyB58,
|
|
6913
|
-
keyMode: "separated_soft",
|
|
6914
7091
|
ownerAttestation: {
|
|
6915
7092
|
payload: attestation.payload,
|
|
6916
7093
|
sig: attestation.sig,
|
|
@@ -6922,9 +7099,8 @@ async function runRegister(opts) {
|
|
|
6922
7099
|
description: answers.description ? answers.description : void 0,
|
|
6923
7100
|
tags: answers.tags.length > 0 ? answers.tags : void 0
|
|
6924
7101
|
};
|
|
6925
|
-
const
|
|
6926
|
-
|
|
6927
|
-
did: result.did,
|
|
7102
|
+
const durableState = {
|
|
7103
|
+
did,
|
|
6928
7104
|
identityPublicKeyB58,
|
|
6929
7105
|
identitySecretKeyB64: Buffer.from(keys.identitySecretKey).toString("base64"),
|
|
6930
7106
|
settlementPublicKeyB58,
|
|
@@ -6933,59 +7109,45 @@ async function runRegister(opts) {
|
|
|
6933
7109
|
scryptSaltB64: Buffer.from(scryptSalt).toString("base64"),
|
|
6934
7110
|
scryptSaltId,
|
|
6935
7111
|
ownerId,
|
|
6936
|
-
//
|
|
6937
|
-
//
|
|
6938
|
-
|
|
6939
|
-
currentAttestationId: result.didDocument.metadata.owner_attestation_id,
|
|
7112
|
+
// Placeholder until the server confirms registration and returns
|
|
7113
|
+
// the canonical attestation id (finalized in Step 8 below).
|
|
7114
|
+
currentAttestationId: "",
|
|
6940
7115
|
// V1-alpha: accountId parked — local state file no longer
|
|
6941
7116
|
// records the field. Existing state files written when the
|
|
6942
7117
|
// field was required will still carry the value; readers
|
|
6943
7118
|
// tolerate either shape.
|
|
6944
7119
|
// accountId: answers.accountId,
|
|
6945
|
-
keyMode: "separated_soft",
|
|
6946
7120
|
name: answers.name,
|
|
6947
7121
|
description: answers.description || void 0,
|
|
6948
7122
|
tags: answers.tags.length > 0 ? answers.tags : void 0,
|
|
6949
7123
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7124
|
+
};
|
|
7125
|
+
deps.save(opts.server, durableState);
|
|
7126
|
+
const result = await api.register(body);
|
|
7127
|
+
if (result.did !== did) {
|
|
7128
|
+
throw new Error(
|
|
7129
|
+
`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}.`
|
|
7130
|
+
);
|
|
7131
|
+
}
|
|
7132
|
+
deps.save(opts.server, {
|
|
7133
|
+
...durableState,
|
|
7134
|
+
currentAttestationId: result.didDocument.metadata.owner_attestation_id
|
|
6950
7135
|
});
|
|
6951
7136
|
try {
|
|
6952
7137
|
recordHome(arpHomeDir());
|
|
6953
7138
|
} catch (registryErr) {
|
|
6954
|
-
if (!opts.json) console.log(
|
|
7139
|
+
if (!opts.json) console.log(import_chalk20.default.dim(`(homes registry write failed: ${registryErr.message})`));
|
|
6955
7140
|
}
|
|
6956
7141
|
if (!opts.json) {
|
|
6957
|
-
console.log(
|
|
6958
|
-
console.log(`${
|
|
6959
|
-
console.log(`${
|
|
6960
|
-
console.log(
|
|
7142
|
+
console.log(import_chalk20.default.green("\nRegistered."));
|
|
7143
|
+
console.log(`${import_chalk20.default.bold("DID")}: ${import_chalk20.default.cyan(result.did)}`);
|
|
7144
|
+
console.log(`${import_chalk20.default.bold("Settlement pubkey")} ${import_chalk20.default.dim("(fund with SOL)")}: ${import_chalk20.default.cyan(settlementPublicKeyB58)}`);
|
|
7145
|
+
console.log(import_chalk20.default.bold("DID document:"));
|
|
6961
7146
|
console.log(formatJson(result.didDocument));
|
|
6962
7147
|
}
|
|
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(`
|
|
7148
|
+
if (!opts.json) console.log(import_chalk20.default.green(`
|
|
7149
|
+
\u2713 Discoverable now via \`heyarp agents\`.`));
|
|
7150
|
+
if (!opts.json) console.log(import_chalk20.default.dim(`
|
|
6989
7151
|
Local state saved to ${arpHomeDir()}/agents.json (mode 0600).`));
|
|
6990
7152
|
if (opts.json) {
|
|
6991
7153
|
console.log(
|
|
@@ -6997,8 +7159,6 @@ Local state saved to ${arpHomeDir()}/agents.json (mode 0600).`));
|
|
|
6997
7159
|
// V1-alpha: accountId parked — `--json` output no longer
|
|
6998
7160
|
// includes it. Uncomment when launchpad↔ARP join lands.
|
|
6999
7161
|
// accountId: answers.accountId,
|
|
7000
|
-
keyMode: "separated_soft",
|
|
7001
|
-
publication: publicationOutcome,
|
|
7002
7162
|
localStatePath: `${arpHomeDir()}/agents.json`,
|
|
7003
7163
|
didDocument: result.didDocument
|
|
7004
7164
|
},
|
|
@@ -7065,7 +7225,7 @@ async function mergeAnswers(opts) {
|
|
|
7065
7225
|
}
|
|
7066
7226
|
const prompted = promptDefs.length > 0 ? await (0, import_prompts2.default)(promptDefs, {
|
|
7067
7227
|
onCancel: () => {
|
|
7068
|
-
console.log(
|
|
7228
|
+
console.log(import_chalk20.default.yellow("\nAborted."));
|
|
7069
7229
|
process.exit(130);
|
|
7070
7230
|
}
|
|
7071
7231
|
}) : {};
|
|
@@ -7087,16 +7247,16 @@ function warnIfOrphanHomesPresent() {
|
|
|
7087
7247
|
try {
|
|
7088
7248
|
others = listHomes().filter((h) => h.path !== current);
|
|
7089
7249
|
} catch (registryErr) {
|
|
7090
|
-
console.log(
|
|
7250
|
+
console.log(import_chalk20.default.dim(`(homes registry unreadable, skipping orphan-home check: ${registryErr.message})`));
|
|
7091
7251
|
return;
|
|
7092
7252
|
}
|
|
7093
7253
|
if (others.length === 0) return;
|
|
7094
|
-
const list = others.map((h) => ` \u2022 ${
|
|
7095
|
-
console.log(
|
|
7254
|
+
const list = others.map((h) => ` \u2022 ${import_chalk20.default.cyan(h.path)} ${import_chalk20.default.dim(`(last seen ${h.lastSeenAt})`)}`).join("\n");
|
|
7255
|
+
console.log(import_chalk20.default.yellow(`
|
|
7096
7256
|
\u26A0 HEYARP_HOME is unset, but other agent homes are registered on this machine:`));
|
|
7097
7257
|
console.log(list);
|
|
7098
7258
|
console.log(
|
|
7099
|
-
|
|
7259
|
+
import_chalk20.default.dim(
|
|
7100
7260
|
` Registering will create a NEW agent under ${current}.
|
|
7101
7261
|
If you meant to add to an existing home, abort (Ctrl-C) and re-run with:
|
|
7102
7262
|
HEYARP_HOME=<path> heyarp register \u2026
|
|
@@ -7109,11 +7269,11 @@ function warnIfAgentsAlreadyRegistered(serverOverride) {
|
|
|
7109
7269
|
const targetServer = resolveServerUrl(serverOverride);
|
|
7110
7270
|
const existing = listAgents().filter((row) => row.serverUrl === targetServer);
|
|
7111
7271
|
if (existing.length === 0) return;
|
|
7112
|
-
const list = existing.map((row) => ` \u2022 ${
|
|
7113
|
-
console.log(
|
|
7272
|
+
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");
|
|
7273
|
+
console.log(import_chalk20.default.yellow("\n\u26A0 ~/.arp/agents.json already has agent(s) for this server:"));
|
|
7114
7274
|
console.log(list);
|
|
7115
7275
|
console.log(
|
|
7116
|
-
|
|
7276
|
+
import_chalk20.default.dim(
|
|
7117
7277
|
" 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
7278
|
)
|
|
7119
7279
|
);
|
|
@@ -7133,12 +7293,12 @@ function freshKeys() {
|
|
|
7133
7293
|
};
|
|
7134
7294
|
}
|
|
7135
7295
|
function loadKeysFromFile(path) {
|
|
7136
|
-
if (!(0,
|
|
7296
|
+
if (!(0, import_node_fs8.existsSync)(path)) {
|
|
7137
7297
|
throw new Error(`--from-keys: file not found at ${path}`);
|
|
7138
7298
|
}
|
|
7139
7299
|
let parsed;
|
|
7140
7300
|
try {
|
|
7141
|
-
parsed = JSON.parse((0,
|
|
7301
|
+
parsed = JSON.parse((0, import_node_fs8.readFileSync)(path, "utf8"));
|
|
7142
7302
|
} catch (err) {
|
|
7143
7303
|
throw new Error(`--from-keys: ${path} is not valid JSON: ${err.message}`);
|
|
7144
7304
|
}
|
|
@@ -7166,20 +7326,9 @@ function assertJsonRequiresYes(opts) {
|
|
|
7166
7326
|
throw new Error("register --json REQUIRES --yes (interactive prompts would corrupt the single-doc JSON output)");
|
|
7167
7327
|
}
|
|
7168
7328
|
}
|
|
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
7329
|
|
|
7181
7330
|
// src/commands/relationships.ts
|
|
7182
|
-
var
|
|
7331
|
+
var import_chalk21 = __toESM(require("chalk"));
|
|
7183
7332
|
init_api();
|
|
7184
7333
|
init_format();
|
|
7185
7334
|
init_state();
|
|
@@ -7202,25 +7351,25 @@ async function runRelationships(positionalDid, opts) {
|
|
|
7202
7351
|
const local = explicitDid !== void 0 ? loadAgentOrThrow(opts.server, explicitDid) : resolveSenderAgent("relationships", opts.server, void 0);
|
|
7203
7352
|
const did = local.did;
|
|
7204
7353
|
const api = new ArpApiClient(opts.server);
|
|
7205
|
-
console.log(
|
|
7206
|
-
console.log(
|
|
7354
|
+
console.log(import_chalk21.default.dim(`Server: ${api.serverUrl}`));
|
|
7355
|
+
console.log(import_chalk21.default.dim(`Signer: ${local.did}`));
|
|
7207
7356
|
const query = { limit };
|
|
7208
7357
|
if (state) query.state = state;
|
|
7209
7358
|
const signer = makeSigner(local);
|
|
7210
7359
|
const rows = await api.listRelationships(did, signer, query);
|
|
7211
7360
|
if (rows.length === 0) {
|
|
7212
|
-
console.log(
|
|
7361
|
+
console.log(import_chalk21.default.dim("\n(no relationships)"));
|
|
7213
7362
|
return;
|
|
7214
7363
|
}
|
|
7215
7364
|
console.log("");
|
|
7216
7365
|
console.log(formatRelationshipsTable(rows, did));
|
|
7217
7366
|
if (opts.verbose) {
|
|
7218
|
-
console.log(
|
|
7367
|
+
console.log(import_chalk21.default.bold("\nFull relationships:"));
|
|
7219
7368
|
for (const r of rows) {
|
|
7220
7369
|
console.log(formatJson(r));
|
|
7221
7370
|
}
|
|
7222
7371
|
}
|
|
7223
|
-
console.log(
|
|
7372
|
+
console.log(import_chalk21.default.dim(`
|
|
7224
7373
|
${rows.length} relationship(s).`));
|
|
7225
7374
|
}
|
|
7226
7375
|
function formatRelationshipsTable(rows, selfDid) {
|
|
@@ -7228,7 +7377,7 @@ function formatRelationshipsTable(rows, selfDid) {
|
|
|
7228
7377
|
const data = rows.map((r) => [r.relationshipId, otherPair(r, selfDid), r.state, r.lastEventAt ?? "(none)", String(r.lastEventIndex)]);
|
|
7229
7378
|
const widths = header.map((h, i) => Math.max(h.length, ...data.map((row) => row[i].length)));
|
|
7230
7379
|
const pad = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ");
|
|
7231
|
-
return [
|
|
7380
|
+
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
7381
|
}
|
|
7233
7382
|
function otherPair(r, selfDid) {
|
|
7234
7383
|
if (r.pairDidA === selfDid) return r.pairDidB;
|
|
@@ -7251,141 +7400,9 @@ function parseLimit6(raw) {
|
|
|
7251
7400
|
return n;
|
|
7252
7401
|
}
|
|
7253
7402
|
|
|
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
7403
|
// src/commands/send-handshake.ts
|
|
7387
|
-
var
|
|
7388
|
-
var
|
|
7404
|
+
var import_sdk13 = require("@heyanon-arp/sdk");
|
|
7405
|
+
var import_chalk22 = __toESM(require("chalk"));
|
|
7389
7406
|
init_api();
|
|
7390
7407
|
init_format();
|
|
7391
7408
|
init_state();
|
|
@@ -7401,10 +7418,10 @@ async function runSendHandshake(recipientDid, opts) {
|
|
|
7401
7418
|
}
|
|
7402
7419
|
const ttlSeconds = parseTtl3(opts.ttl);
|
|
7403
7420
|
const api = new ArpApiClient(opts.server);
|
|
7404
|
-
console.log(
|
|
7421
|
+
console.log(import_chalk22.default.dim(`Server: ${api.serverUrl}`));
|
|
7405
7422
|
const sender = resolveSenderAgent("send-handshake", opts.server, opts.fromDid);
|
|
7406
|
-
console.log(
|
|
7407
|
-
console.log(
|
|
7423
|
+
console.log(import_chalk22.default.dim(`Sender: ${sender.did}`));
|
|
7424
|
+
console.log(import_chalk22.default.dim(`Recipient: ${recipientDid}`));
|
|
7408
7425
|
const content = {};
|
|
7409
7426
|
if (opts.greeting) content.greeting = opts.greeting;
|
|
7410
7427
|
if (opts.intent) content.intent = opts.intent;
|
|
@@ -7412,46 +7429,46 @@ async function runSendHandshake(recipientDid, opts) {
|
|
|
7412
7429
|
const nextSequence = (sender.lastSenderSequence ?? 0) + 1;
|
|
7413
7430
|
const protectedBlock = {
|
|
7414
7431
|
protocol_version: "arp/0.1",
|
|
7415
|
-
purpose:
|
|
7416
|
-
message_id: (0,
|
|
7432
|
+
purpose: import_sdk13.Purpose.ENVELOPE,
|
|
7433
|
+
message_id: (0, import_sdk13.uuidV4)(),
|
|
7417
7434
|
sender_did: sender.did,
|
|
7418
7435
|
recipient_did: recipientDid,
|
|
7419
7436
|
relationship_id: null,
|
|
7420
7437
|
sender_sequence: nextSequence,
|
|
7421
|
-
sender_nonce: (0,
|
|
7422
|
-
timestamp: (0,
|
|
7423
|
-
expires_at: (0,
|
|
7438
|
+
sender_nonce: (0, import_sdk13.senderNonce)(),
|
|
7439
|
+
timestamp: (0, import_sdk13.rfc3339)(),
|
|
7440
|
+
expires_at: (0, import_sdk13.expiresAt)(ttlSeconds),
|
|
7424
7441
|
delivery_id: null
|
|
7425
7442
|
};
|
|
7426
7443
|
const signer = makeSigner(sender);
|
|
7427
|
-
const envelope = (0,
|
|
7444
|
+
const envelope = (0, import_sdk13.signEnvelope)({
|
|
7428
7445
|
protected: protectedBlock,
|
|
7429
7446
|
body,
|
|
7430
7447
|
identitySecretKey: signer.identitySecretKey
|
|
7431
7448
|
});
|
|
7432
7449
|
if (opts.verbose) {
|
|
7433
|
-
console.log(
|
|
7450
|
+
console.log(import_chalk22.default.bold("\nEnvelope (pre-send):"));
|
|
7434
7451
|
console.log(formatJson(envelope));
|
|
7435
7452
|
}
|
|
7436
7453
|
const result = await api.ingest(envelope);
|
|
7437
7454
|
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(`${
|
|
7455
|
+
console.log(import_chalk22.default.green("\nDelivered."));
|
|
7456
|
+
console.log(`${import_chalk22.default.bold("Event id")}: ${import_chalk22.default.cyan(result.eventId)}`);
|
|
7457
|
+
console.log(`${import_chalk22.default.bold("Relationship id")}: ${import_chalk22.default.cyan(result.relationshipId)}`);
|
|
7458
|
+
console.log(`${import_chalk22.default.bold("Chain index")}: ${import_chalk22.default.cyan(String(result.relationshipEventIndex))}`);
|
|
7459
|
+
console.log(`${import_chalk22.default.bold("Server timestamp")}: ${import_chalk22.default.cyan(result.serverTimestamp)}`);
|
|
7460
|
+
console.log(`${import_chalk22.default.bold("Signed message hash")}: ${import_chalk22.default.cyan(result.signedMessageHash)}`);
|
|
7461
|
+
console.log(`${import_chalk22.default.bold("Server event hash")}: ${import_chalk22.default.cyan(result.serverEventHash)}`);
|
|
7445
7462
|
if (result.prevServerEventHash) {
|
|
7446
|
-
console.log(`${
|
|
7463
|
+
console.log(`${import_chalk22.default.bold("Prev server event hash")}: ${import_chalk22.default.cyan(result.prevServerEventHash)}`);
|
|
7447
7464
|
} else {
|
|
7448
|
-
console.log(`${
|
|
7465
|
+
console.log(`${import_chalk22.default.bold("Prev server event hash")}: ${import_chalk22.default.dim("(null \u2014 first event of this relationship)")}`);
|
|
7449
7466
|
}
|
|
7450
7467
|
if (opts.verbose) {
|
|
7451
|
-
console.log(
|
|
7468
|
+
console.log(import_chalk22.default.bold("\nFull server response:"));
|
|
7452
7469
|
console.log(formatJson(result));
|
|
7453
7470
|
}
|
|
7454
|
-
console.log(
|
|
7471
|
+
console.log(import_chalk22.default.dim(`
|
|
7455
7472
|
Local sender_sequence advanced to ${nextSequence}.`));
|
|
7456
7473
|
}
|
|
7457
7474
|
function parseTtl3(raw) {
|
|
@@ -7467,8 +7484,8 @@ function isDid(s) {
|
|
|
7467
7484
|
}
|
|
7468
7485
|
|
|
7469
7486
|
// src/commands/send-handshake-response.ts
|
|
7470
|
-
var
|
|
7471
|
-
var
|
|
7487
|
+
var import_sdk14 = require("@heyanon-arp/sdk");
|
|
7488
|
+
var import_chalk23 = __toESM(require("chalk"));
|
|
7472
7489
|
init_api();
|
|
7473
7490
|
init_format();
|
|
7474
7491
|
init_state();
|
|
@@ -7482,7 +7499,7 @@ function registerSendHandshakeResponseCommand(root) {
|
|
|
7482
7499
|
// We don't use commander's requiredOption because it
|
|
7483
7500
|
// would fire for the accept path too; validate manually
|
|
7484
7501
|
// after decision is parsed.
|
|
7485
|
-
`When --decision=decline: required reason code (one of: ${
|
|
7502
|
+
`When --decision=decline: required reason code (one of: ${import_sdk14.DECLINE_REASONS.join(", ")}). Carried in body.content.reason.`
|
|
7486
7503
|
).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
7504
|
"--json",
|
|
7488
7505
|
"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 +7530,11 @@ async function runSendHandshakeResponse(recipientDid, opts) {
|
|
|
7513
7530
|
declinePayload = detail ? { reason, reasonDetail: detail } : { reason };
|
|
7514
7531
|
}
|
|
7515
7532
|
const api = new ArpApiClient(opts.server);
|
|
7516
|
-
progress(opts.json,
|
|
7533
|
+
progress(opts.json, import_chalk23.default.dim(`Server: ${api.serverUrl}`));
|
|
7517
7534
|
const sender = resolveSenderAgent("send-handshake-response", opts.server, opts.fromDid);
|
|
7518
|
-
progress(opts.json,
|
|
7519
|
-
progress(opts.json,
|
|
7520
|
-
progress(opts.json,
|
|
7535
|
+
progress(opts.json, import_chalk23.default.dim(`Sender: ${sender.did}`));
|
|
7536
|
+
progress(opts.json, import_chalk23.default.dim(`Recipient: ${recipientDid}`));
|
|
7537
|
+
progress(opts.json, import_chalk23.default.dim(`Decision: ${decision}`));
|
|
7521
7538
|
const signer = makeSigner(sender);
|
|
7522
7539
|
if (!opts.force) {
|
|
7523
7540
|
try {
|
|
@@ -7546,13 +7563,13 @@ async function runSendHandshakeResponse(recipientDid, opts) {
|
|
|
7546
7563
|
});
|
|
7547
7564
|
return;
|
|
7548
7565
|
}
|
|
7549
|
-
progress(opts.json,
|
|
7566
|
+
progress(opts.json, import_chalk23.default.yellow(`
|
|
7550
7567
|
[--idempotency] Relationship ${existing.relationshipId} with ${recipientDid} is already 'active'.`));
|
|
7551
7568
|
progress(
|
|
7552
7569
|
opts.json,
|
|
7553
|
-
|
|
7570
|
+
import_chalk23.default.dim(`A previous accept from this signer (event ${previousResponseFromMe.eventId}) landed successfully. Skipping re-send (use --force to override).`)
|
|
7554
7571
|
);
|
|
7555
|
-
progress(opts.json,
|
|
7572
|
+
progress(opts.json, import_chalk23.default.dim(`Last event index: ${existing.lastEventIndex}, last server event hash: ${existing.lastServerEventHash ?? "(none)"}`));
|
|
7556
7573
|
return;
|
|
7557
7574
|
}
|
|
7558
7575
|
throw new Error(
|
|
@@ -7563,7 +7580,7 @@ async function runSendHandshakeResponse(recipientDid, opts) {
|
|
|
7563
7580
|
if (probeErr instanceof Error && /CLOSED|terminated|already 'active' from a previous ACCEPT|original initiator/i.test(probeErr.message)) {
|
|
7564
7581
|
throw probeErr;
|
|
7565
7582
|
}
|
|
7566
|
-
progress(opts.json,
|
|
7583
|
+
progress(opts.json, import_chalk23.default.dim(`(idempotency probe failed; proceeding anyway: ${probeErr.message})`));
|
|
7567
7584
|
}
|
|
7568
7585
|
}
|
|
7569
7586
|
const content = { decision };
|
|
@@ -7576,24 +7593,24 @@ async function runSendHandshakeResponse(recipientDid, opts) {
|
|
|
7576
7593
|
const nextSequence = (sender.lastSenderSequence ?? 0) + 1;
|
|
7577
7594
|
const protectedBlock = {
|
|
7578
7595
|
protocol_version: "arp/0.1",
|
|
7579
|
-
purpose:
|
|
7580
|
-
message_id: (0,
|
|
7596
|
+
purpose: import_sdk14.Purpose.ENVELOPE,
|
|
7597
|
+
message_id: (0, import_sdk14.uuidV4)(),
|
|
7581
7598
|
sender_did: sender.did,
|
|
7582
7599
|
recipient_did: recipientDid,
|
|
7583
7600
|
relationship_id: null,
|
|
7584
7601
|
sender_sequence: nextSequence,
|
|
7585
|
-
sender_nonce: (0,
|
|
7586
|
-
timestamp: (0,
|
|
7587
|
-
expires_at: (0,
|
|
7602
|
+
sender_nonce: (0, import_sdk14.senderNonce)(),
|
|
7603
|
+
timestamp: (0, import_sdk14.rfc3339)(),
|
|
7604
|
+
expires_at: (0, import_sdk14.expiresAt)(ttlSeconds),
|
|
7588
7605
|
delivery_id: null
|
|
7589
7606
|
};
|
|
7590
|
-
const envelope = (0,
|
|
7607
|
+
const envelope = (0, import_sdk14.signEnvelope)({
|
|
7591
7608
|
protected: protectedBlock,
|
|
7592
7609
|
body,
|
|
7593
7610
|
identitySecretKey: signer.identitySecretKey
|
|
7594
7611
|
});
|
|
7595
7612
|
if (opts.verbose) {
|
|
7596
|
-
console.log(
|
|
7613
|
+
console.log(import_chalk23.default.bold("\nEnvelope (pre-send):"));
|
|
7597
7614
|
console.log(formatJson(envelope));
|
|
7598
7615
|
}
|
|
7599
7616
|
const result = await api.ingest(envelope);
|
|
@@ -7613,23 +7630,23 @@ async function runSendHandshakeResponse(recipientDid, opts) {
|
|
|
7613
7630
|
});
|
|
7614
7631
|
return;
|
|
7615
7632
|
}
|
|
7616
|
-
console.log(
|
|
7617
|
-
console.log(`${
|
|
7618
|
-
console.log(`${
|
|
7619
|
-
console.log(`${
|
|
7620
|
-
console.log(`${
|
|
7621
|
-
console.log(`${
|
|
7622
|
-
console.log(`${
|
|
7633
|
+
console.log(import_chalk23.default.green("\nDelivered."));
|
|
7634
|
+
console.log(`${import_chalk23.default.bold("Event id")}: ${import_chalk23.default.cyan(result.eventId)}`);
|
|
7635
|
+
console.log(`${import_chalk23.default.bold("Relationship id")}: ${import_chalk23.default.cyan(result.relationshipId)}`);
|
|
7636
|
+
console.log(`${import_chalk23.default.bold("Chain index")}: ${import_chalk23.default.cyan(String(result.relationshipEventIndex))}`);
|
|
7637
|
+
console.log(`${import_chalk23.default.bold("Server timestamp")}: ${import_chalk23.default.cyan(result.serverTimestamp)}`);
|
|
7638
|
+
console.log(`${import_chalk23.default.bold("Signed message hash")}: ${import_chalk23.default.cyan(result.signedMessageHash)}`);
|
|
7639
|
+
console.log(`${import_chalk23.default.bold("Server event hash")}: ${import_chalk23.default.cyan(result.serverEventHash)}`);
|
|
7623
7640
|
if (result.prevServerEventHash) {
|
|
7624
|
-
console.log(`${
|
|
7641
|
+
console.log(`${import_chalk23.default.bold("Prev server event hash")}: ${import_chalk23.default.cyan(result.prevServerEventHash)}`);
|
|
7625
7642
|
} else {
|
|
7626
|
-
console.log(`${
|
|
7643
|
+
console.log(`${import_chalk23.default.bold("Prev server event hash")}: ${import_chalk23.default.dim("(null \u2014 first event of this relationship)")}`);
|
|
7627
7644
|
}
|
|
7628
7645
|
if (opts.verbose) {
|
|
7629
|
-
console.log(
|
|
7646
|
+
console.log(import_chalk23.default.bold("\nFull server response:"));
|
|
7630
7647
|
console.log(formatJson(result));
|
|
7631
7648
|
}
|
|
7632
|
-
console.log(
|
|
7649
|
+
console.log(import_chalk23.default.dim(`
|
|
7633
7650
|
Local sender_sequence advanced to ${nextSequence}.`));
|
|
7634
7651
|
}
|
|
7635
7652
|
function parseDecision(raw) {
|
|
@@ -7688,7 +7705,7 @@ init_status();
|
|
|
7688
7705
|
init_wallet();
|
|
7689
7706
|
|
|
7690
7707
|
// src/commands/watch.ts
|
|
7691
|
-
var
|
|
7708
|
+
var import_chalk24 = __toESM(require("chalk"));
|
|
7692
7709
|
init_api();
|
|
7693
7710
|
init_format();
|
|
7694
7711
|
init_state();
|
|
@@ -7702,9 +7719,9 @@ async function runWatch(relationshipId, opts) {
|
|
|
7702
7719
|
const local = resolveSenderAgent("watch", opts.server, opts.fromDid);
|
|
7703
7720
|
const api = new ArpApiClient(opts.server);
|
|
7704
7721
|
if (!opts.json) {
|
|
7705
|
-
console.log(
|
|
7706
|
-
console.log(
|
|
7707
|
-
console.log(
|
|
7722
|
+
console.log(import_chalk24.default.dim(`Server: ${api.serverUrl}`));
|
|
7723
|
+
console.log(import_chalk24.default.dim(`Signer: ${local.did}`));
|
|
7724
|
+
console.log(import_chalk24.default.dim(`Watching: ${relationshipId}`));
|
|
7708
7725
|
}
|
|
7709
7726
|
const controller = new AbortController();
|
|
7710
7727
|
let userAborted = false;
|
|
@@ -7723,7 +7740,7 @@ async function runWatch(relationshipId, opts) {
|
|
|
7723
7740
|
}
|
|
7724
7741
|
if (event.type === "heartbeat") continue;
|
|
7725
7742
|
if (event.type === "connected") {
|
|
7726
|
-
console.log(
|
|
7743
|
+
console.log(import_chalk24.default.green(`\u25CF stream open \u2014 watching ${relationshipId}`));
|
|
7727
7744
|
continue;
|
|
7728
7745
|
}
|
|
7729
7746
|
if (event.type === "envelope") {
|
|
@@ -7737,7 +7754,7 @@ async function runWatch(relationshipId, opts) {
|
|
|
7737
7754
|
}
|
|
7738
7755
|
continue;
|
|
7739
7756
|
}
|
|
7740
|
-
console.log(
|
|
7757
|
+
console.log(import_chalk24.default.dim(`(unknown event: ${event.type})`));
|
|
7741
7758
|
}
|
|
7742
7759
|
if (!userAborted) {
|
|
7743
7760
|
throw new Error(`watch ${relationshipId}: stream ended unexpectedly (server may have restarted, or the change stream errored). Re-run to reconnect.`);
|
|
@@ -7745,7 +7762,7 @@ async function runWatch(relationshipId, opts) {
|
|
|
7745
7762
|
} catch (err) {
|
|
7746
7763
|
const name = err.name;
|
|
7747
7764
|
if (name === "AbortError" || userAborted) {
|
|
7748
|
-
if (!opts.json) console.log(
|
|
7765
|
+
if (!opts.json) console.log(import_chalk24.default.dim("\nstream closed."));
|
|
7749
7766
|
return;
|
|
7750
7767
|
}
|
|
7751
7768
|
throw err;
|
|
@@ -7759,21 +7776,21 @@ function formatWatchLine(ev, selfDid, opts = {}) {
|
|
|
7759
7776
|
const type = ev.type.padEnd(20);
|
|
7760
7777
|
const direction = directionLabel2(ev, selfDid, opts);
|
|
7761
7778
|
const hash = opts.fullIds ? ev.serverEventHash : hashHead4(ev.serverEventHash);
|
|
7762
|
-
return `${
|
|
7779
|
+
return `${import_chalk24.default.dim(`[${ts}]`)} ${type} ${direction} ${import_chalk24.default.cyan(hash)}`;
|
|
7763
7780
|
}
|
|
7764
7781
|
function directionLabel2(ev, selfDid, opts = {}) {
|
|
7765
7782
|
const senderHead = opts.fullIds ? ev.senderDid : didHead4(ev.senderDid);
|
|
7766
7783
|
const recipientHead = opts.fullIds ? ev.recipientDid : didHead4(ev.recipientDid);
|
|
7767
|
-
if (ev.senderDid === selfDid) return `${
|
|
7768
|
-
if (ev.recipientDid === selfDid) return `${
|
|
7769
|
-
return `${
|
|
7784
|
+
if (ev.senderDid === selfDid) return `${import_chalk24.default.bold("me")} \u2192 ${import_chalk24.default.dim(recipientHead)}`;
|
|
7785
|
+
if (ev.recipientDid === selfDid) return `${import_chalk24.default.dim(senderHead)} \u2192 ${import_chalk24.default.bold("me")}`;
|
|
7786
|
+
return `${import_chalk24.default.dim(senderHead)} \u2192 ${import_chalk24.default.dim(recipientHead)}`;
|
|
7770
7787
|
}
|
|
7771
7788
|
function didHead4(did) {
|
|
7772
7789
|
if (did.length <= 20) return did;
|
|
7773
7790
|
return `${did.slice(0, 20)}...`;
|
|
7774
7791
|
}
|
|
7775
7792
|
function hashHead4(hash) {
|
|
7776
|
-
if (!hash) return
|
|
7793
|
+
if (!hash) return import_chalk24.default.dim("(none)");
|
|
7777
7794
|
if (hash.length <= 14) return hash;
|
|
7778
7795
|
return `${hash.slice(0, 14)}...`;
|
|
7779
7796
|
}
|
|
@@ -7785,7 +7802,7 @@ function formatClock(iso) {
|
|
|
7785
7802
|
}
|
|
7786
7803
|
|
|
7787
7804
|
// src/commands/whoami.ts
|
|
7788
|
-
var
|
|
7805
|
+
var import_chalk25 = __toESM(require("chalk"));
|
|
7789
7806
|
init_api();
|
|
7790
7807
|
init_format();
|
|
7791
7808
|
init_state();
|
|
@@ -7798,10 +7815,10 @@ function registerWhoamiCommand(root) {
|
|
|
7798
7815
|
// Persona scripts want the local settlement pubkey for
|
|
7799
7816
|
// `--payer-settlement-pubkey` / on-chain ops without a
|
|
7800
7817
|
// 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
|
|
7818
|
+
// GET, prints just the resolved DID + pubkeys from local
|
|
7819
|
+
// state. (V1-alpha: accountId parked, no longer in --local
|
|
7820
|
+
// output.)
|
|
7821
|
+
"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
7822
|
false
|
|
7806
7823
|
).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
7824
|
try {
|
|
@@ -7815,18 +7832,16 @@ function registerWhoamiCommand(root) {
|
|
|
7815
7832
|
// longer carries the field. Uncomment when launchpad↔ARP
|
|
7816
7833
|
// join lands.
|
|
7817
7834
|
// accountId: local.accountId,
|
|
7818
|
-
keyMode: local.keyMode,
|
|
7819
7835
|
name: local.name
|
|
7820
7836
|
};
|
|
7821
7837
|
if (opts.local) {
|
|
7822
7838
|
if (opts.json) {
|
|
7823
7839
|
console.log(formatJson(localJson));
|
|
7824
7840
|
} 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}`);
|
|
7841
|
+
console.log(import_chalk25.default.bold("Local agent:"));
|
|
7842
|
+
console.log(` DID: ${import_chalk25.default.cyan(local.did)}`);
|
|
7843
|
+
console.log(` Settlement pubkey: ${import_chalk25.default.cyan(local.settlementPublicKeyB58)}`);
|
|
7844
|
+
console.log(` Identity pubkey: ${import_chalk25.default.cyan(local.identityPublicKeyB58)}`);
|
|
7830
7845
|
if (local.name) console.log(` Name: ${local.name}`);
|
|
7831
7846
|
}
|
|
7832
7847
|
return;
|
|
@@ -7837,12 +7852,12 @@ function registerWhoamiCommand(root) {
|
|
|
7837
7852
|
if (opts.json) {
|
|
7838
7853
|
console.log(formatJson({ local: localJson, server: agent }));
|
|
7839
7854
|
} else {
|
|
7840
|
-
console.log(
|
|
7841
|
-
console.log(
|
|
7842
|
-
console.log(` DID: ${
|
|
7843
|
-
console.log(` Settlement pubkey: ${
|
|
7844
|
-
console.log(` Identity pubkey: ${
|
|
7845
|
-
console.log(
|
|
7855
|
+
console.log(import_chalk25.default.dim(`Server: ${api.serverUrl}`));
|
|
7856
|
+
console.log(import_chalk25.default.bold("\nLocal agent:"));
|
|
7857
|
+
console.log(` DID: ${import_chalk25.default.cyan(local.did)}`);
|
|
7858
|
+
console.log(` Settlement pubkey: ${import_chalk25.default.cyan(local.settlementPublicKeyB58)}`);
|
|
7859
|
+
console.log(` Identity pubkey: ${import_chalk25.default.cyan(local.identityPublicKeyB58)}`);
|
|
7860
|
+
console.log(import_chalk25.default.bold("\nServer profile:"));
|
|
7846
7861
|
console.log(formatJson(agent));
|
|
7847
7862
|
}
|
|
7848
7863
|
} catch (err) {
|
|
@@ -7853,8 +7868,8 @@ function registerWhoamiCommand(root) {
|
|
|
7853
7868
|
}
|
|
7854
7869
|
|
|
7855
7870
|
// src/commands/work.ts
|
|
7856
|
-
var
|
|
7857
|
-
var
|
|
7871
|
+
var import_sdk15 = require("@heyanon-arp/sdk");
|
|
7872
|
+
var import_chalk26 = __toESM(require("chalk"));
|
|
7858
7873
|
init_api();
|
|
7859
7874
|
init_format();
|
|
7860
7875
|
init_id_format();
|
|
@@ -7873,7 +7888,16 @@ var POST_COMMIT_ERROR_CODES3 = /* @__PURE__ */ new Set([
|
|
|
7873
7888
|
"WORK_REQUEST_ALREADY_EXISTS",
|
|
7874
7889
|
"WORK_REQUEST_NOT_FOUND",
|
|
7875
7890
|
"WORK_RESPONDER_IS_CALLER",
|
|
7876
|
-
"WORK_INVALID_STATE"
|
|
7891
|
+
"WORK_INVALID_STATE",
|
|
7892
|
+
// M6 lock-at-accept: the work handler's LOCKED gate emits
|
|
7893
|
+
// `DELEGATION_PENDING_LOCK` (409) when the delegation is funded but
|
|
7894
|
+
// the on-chain lock isn't confirmed yet (state
|
|
7895
|
+
// `pending_lock_finalization`). It fires from the body handler AFTER
|
|
7896
|
+
// the event row is committed — same lifecycle as
|
|
7897
|
+
// `WORK_DELEGATION_NOT_ACTIVE` — so the CLI must advance
|
|
7898
|
+
// `lastSenderSequence`, otherwise a retry once the lock confirms
|
|
7899
|
+
// reuses the consumed sequence and trips `ENV_SEQUENCE_BACKWARDS`.
|
|
7900
|
+
"DELEGATION_PENDING_LOCK"
|
|
7877
7901
|
]);
|
|
7878
7902
|
function registerRequest(parent) {
|
|
7879
7903
|
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 +7924,17 @@ async function runRequest(recipientDid, delegationId, opts) {
|
|
|
7900
7924
|
params
|
|
7901
7925
|
};
|
|
7902
7926
|
const body = { type: "work_request", content };
|
|
7903
|
-
console.log(
|
|
7904
|
-
console.log(
|
|
7905
|
-
console.log(
|
|
7906
|
-
console.log(
|
|
7907
|
-
console.log(
|
|
7927
|
+
console.log(import_chalk26.default.dim(`Server: ${api.serverUrl}`));
|
|
7928
|
+
console.log(import_chalk26.default.dim(`Sender: ${sender.did}`));
|
|
7929
|
+
console.log(import_chalk26.default.dim(`Recipient: ${recipientDid}`));
|
|
7930
|
+
console.log(import_chalk26.default.dim(`Delegation: ${delegationId}`));
|
|
7931
|
+
console.log(import_chalk26.default.dim(`Request id: ${requestId}`));
|
|
7908
7932
|
const result = await sendWorkEnvelope({ api, sender, recipientDid, body, ttlSeconds, verbose: opts.verbose, server: opts.server });
|
|
7909
7933
|
printIngestResult3(result);
|
|
7910
|
-
console.log(
|
|
7934
|
+
console.log(import_chalk26.default.dim(`
|
|
7911
7935
|
The payee can reply with:`));
|
|
7912
|
-
console.log(
|
|
7913
|
-
console.log(
|
|
7936
|
+
console.log(import_chalk26.default.dim(` heyarp work respond ${result.relationshipId} ${delegationId} ${requestId} --output '<json>'`));
|
|
7937
|
+
console.log(import_chalk26.default.dim(` heyarp work respond ${result.relationshipId} ${delegationId} ${requestId} --error CODE:message`));
|
|
7914
7938
|
}
|
|
7915
7939
|
function registerRespond(parent) {
|
|
7916
7940
|
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 +7959,13 @@ async function runRespond(relationshipId, delegationId, requestId, opts) {
|
|
|
7935
7959
|
...responsePayload
|
|
7936
7960
|
};
|
|
7937
7961
|
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(
|
|
7962
|
+
console.log(import_chalk26.default.dim(`Server: ${api.serverUrl}`));
|
|
7963
|
+
console.log(import_chalk26.default.dim(`Sender: ${sender.did}`));
|
|
7964
|
+
console.log(import_chalk26.default.dim(`Recipient: ${recipientDid}`));
|
|
7965
|
+
console.log(import_chalk26.default.dim(`Relationship: ${relationshipId}`));
|
|
7966
|
+
console.log(import_chalk26.default.dim(`Delegation: ${delegationId}`));
|
|
7967
|
+
console.log(import_chalk26.default.dim(`Request id: ${requestId}`));
|
|
7968
|
+
console.log(import_chalk26.default.dim(`Outcome: ${responsePayload.output ? "success" : "error"}`));
|
|
7945
7969
|
const result = await sendWorkEnvelope({ api, sender, recipientDid, body, ttlSeconds, verbose: opts.verbose, server: opts.server });
|
|
7946
7970
|
printIngestResult3(result);
|
|
7947
7971
|
}
|
|
@@ -7949,25 +7973,25 @@ async function sendWorkEnvelope(args) {
|
|
|
7949
7973
|
const nextSequence = (args.sender.lastSenderSequence ?? 0) + 1;
|
|
7950
7974
|
const protectedBlock = {
|
|
7951
7975
|
protocol_version: "arp/0.1",
|
|
7952
|
-
purpose:
|
|
7953
|
-
message_id: (0,
|
|
7976
|
+
purpose: import_sdk15.Purpose.ENVELOPE,
|
|
7977
|
+
message_id: (0, import_sdk15.uuidV4)(),
|
|
7954
7978
|
sender_did: args.sender.did,
|
|
7955
7979
|
recipient_did: args.recipientDid,
|
|
7956
7980
|
relationship_id: null,
|
|
7957
7981
|
sender_sequence: nextSequence,
|
|
7958
|
-
sender_nonce: (0,
|
|
7959
|
-
timestamp: (0,
|
|
7960
|
-
expires_at: (0,
|
|
7982
|
+
sender_nonce: (0, import_sdk15.senderNonce)(),
|
|
7983
|
+
timestamp: (0, import_sdk15.rfc3339)(),
|
|
7984
|
+
expires_at: (0, import_sdk15.expiresAt)(args.ttlSeconds),
|
|
7961
7985
|
delivery_id: null
|
|
7962
7986
|
};
|
|
7963
7987
|
const signer = makeSigner(args.sender);
|
|
7964
|
-
const envelope = (0,
|
|
7988
|
+
const envelope = (0, import_sdk15.signEnvelope)({
|
|
7965
7989
|
protected: protectedBlock,
|
|
7966
7990
|
body: args.body,
|
|
7967
7991
|
identitySecretKey: signer.identitySecretKey
|
|
7968
7992
|
});
|
|
7969
7993
|
if (args.verbose) {
|
|
7970
|
-
console.log(
|
|
7994
|
+
console.log(import_chalk26.default.bold("\nEnvelope (pre-send):"));
|
|
7971
7995
|
console.log(formatJson(envelope));
|
|
7972
7996
|
}
|
|
7973
7997
|
try {
|
|
@@ -8007,12 +8031,12 @@ async function resolveResponseRecipient(cmdName, api, signer, args) {
|
|
|
8007
8031
|
);
|
|
8008
8032
|
}
|
|
8009
8033
|
function printIngestResult3(result) {
|
|
8010
|
-
console.log(
|
|
8011
|
-
console.log(`${
|
|
8012
|
-
console.log(`${
|
|
8013
|
-
console.log(`${
|
|
8014
|
-
console.log(`${
|
|
8015
|
-
console.log(`${
|
|
8034
|
+
console.log(import_chalk26.default.green("\nDelivered."));
|
|
8035
|
+
console.log(`${import_chalk26.default.bold("Event id")}: ${import_chalk26.default.cyan(result.eventId)}`);
|
|
8036
|
+
console.log(`${import_chalk26.default.bold("Relationship id")}: ${import_chalk26.default.cyan(result.relationshipId)}`);
|
|
8037
|
+
console.log(`${import_chalk26.default.bold("Chain index")}: ${import_chalk26.default.cyan(String(result.relationshipEventIndex))}`);
|
|
8038
|
+
console.log(`${import_chalk26.default.bold("Server timestamp")}: ${import_chalk26.default.cyan(result.serverTimestamp)}`);
|
|
8039
|
+
console.log(`${import_chalk26.default.bold("Server event hash")}: ${import_chalk26.default.cyan(result.serverEventHash)}`);
|
|
8016
8040
|
}
|
|
8017
8041
|
function parseJsonObject(cmdName, flagName, raw) {
|
|
8018
8042
|
let parsed;
|
|
@@ -8050,13 +8074,13 @@ function parseParamsInput(cmdName, opts) {
|
|
|
8050
8074
|
return parseJsonObject(cmdName, "--params", opts.params ?? "{}");
|
|
8051
8075
|
}
|
|
8052
8076
|
function readJsonObjectFile(cmdName, flagName, path) {
|
|
8053
|
-
const { existsSync:
|
|
8054
|
-
if (!
|
|
8077
|
+
const { existsSync: existsSync6, readFileSync: readFileSync8 } = require("fs");
|
|
8078
|
+
if (!existsSync6(path)) {
|
|
8055
8079
|
throw new Error(`${cmdName}: ${flagName} file not found at ${path}`);
|
|
8056
8080
|
}
|
|
8057
8081
|
let raw;
|
|
8058
8082
|
try {
|
|
8059
|
-
raw =
|
|
8083
|
+
raw = readFileSync8(path, "utf8");
|
|
8060
8084
|
} catch (err) {
|
|
8061
8085
|
const detail = err instanceof Error ? err.message : String(err);
|
|
8062
8086
|
throw new Error(`${cmdName}: failed to read ${flagName} (${path}): ${detail}`);
|
|
@@ -8104,7 +8128,7 @@ function parseTtl5(cmdName, raw) {
|
|
|
8104
8128
|
}
|
|
8105
8129
|
function parseRequestId(cmdName, raw) {
|
|
8106
8130
|
if (raw === void 0 || raw === "") {
|
|
8107
|
-
return (0,
|
|
8131
|
+
return (0, import_sdk15.uuidV4)();
|
|
8108
8132
|
}
|
|
8109
8133
|
if (raw.length === 0) {
|
|
8110
8134
|
throw new Error(`${cmdName}: --request-id must be a non-empty string`);
|
|
@@ -8125,7 +8149,7 @@ function requireDid3(cmdName, did, label) {
|
|
|
8125
8149
|
}
|
|
8126
8150
|
|
|
8127
8151
|
// src/commands/work-list.ts
|
|
8128
|
-
var
|
|
8152
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
8129
8153
|
init_api();
|
|
8130
8154
|
init_format();
|
|
8131
8155
|
init_state();
|
|
@@ -8155,9 +8179,9 @@ async function runWorkList(relationshipId, opts) {
|
|
|
8155
8179
|
const api = new ArpApiClient(opts.server);
|
|
8156
8180
|
const sender = resolveSenderAgent("work-list", opts.server, opts.fromDid);
|
|
8157
8181
|
if (!opts.json) {
|
|
8158
|
-
console.log(
|
|
8159
|
-
console.log(
|
|
8160
|
-
console.log(
|
|
8182
|
+
console.log(import_chalk27.default.dim(`Server: ${api.serverUrl}`));
|
|
8183
|
+
console.log(import_chalk27.default.dim(`Signer: ${sender.did}`));
|
|
8184
|
+
console.log(import_chalk27.default.dim(`Relationship: ${relationshipId}`));
|
|
8161
8185
|
}
|
|
8162
8186
|
const query = { limit };
|
|
8163
8187
|
if (state) query.state = state;
|
|
@@ -8170,7 +8194,7 @@ async function runWorkList(relationshipId, opts) {
|
|
|
8170
8194
|
return;
|
|
8171
8195
|
}
|
|
8172
8196
|
if (rows.length === 0) {
|
|
8173
|
-
console.log(
|
|
8197
|
+
console.log(import_chalk27.default.dim("\n(no work-logs for this relationship)"));
|
|
8174
8198
|
return;
|
|
8175
8199
|
}
|
|
8176
8200
|
console.log("");
|
|
@@ -8187,36 +8211,36 @@ async function runWorkList(relationshipId, opts) {
|
|
|
8187
8211
|
}));
|
|
8188
8212
|
}
|
|
8189
8213
|
const lastId = rows[rows.length - 1].id;
|
|
8190
|
-
console.log(
|
|
8214
|
+
console.log(import_chalk27.default.dim(`
|
|
8191
8215
|
${rows.length} work-log row(s). Paginate with --after ${lastId}.`));
|
|
8192
8216
|
}
|
|
8193
8217
|
function formatWorkLogLine(w, selfDid, opts = {}) {
|
|
8194
8218
|
const delegationPart = opts.fullIds ? w.delegationId : idHead3(w.delegationId);
|
|
8195
8219
|
const requestPart = opts.fullIds ? w.requestId : truncate3(w.requestId, 16);
|
|
8196
|
-
const id =
|
|
8220
|
+
const id = import_chalk27.default.bold(`${delegationPart}/${requestPart}`);
|
|
8197
8221
|
const state = colorState3(w.state).padEnd(stateColumnWidth3());
|
|
8198
8222
|
const peerCallerHead = opts.fullIds ? w.callerDid : didHead5(w.callerDid);
|
|
8199
8223
|
const peerPayeeHead = opts.fullIds ? w.payeeDid : didHead5(w.payeeDid);
|
|
8200
|
-
const direction = w.callerDid === selfDid ? `${
|
|
8224
|
+
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
8225
|
const outcome = formatOutcome(w);
|
|
8202
8226
|
return `${id} ${state} ${direction} ${outcome}`;
|
|
8203
8227
|
}
|
|
8204
8228
|
function colorState3(s) {
|
|
8205
8229
|
switch (s) {
|
|
8206
8230
|
case "requested":
|
|
8207
|
-
return
|
|
8231
|
+
return import_chalk27.default.yellow("requested");
|
|
8208
8232
|
case "responded":
|
|
8209
|
-
return
|
|
8233
|
+
return import_chalk27.default.green("responded");
|
|
8210
8234
|
}
|
|
8211
8235
|
}
|
|
8212
8236
|
function stateColumnWidth3() {
|
|
8213
8237
|
return 9;
|
|
8214
8238
|
}
|
|
8215
8239
|
function formatOutcome(w) {
|
|
8216
|
-
if (w.state === "requested") return
|
|
8217
|
-
if (w.responseError) return
|
|
8218
|
-
if (w.responseOutput) return
|
|
8219
|
-
return
|
|
8240
|
+
if (w.state === "requested") return import_chalk27.default.dim("(in flight)");
|
|
8241
|
+
if (w.responseError) return import_chalk27.default.red(`error ${w.responseError.code}: ${truncate3(w.responseError.message, 32)}`);
|
|
8242
|
+
if (w.responseOutput) return import_chalk27.default.cyan("ok");
|
|
8243
|
+
return import_chalk27.default.dim("(empty response)");
|
|
8220
8244
|
}
|
|
8221
8245
|
function idHead3(id) {
|
|
8222
8246
|
if (id.length <= 12) return id;
|
|
@@ -8279,10 +8303,8 @@ async function main() {
|
|
|
8279
8303
|
registerDidDocCommand(program);
|
|
8280
8304
|
registerDoctorCommand(program);
|
|
8281
8305
|
registerEscrowCommands(program);
|
|
8282
|
-
registerExamplesCommand(program);
|
|
8283
8306
|
registerWhoamiCommand(program);
|
|
8284
8307
|
registerLifecycleCommands(program);
|
|
8285
|
-
registerRotateCommand(program);
|
|
8286
8308
|
registerSendHandshakeCommand(program);
|
|
8287
8309
|
registerSendHandshakeResponseCommand(program);
|
|
8288
8310
|
registerInboxCommand(program);
|