@heyanon-arp/cli 0.0.15 → 0.0.17
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/bin/heyarp.cjs +47 -0
- package/dist/cli.js +599 -571
- package/dist/cli.js.map +1 -1
- package/package.json +4 -3
package/dist/cli.js
CHANGED
|
@@ -34,10 +34,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
34
34
|
function arpHomeDir() {
|
|
35
35
|
const fromEnv = process.env.HEYARP_HOME;
|
|
36
36
|
if (fromEnv && fromEnv.length > 0) return fromEnv;
|
|
37
|
-
return (0, import_node_path.join)((0, import_node_os.homedir)(), ".
|
|
37
|
+
return (0, import_node_path.join)((0, import_node_os.homedir)(), ".heyarp");
|
|
38
38
|
}
|
|
39
39
|
function homesRegistryPath() {
|
|
40
|
-
return (0, import_node_path.join)((0, import_node_os.homedir)(), ".
|
|
40
|
+
return (0, import_node_path.join)((0, import_node_os.homedir)(), ".heyarp", "homes.json");
|
|
41
41
|
}
|
|
42
42
|
var import_node_os, import_node_path;
|
|
43
43
|
var init_paths = __esm({
|
|
@@ -354,9 +354,8 @@ var init_api = __esm({
|
|
|
354
354
|
}
|
|
355
355
|
/**
|
|
356
356
|
* Signed-request authenticated GET /v1/agents/:did. Owner-only on
|
|
357
|
-
* the server side
|
|
358
|
-
*
|
|
359
|
-
* key still verifies.
|
|
357
|
+
* the server side; it returns the same shape as /did-document —
|
|
358
|
+
* useful as a sanity check that a stored secret key still verifies.
|
|
360
359
|
*/
|
|
361
360
|
async getAgent(did, signer) {
|
|
362
361
|
return this.signedRequest("GET", `/v1/agents/${encodeURIComponent(did)}`, null, signer);
|
|
@@ -723,17 +722,17 @@ var import_simple_update_notifier = __toESM(require("simple-update-notifier"));
|
|
|
723
722
|
// package.json
|
|
724
723
|
var package_default = {
|
|
725
724
|
name: "@heyanon-arp/cli",
|
|
726
|
-
version: "0.0.
|
|
725
|
+
version: "0.0.17",
|
|
727
726
|
description: "Command-line client for the Agent Relationship Protocol \u2014 register agents, sign envelopes, run escrowed work cycles on Solana.",
|
|
728
727
|
license: "MIT",
|
|
729
728
|
keywords: ["arp", "agent-relationship-protocol", "did", "solana", "escrow", "ed25519", "agents", "a2a", "cli"],
|
|
730
729
|
bin: {
|
|
731
|
-
heyarp: "./
|
|
730
|
+
heyarp: "./bin/heyarp.cjs"
|
|
732
731
|
},
|
|
733
732
|
publishConfig: {
|
|
734
733
|
access: "public"
|
|
735
734
|
},
|
|
736
|
-
files: ["dist", "LICENSE", "README.md"],
|
|
735
|
+
files: ["dist", "bin", "LICENSE", "README.md"],
|
|
737
736
|
engines: {
|
|
738
737
|
node: ">=22"
|
|
739
738
|
},
|
|
@@ -805,6 +804,14 @@ function onboardingHelpFooter() {
|
|
|
805
804
|
`${import_chalk.default.dim("Then:")} ${import_chalk.default.cyan("heyarp register")} ${import_chalk.default.dim("\u2192 handshake \u2192 delegation \u2192 work \u2192 receipt \u2192 on-chain claim.")}`
|
|
806
805
|
].join("\n");
|
|
807
806
|
}
|
|
807
|
+
function optionPlacementHelpNote() {
|
|
808
|
+
return [
|
|
809
|
+
"",
|
|
810
|
+
`${import_chalk.default.bold("Option placement:")} only ${import_chalk.default.cyan("--trace")}, ${import_chalk.default.cyan("--help")}, ${import_chalk.default.cyan("--version")} are global (before the command).`,
|
|
811
|
+
`Everything else \u2014 ${import_chalk.default.cyan("--json")}, ${import_chalk.default.cyan("--from-did")}, ${import_chalk.default.cyan("--verbose")}, ${import_chalk.default.cyan("--server")}, \u2026 \u2014 is a command option and goes AFTER the command:`,
|
|
812
|
+
` ${import_chalk.default.cyan("heyarp <command> [options]")} ${import_chalk.default.dim("e.g. heyarp relationships --json --from-did did:arp:\u2026")}`
|
|
813
|
+
].join("\n");
|
|
814
|
+
}
|
|
808
815
|
function toCliErrorJson(err, includeStack = false) {
|
|
809
816
|
const { ApiError: ApiError2 } = (init_api(), __toCommonJS(api_exports));
|
|
810
817
|
if (err instanceof ApiError2) {
|
|
@@ -822,7 +829,9 @@ function toCliErrorJson(err, includeStack = false) {
|
|
|
822
829
|
}
|
|
823
830
|
function emitError(err, opts) {
|
|
824
831
|
if (opts.json) {
|
|
825
|
-
|
|
832
|
+
const obj = toCliErrorJson(err, opts.verbose);
|
|
833
|
+
if (opts.hint) obj.hint = opts.hint;
|
|
834
|
+
console.error(JSON.stringify(obj));
|
|
826
835
|
return;
|
|
827
836
|
}
|
|
828
837
|
const { ApiError: ApiError2 } = (init_api(), __toCommonJS(api_exports));
|
|
@@ -831,6 +840,7 @@ function emitError(err, opts) {
|
|
|
831
840
|
} else {
|
|
832
841
|
console.error(formatGenericError(err, opts.verbose));
|
|
833
842
|
}
|
|
843
|
+
if (opts.hint) console.error(`${import_chalk.default.yellow("hint")}: ${opts.hint}`);
|
|
834
844
|
}
|
|
835
845
|
function emitActionError(err, command) {
|
|
836
846
|
let root = command;
|
|
@@ -1163,19 +1173,28 @@ function listAgents() {
|
|
|
1163
1173
|
var import_chalk2 = __toESM(require("chalk"));
|
|
1164
1174
|
init_api();
|
|
1165
1175
|
function registerLifecycleCommands(root) {
|
|
1166
|
-
root.command("update").description("Update an agent profile (name / description / tags). At least one flag is required.").argument("
|
|
1176
|
+
root.command("update").description("Update an agent profile (name / description / tags). At least one flag is required.").argument("[did]", "Agent DID (did:arp:...) \u2014 optional when --from-did is given OR exactly one agent is registered for the resolved server.").option("--server <url>", "Override ARP server base URL").option("--from-did <did>", "Alias for the positional <did> \u2014 accepted for consistency with other signed commands. Must match the positional if both are given.").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", accumulate, []).option("--clear-tags", "Drop all tags (cannot be combined with --tag)", false).option("--json", "Machine-readable mode \u2014 emit the updated profile as a single JSON object on stdout (AgentPublic). Prelude moves to stderr. ", false).action(
|
|
1167
1177
|
async (did, opts) => {
|
|
1178
|
+
if (did !== void 0 && opts.fromDid !== void 0 && did !== opts.fromDid) {
|
|
1179
|
+
throw new Error(`update: positional <did> (${did}) and --from-did (${opts.fromDid}) disagree \u2014 pass only one`);
|
|
1180
|
+
}
|
|
1181
|
+
const explicitDid = did ?? opts.fromDid;
|
|
1182
|
+
const targetDid = (explicitDid !== void 0 ? loadAgentOrThrow(opts.server, explicitDid) : resolveSenderAgent("update", opts.server, void 0)).did;
|
|
1168
1183
|
const body = buildUpdateBody(opts);
|
|
1169
1184
|
if (Object.keys(body).length === 0) {
|
|
1170
1185
|
throw new Error("update: pass at least one of --name / --description / --tag / --clear-tags");
|
|
1171
1186
|
}
|
|
1172
|
-
const agent = await actSigned(
|
|
1173
|
-
updateAgentLocal(opts.server,
|
|
1187
|
+
const agent = await actSigned(targetDid, opts.server, opts.json, (api, signer) => api.updateAgent(targetDid, body, signer));
|
|
1188
|
+
updateAgentLocal(opts.server, targetDid, {
|
|
1174
1189
|
name: agent.name,
|
|
1175
1190
|
description: agent.description,
|
|
1176
1191
|
tags: agent.tags
|
|
1177
1192
|
});
|
|
1178
|
-
|
|
1193
|
+
if (opts.json) {
|
|
1194
|
+
jsonOut(agent);
|
|
1195
|
+
} else {
|
|
1196
|
+
printAgent("Updated", agent);
|
|
1197
|
+
}
|
|
1179
1198
|
}
|
|
1180
1199
|
);
|
|
1181
1200
|
}
|
|
@@ -1196,11 +1215,11 @@ function buildUpdateBody(opts) {
|
|
|
1196
1215
|
}
|
|
1197
1216
|
return body;
|
|
1198
1217
|
}
|
|
1199
|
-
async function actSigned(did, serverOverride, act) {
|
|
1218
|
+
async function actSigned(did, serverOverride, json, act) {
|
|
1200
1219
|
const local = loadAgentOrThrow(serverOverride, did);
|
|
1201
1220
|
const api = new ArpApiClient(serverOverride);
|
|
1202
|
-
|
|
1203
|
-
|
|
1221
|
+
progress(json, import_chalk2.default.dim(`Server: ${api.serverUrl}`));
|
|
1222
|
+
progress(json, import_chalk2.default.dim(`Signer: ${local.did}`));
|
|
1204
1223
|
const signer = makeSigner(local);
|
|
1205
1224
|
return act(api, signer);
|
|
1206
1225
|
}
|
|
@@ -1246,10 +1265,10 @@ function registerAgentsCommand(root) {
|
|
|
1246
1265
|
}
|
|
1247
1266
|
async function runAgents(opts) {
|
|
1248
1267
|
const limit = parseLimit(opts.limit);
|
|
1249
|
-
if (opts.sort && opts.sort !==
|
|
1268
|
+
if (opts.sort && opts.sort !== import_sdk2.DiscoverySorts.REPUTATION && opts.sort !== import_sdk2.DiscoverySorts.RECENT && opts.sort !== import_sdk2.DiscoverySorts.CREATED) {
|
|
1250
1269
|
throw new Error(`agents: --sort must be 'reputation', 'recent', or 'created' (got '${opts.sort}')`);
|
|
1251
1270
|
}
|
|
1252
|
-
const sort = opts.sort ===
|
|
1271
|
+
const sort = opts.sort === import_sdk2.DiscoverySorts.RECENT ? import_sdk2.DiscoverySorts.RECENT : opts.sort === import_sdk2.DiscoverySorts.CREATED ? import_sdk2.DiscoverySorts.CREATED : import_sdk2.DiscoverySorts.REPUTATION;
|
|
1253
1272
|
const api = new ArpApiClient(opts.server);
|
|
1254
1273
|
progress(opts.json, import_chalk3.default.dim(`Server: ${api.serverUrl}`));
|
|
1255
1274
|
const query = { limit, sort };
|
|
@@ -1258,7 +1277,7 @@ async function runAgents(opts) {
|
|
|
1258
1277
|
if (opts.accountId) query.accountId = opts.accountId;
|
|
1259
1278
|
if (opts.accepts) query.accepts = resolveAcceptsAsset(opts.accepts);
|
|
1260
1279
|
if (opts.online) query.online = true;
|
|
1261
|
-
if (sort ===
|
|
1280
|
+
if (sort === import_sdk2.DiscoverySorts.CREATED) {
|
|
1262
1281
|
if (opts.after) query.after = opts.after;
|
|
1263
1282
|
} else {
|
|
1264
1283
|
query.page = parsePage(opts.page);
|
|
@@ -1297,10 +1316,10 @@ ${rule}`);
|
|
|
1297
1316
|
`);
|
|
1298
1317
|
}
|
|
1299
1318
|
if (pagination.hasMore) {
|
|
1300
|
-
if (sort ===
|
|
1319
|
+
if (sort === import_sdk2.DiscoverySorts.CREATED && pagination.nextCursor) {
|
|
1301
1320
|
console.log(import_chalk3.default.dim(`
|
|
1302
1321
|
Next page: re-run with --after ${pagination.nextCursor}`));
|
|
1303
|
-
} else if (sort !==
|
|
1322
|
+
} else if (sort !== import_sdk2.DiscoverySorts.CREATED) {
|
|
1304
1323
|
console.log(import_chalk3.default.dim(`
|
|
1305
1324
|
Next page: re-run with --page ${parsePage(opts.page) + 1}`));
|
|
1306
1325
|
}
|
|
@@ -1496,13 +1515,14 @@ function registerAssetsCommand(root) {
|
|
|
1496
1515
|
}
|
|
1497
1516
|
|
|
1498
1517
|
// src/commands/block.ts
|
|
1518
|
+
var import_sdk4 = require("@heyanon-arp/sdk");
|
|
1499
1519
|
var import_chalk5 = __toESM(require("chalk"));
|
|
1500
1520
|
init_api();
|
|
1501
1521
|
function resolveSigningAgent(opts) {
|
|
1502
1522
|
return opts.fromDid !== void 0 ? loadAgentOrThrow(opts.server, opts.fromDid) : resolveSenderAgent("block", opts.server, void 0);
|
|
1503
1523
|
}
|
|
1504
1524
|
function formatBlockRow(b) {
|
|
1505
|
-
const scope = b.scope ===
|
|
1525
|
+
const scope = b.scope === import_sdk4.InboxBlockScopes.ACCOUNT ? import_chalk5.default.yellow("account") : import_chalk5.default.cyan("did");
|
|
1506
1526
|
const reason = b.reason ? import_chalk5.default.dim(` \u2014 ${b.reason}`) : "";
|
|
1507
1527
|
return `${scope.padEnd(7)} ${b.did}${reason} ${import_chalk5.default.dim(b.blockedAt)}`;
|
|
1508
1528
|
}
|
|
@@ -1527,7 +1547,7 @@ async function runAdd(did, opts) {
|
|
|
1527
1547
|
jsonOut(result);
|
|
1528
1548
|
return;
|
|
1529
1549
|
}
|
|
1530
|
-
const what = result.scope ===
|
|
1550
|
+
const what = result.scope === import_sdk4.InboxBlockScopes.ACCOUNT ? `the owner account of ${did} (all its agents)` : did;
|
|
1531
1551
|
console.log(import_chalk5.default.green(`\u2713 Blocked ${what}.`));
|
|
1532
1552
|
}
|
|
1533
1553
|
async function runRemove(did, opts) {
|
|
@@ -1564,7 +1584,7 @@ var import_chalk6 = __toESM(require("chalk"));
|
|
|
1564
1584
|
init_api();
|
|
1565
1585
|
init_config();
|
|
1566
1586
|
function registerConfigCommand(root) {
|
|
1567
|
-
const config = root.command("config").description("Read/write persistent CLI configuration (~/.
|
|
1587
|
+
const config = root.command("config").description("Read/write persistent CLI configuration (~/.heyarp/config.json)").option("--server <url>", "Override the server URL whose bucket per-server keys (e.g. defaultDid) read/write to");
|
|
1568
1588
|
config.command("set <key> <value>").description(`Persist a config value. Global keys: ${GLOBAL_CONFIG_KEYS.join(", ")}. Per-server keys: ${SERVER_CONFIG_KEYS.join(", ")}`).action((key, value) => {
|
|
1569
1589
|
const opts = config.opts();
|
|
1570
1590
|
if (isGlobalConfigKey(key)) {
|
|
@@ -1643,14 +1663,15 @@ function unknownKey(key) {
|
|
|
1643
1663
|
|
|
1644
1664
|
// src/commands/delegation.ts
|
|
1645
1665
|
var import_node_fs4 = require("fs");
|
|
1646
|
-
var
|
|
1666
|
+
var import_sdk10 = require("@heyanon-arp/sdk");
|
|
1647
1667
|
var import_chalk8 = __toESM(require("chalk"));
|
|
1648
1668
|
init_api();
|
|
1649
1669
|
|
|
1650
1670
|
// src/id-format.ts
|
|
1671
|
+
var import_sdk5 = require("@heyanon-arp/sdk");
|
|
1651
1672
|
var UUID_RE = /^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$/;
|
|
1652
1673
|
var OBJECT_ID_24_HEX_RE = /^[0-9a-f]{24}$/;
|
|
1653
|
-
var SHA256_PREFIX_RE =
|
|
1674
|
+
var SHA256_PREFIX_RE = import_sdk5.SHA256_HEX_RE;
|
|
1654
1675
|
var UUID_NO_DASHES_RE = /^[a-fA-F0-9]{32}$/;
|
|
1655
1676
|
function describeNonUuidShape(raw) {
|
|
1656
1677
|
if (raw === "") return "empty string";
|
|
@@ -1682,11 +1703,11 @@ function requireUuid(cmdName, raw, label) {
|
|
|
1682
1703
|
}
|
|
1683
1704
|
|
|
1684
1705
|
// src/commands/status.ts
|
|
1685
|
-
var
|
|
1706
|
+
var import_sdk6 = require("@heyanon-arp/sdk");
|
|
1686
1707
|
var import_chalk7 = __toESM(require("chalk"));
|
|
1687
1708
|
init_api();
|
|
1688
1709
|
var UNTIL_PHASES = [
|
|
1689
|
-
"delegation.
|
|
1710
|
+
"delegation.offered",
|
|
1690
1711
|
"delegation.accepted",
|
|
1691
1712
|
"delegation.locked",
|
|
1692
1713
|
"delegation.disputing",
|
|
@@ -1695,7 +1716,6 @@ var UNTIL_PHASES = [
|
|
|
1695
1716
|
"work.requested",
|
|
1696
1717
|
"work.responded",
|
|
1697
1718
|
"receipt.proposed",
|
|
1698
|
-
"receipt.cosigned",
|
|
1699
1719
|
"relationship.pending",
|
|
1700
1720
|
"relationship.active",
|
|
1701
1721
|
"relationship.paused",
|
|
@@ -1784,13 +1804,13 @@ async function awaitFsmTransitionAfterAction(input) {
|
|
|
1784
1804
|
}
|
|
1785
1805
|
}
|
|
1786
1806
|
async function runWaitLoop(opts) {
|
|
1787
|
-
const isTerminal = (s) => s.cycleComplete || s.relationshipState ===
|
|
1807
|
+
const isTerminal = (s) => s.cycleComplete || s.relationshipState === import_sdk6.RelationshipStates.CLOSED || s.relationshipState === "not_found";
|
|
1788
1808
|
const isActionable = (s) => {
|
|
1789
1809
|
if (s.nextActionOwner === "me") {
|
|
1790
1810
|
if (s.relationshipState === "unknown") return false;
|
|
1791
1811
|
return true;
|
|
1792
1812
|
}
|
|
1793
|
-
if (s.nextActionOwner === "either" && s.relationshipState ===
|
|
1813
|
+
if (s.nextActionOwner === "either" && s.relationshipState === import_sdk6.RelationshipStates.ACTIVE) {
|
|
1794
1814
|
return true;
|
|
1795
1815
|
}
|
|
1796
1816
|
return false;
|
|
@@ -1926,43 +1946,41 @@ function parseUntilPhase(raw) {
|
|
|
1926
1946
|
function isPhaseTerminallyUnreachable(phase, s) {
|
|
1927
1947
|
if (untilPhaseMatched(phase, s)) return false;
|
|
1928
1948
|
if (s.relationshipState === "not_found") return true;
|
|
1929
|
-
if (s.relationshipState ===
|
|
1949
|
+
if (s.relationshipState === import_sdk6.RelationshipStates.CLOSED && phase !== "relationship.closed") return true;
|
|
1930
1950
|
return false;
|
|
1931
1951
|
}
|
|
1932
1952
|
function untilPhaseMatched(phase, s) {
|
|
1933
1953
|
switch (phase) {
|
|
1934
|
-
case "delegation.
|
|
1935
|
-
return s.latestDelegation?.state ===
|
|
1954
|
+
case "delegation.offered":
|
|
1955
|
+
return s.latestDelegation?.state === import_sdk6.DelegationStates.OFFERED;
|
|
1936
1956
|
case "delegation.accepted":
|
|
1937
|
-
return s.latestDelegation?.state ===
|
|
1957
|
+
return s.latestDelegation?.state === import_sdk6.DelegationStates.ACCEPTED;
|
|
1938
1958
|
case "delegation.locked":
|
|
1939
|
-
return s.latestDelegation?.state ===
|
|
1959
|
+
return s.latestDelegation?.state === import_sdk6.DelegationStates.LOCKED;
|
|
1940
1960
|
case "delegation.disputing":
|
|
1941
|
-
return s.latestDelegation?.state ===
|
|
1961
|
+
return s.latestDelegation?.state === import_sdk6.DelegationStates.DISPUTING;
|
|
1942
1962
|
case "delegation.canceled":
|
|
1943
|
-
return s.latestDelegation?.state ===
|
|
1963
|
+
return s.latestDelegation?.state === import_sdk6.DelegationStates.CANCELED;
|
|
1944
1964
|
case "delegation.declined":
|
|
1945
|
-
return s.latestDelegation?.state ===
|
|
1965
|
+
return s.latestDelegation?.state === import_sdk6.DelegationStates.DECLINED;
|
|
1946
1966
|
case "work.requested":
|
|
1947
|
-
return s.latestWorkLog?.state ===
|
|
1967
|
+
return s.latestWorkLog?.state === import_sdk6.WorkLogStates.REQUESTED;
|
|
1948
1968
|
case "work.responded":
|
|
1949
|
-
return s.latestWorkLog?.state ===
|
|
1969
|
+
return s.latestWorkLog?.state === import_sdk6.WorkLogStates.RESPONDED;
|
|
1950
1970
|
case "receipt.proposed":
|
|
1951
|
-
return s.latestReceipt
|
|
1952
|
-
case "receipt.cosigned":
|
|
1953
|
-
return s.latestReceipt?.state === "cosigned";
|
|
1971
|
+
return s.latestReceipt != null;
|
|
1954
1972
|
case "relationship.pending":
|
|
1955
|
-
return s.relationshipState ===
|
|
1973
|
+
return s.relationshipState === import_sdk6.RelationshipStates.PENDING;
|
|
1956
1974
|
case "relationship.active":
|
|
1957
|
-
return s.relationshipState ===
|
|
1975
|
+
return s.relationshipState === import_sdk6.RelationshipStates.ACTIVE;
|
|
1958
1976
|
case "relationship.paused":
|
|
1959
|
-
return s.relationshipState ===
|
|
1977
|
+
return s.relationshipState === import_sdk6.RelationshipStates.PAUSED;
|
|
1960
1978
|
case "relationship.closed":
|
|
1961
|
-
return s.relationshipState ===
|
|
1979
|
+
return s.relationshipState === import_sdk6.RelationshipStates.CLOSED;
|
|
1962
1980
|
case "cycle.complete":
|
|
1963
1981
|
return s.cycleComplete;
|
|
1964
1982
|
case "cycle.released":
|
|
1965
|
-
return s.latestDelegation?.state ===
|
|
1983
|
+
return s.latestDelegation?.state === import_sdk6.DelegationStates.COMPLETED;
|
|
1966
1984
|
}
|
|
1967
1985
|
}
|
|
1968
1986
|
function parseWaitInterval(raw) {
|
|
@@ -2013,7 +2031,7 @@ async function composeStatus(api, signerDid, relationshipId, signer) {
|
|
|
2013
2031
|
const relationship = relationships.find((r) => r.relationshipId === relationshipId);
|
|
2014
2032
|
const counterpartyDid = relationship ? relationship.pairDidA === signerDid ? relationship.pairDidB : relationship.pairDidA : inferCounterpartyFromEntities(signerDid, allDelegations);
|
|
2015
2033
|
const delegations = allDelegations;
|
|
2016
|
-
const latestDelegation = pickLatestLive(delegations, [
|
|
2034
|
+
const latestDelegation = pickLatestLive(delegations, [...import_sdk6.DELEGATION_ACTIVE_STATES]);
|
|
2017
2035
|
const [workLogs, receipts] = await Promise.all([
|
|
2018
2036
|
latestDelegation ? fetchAllPages(
|
|
2019
2037
|
(after) => api.listWorkLogs(relationshipId, signer, { limit: 100, delegationId: latestDelegation.delegationId, ...after ? { after } : {} })
|
|
@@ -2077,8 +2095,8 @@ function findReceiptForWorkLog(receipts, workLog, allWorkLogs = [workLog]) {
|
|
|
2077
2095
|
};
|
|
2078
2096
|
const responseBody = workLog.responseOutput !== void 0 ? { type: "work_response", content: { delegation_id: workLog.delegationId, request_id: workLog.requestId, output: workLog.responseOutput } } : workLog.responseError !== void 0 ? { type: "work_response", content: { delegation_id: workLog.delegationId, request_id: workLog.requestId, error: workLog.responseError } } : null;
|
|
2079
2097
|
if (!responseBody) return null;
|
|
2080
|
-
const expectedRequestHash = (0,
|
|
2081
|
-
const expectedResponseHash = (0,
|
|
2098
|
+
const expectedRequestHash = (0, import_sdk6.canonicalSha256Hex)(requestBody);
|
|
2099
|
+
const expectedResponseHash = (0, import_sdk6.canonicalSha256Hex)(responseBody);
|
|
2082
2100
|
const matches = receipts.filter((r) => r.requestHash === expectedRequestHash && r.responseHash === expectedResponseHash);
|
|
2083
2101
|
if (matches.length > 0) return pickLatest(matches);
|
|
2084
2102
|
const respondedSiblings = allWorkLogs.filter((wl) => wl.delegationId === workLog.delegationId && (wl.responseOutput !== void 0 || wl.responseError !== void 0)).length;
|
|
@@ -2120,49 +2138,49 @@ function nextAction(input) {
|
|
|
2120
2138
|
complete: false
|
|
2121
2139
|
};
|
|
2122
2140
|
}
|
|
2123
|
-
if (relationship.state ===
|
|
2141
|
+
if (relationship.state === import_sdk6.RelationshipStates.PENDING) {
|
|
2124
2142
|
return {
|
|
2125
2143
|
hint: "Relationship is PENDING \u2014 one side owes `heyarp send-handshake-response <peer> --decision accept | decline`",
|
|
2126
2144
|
owner: "either",
|
|
2127
2145
|
complete: false
|
|
2128
2146
|
};
|
|
2129
2147
|
}
|
|
2130
|
-
if (relationship.state ===
|
|
2148
|
+
if (relationship.state === import_sdk6.RelationshipStates.PAUSED) {
|
|
2131
2149
|
return { hint: "Relationship is PAUSED \u2014 owner must `heyarp unpause` before any envelopes flow", owner: "either", complete: false };
|
|
2132
2150
|
}
|
|
2133
|
-
if (relationship.state ===
|
|
2151
|
+
if (relationship.state === import_sdk6.RelationshipStates.CLOSED) {
|
|
2134
2152
|
return { hint: "Relationship is CLOSED \u2014 terminal state, no further action possible. Start a fresh handshake to reopen.", owner: "none", complete: false };
|
|
2135
2153
|
}
|
|
2136
|
-
if (!latestDelegation || latestDelegation.state ===
|
|
2154
|
+
if (!latestDelegation || latestDelegation.state === import_sdk6.DelegationStates.DECLINED || latestDelegation.state === import_sdk6.DelegationStates.CANCELED) {
|
|
2137
2155
|
return {
|
|
2138
2156
|
hint: "No live delegation \u2014 buyer offers (terms only, no lock) `heyarp delegation offer <peer> --delegation-id <new-uuid> --title \u2026 --scope \u2026 --amount \u2026 --currency SOL:solana-devnet --deadline \u2026`, then funds the escrow lock AFTER the worker accepts via `heyarp delegation fund <del-id> --escrow-lock-from-file <path>`",
|
|
2139
2157
|
owner: "either",
|
|
2140
2158
|
complete: false
|
|
2141
2159
|
};
|
|
2142
2160
|
}
|
|
2143
|
-
if (latestDelegation.state ===
|
|
2161
|
+
if (latestDelegation.state === import_sdk6.DelegationStates.DISPUTING) {
|
|
2144
2162
|
return {
|
|
2145
2163
|
hint: `Delegation ${latestDelegation.delegationId} is DISPUTING \u2014 the buyer opened an on-chain dispute (their stake is escrowed too). The operator may rule until the dispute window lapses; after that either party closes with \`heyarp escrow dispute close ${latestDelegation.delegationId}\` (escrow returns to the buyer, both stakes return). Deadline: \`heyarp escrow show ${latestDelegation.delegationId}\`. No envelopes to send meanwhile.`,
|
|
2146
2164
|
owner: "none",
|
|
2147
2165
|
complete: false
|
|
2148
2166
|
};
|
|
2149
2167
|
}
|
|
2150
|
-
if (latestDelegation.state ===
|
|
2168
|
+
if (latestDelegation.state === import_sdk6.DelegationStates.COMPLETED) {
|
|
2151
2169
|
return {
|
|
2152
2170
|
hint: "Cycle COMPLETE \u2014 payment claimed on chain (lock paid; the receipt carries releaseStatus=paid)",
|
|
2153
2171
|
owner: "none",
|
|
2154
2172
|
complete: true
|
|
2155
2173
|
};
|
|
2156
2174
|
}
|
|
2157
|
-
if (latestDelegation.state ===
|
|
2175
|
+
if (latestDelegation.state === import_sdk6.DelegationStates.FAILED || latestDelegation.state === import_sdk6.DelegationStates.REFUNDED || latestDelegation.state === import_sdk6.DelegationStates.DISPUTE_RESOLVED) {
|
|
2158
2176
|
const stateLabel = latestDelegation.state.toUpperCase();
|
|
2159
|
-
const reason = latestDelegation.state ===
|
|
2177
|
+
const reason = latestDelegation.state === import_sdk6.DelegationStates.FAILED ? "on-chain escrow lock failed to finalise (typical causes: insufficient payer funds, wrong program-id, ProgramState PDA uninitialised \u2014 check `heyarp delegations <rel-id> --json | jq .[].escrowError` for the worker-side reason)" : latestDelegation.state === import_sdk6.DelegationStates.REFUNDED ? (
|
|
2160
2178
|
// Keyed on the DELEGATION row's releaseStatus, not the
|
|
2161
2179
|
// receipt's: in the claim-expired path the worker never
|
|
2162
2180
|
// submitted anything, so no receipt row exists to carry
|
|
2163
2181
|
// the outcome — the delegation copy is always present
|
|
2164
2182
|
// on indexer-driven terminals.
|
|
2165
|
-
latestDelegation.releaseStatus ===
|
|
2183
|
+
latestDelegation.releaseStatus === import_sdk6.LockStates.DISPUTE_CLOSED ? "the dispute window lapsed without an operator ruling and the dispute was closed \u2014 escrow returned to the buyer, both stakes returned (work was delivered but not paid)" : latestDelegation.releaseStatus === import_sdk6.LockStates.REVOKED ? "the work window lapsed without an on-chain submit \u2014 the buyer reclaimed the escrow AND the worker stake (claim_expired_work)" : "lock was refunded after expiry \u2014 no work was delivered against this delegation"
|
|
2166
2184
|
) : "admin closed the delegation via the dispute-resolution path";
|
|
2167
2185
|
return {
|
|
2168
2186
|
hint: `Delegation ${latestDelegation.delegationId} is ${stateLabel} (terminal) \u2014 ${reason}. Issue a fresh \`heyarp delegation offer <peer> --delegation-id <new-uuid> \u2026\` to retry (FSM does not auto-retry).`,
|
|
@@ -2170,16 +2188,16 @@ function nextAction(input) {
|
|
|
2170
2188
|
complete: false
|
|
2171
2189
|
};
|
|
2172
2190
|
}
|
|
2173
|
-
if (latestDelegation.state ===
|
|
2191
|
+
if (latestDelegation.state === import_sdk6.DelegationStates.OFFERED) {
|
|
2174
2192
|
const iAmOfferer = latestDelegation.offererDid === signerDid;
|
|
2175
|
-
const stateLabel =
|
|
2193
|
+
const stateLabel = "OFFERED";
|
|
2176
2194
|
return {
|
|
2177
2195
|
hint: iAmOfferer ? `Delegation ${latestDelegation.delegationId} is ${stateLabel} \u2014 counterparty owes \`heyarp delegation accept\` / \`decline\`` : `Delegation ${latestDelegation.delegationId} is ${stateLabel} \u2014 you owe \`heyarp delegation accept\` / \`decline\``,
|
|
2178
2196
|
owner: iAmOfferer ? "counterparty" : "me",
|
|
2179
2197
|
complete: false
|
|
2180
2198
|
};
|
|
2181
2199
|
}
|
|
2182
|
-
if (latestDelegation.state ===
|
|
2200
|
+
if (latestDelegation.state === import_sdk6.DelegationStates.ACCEPTED && !latestWorkLog) {
|
|
2183
2201
|
const iAmOfferer = latestDelegation.offererDid === signerDid;
|
|
2184
2202
|
return {
|
|
2185
2203
|
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`,
|
|
@@ -2187,7 +2205,7 @@ function nextAction(input) {
|
|
|
2187
2205
|
complete: false
|
|
2188
2206
|
};
|
|
2189
2207
|
}
|
|
2190
|
-
if (latestDelegation.state ===
|
|
2208
|
+
if (latestDelegation.state === import_sdk6.DelegationStates.PENDING_LOCK_FINALIZATION && !latestWorkLog) {
|
|
2191
2209
|
return {
|
|
2192
2210
|
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\`.`,
|
|
2193
2211
|
owner: "none",
|
|
@@ -2202,7 +2220,7 @@ function nextAction(input) {
|
|
|
2202
2220
|
complete: false
|
|
2203
2221
|
};
|
|
2204
2222
|
}
|
|
2205
|
-
if (latestWorkLog.state ===
|
|
2223
|
+
if (latestWorkLog.state === import_sdk6.WorkLogStates.REQUESTED) {
|
|
2206
2224
|
const iAmPayee = latestWorkLog.payeeDid === signerDid;
|
|
2207
2225
|
return {
|
|
2208
2226
|
hint: iAmPayee ? `work_request ${latestWorkLog.requestId} is REQUESTED \u2014 you owe \`heyarp work respond\` (output OR --error)` : `work_request ${latestWorkLog.requestId} is REQUESTED \u2014 counterparty (payee) owes \`heyarp work respond\``,
|
|
@@ -2233,24 +2251,21 @@ function nextAction(input) {
|
|
|
2233
2251
|
complete: false
|
|
2234
2252
|
};
|
|
2235
2253
|
}
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
};
|
|
2246
|
-
}
|
|
2247
|
-
return { hint: "Cycle COMPLETE \u2014 settlement recorded", owner: "none", complete: true };
|
|
2254
|
+
const iAmCaller = latestReceipt.callerDid === signerDid;
|
|
2255
|
+
return {
|
|
2256
|
+
// There is no cosign step — buyer consent is the on-chain
|
|
2257
|
+
// claim_work_payment tx, worker recourse is the
|
|
2258
|
+
// review-window self-claim.
|
|
2259
|
+
hint: iAmCaller ? `Receipt PROPOSED \u2014 you (caller): review the deliverable and approve payment ON-CHAIN with \`heyarp escrow claim ${latestReceipt.delegationId}\` (irreversible release; \`heyarp escrow dispute open ${latestReceipt.delegationId}\` within the review window if the work is unacceptable)` : `Receipt PROPOSED \u2014 counterparty (the buyer) owes the on-chain approval (\`heyarp escrow claim\`); once the review window lapses you may self-claim with \`heyarp escrow claim ${latestReceipt.delegationId}\``,
|
|
2260
|
+
owner: iAmCaller ? "me" : "counterparty",
|
|
2261
|
+
complete: false
|
|
2262
|
+
};
|
|
2248
2263
|
}
|
|
2249
2264
|
function formatPhaseSnapshot(s) {
|
|
2250
2265
|
const parts = [];
|
|
2251
2266
|
parts.push(`delegation=${s.latestDelegation?.state ?? "\u2205"}`);
|
|
2252
2267
|
if (s.latestWorkLog) parts.push(`work=${s.latestWorkLog.state}`);
|
|
2253
|
-
if (s.latestReceipt) parts.push(
|
|
2268
|
+
if (s.latestReceipt) parts.push("receipt=proposed");
|
|
2254
2269
|
return parts.join(", ");
|
|
2255
2270
|
}
|
|
2256
2271
|
function formatStatusReport(s) {
|
|
@@ -2280,7 +2295,7 @@ function formatStatusReport(s) {
|
|
|
2280
2295
|
lines.push("");
|
|
2281
2296
|
lines.push(import_chalk7.default.bold("Latest receipt"));
|
|
2282
2297
|
if (s.latestReceipt) {
|
|
2283
|
-
lines.push(`
|
|
2298
|
+
lines.push(` ${stateColor("proposed")} verdict=${s.latestReceipt.verdictProposed}`);
|
|
2284
2299
|
} else {
|
|
2285
2300
|
lines.push(import_chalk7.default.dim(" (none)"));
|
|
2286
2301
|
}
|
|
@@ -2294,26 +2309,26 @@ function formatStatusReport(s) {
|
|
|
2294
2309
|
}
|
|
2295
2310
|
function cycleHeadline(s) {
|
|
2296
2311
|
if (s.cycleComplete) {
|
|
2297
|
-
return `${import_chalk7.default.bold("Cycle:")} ${import_chalk7.default.green.bold("COMPLETE")} ${import_chalk7.default.dim("\u2014
|
|
2312
|
+
return `${import_chalk7.default.bold("Cycle:")} ${import_chalk7.default.green.bold("COMPLETE")} ${import_chalk7.default.dim("\u2014 payment proven on chain")}`;
|
|
2298
2313
|
}
|
|
2299
2314
|
switch (s.relationshipState) {
|
|
2300
2315
|
case "not_found":
|
|
2301
2316
|
return `${import_chalk7.default.bold("Cycle:")} ${import_chalk7.default.red.bold("NOT FOUND")} ${import_chalk7.default.dim("\u2014 relationship missing or signer is not a member")}`;
|
|
2302
2317
|
case "unknown":
|
|
2303
2318
|
return `${import_chalk7.default.bold("Cycle:")} ${import_chalk7.default.yellow.bold("UNKNOWN")} ${import_chalk7.default.dim("\u2014 state probe inconclusive; hint below is advisory")}`;
|
|
2304
|
-
case
|
|
2319
|
+
case import_sdk6.RelationshipStates.CLOSED:
|
|
2305
2320
|
return `${import_chalk7.default.bold("Cycle:")} ${import_chalk7.default.red.bold("CLOSED")} ${import_chalk7.default.dim("\u2014 relationship terminal, no further protocol action")}`;
|
|
2306
|
-
case
|
|
2321
|
+
case import_sdk6.RelationshipStates.PAUSED:
|
|
2307
2322
|
return `${import_chalk7.default.bold("Cycle:")} ${import_chalk7.default.yellow.bold("PAUSED")} ${import_chalk7.default.dim("\u2014 relationship soft-disabled, resume to continue")}`;
|
|
2308
|
-
case
|
|
2323
|
+
case import_sdk6.RelationshipStates.PENDING:
|
|
2309
2324
|
return `${import_chalk7.default.bold("Cycle:")} ${import_chalk7.default.yellow.bold("PENDING")} ${import_chalk7.default.dim("\u2014 awaiting handshake_response")}`;
|
|
2310
|
-
case
|
|
2325
|
+
case import_sdk6.RelationshipStates.ACTIVE:
|
|
2311
2326
|
default:
|
|
2312
2327
|
return `${import_chalk7.default.bold("Cycle:")} ${import_chalk7.default.cyan.bold("ACTIVE")} ${import_chalk7.default.dim('\u2014 work in progress; see "Next action" below')}`;
|
|
2313
2328
|
}
|
|
2314
2329
|
}
|
|
2315
2330
|
function stateColor(state) {
|
|
2316
|
-
const c = state === "active" || state === "accepted" || state === "locked" || state === "
|
|
2331
|
+
const c = state === "active" || state === "accepted" || state === "locked" || state === "responded" || state === "completed" ? import_chalk7.default.green : state === "pending" || state === "requested" || state === "offered" || state === "pending_lock_finalization" ? import_chalk7.default.yellow : state === "closed" || state === "declined" || state === "canceled" || state === "replaced" || state === "not_found" || // Terminal failure states deserve the same red
|
|
2317
2332
|
// treatment as declined/canceled so an operator
|
|
2318
2333
|
// scanning `heyarp status` immediately sees
|
|
2319
2334
|
// "this is dead, don't follow the hint blindly".
|
|
@@ -2322,41 +2337,41 @@ function stateColor(state) {
|
|
|
2322
2337
|
}
|
|
2323
2338
|
|
|
2324
2339
|
// src/commands/wallet.ts
|
|
2325
|
-
var
|
|
2340
|
+
var import_sdk9 = require("@heyanon-arp/sdk");
|
|
2326
2341
|
var import_utils = require("@noble/hashes/utils");
|
|
2327
2342
|
var import_web32 = require("@solana/web3.js");
|
|
2328
2343
|
init_api();
|
|
2329
2344
|
init_config();
|
|
2330
2345
|
|
|
2331
2346
|
// src/solana/escrow-ix.ts
|
|
2332
|
-
var
|
|
2347
|
+
var import_sdk7 = require("@heyanon-arp/sdk");
|
|
2333
2348
|
var import_web3 = require("@solana/web3.js");
|
|
2334
|
-
var SPL_TOKEN_PROGRAM_ID = new import_web3.PublicKey(
|
|
2335
|
-
var ASSOCIATED_TOKEN_PROGRAM_ID = new import_web3.PublicKey(
|
|
2336
|
-
var NATIVE_SOL_MINT = new import_web3.PublicKey(
|
|
2349
|
+
var SPL_TOKEN_PROGRAM_ID = new import_web3.PublicKey(import_sdk7.SPL_TOKEN_PROGRAM_ID_BASE58);
|
|
2350
|
+
var ASSOCIATED_TOKEN_PROGRAM_ID = new import_web3.PublicKey(import_sdk7.ASSOCIATED_TOKEN_PROGRAM_ID_BASE58);
|
|
2351
|
+
var NATIVE_SOL_MINT = new import_web3.PublicKey(import_sdk7.NATIVE_SOL_MINT_BASE58);
|
|
2337
2352
|
function deriveLockPda(programId, lockId) {
|
|
2338
|
-
return import_web3.PublicKey.findProgramAddressSync([Buffer.from(
|
|
2353
|
+
return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk7.ESCROW_PDA_SEEDS.LOCK), Buffer.from(lockId)], programId)[0];
|
|
2339
2354
|
}
|
|
2340
2355
|
function deriveEscrowPda(programId, lockId) {
|
|
2341
|
-
return import_web3.PublicKey.findProgramAddressSync([Buffer.from(
|
|
2356
|
+
return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk7.ESCROW_PDA_SEEDS.ESCROW), Buffer.from(lockId)], programId)[0];
|
|
2342
2357
|
}
|
|
2343
2358
|
function deriveConfigPda(programId) {
|
|
2344
|
-
return import_web3.PublicKey.findProgramAddressSync([Buffer.from(
|
|
2359
|
+
return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk7.ESCROW_PDA_SEEDS.CONFIG)], programId)[0];
|
|
2345
2360
|
}
|
|
2346
2361
|
function deriveStakeVaultPda(programId) {
|
|
2347
|
-
return import_web3.PublicKey.findProgramAddressSync([Buffer.from(
|
|
2362
|
+
return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk7.ESCROW_PDA_SEEDS.STAKE_VAULT)], programId)[0];
|
|
2348
2363
|
}
|
|
2349
2364
|
function deriveCollateralConfigPda(programId, mint) {
|
|
2350
|
-
return import_web3.PublicKey.findProgramAddressSync([Buffer.from(
|
|
2365
|
+
return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk7.ESCROW_PDA_SEEDS.COLLATERAL), mint.toBuffer()], programId)[0];
|
|
2351
2366
|
}
|
|
2352
2367
|
function deriveDisputeResolutionPda(programId, lockId) {
|
|
2353
|
-
return import_web3.PublicKey.findProgramAddressSync([Buffer.from(
|
|
2368
|
+
return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk7.ESCROW_PDA_SEEDS.DISPUTE_RESOLUTION), Buffer.from(lockId)], programId)[0];
|
|
2354
2369
|
}
|
|
2355
2370
|
function deriveOperatorAuthPda(programId, operator) {
|
|
2356
|
-
return import_web3.PublicKey.findProgramAddressSync([Buffer.from(
|
|
2371
|
+
return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk7.ESCROW_PDA_SEEDS.OPERATOR_AUTH), operator.toBuffer()], programId)[0];
|
|
2357
2372
|
}
|
|
2358
2373
|
function deriveEventAuthorityPda(programId) {
|
|
2359
|
-
return import_web3.PublicKey.findProgramAddressSync([Buffer.from(
|
|
2374
|
+
return import_web3.PublicKey.findProgramAddressSync([Buffer.from(import_sdk7.ESCROW_PDA_SEEDS.EVENT_AUTHORITY)], programId)[0];
|
|
2360
2375
|
}
|
|
2361
2376
|
function deriveAta(owner, mint) {
|
|
2362
2377
|
return import_web3.PublicKey.findProgramAddressSync([owner.toBuffer(), SPL_TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()], ASSOCIATED_TOKEN_PROGRAM_ID)[0];
|
|
@@ -2371,12 +2386,12 @@ function meta(pubkey, opts = {}) {
|
|
|
2371
2386
|
return { pubkey, isSigner: opts.signer === true, isWritable: opts.writable === true };
|
|
2372
2387
|
}
|
|
2373
2388
|
function noArgIx(programId, name, keys) {
|
|
2374
|
-
return new import_web3.TransactionInstruction({ programId, keys, data: Buffer.from((0,
|
|
2389
|
+
return new import_web3.TransactionInstruction({ programId, keys, data: Buffer.from((0, import_sdk7.buildLifecycleIxData)(name)) });
|
|
2375
2390
|
}
|
|
2376
2391
|
function buildCreateLockIx(input) {
|
|
2377
2392
|
const { programId, lockId } = input;
|
|
2378
2393
|
const native = input.mint === null;
|
|
2379
|
-
const data = Buffer.from((0,
|
|
2394
|
+
const data = Buffer.from((0, import_sdk7.buildCreateLockIxData)({ lockId, amount: input.amount, conditionHash: input.conditionHash }, { native }));
|
|
2380
2395
|
const head = [
|
|
2381
2396
|
meta(input.payer, { signer: true, writable: true }),
|
|
2382
2397
|
meta(input.payee),
|
|
@@ -2512,7 +2527,7 @@ function buildOpenDisputeIx(input) {
|
|
|
2512
2527
|
function buildResolveDisputeIx(input) {
|
|
2513
2528
|
const { programId, lockId } = input;
|
|
2514
2529
|
const native = input.mint === null;
|
|
2515
|
-
const data = Buffer.from((0,
|
|
2530
|
+
const data = Buffer.from((0, import_sdk7.buildResolveDisputeIxData)(input.args, { native }));
|
|
2516
2531
|
if (native) {
|
|
2517
2532
|
return new import_web3.TransactionInstruction({
|
|
2518
2533
|
programId,
|
|
@@ -2589,16 +2604,17 @@ function buildCloseDisputeIx(input) {
|
|
|
2589
2604
|
]);
|
|
2590
2605
|
}
|
|
2591
2606
|
async function fetchLockAccount(conn, programId, delegationId) {
|
|
2592
|
-
const lockId = (0,
|
|
2607
|
+
const lockId = (0, import_sdk7.deriveLockId)(delegationId);
|
|
2593
2608
|
const lockPda = deriveLockPda(programId, lockId);
|
|
2594
2609
|
const info = await conn.getAccountInfo(lockPda);
|
|
2595
2610
|
if (!info) return null;
|
|
2596
|
-
return { lockId, lockPda, lock: (0,
|
|
2611
|
+
return { lockId, lockPda, lock: (0, import_sdk7.decodeLockAccount)(Uint8Array.from(info.data)) };
|
|
2597
2612
|
}
|
|
2598
2613
|
|
|
2599
2614
|
// src/commands/token-amount.ts
|
|
2615
|
+
var import_sdk8 = require("@heyanon-arp/sdk");
|
|
2600
2616
|
function toBaseUnits(amountDecimal, decimals) {
|
|
2601
|
-
if (
|
|
2617
|
+
if (!(0, import_sdk8.isDecimalAmountString)(amountDecimal)) {
|
|
2602
2618
|
throw new Error(`amount '${amountDecimal}' is not a non-negative decimal number`);
|
|
2603
2619
|
}
|
|
2604
2620
|
if (!Number.isInteger(decimals) || decimals < 0 || decimals > 255) {
|
|
@@ -2627,8 +2643,8 @@ function normaliseDelegationId(raw) {
|
|
|
2627
2643
|
}
|
|
2628
2644
|
throw new Error(`wallet: --delegation-id must be either 'del_<uuid>' or a bare canonical-lowercase UUID (got '${raw}')`);
|
|
2629
2645
|
}
|
|
2630
|
-
var SPL_TOKEN_PROGRAM_ID2 = new import_web32.PublicKey(
|
|
2631
|
-
var ASSOCIATED_TOKEN_PROGRAM_ID2 = new import_web32.PublicKey(
|
|
2646
|
+
var SPL_TOKEN_PROGRAM_ID2 = new import_web32.PublicKey(import_sdk9.SPL_TOKEN_PROGRAM_ID_BASE58);
|
|
2647
|
+
var ASSOCIATED_TOKEN_PROGRAM_ID2 = new import_web32.PublicKey(import_sdk9.ASSOCIATED_TOKEN_PROGRAM_ID_BASE58);
|
|
2632
2648
|
var FALLBACK_RPC_URL = "https://api.mainnet-beta.solana.com";
|
|
2633
2649
|
function resolveRpcUrl(opts) {
|
|
2634
2650
|
if (opts.rpcUrl !== void 0 && opts.rpcUrl !== "") return opts.rpcUrl;
|
|
@@ -2639,7 +2655,16 @@ function resolveRpcUrl(opts) {
|
|
|
2639
2655
|
if (fromConfig !== void 0 && fromConfig.length > 0) return fromConfig;
|
|
2640
2656
|
return FALLBACK_RPC_URL;
|
|
2641
2657
|
}
|
|
2642
|
-
|
|
2658
|
+
function redactRpcUrl(url) {
|
|
2659
|
+
try {
|
|
2660
|
+
const u = new URL(url);
|
|
2661
|
+
const hasSecret = u.pathname !== "" && u.pathname !== "/" || u.search !== "" || u.username !== "" || u.password !== "";
|
|
2662
|
+
return hasSecret ? `${u.protocol}//${u.host}/<redacted>` : url;
|
|
2663
|
+
} catch {
|
|
2664
|
+
return "<redacted>";
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
var FALLBACK_PROGRAM_ID = import_sdk9.ESCROW_PROGRAM_ID_BASE58;
|
|
2643
2668
|
async function resolveProgramIdWithSource(api, opts) {
|
|
2644
2669
|
if (opts.programId !== void 0 && opts.programId !== "") {
|
|
2645
2670
|
return { programId: opts.programId, source: "flag" };
|
|
@@ -2699,10 +2724,7 @@ function registerDerivePdas(cmd) {
|
|
|
2699
2724
|
).requiredOption("--delegation-id <id>", "Delegation UUID (canonical `del_<uuid>` or bare `<uuid>` \u2014 both accepted)").option("--server <url>", "Override server URL (used only for --program-id auto-discovery)").option(
|
|
2700
2725
|
"--program-id <pubkey>",
|
|
2701
2726
|
"Deployed ARP escrow program id. Precedence: --program-id flag > ARP_ESCROW_PROGRAM_ID env > GET /v1/escrow/config (auto-discover). NO hardcoded fallback \u2014 wrong-program PDAs are silently wrong, so resolution failure is a loud error."
|
|
2702
|
-
).option("--
|
|
2703
|
-
"--recipient-pubkey <base58>",
|
|
2704
|
-
"Accepted for symmetry with `wallet create-lock` but never read \u2014 PDAs depend ONLY on program_id + delegation_id; recipient pubkey has no derivation role."
|
|
2705
|
-
).option("--condition-hash <hex>", "Accepted for symmetry with `wallet create-lock` but never read \u2014 PDAs depend ONLY on program_id + delegation_id.").option("--amount-lamports <int>", "Accepted for symmetry with `wallet create-lock` but never read \u2014 PDAs depend ONLY on program_id + delegation_id.").option("--expiry-secs <int>", "Accepted for symmetry with `wallet create-lock` but never read \u2014 PDAs depend ONLY on program_id + delegation_id.").option("--json", "Emit JSON instead of human text (jq-pipeable: `.lock_pda`, `.escrow_pda`, `.config_pda`, `.lock_id_hex`).", false).action(async (opts) => {
|
|
2727
|
+
).option("--json", "Emit JSON instead of human text (jq-pipeable: `.lock_pda`, `.escrow_pda`, `.config_pda`, `.lock_id_hex`).", false).action(async (opts) => {
|
|
2706
2728
|
try {
|
|
2707
2729
|
const out = await derivePdasHandler(opts);
|
|
2708
2730
|
if (opts.json) {
|
|
@@ -2726,7 +2748,7 @@ async function derivePdasHandler(opts) {
|
|
|
2726
2748
|
const normalisedDelegationId = normaliseDelegationId(opts.delegationId);
|
|
2727
2749
|
const api = new ArpApiClient(opts.server);
|
|
2728
2750
|
const programId = new import_web32.PublicKey(await resolveProgramIdStrict(api, opts, "wallet derive-pdas"));
|
|
2729
|
-
const lockIdBytes = (0,
|
|
2751
|
+
const lockIdBytes = (0, import_sdk9.deriveLockId)(normalisedDelegationId);
|
|
2730
2752
|
const lockIdSeed = Buffer.from(lockIdBytes);
|
|
2731
2753
|
const lockIdHex = Buffer.from(lockIdBytes).toString("hex");
|
|
2732
2754
|
const [lockPda] = import_web32.PublicKey.findProgramAddressSync([Buffer.from("lock"), lockIdSeed], programId);
|
|
@@ -2738,30 +2760,29 @@ async function derivePdasHandler(opts) {
|
|
|
2738
2760
|
lock_id_hex: lockIdHex,
|
|
2739
2761
|
program_id: programId.toBase58(),
|
|
2740
2762
|
lock_pda: lockPda.toBase58(),
|
|
2741
|
-
// Single escrow PDA
|
|
2742
|
-
// pair in the new contract.
|
|
2763
|
+
// Single escrow PDA at `[b"escrow", lock_id]`.
|
|
2743
2764
|
escrow_pda: escrowPda.toBase58(),
|
|
2744
2765
|
config_pda: configPda.toBase58(),
|
|
2745
2766
|
event_authority_pda: eventAuthorityPda.toBase58()
|
|
2746
2767
|
};
|
|
2747
2768
|
}
|
|
2748
2769
|
var TERMINAL_METHOD = {
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2770
|
+
[import_sdk9.LockStates.PAID]: "claim_work_payment",
|
|
2771
|
+
[import_sdk9.LockStates.CANCELED]: "cancel_lock",
|
|
2772
|
+
[import_sdk9.LockStates.REVOKED]: "claim_expired_work",
|
|
2773
|
+
[import_sdk9.LockStates.DISPUTE_RESOLVED]: "resolve_dispute",
|
|
2774
|
+
[import_sdk9.LockStates.DISPUTE_CLOSED]: "close_dispute"
|
|
2754
2775
|
};
|
|
2755
2776
|
function registerVerifyRelease(cmd) {
|
|
2756
2777
|
cmd.command("verify-release").description(
|
|
2757
|
-
"Post-cycle on-chain assertion. Decodes the Lock account and reports the
|
|
2778
|
+
"Post-cycle on-chain assertion. Decodes the Lock account and reports the state verdict: `released: true` IFF the lock is `paid` (the payee was paid via claim_work_payment); exits non-zero otherwise so `set -euo pipefail` scripts catch incomplete cycles. For the full decoded Lock use `heyarp escrow show`."
|
|
2758
2779
|
).requiredOption("--delegation-id <id>", "Delegation UUID (canonical `del_<uuid>` or bare `<uuid>` \u2014 both accepted)").option("--server <url>", "Override ARP server URL (used only for --program-id auto-discovery)").option(
|
|
2759
2780
|
"--rpc-url <url>",
|
|
2760
2781
|
"Solana RPC URL. STRICT: --rpc-url flag > ARP_ESCROW_RPC_URL env > `heyarp config set rpcUrl` > FAIL \u2014 a wrong-cluster read would silently report lock_never_created."
|
|
2761
2782
|
).option(
|
|
2762
2783
|
"--program-id <pubkey>",
|
|
2763
2784
|
"Deployed ARP escrow program id. STRICT: flag > ARP_ESCROW_PROGRAM_ID env > GET /v1/escrow/config > FAIL \u2014 a wrong-program read would silently report lock_never_created."
|
|
2764
|
-
).option("--
|
|
2785
|
+
).option("--from-did <did>", "Accepted for scripting symmetry with the signing commands; verify-release reads on-chain state only and needs no signer, so this is ignored.").option("--json", "Emit JSON instead of human text. jq-pipeable.", false).action(async (opts) => {
|
|
2765
2786
|
try {
|
|
2766
2787
|
const out = await verifyReleaseHandler(opts);
|
|
2767
2788
|
if (opts.json) {
|
|
@@ -2790,16 +2811,13 @@ function registerVerifyRelease(cmd) {
|
|
|
2790
2811
|
});
|
|
2791
2812
|
}
|
|
2792
2813
|
async function verifyReleaseHandler(opts) {
|
|
2793
|
-
if (opts.noSigRetry) {
|
|
2794
|
-
process.stderr.write("wallet verify-release: --no-sig-retry is a no-op on V2 (locks are never closed) \u2014 ignoring\n");
|
|
2795
|
-
}
|
|
2796
2814
|
const normalisedDelegationId = normaliseDelegationId(opts.delegationId);
|
|
2797
2815
|
const api = new ArpApiClient(opts.server);
|
|
2798
2816
|
const programId = new import_web32.PublicKey(await resolveProgramIdStrict(api, opts));
|
|
2799
2817
|
const rpcUrl = resolveRpcUrlStrict(opts);
|
|
2800
2818
|
const conn = new import_web32.Connection(rpcUrl, "confirmed");
|
|
2801
2819
|
const fetched = await fetchLockAccount(conn, programId, normalisedDelegationId);
|
|
2802
|
-
const lockId = (0,
|
|
2820
|
+
const lockId = (0, import_sdk9.deriveLockId)(normalisedDelegationId);
|
|
2803
2821
|
const lockPda = deriveLockPda(programId, lockId);
|
|
2804
2822
|
const escrowPda = deriveEscrowPda(programId, lockId);
|
|
2805
2823
|
if (!fetched) {
|
|
@@ -2810,7 +2828,7 @@ async function verifyReleaseHandler(opts) {
|
|
|
2810
2828
|
lock_account_exists: false,
|
|
2811
2829
|
released: false,
|
|
2812
2830
|
status: "lock_never_created",
|
|
2813
|
-
rpc_url: rpcUrl
|
|
2831
|
+
rpc_url: redactRpcUrl(rpcUrl)
|
|
2814
2832
|
};
|
|
2815
2833
|
}
|
|
2816
2834
|
const lock = fetched.lock;
|
|
@@ -2820,7 +2838,7 @@ async function verifyReleaseHandler(opts) {
|
|
|
2820
2838
|
lock_pda: lockPda.toBase58(),
|
|
2821
2839
|
escrow_pda: escrowPda.toBase58(),
|
|
2822
2840
|
lock_account_exists: true,
|
|
2823
|
-
released: status ===
|
|
2841
|
+
released: status === import_sdk9.LockStates.PAID,
|
|
2824
2842
|
status,
|
|
2825
2843
|
...TERMINAL_METHOD[status] !== void 0 ? { release_method: TERMINAL_METHOD[status] } : {},
|
|
2826
2844
|
lock_state: lock.stateByte,
|
|
@@ -2830,23 +2848,23 @@ async function verifyReleaseHandler(opts) {
|
|
|
2830
2848
|
}
|
|
2831
2849
|
function renderStatusLine(status) {
|
|
2832
2850
|
switch (status) {
|
|
2833
|
-
case
|
|
2851
|
+
case import_sdk9.LockStates.PAID:
|
|
2834
2852
|
return "\u2713 paid \u2014 claim_work_payment landed (buyer approval or post-review self-claim); the payee was paid net of any protocol fee";
|
|
2835
|
-
case
|
|
2853
|
+
case import_sdk9.LockStates.CREATED:
|
|
2836
2854
|
return "\u2717 created \u2014 funded, awaiting the worker accept_lock (stake) \u2014 or a buyer cancel";
|
|
2837
|
-
case
|
|
2855
|
+
case import_sdk9.LockStates.IN_PROGRESS:
|
|
2838
2856
|
return "\u2717 in_progress \u2014 worker accepted + staked; work window running";
|
|
2839
|
-
case
|
|
2857
|
+
case import_sdk9.LockStates.SUBMITTED:
|
|
2840
2858
|
return "\u2717 submitted \u2014 work delivered on-chain; review window running (buyer claims to approve, disputes to refuse; worker self-claims after expiry)";
|
|
2841
|
-
case
|
|
2859
|
+
case import_sdk9.LockStates.DISPUTING:
|
|
2842
2860
|
return "\u2717 disputing \u2014 buyer disputed; operator has the dispute window to rule, after that either party can close";
|
|
2843
|
-
case
|
|
2861
|
+
case import_sdk9.LockStates.CANCELED:
|
|
2844
2862
|
return "\u2717 canceled \u2014 buyer canceled pre-accept; escrow returned";
|
|
2845
|
-
case
|
|
2863
|
+
case import_sdk9.LockStates.REVOKED:
|
|
2846
2864
|
return "\u2717 revoked \u2014 work window lapsed unsubmitted; buyer reclaimed the escrow + the worker stake";
|
|
2847
|
-
case
|
|
2865
|
+
case import_sdk9.LockStates.DISPUTE_RESOLVED:
|
|
2848
2866
|
return "\u2717 dispute_resolved \u2014 operator ruled; winner took the escrow per the on-chain split";
|
|
2849
|
-
case
|
|
2867
|
+
case import_sdk9.LockStates.DISPUTE_CLOSED:
|
|
2850
2868
|
return "\u2717 dispute_closed \u2014 dispute window lapsed unresolved; escrow returned to the buyer, stakes returned";
|
|
2851
2869
|
case "lock_never_created":
|
|
2852
2870
|
return "\u2717 lock_never_created \u2014 no Lock PDA on this cluster/program; create_lock never fired (or wrong --rpc-url/--program-id)";
|
|
@@ -2854,14 +2872,11 @@ function renderStatusLine(status) {
|
|
|
2854
2872
|
}
|
|
2855
2873
|
function registerCreateLock(cmd) {
|
|
2856
2874
|
cmd.command("create-lock").description(
|
|
2857
|
-
"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.
|
|
2875
|
+
"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. 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."
|
|
2858
2876
|
).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(
|
|
2859
2877
|
"--amount <decimal>",
|
|
2860
2878
|
"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."
|
|
2861
2879
|
).requiredOption("--condition-hash <hex>", "32-byte hex condition_hash from `heyarp escrow derive-condition-hash` (binds the delegation terms)").option(
|
|
2862
|
-
"--expiry-secs <int>",
|
|
2863
|
-
"DEPRECATED no-op \u2014 V2 derives deadlines from the on-chain Config windows; the flag is tolerated so old scripts keep running and dies with the V2 builder rewrite."
|
|
2864
|
-
).option(
|
|
2865
2880
|
"--rpc-url <url>",
|
|
2866
2881
|
`Solana RPC URL. Default precedence: --rpc-url flag > ARP_ESCROW_RPC_URL env > \`heyarp config set rpcUrl\` > built-in fallback (${FALLBACK_RPC_URL}).`
|
|
2867
2882
|
).option(
|
|
@@ -3029,16 +3044,13 @@ async function createLockHandler(opts) {
|
|
|
3029
3044
|
const normalisedDelegationId = normaliseDelegationId(opts.delegationId);
|
|
3030
3045
|
const payee = parsePubkey(opts.recipientPubkey, "--recipient-pubkey");
|
|
3031
3046
|
const conditionHash = parseHex32(opts.conditionHash, "--condition-hash");
|
|
3032
|
-
if (opts.expirySecs !== void 0) {
|
|
3033
|
-
process.stderr.write("wallet create-lock: --expiry-secs is a no-op on V2 (deadlines come from the on-chain Config windows) \u2014 ignoring\n");
|
|
3034
|
-
}
|
|
3035
3047
|
const api = new ArpApiClient(opts.server);
|
|
3036
3048
|
const offlineMode = typeof opts.programId === "string" && opts.programId !== "" || typeof process.env.ARP_ESCROW_PROGRAM_ID === "string" && process.env.ARP_ESCROW_PROGRAM_ID !== "";
|
|
3037
3049
|
const clusterTag = opts.clusterTag !== void 0 ? Number.parseInt(opts.clusterTag, 10) : 0;
|
|
3038
3050
|
if (clusterTag !== 0 && clusterTag !== 1) {
|
|
3039
3051
|
throw new Error(`--cluster-tag must be 0 (devnet) or 1 (mainnet) (got ${clusterTag})`);
|
|
3040
3052
|
}
|
|
3041
|
-
const clusterCaip2 = clusterTag === 1 ?
|
|
3053
|
+
const clusterCaip2 = clusterTag === 1 ? import_sdk9.SOLANA_CLUSTER_IDS["solana-mainnet"] : import_sdk9.SOLANA_CLUSTER_IDS["solana-devnet"];
|
|
3042
3054
|
const expectedLockAsset = typeof opts.mintPubkey === "string" && opts.mintPubkey !== "" ? { kind: "spl", mint: parsePubkey(opts.mintPubkey, "--mint-pubkey").toBase58(), cluster: clusterCaip2 } : { kind: "native" };
|
|
3043
3055
|
if (!offlineMode) {
|
|
3044
3056
|
await preflightLockCurrency(api, agent, normalisedDelegationId, expectedLockAsset);
|
|
@@ -3048,7 +3060,7 @@ async function createLockHandler(opts) {
|
|
|
3048
3060
|
const conn = new import_web32.Connection(rpcUrl, "confirmed");
|
|
3049
3061
|
const asset = await resolveCreateLockAsset(conn, opts, payerKp.publicKey, clusterCaip2);
|
|
3050
3062
|
const amount = asset.amount;
|
|
3051
|
-
const lockIdBytes = (0,
|
|
3063
|
+
const lockIdBytes = (0, import_sdk9.deriveLockId)(normalisedDelegationId);
|
|
3052
3064
|
const ix = buildCreateLockIx({
|
|
3053
3065
|
programId,
|
|
3054
3066
|
lockId: lockIdBytes,
|
|
@@ -3132,63 +3144,12 @@ function registerDelegationCommands(root) {
|
|
|
3132
3144
|
registerDecline(cmd);
|
|
3133
3145
|
registerCancel(cmd);
|
|
3134
3146
|
}
|
|
3135
|
-
var POST_COMMIT_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
3136
|
-
"DELEGATION_ALREADY_EXISTS",
|
|
3137
|
-
"DELEGATION_INVALID_STATE",
|
|
3138
|
-
"DELEGATION_NOT_FOUND",
|
|
3139
|
-
"DELEGATION_RELATIONSHIP_MISMATCH",
|
|
3140
|
-
"DELEGATION_ACCEPTER_IS_OFFERER",
|
|
3141
|
-
"DELEGATION_DECLINER_IS_OFFERER",
|
|
3142
|
-
"DELEGATION_CANCELER_NOT_OFFERER",
|
|
3143
|
-
// PricingPolicy: the offer's terms fell outside the RECIPIENT's
|
|
3144
|
-
// published accept-prefs. Fires in `handleOffer` AFTER the event
|
|
3145
|
-
// row commit (pre-materialization on the server), so the sequence
|
|
3146
|
-
// was consumed — advance it or the corrected re-offer trips
|
|
3147
|
-
// ENV_SEQUENCE_BACKWARDS.
|
|
3148
|
-
// Asset whitelist gate: the offer currency is not a whitelisted
|
|
3149
|
-
// payment asset on this server (or its decimals diverge from the
|
|
3150
|
-
// canonical entry). Same lifecycle as the pricing gate — sequence
|
|
3151
|
-
// consumed. Fix the --currency and re-offer.
|
|
3152
|
-
"DELEGATION_ASSET_NOT_ALLOWED",
|
|
3153
|
-
"DELEGATION_PRICING_MISMATCH",
|
|
3154
|
-
// Capacity gate: the recipient is at its published
|
|
3155
|
-
// maxActiveDelegations cap (or closed with 0). Same lifecycle as
|
|
3156
|
-
// the pricing gate — sequence consumed. TRANSIENT: retry later.
|
|
3157
|
-
"DELEGATION_CAPACITY_EXCEEDED",
|
|
3158
|
-
// `DELEGATION_PENDING_LOCK` fires from the body handler's
|
|
3159
|
-
// `requireDelegationInState` AFTER the event row is persisted
|
|
3160
|
-
// (same code path as DELEGATION_INVALID_STATE), so an accept
|
|
3161
|
-
// against a PENDING_LOCK delegation consumes sender_sequence
|
|
3162
|
-
// even though it rejects.
|
|
3163
|
-
"DELEGATION_PENDING_LOCK",
|
|
3164
|
-
// M6 lock-at-accept: the `fund` body handler (`handleFund`) emits
|
|
3165
|
-
// these AFTER the event row is committed (same lifecycle as
|
|
3166
|
-
// DELEGATION_INVALID_STATE). `DELEGATION_FUNDER_NOT_OFFERER` (403)
|
|
3167
|
-
// when a non-offerer attempts the fund; `DELEGATION_ALREADY_FUNDED`
|
|
3168
|
-
// (409) on a re-fund of a delegation that already moved into the
|
|
3169
|
-
// lock lifecycle (pending_lock_finalization / locked, or an
|
|
3170
|
-
// in-flight create_lock op). Both consume sender_sequence, so the
|
|
3171
|
-
// CLI must advance `lastSenderSequence` or a retry trips
|
|
3172
|
-
// `ENV_SEQUENCE_BACKWARDS`.
|
|
3173
|
-
"DELEGATION_FUNDER_NOT_OFFERER",
|
|
3174
|
-
"DELEGATION_ALREADY_FUNDED",
|
|
3175
|
-
// V1.5 lock-validator mint.owner pre-flight — kept here for
|
|
3176
|
-
// documentation, but the `isPostCommitErrorCode` helper below
|
|
3177
|
-
// also matches any `ESC_LOCK_*` prefix. The full lock-validator
|
|
3178
|
-
// cross-check set (ID_MISMATCH, CONDITION_HASH_MISMATCH,
|
|
3179
|
-
// AMOUNT_DELEGATION_MISMATCH, EXPIRY_TOO_*, PDA_*, etc.) ALSO
|
|
3180
|
-
// fires after the delegation event is committed; missing
|
|
3181
|
-
// entries here would leave `lastSenderSequence` stale and stall
|
|
3182
|
-
// retries on ENV_SEQUENCE_BACKWARDS.
|
|
3183
|
-
"ESC_LOCK_MINT_RPC_FAILED",
|
|
3184
|
-
"ESC_LOCK_MINT_NOT_FOUND",
|
|
3185
|
-
"ESC_LOCK_MINT_OWNER_MISMATCH"
|
|
3186
|
-
]);
|
|
3187
|
-
function isPostCommitErrorCode(code) {
|
|
3188
|
-
return POST_COMMIT_ERROR_CODES.has(code) || code.startsWith("ESC_LOCK_") || code.startsWith("SDK_");
|
|
3189
|
-
}
|
|
3190
3147
|
function registerOffer(parent) {
|
|
3191
|
-
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("--amount <s>", 'Optional decimal-as-string amount (e.g. "10.00"). REQUIRES --currency if set.').option("--currency <s>", `Asset identifier: shorthand (${
|
|
3148
|
+
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("--amount <s>", 'Optional decimal-as-string amount (e.g. "10.00"). REQUIRES --currency if set.').option("--currency <s>", `Asset identifier: shorthand (${import_sdk10.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 ${import_sdk10.MAX_ENVELOPE_TTL_SECONDS} = 24h)`, "3600").option("--verbose", "Print the full envelope before sending and the full server response. Mutually exclusive with --json.", false).option(
|
|
3149
|
+
"--json",
|
|
3150
|
+
'Machine-readable mode \u2014 emit a single JSON object on stdout ({ok, action:"offer", delegationId, eventId, relationshipId, relationshipEventIndex, serverTimestamp, serverEventHash}). Prelude + the "reference this delegation" hints move to stderr; on failure stderr carries `{code, message}`. Mutually exclusive with --verbose.',
|
|
3151
|
+
false
|
|
3152
|
+
).option(
|
|
3192
3153
|
"--wait-until <phase>",
|
|
3193
3154
|
'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.'
|
|
3194
3155
|
).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(
|
|
@@ -3294,6 +3255,11 @@ function assembleEscrowLockAttachment(opts) {
|
|
|
3294
3255
|
};
|
|
3295
3256
|
}
|
|
3296
3257
|
async function runOffer(recipientDid, opts) {
|
|
3258
|
+
if (opts.verbose && opts.json) {
|
|
3259
|
+
throw new Error(
|
|
3260
|
+
"delegation offer: --verbose and --json are mutually exclusive. --json emits the structured server response; --verbose adds dumps that would break `--json | jq`."
|
|
3261
|
+
);
|
|
3262
|
+
}
|
|
3297
3263
|
requireDid("delegation offer", recipientDid, "<recipient-did>");
|
|
3298
3264
|
const ttlSeconds = parseTtl("delegation offer", opts.ttl);
|
|
3299
3265
|
if (!opts.title || opts.title.length === 0) {
|
|
@@ -3301,7 +3267,7 @@ async function runOffer(recipientDid, opts) {
|
|
|
3301
3267
|
}
|
|
3302
3268
|
const terms = parseOfferTerms("delegation offer", opts);
|
|
3303
3269
|
const offeredAssetId = terms.currency?.asset_id;
|
|
3304
|
-
if (offeredAssetId && !(0,
|
|
3270
|
+
if (offeredAssetId && !(0, import_sdk10.isWhitelistedAssetId)(offeredAssetId)) {
|
|
3305
3271
|
console.error(
|
|
3306
3272
|
import_chalk8.default.yellow(
|
|
3307
3273
|
`warning: currency '${offeredAssetId}' is not in the static payment whitelist \u2014 the server will reject the offer (DELEGATION_ASSET_NOT_ALLOWED) unless its deploy registers this asset. See 'heyarp assets'.`
|
|
@@ -3322,21 +3288,21 @@ async function runOffer(recipientDid, opts) {
|
|
|
3322
3288
|
const api = new ArpApiClient(opts.server);
|
|
3323
3289
|
const sender = resolveSenderAgent("delegation offer", opts.server, opts.fromDid);
|
|
3324
3290
|
const content = {
|
|
3325
|
-
action:
|
|
3291
|
+
action: import_sdk10.DelegationActions.OFFER,
|
|
3326
3292
|
delegation_id: delegationId,
|
|
3327
3293
|
title: opts.title,
|
|
3328
3294
|
...terms
|
|
3329
3295
|
};
|
|
3330
3296
|
const body = { type: "delegation", content };
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3297
|
+
progress(opts.json, import_chalk8.default.dim(`Server: ${api.serverUrl}`));
|
|
3298
|
+
progress(opts.json, import_chalk8.default.dim(`Sender: ${sender.did}`));
|
|
3299
|
+
progress(opts.json, import_chalk8.default.dim(`Recipient: ${recipientDid}`));
|
|
3300
|
+
progress(opts.json, import_chalk8.default.dim(`Delegation: ${delegationId}`));
|
|
3335
3301
|
let result;
|
|
3336
3302
|
try {
|
|
3337
3303
|
result = await sendDelegationEnvelope({ api, sender, recipientDid, body, ttlSeconds, verbose: opts.verbose, server: opts.server });
|
|
3338
3304
|
} catch (err) {
|
|
3339
|
-
if (err instanceof ApiError && err.payload.code ===
|
|
3305
|
+
if (err instanceof ApiError && err.payload.code === import_sdk10.DelegationOfferRejectionCodes.ASSET_NOT_ALLOWED) {
|
|
3340
3306
|
const d = err.payload.details;
|
|
3341
3307
|
console.error(import_chalk8.default.yellow(`
|
|
3342
3308
|
The server's payment-asset whitelist rejected this currency.`));
|
|
@@ -3349,7 +3315,7 @@ See the whitelist (shorthand keys + canonical decimals):
|
|
|
3349
3315
|
heyarp assets
|
|
3350
3316
|
then re-offer with a whitelisted --currency.`));
|
|
3351
3317
|
}
|
|
3352
|
-
if (err instanceof ApiError && err.payload.code ===
|
|
3318
|
+
if (err instanceof ApiError && err.payload.code === import_sdk10.DelegationOfferRejectionCodes.PRICING_MISMATCH) {
|
|
3353
3319
|
const d = err.payload.details;
|
|
3354
3320
|
console.error(import_chalk8.default.yellow(`
|
|
3355
3321
|
The recipient's published accept-prefs rejected this offer${d?.reason ? ` (mismatch: ${d.reason})` : ""}.`));
|
|
@@ -3366,7 +3332,7 @@ then re-run with matching --currency / --amount.`
|
|
|
3366
3332
|
)
|
|
3367
3333
|
);
|
|
3368
3334
|
}
|
|
3369
|
-
if (err instanceof ApiError && err.payload.code ===
|
|
3335
|
+
if (err instanceof ApiError && err.payload.code === import_sdk10.DelegationOfferRejectionCodes.CAPACITY_EXCEEDED) {
|
|
3370
3336
|
const d = err.payload.details;
|
|
3371
3337
|
const cap = d?.maxActive === 0 ? "closed for new offers (busy)" : `at capacity (${d?.currentActive}/${d?.maxActive} active delegations)`;
|
|
3372
3338
|
console.error(import_chalk8.default.yellow(`
|
|
@@ -3375,17 +3341,31 @@ The recipient is ${cap}.`));
|
|
|
3375
3341
|
}
|
|
3376
3342
|
throw err;
|
|
3377
3343
|
}
|
|
3378
|
-
|
|
3379
|
-
|
|
3344
|
+
if (opts.json) {
|
|
3345
|
+
jsonOut({
|
|
3346
|
+
ok: true,
|
|
3347
|
+
action: "offer",
|
|
3348
|
+
delegationId,
|
|
3349
|
+
eventId: result.eventId,
|
|
3350
|
+
relationshipId: result.relationshipId,
|
|
3351
|
+
relationshipEventIndex: result.relationshipEventIndex,
|
|
3352
|
+
serverTimestamp: result.serverTimestamp,
|
|
3353
|
+
serverEventHash: result.serverEventHash,
|
|
3354
|
+
prevServerEventHash: result.prevServerEventHash ?? null
|
|
3355
|
+
});
|
|
3356
|
+
} else {
|
|
3357
|
+
printIngestResult(result);
|
|
3358
|
+
console.log(import_chalk8.default.dim(`
|
|
3380
3359
|
Reference this delegation on subsequent calls with:`));
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3360
|
+
console.log(import_chalk8.default.dim(` heyarp delegation accept ${result.relationshipId} ${delegationId}`));
|
|
3361
|
+
console.log(import_chalk8.default.dim(` heyarp delegation decline ${result.relationshipId} ${delegationId}`));
|
|
3362
|
+
console.log(import_chalk8.default.dim(` heyarp delegation cancel ${result.relationshipId} ${delegationId}`));
|
|
3363
|
+
console.log(import_chalk8.default.dim(`
|
|
3385
3364
|
After the worker accepts, fund the escrow lock:`));
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3365
|
+
console.log(import_chalk8.default.dim(` heyarp wallet create-lock --delegation-id ${delegationId} --condition-hash <hex> ... > lock.json`));
|
|
3366
|
+
console.log(import_chalk8.default.dim(` heyarp delegation fund ${delegationId} --escrow-lock-from-file lock.json`));
|
|
3367
|
+
}
|
|
3368
|
+
if (opts.waitUntil && !opts.json) {
|
|
3389
3369
|
const untilPhase = parseUntilPhase(opts.waitUntil);
|
|
3390
3370
|
if (untilPhase === void 0) {
|
|
3391
3371
|
throw new Error(`delegation offer: --wait-until requires a phase value (got ${JSON.stringify(opts.waitUntil)})`);
|
|
@@ -3405,7 +3385,7 @@ After the worker accepts, fund the escrow lock:`));
|
|
|
3405
3385
|
}
|
|
3406
3386
|
}
|
|
3407
3387
|
function registerFund(parent) {
|
|
3408
|
-
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>",
|
|
3388
|
+
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 ${import_sdk10.MAX_ENVELOPE_TTL_SECONDS} = 24h)`, "3600").option("--verbose", "Print the full envelope before sending and the full server response. Mutually exclusive with --json.", false).option(
|
|
3409
3389
|
"--json",
|
|
3410
3390
|
'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.',
|
|
3411
3391
|
false
|
|
@@ -3469,7 +3449,7 @@ async function runFund(delegationId, opts) {
|
|
|
3469
3449
|
);
|
|
3470
3450
|
}
|
|
3471
3451
|
}
|
|
3472
|
-
const content = { action:
|
|
3452
|
+
const content = { action: import_sdk10.DelegationActions.FUND, delegation_id: delegationId };
|
|
3473
3453
|
const body = { type: "delegation", content };
|
|
3474
3454
|
const attachments = { escrow_lock: escrowResult.attachment };
|
|
3475
3455
|
progress(opts.json, import_chalk8.default.dim(`Server: ${api.serverUrl}`));
|
|
@@ -3570,8 +3550,12 @@ function registerDecline(parent) {
|
|
|
3570
3550
|
"--reason <code>",
|
|
3571
3551
|
// surface the closed enum at help time so operators
|
|
3572
3552
|
// don't have to read source to find acceptable values.
|
|
3573
|
-
`Required: decline reason code (one of: ${
|
|
3574
|
-
).option("--reason-detail <s>", 'Optional free-text elaboration alongside --reason (e.g. "rate floor 0.20 USDC"). Max 512 chars.').option("--verbose", "Print the full envelope before sending and the full server response", false).option(
|
|
3553
|
+
`Required: decline reason code (one of: ${import_sdk10.DECLINE_REASONS.join(", ")}). Carried in body.content.reason so the counterparty's reactor can branch on it.`
|
|
3554
|
+
).option("--reason-detail <s>", 'Optional free-text elaboration alongside --reason (e.g. "rate floor 0.20 USDC"). Max 512 chars.').option("--verbose", "Print the full envelope before sending and the full server response. Mutually exclusive with --json.", false).option(
|
|
3555
|
+
"--json",
|
|
3556
|
+
'Machine-readable mode \u2014 emit a single JSON object on stdout ({ok, action:"decline", delegationId, eventId, relationshipId, relationshipEventIndex, serverTimestamp, serverEventHash}). Prelude + pending-lock poll chatter move off stdout; on failure stderr carries `{code, message}`. Mutually exclusive with --verbose.',
|
|
3557
|
+
false
|
|
3558
|
+
).option(
|
|
3575
3559
|
"--no-wait-for-lock",
|
|
3576
3560
|
"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)."
|
|
3577
3561
|
).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) => {
|
|
@@ -3579,7 +3563,11 @@ function registerDecline(parent) {
|
|
|
3579
3563
|
});
|
|
3580
3564
|
}
|
|
3581
3565
|
function registerCancel(parent) {
|
|
3582
|
-
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(
|
|
3566
|
+
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. Mutually exclusive with --json.", false).option(
|
|
3567
|
+
"--json",
|
|
3568
|
+
'Machine-readable mode \u2014 emit a single JSON object on stdout ({ok, action:"cancel", delegationId, eventId, relationshipId, relationshipEventIndex, serverTimestamp, serverEventHash}). Prelude + pending-lock poll chatter move off stdout; on failure stderr carries `{code, message}`. Mutually exclusive with --verbose.',
|
|
3569
|
+
false
|
|
3570
|
+
).option(
|
|
3583
3571
|
"--no-wait-for-lock",
|
|
3584
3572
|
"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)."
|
|
3585
3573
|
).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) => {
|
|
@@ -3599,7 +3587,7 @@ async function runFollowupAction(relationshipId, delegationId, action, opts) {
|
|
|
3599
3587
|
const lockWaitTimeoutSec = parseLockWaitTimeout(cmdName, opts.lockWaitTimeout);
|
|
3600
3588
|
const lockWaitIntervalSec = parseLockWaitInterval(cmdName, opts.lockWaitInterval);
|
|
3601
3589
|
let declinePayload = null;
|
|
3602
|
-
if (action ===
|
|
3590
|
+
if (action === import_sdk10.DelegationActions.DECLINE) {
|
|
3603
3591
|
const reason = parseDeclineReason(cmdName, opts.reason);
|
|
3604
3592
|
const validatedDetail = parseReasonDetail(cmdName, opts.reasonDetail);
|
|
3605
3593
|
declinePayload = validatedDetail ? { reason, reasonDetail: validatedDetail } : { reason };
|
|
@@ -3608,7 +3596,7 @@ async function runFollowupAction(relationshipId, delegationId, action, opts) {
|
|
|
3608
3596
|
const sender = resolveSenderAgent(cmdName, opts.server, opts.fromDid);
|
|
3609
3597
|
const signer = makeSigner(sender);
|
|
3610
3598
|
const resolved = await resolveDelegationRefs(cmdName, api, signer, { relationshipId, delegationId, action, selfDid: sender.did });
|
|
3611
|
-
if (resolved.state ===
|
|
3599
|
+
if (resolved.state === import_sdk10.DelegationStates.PENDING_LOCK_FINALIZATION && opts.waitForLock !== false) {
|
|
3612
3600
|
await awaitDelegationLockFinalized(cmdName, api, signer, {
|
|
3613
3601
|
relationshipId,
|
|
3614
3602
|
delegationId,
|
|
@@ -3633,7 +3621,7 @@ async function runFollowupAction(relationshipId, delegationId, action, opts) {
|
|
|
3633
3621
|
progress(opts.json, import_chalk8.default.dim(`Server: ${api.serverUrl}`));
|
|
3634
3622
|
progress(opts.json, import_chalk8.default.dim(`Sender: ${sender.did}`));
|
|
3635
3623
|
progress(opts.json, import_chalk8.default.dim(`Relationship: ${relationshipId}`));
|
|
3636
|
-
progress(opts.json, import_chalk8.default.dim(`Delegation: ${delegationId} (action=${action}${action ===
|
|
3624
|
+
progress(opts.json, import_chalk8.default.dim(`Delegation: ${delegationId} (action=${action}${action === import_sdk10.DelegationActions.DECLINE ? `, reason=${content.reason}` : ""})`));
|
|
3637
3625
|
const result = await sendDelegationEnvelope({
|
|
3638
3626
|
api,
|
|
3639
3627
|
sender,
|
|
@@ -3662,9 +3650,9 @@ async function runFollowupAction(relationshipId, delegationId, action, opts) {
|
|
|
3662
3650
|
async function sendDelegationEnvelope(args) {
|
|
3663
3651
|
const nextSequence = (args.sender.lastSenderSequence ?? 0) + 1;
|
|
3664
3652
|
const protectedBlock = {
|
|
3665
|
-
protocol_version:
|
|
3666
|
-
purpose:
|
|
3667
|
-
message_id: (0,
|
|
3653
|
+
protocol_version: import_sdk10.CURRENT_PROTOCOL_VERSION,
|
|
3654
|
+
purpose: import_sdk10.Purpose.ENVELOPE,
|
|
3655
|
+
message_id: (0, import_sdk10.uuidV4)(),
|
|
3668
3656
|
sender_did: args.sender.did,
|
|
3669
3657
|
recipient_did: args.recipientDid,
|
|
3670
3658
|
// `relationship_id: null` matches the handshake
|
|
@@ -3673,13 +3661,13 @@ async function sendDelegationEnvelope(args) {
|
|
|
3673
3661
|
// existing relationship row.
|
|
3674
3662
|
relationship_id: null,
|
|
3675
3663
|
sender_sequence: nextSequence,
|
|
3676
|
-
sender_nonce: (0,
|
|
3677
|
-
timestamp: (0,
|
|
3678
|
-
expires_at: (0,
|
|
3664
|
+
sender_nonce: (0, import_sdk10.senderNonce)(),
|
|
3665
|
+
timestamp: (0, import_sdk10.rfc3339)(),
|
|
3666
|
+
expires_at: (0, import_sdk10.expiresAt)(args.ttlSeconds),
|
|
3679
3667
|
delivery_id: null
|
|
3680
3668
|
};
|
|
3681
3669
|
const signer = makeSigner(args.sender);
|
|
3682
|
-
const envelope = (0,
|
|
3670
|
+
const envelope = (0, import_sdk10.signEnvelope)({
|
|
3683
3671
|
protected: protectedBlock,
|
|
3684
3672
|
body: args.body,
|
|
3685
3673
|
identitySecretKey: signer.identitySecretKey,
|
|
@@ -3694,7 +3682,7 @@ async function sendDelegationEnvelope(args) {
|
|
|
3694
3682
|
updateAgentLocal(args.server, args.sender.did, { lastSenderSequence: nextSequence });
|
|
3695
3683
|
return result;
|
|
3696
3684
|
} catch (err) {
|
|
3697
|
-
if (err instanceof ApiError && isPostCommitErrorCode(err.payload.code)) {
|
|
3685
|
+
if (err instanceof ApiError && (0, import_sdk10.isPostCommitErrorCode)(err.payload.code)) {
|
|
3698
3686
|
updateAgentLocal(args.server, args.sender.did, { lastSenderSequence: nextSequence });
|
|
3699
3687
|
}
|
|
3700
3688
|
throw err;
|
|
@@ -3716,7 +3704,7 @@ async function resolveDelegationRefs(cmdName, api, signer, args) {
|
|
|
3716
3704
|
);
|
|
3717
3705
|
}
|
|
3718
3706
|
let recipientDid;
|
|
3719
|
-
if (args.action ===
|
|
3707
|
+
if (args.action === import_sdk10.DelegationActions.CANCEL) {
|
|
3720
3708
|
const firstEvents = await api.listEvents(args.relationshipId, signer, { since: 0, limit: 1 });
|
|
3721
3709
|
const handshake = firstEvents[0];
|
|
3722
3710
|
if (!handshake) {
|
|
@@ -3756,7 +3744,7 @@ async function awaitDelegationLockFinalized(cmdName, api, signer, args) {
|
|
|
3756
3744
|
after = page[page.length - 1].id;
|
|
3757
3745
|
}
|
|
3758
3746
|
};
|
|
3759
|
-
const outcome = await (0,
|
|
3747
|
+
const outcome = await (0, import_sdk10.pollUntil)({
|
|
3760
3748
|
fetch: fetchRow,
|
|
3761
3749
|
// Match on either "row not found at all" OR "state moved
|
|
3762
3750
|
// past pending_lock_finalization". The post-poll branch
|
|
@@ -3766,7 +3754,7 @@ async function awaitDelegationLockFinalized(cmdName, api, signer, args) {
|
|
|
3766
3754
|
// rows return cleanly. Polling on null would loop pointlessly
|
|
3767
3755
|
// until the deadline fires and surface a misleading "timed
|
|
3768
3756
|
// out" message for what's actually a wrong-id problem.
|
|
3769
|
-
predicate: (row2) => row2 === null || row2.state !==
|
|
3757
|
+
predicate: (row2) => row2 === null || row2.state !== import_sdk10.DelegationStates.PENDING_LOCK_FINALIZATION,
|
|
3770
3758
|
intervalMs: args.intervalSec * 1e3,
|
|
3771
3759
|
timeoutMs: args.timeoutSec * 1e3,
|
|
3772
3760
|
// Swallow ONLY transient errors. A 4xx during the poll is a
|
|
@@ -3797,7 +3785,7 @@ async function awaitDelegationLockFinalized(cmdName, api, signer, args) {
|
|
|
3797
3785
|
`${cmdName}: delegation ${args.delegationId} disappeared from relationship ${args.relationshipId} during --wait-for-lock pre-flight. Re-check the delegation id with \`heyarp delegations ${args.relationshipId}\`.`
|
|
3798
3786
|
);
|
|
3799
3787
|
}
|
|
3800
|
-
if (row.state !==
|
|
3788
|
+
if (row.state !== import_sdk10.DelegationStates.OFFERED) {
|
|
3801
3789
|
throw new Error(
|
|
3802
3790
|
`${cmdName}: delegation ${args.delegationId} transitioned to '${row.state}' while waiting for on-chain lock confirmation; cannot ${args.action}. Re-read the delegation timeline with \`heyarp delegations ${args.relationshipId}\` to see the latest state.`
|
|
3803
3791
|
);
|
|
@@ -3857,7 +3845,7 @@ function parseOfferTerms(cmdName, opts) {
|
|
|
3857
3845
|
if (opts.amount) {
|
|
3858
3846
|
out.amount = opts.amount;
|
|
3859
3847
|
if (!opts.currency) {
|
|
3860
|
-
throw new Error(`${cmdName}: --amount requires --currency. Shorthand: ${
|
|
3848
|
+
throw new Error(`${cmdName}: --amount requires --currency. Shorthand: ${import_sdk10.WELL_KNOWN_ASSET_KEYS.join(", ")}, or raw CAIP-19 + --currency-decimals.`);
|
|
3861
3849
|
}
|
|
3862
3850
|
out.currency = buildAssetIdentifier(cmdName, DELEGATION_CURRENCY_FLAGS, opts.currency, opts.currencyDecimals, opts.currencySymbol);
|
|
3863
3851
|
} else if (opts.currency) {
|
|
@@ -3880,7 +3868,7 @@ function resolveOfferDelegationId(rawCliId, escrow) {
|
|
|
3880
3868
|
cliId = rawCliId.toLowerCase();
|
|
3881
3869
|
}
|
|
3882
3870
|
if (escrow === void 0) {
|
|
3883
|
-
return cliId ?? (0,
|
|
3871
|
+
return cliId ?? (0, import_sdk10.uuidV4)();
|
|
3884
3872
|
}
|
|
3885
3873
|
if (escrow.delegationIdFromLock !== void 0) {
|
|
3886
3874
|
const fileId = escrow.delegationIdFromLock;
|
|
@@ -3915,10 +3903,10 @@ function collectRepeated(value, previous) {
|
|
|
3915
3903
|
}
|
|
3916
3904
|
function parseDeclineReason(cmdName, raw) {
|
|
3917
3905
|
if (raw === void 0 || raw === "") {
|
|
3918
|
-
throw new Error(`${cmdName}: --reason is required when declining (one of: ${
|
|
3906
|
+
throw new Error(`${cmdName}: --reason is required when declining (one of: ${import_sdk10.DECLINE_REASONS.join(", ")})`);
|
|
3919
3907
|
}
|
|
3920
|
-
if (!(0,
|
|
3921
|
-
throw new Error(`${cmdName}: --reason must be one of ${
|
|
3908
|
+
if (!(0, import_sdk10.isDeclineReason)(raw)) {
|
|
3909
|
+
throw new Error(`${cmdName}: --reason must be one of ${import_sdk10.DECLINE_REASONS.join(", ")} (got '${raw}')`);
|
|
3922
3910
|
}
|
|
3923
3911
|
return raw;
|
|
3924
3912
|
}
|
|
@@ -3933,10 +3921,10 @@ function parseReasonDetail(cmdName, raw) {
|
|
|
3933
3921
|
return raw;
|
|
3934
3922
|
}
|
|
3935
3923
|
function buildAssetIdentifier(cmdName, labels, rawCurrency, rawDecimals, rawSymbol) {
|
|
3936
|
-
const resolved = (0,
|
|
3924
|
+
const resolved = (0, import_sdk10.resolveAsset)(rawCurrency);
|
|
3937
3925
|
if (!resolved) {
|
|
3938
3926
|
throw new Error(
|
|
3939
|
-
`${cmdName}: ${labels.currencyFlag} '${rawCurrency}' is not a known shorthand or a valid CAIP-19 string. Shorthand: ${
|
|
3927
|
+
`${cmdName}: ${labels.currencyFlag} '${rawCurrency}' is not a known shorthand or a valid CAIP-19 string. Shorthand: ${import_sdk10.WELL_KNOWN_ASSET_KEYS.join(", ")}. Or pass a raw CAIP-19 id (e.g. "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/spl:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") with ${labels.decimalsFlag}.`
|
|
3940
3928
|
);
|
|
3941
3929
|
}
|
|
3942
3930
|
let decimals = resolved.decimals;
|
|
@@ -3945,20 +3933,20 @@ function buildAssetIdentifier(cmdName, labels, rawCurrency, rawDecimals, rawSymb
|
|
|
3945
3933
|
throw new Error(`${cmdName}: ${labels.currencyFlag} is a raw CAIP-19 string; ${labels.decimalsFlag} (0-18) is required to convert amounts to base units.`);
|
|
3946
3934
|
}
|
|
3947
3935
|
const parsed = Number(rawDecimals);
|
|
3948
|
-
if (!Number.isInteger(parsed) || parsed <
|
|
3936
|
+
if (!Number.isInteger(parsed) || parsed < import_sdk10.ASSET_DECIMALS_MIN || parsed > import_sdk10.ASSET_DECIMALS_MAX) {
|
|
3949
3937
|
throw new Error(`${cmdName}: ${labels.decimalsFlag} must be an integer in [0, 18] (got '${rawDecimals}').`);
|
|
3950
3938
|
}
|
|
3951
3939
|
decimals = parsed;
|
|
3952
3940
|
} else if (rawDecimals !== void 0) {
|
|
3953
3941
|
const parsed = Number(rawDecimals);
|
|
3954
|
-
if (!Number.isInteger(parsed) || parsed <
|
|
3942
|
+
if (!Number.isInteger(parsed) || parsed < import_sdk10.ASSET_DECIMALS_MIN || parsed > import_sdk10.ASSET_DECIMALS_MAX) {
|
|
3955
3943
|
throw new Error(`${cmdName}: ${labels.decimalsFlag} must be an integer in [0, 18] (got '${rawDecimals}').`);
|
|
3956
3944
|
}
|
|
3957
3945
|
decimals = parsed;
|
|
3958
3946
|
}
|
|
3959
3947
|
let symbol = resolved.symbol;
|
|
3960
3948
|
if (rawSymbol !== void 0) {
|
|
3961
|
-
if (rawSymbol.length
|
|
3949
|
+
if (rawSymbol.length < import_sdk10.ASSET_SYMBOL_MIN_LEN || rawSymbol.length > import_sdk10.ASSET_SYMBOL_MAX_LEN) {
|
|
3962
3950
|
throw new Error(`${cmdName}: ${labels.symbolFlag} must be 1-16 chars (got length ${rawSymbol.length}).`);
|
|
3963
3951
|
}
|
|
3964
3952
|
symbol = rawSymbol;
|
|
@@ -3972,9 +3960,10 @@ var DELEGATION_CURRENCY_FLAGS = {
|
|
|
3972
3960
|
};
|
|
3973
3961
|
|
|
3974
3962
|
// src/commands/delegations.ts
|
|
3963
|
+
var import_sdk11 = require("@heyanon-arp/sdk");
|
|
3975
3964
|
var import_chalk9 = __toESM(require("chalk"));
|
|
3976
3965
|
init_api();
|
|
3977
|
-
var ALLOWED_STATES = /* @__PURE__ */ new Set([
|
|
3966
|
+
var ALLOWED_STATES = /* @__PURE__ */ new Set([import_sdk11.DelegationStates.OFFERED, import_sdk11.DelegationStates.ACCEPTED, import_sdk11.DelegationStates.DECLINED, import_sdk11.DelegationStates.CANCELED]);
|
|
3978
3967
|
function registerDelegationsCommand(root) {
|
|
3979
3968
|
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(
|
|
3980
3969
|
"--verbose",
|
|
@@ -4033,7 +4022,7 @@ function formatDelegationLine(d, selfDid, opts = {}) {
|
|
|
4033
4022
|
const amount = formatAmount(d);
|
|
4034
4023
|
const title = d.title ? import_chalk9.default.dim(`"${truncate2(d.title, 40)}"`) : import_chalk9.default.dim("(no title)");
|
|
4035
4024
|
let declineSuffix = "";
|
|
4036
|
-
if (d.state ===
|
|
4025
|
+
if (d.state === import_sdk11.DelegationStates.DECLINED && d.declineReason) {
|
|
4037
4026
|
const detail = d.declineReasonDetail ? `: ${truncate2(d.declineReasonDetail, 40)}` : "";
|
|
4038
4027
|
declineSuffix = ` ${import_chalk9.default.dim(`(reason: ${d.declineReason}${detail})`)}`;
|
|
4039
4028
|
}
|
|
@@ -4041,8 +4030,6 @@ function formatDelegationLine(d, selfDid, opts = {}) {
|
|
|
4041
4030
|
}
|
|
4042
4031
|
function colorState(s) {
|
|
4043
4032
|
switch (s) {
|
|
4044
|
-
case "proposed":
|
|
4045
|
-
return import_chalk9.default.yellow("proposed");
|
|
4046
4033
|
// `offered` is the server's actual emitted state immediately
|
|
4047
4034
|
// after `delegation offer`; `pending_lock_finalization` is the
|
|
4048
4035
|
// transient state while the relayer submits create_lock. Both
|
|
@@ -4050,34 +4037,34 @@ function colorState(s) {
|
|
|
4050
4037
|
// returns `undefined` and `formatDelegationLine` crashes with
|
|
4051
4038
|
// `TypeError: Cannot read properties of undefined (reading
|
|
4052
4039
|
// 'padEnd')` on any listing containing such a row.
|
|
4053
|
-
case
|
|
4040
|
+
case import_sdk11.DelegationStates.OFFERED:
|
|
4054
4041
|
return import_chalk9.default.yellow("offered");
|
|
4055
|
-
case
|
|
4042
|
+
case import_sdk11.DelegationStates.PENDING_LOCK_FINALIZATION:
|
|
4056
4043
|
return import_chalk9.default.yellow("pending_lock");
|
|
4057
|
-
case
|
|
4044
|
+
case import_sdk11.DelegationStates.ACCEPTED:
|
|
4058
4045
|
return import_chalk9.default.green("accepted");
|
|
4059
|
-
//
|
|
4060
|
-
//
|
|
4061
|
-
//
|
|
4062
|
-
case
|
|
4046
|
+
// The on-chain escrow lock is confirmed. Distinct branch so a
|
|
4047
|
+
// LOCKED row renders without hitting the defensive fallback
|
|
4048
|
+
// (which would otherwise echo the raw state).
|
|
4049
|
+
case import_sdk11.DelegationStates.LOCKED:
|
|
4063
4050
|
return import_chalk9.default.green("locked");
|
|
4064
|
-
case
|
|
4051
|
+
case import_sdk11.DelegationStates.DECLINED:
|
|
4065
4052
|
return import_chalk9.default.red("declined");
|
|
4066
|
-
case
|
|
4053
|
+
case import_sdk11.DelegationStates.CANCELED:
|
|
4067
4054
|
return import_chalk9.default.dim("canceled");
|
|
4068
|
-
// Terminal escrow outcomes
|
|
4055
|
+
// Terminal escrow outcomes: completed = payee paid;
|
|
4069
4056
|
// failed = create_lock never landed; refunded = funds returned
|
|
4070
4057
|
// to the buyer (work expired / dispute closed);
|
|
4071
4058
|
// dispute_resolved = operator ruled.
|
|
4072
|
-
case
|
|
4059
|
+
case import_sdk11.DelegationStates.COMPLETED:
|
|
4073
4060
|
return import_chalk9.default.green("completed");
|
|
4074
|
-
case
|
|
4061
|
+
case import_sdk11.DelegationStates.FAILED:
|
|
4075
4062
|
return import_chalk9.default.red("failed");
|
|
4076
|
-
case
|
|
4063
|
+
case import_sdk11.DelegationStates.REFUNDED:
|
|
4077
4064
|
return import_chalk9.default.red("refunded");
|
|
4078
|
-
case
|
|
4065
|
+
case import_sdk11.DelegationStates.DISPUTING:
|
|
4079
4066
|
return import_chalk9.default.yellow("disputing");
|
|
4080
|
-
case
|
|
4067
|
+
case import_sdk11.DelegationStates.DISPUTE_RESOLVED:
|
|
4081
4068
|
return import_chalk9.default.red("dispute_resolved");
|
|
4082
4069
|
default: {
|
|
4083
4070
|
const _exhaustive = s;
|
|
@@ -4123,7 +4110,7 @@ function parseLimit2(raw) {
|
|
|
4123
4110
|
}
|
|
4124
4111
|
|
|
4125
4112
|
// src/commands/did-doc.ts
|
|
4126
|
-
var
|
|
4113
|
+
var import_sdk12 = require("@heyanon-arp/sdk");
|
|
4127
4114
|
init_api();
|
|
4128
4115
|
function registerDidDocCommand(root) {
|
|
4129
4116
|
root.command("did-doc").description(
|
|
@@ -4132,7 +4119,7 @@ function registerDidDocCommand(root) {
|
|
|
4132
4119
|
"--field <path>",
|
|
4133
4120
|
"Extract a single value via a dotted path. Supports object property access (`id`), numeric array indexes (`verificationMethod.0`), and `#fragment` array selectors that match a verification-method or service entry by its `id` suffix (`verificationMethod.#settlement.publicKeyMultibase`). Two well-known shortcuts skip the path entirely AND strip the multibase prefix: `settlementPublicKey` / `identityPublicKey` emit the raw base58 pubkey ready for flags like `--recipient-pubkey`. Scalar values (string / number / boolean) emit raw to stdout for shell composition (`KEY=$(heyarp did-doc ... --field ...)`); objects / arrays emit as pretty-printed JSON. Mutually exclusive with `--json` since `--field` already controls the shape."
|
|
4134
4121
|
).action(async (did, opts) => {
|
|
4135
|
-
if (!(0,
|
|
4122
|
+
if (!(0, import_sdk12.isValidDid)(did)) {
|
|
4136
4123
|
throw new Error(`'${did}' is not a syntactically valid did:arp identifier`);
|
|
4137
4124
|
}
|
|
4138
4125
|
if (opts.json && opts.field !== void 0) {
|
|
@@ -4244,7 +4231,7 @@ function describeShape(value) {
|
|
|
4244
4231
|
}
|
|
4245
4232
|
|
|
4246
4233
|
// src/commands/doctor.ts
|
|
4247
|
-
var
|
|
4234
|
+
var import_sdk13 = require("@heyanon-arp/sdk");
|
|
4248
4235
|
var import_chalk10 = __toESM(require("chalk"));
|
|
4249
4236
|
init_api();
|
|
4250
4237
|
function registerDoctorCommand(root) {
|
|
@@ -4254,7 +4241,7 @@ function registerDoctorCommand(root) {
|
|
|
4254
4241
|
}
|
|
4255
4242
|
var LISTENING_THRESHOLD_SECONDS = 15 * 60;
|
|
4256
4243
|
async function runDoctor(did, opts) {
|
|
4257
|
-
if (!(0,
|
|
4244
|
+
if (!(0, import_sdk13.isValidDid)(did)) {
|
|
4258
4245
|
throw new Error(`'${did}' is not a syntactically valid did:arp identifier`);
|
|
4259
4246
|
}
|
|
4260
4247
|
const api = new ArpApiClient(opts.server);
|
|
@@ -4407,14 +4394,14 @@ function formatHints(event) {
|
|
|
4407
4394
|
}
|
|
4408
4395
|
|
|
4409
4396
|
// src/commands/escrow.ts
|
|
4410
|
-
var
|
|
4397
|
+
var import_sdk15 = require("@heyanon-arp/sdk");
|
|
4411
4398
|
var import_utils2 = require("@noble/hashes/utils");
|
|
4412
4399
|
var import_chalk12 = __toESM(require("chalk"));
|
|
4413
4400
|
init_api();
|
|
4414
4401
|
|
|
4415
4402
|
// src/commands/escrow-actions.ts
|
|
4416
4403
|
var import_node_crypto = require("crypto");
|
|
4417
|
-
var
|
|
4404
|
+
var import_sdk14 = require("@heyanon-arp/sdk");
|
|
4418
4405
|
var import_web33 = require("@solana/web3.js");
|
|
4419
4406
|
init_api();
|
|
4420
4407
|
var FEE_BUFFER_LAMPORTS = 10000000n;
|
|
@@ -4428,7 +4415,7 @@ async function setup(cmd, delegationIdArg, opts) {
|
|
|
4428
4415
|
const delegationId = normaliseDelegationId(delegationIdArg);
|
|
4429
4416
|
const fetched = await fetchLockAccount(conn, programId, delegationId);
|
|
4430
4417
|
if (!fetched) {
|
|
4431
|
-
const lockPda = deriveLockPda(programId, (0,
|
|
4418
|
+
const lockPda = deriveLockPda(programId, (0, import_sdk14.deriveLockId)(delegationId)).toBase58();
|
|
4432
4419
|
throw new Error(
|
|
4433
4420
|
`${cmd}: no on-chain Lock for delegation ${delegationId} (PDA ${lockPda} not found on ${rpcUrl}). Either the buyer hasn't funded yet (delegation fund \u2192 server relays create_lock), or you're pointing at the wrong cluster/program.`
|
|
4434
4421
|
);
|
|
@@ -4453,7 +4440,7 @@ function nowSecs() {
|
|
|
4453
4440
|
function deadlinePassed(ctx) {
|
|
4454
4441
|
return ctx.lock.expiry > 0n && nowSecs() >= ctx.lock.expiry;
|
|
4455
4442
|
}
|
|
4456
|
-
var SYSTEM_PROGRAM_B58 =
|
|
4443
|
+
var SYSTEM_PROGRAM_B58 = import_sdk14.SYSTEM_PROGRAM_ID_BASE58;
|
|
4457
4444
|
function effectiveFeeRecipient(lock) {
|
|
4458
4445
|
if (lock.feeRecipientAtLock !== SYSTEM_PROGRAM_B58) return new import_web33.PublicKey(lock.feeRecipientAtLock);
|
|
4459
4446
|
if (lock.treasuryAtLock && lock.treasuryAtLock !== SYSTEM_PROGRAM_B58) return new import_web33.PublicKey(lock.treasuryAtLock);
|
|
@@ -4503,7 +4490,7 @@ function emit(ctx, action, signature, extra = {}) {
|
|
|
4503
4490
|
lock_id: ctx.lock.lockId,
|
|
4504
4491
|
state_before: ctx.lock.state,
|
|
4505
4492
|
signature,
|
|
4506
|
-
rpc_url: ctx.rpcUrl,
|
|
4493
|
+
rpc_url: redactRpcUrl(ctx.rpcUrl),
|
|
4507
4494
|
...extra
|
|
4508
4495
|
};
|
|
4509
4496
|
if (ctx.opts.json) {
|
|
@@ -4515,7 +4502,7 @@ function emit(ctx, action, signature, extra = {}) {
|
|
|
4515
4502
|
}
|
|
4516
4503
|
async function acceptHandler(delegationId, opts) {
|
|
4517
4504
|
const ctx = await setup("escrow accept", delegationId, opts);
|
|
4518
|
-
requireState(ctx, [
|
|
4505
|
+
requireState(ctx, [import_sdk14.LockStates.CREATED], "Accept is only possible before any other transition; if it shows in_progress you already accepted.");
|
|
4519
4506
|
requireSigner(ctx, "payee", ctx.lock.payee);
|
|
4520
4507
|
const stake = ctx.lock.workerStakeAtLock;
|
|
4521
4508
|
await preflightLamports(ctx, stake, `the worker stake (${stake} lamports, returned when the lock settles in your favour)`);
|
|
@@ -4525,7 +4512,7 @@ async function acceptHandler(delegationId, opts) {
|
|
|
4525
4512
|
}
|
|
4526
4513
|
async function submitWorkHandler(delegationId, opts) {
|
|
4527
4514
|
const ctx = await setup("escrow submit-work", delegationId, opts);
|
|
4528
|
-
requireState(ctx, [
|
|
4515
|
+
requireState(ctx, [import_sdk14.LockStates.IN_PROGRESS], "Submit needs an accepted lock ('created' \u2192 run `heyarp escrow accept` first; terminal \u2192 the cycle already ended).");
|
|
4529
4516
|
requireSigner(ctx, "payee", ctx.lock.payee);
|
|
4530
4517
|
if (deadlinePassed(ctx)) {
|
|
4531
4518
|
throw new Error(
|
|
@@ -4536,7 +4523,7 @@ async function submitWorkHandler(delegationId, opts) {
|
|
|
4536
4523
|
let reviewDeadline = null;
|
|
4537
4524
|
try {
|
|
4538
4525
|
const fresh = await fetchLockAccount(ctx.conn, ctx.programId, ctx.delegationId);
|
|
4539
|
-
if (fresh && fresh.lock.state ===
|
|
4526
|
+
if (fresh && fresh.lock.state === import_sdk14.LockStates.SUBMITTED && fresh.lock.expiry > 0n) {
|
|
4540
4527
|
reviewDeadline = new Date(Number(fresh.lock.expiry) * 1e3).toISOString();
|
|
4541
4528
|
}
|
|
4542
4529
|
} catch {
|
|
@@ -4549,22 +4536,25 @@ async function submitWorkHandler(delegationId, opts) {
|
|
|
4549
4536
|
}
|
|
4550
4537
|
async function claimHandler(delegationId, opts) {
|
|
4551
4538
|
const ctx = await setup("escrow claim", delegationId, opts);
|
|
4552
|
-
requireState(ctx, [
|
|
4539
|
+
requireState(ctx, [import_sdk14.LockStates.SUBMITTED], "Claim needs submitted work ('in_progress' \u2192 the worker must `escrow submit-work` first; 'paid' \u2192 already claimed).");
|
|
4553
4540
|
const me = ctx.keypair.publicKey.toBase58();
|
|
4554
4541
|
let role;
|
|
4555
4542
|
if (me === ctx.lock.payer) {
|
|
4556
|
-
role =
|
|
4543
|
+
role = import_sdk14.EscrowReleaseMethods.BUYER_APPROVED;
|
|
4557
4544
|
} else if (me === ctx.lock.payee) {
|
|
4558
4545
|
if (!deadlinePassed(ctx)) {
|
|
4559
4546
|
throw new Error(
|
|
4560
4547
|
`escrow claim: the review window is still open (until ${expiryIso(ctx)}). As the worker you can self-claim only AFTER it lapses; before that the buyer must approve (or dispute).`
|
|
4561
4548
|
);
|
|
4562
4549
|
}
|
|
4563
|
-
role =
|
|
4550
|
+
role = import_sdk14.EscrowReleaseMethods.REVIEW_TIMEOUT;
|
|
4564
4551
|
} else {
|
|
4565
4552
|
throw new Error(`escrow claim: your settlement key ${me} is neither the payer (${ctx.lock.payer}) nor the payee (${ctx.lock.payee}) of this lock.`);
|
|
4566
4553
|
}
|
|
4567
|
-
progress(
|
|
4554
|
+
progress(
|
|
4555
|
+
ctx.opts.json,
|
|
4556
|
+
role === import_sdk14.EscrowReleaseMethods.BUYER_APPROVED ? "approving payment as the buyer \u2014 this RELEASES the escrow and is irreversible" : "self-claiming after review timeout"
|
|
4557
|
+
);
|
|
4568
4558
|
const sig = await sendIx(
|
|
4569
4559
|
ctx,
|
|
4570
4560
|
buildClaimWorkPaymentIx({
|
|
@@ -4581,7 +4571,7 @@ async function claimHandler(delegationId, opts) {
|
|
|
4581
4571
|
}
|
|
4582
4572
|
async function cancelHandler(delegationId, opts) {
|
|
4583
4573
|
const ctx = await setup("escrow cancel", delegationId, opts);
|
|
4584
|
-
requireState(ctx, [
|
|
4574
|
+
requireState(ctx, [import_sdk14.LockStates.CREATED], "Cancel is the pre-accept exit only \u2014 once the worker accepted ('in_progress') the windows model governs.");
|
|
4585
4575
|
requireSigner(ctx, "payer", ctx.lock.payer);
|
|
4586
4576
|
const sig = await sendIx(
|
|
4587
4577
|
ctx,
|
|
@@ -4596,7 +4586,7 @@ async function cancelHandler(delegationId, opts) {
|
|
|
4596
4586
|
}
|
|
4597
4587
|
async function claimExpiredHandler(delegationId, opts) {
|
|
4598
4588
|
const ctx = await setup("escrow claim-expired", delegationId, opts);
|
|
4599
|
-
requireState(ctx, [
|
|
4589
|
+
requireState(ctx, [import_sdk14.LockStates.IN_PROGRESS], "Claim-expired recovers an accepted-but-never-submitted lock; 'submitted' work goes through claim/dispute instead.");
|
|
4600
4590
|
requireSigner(ctx, "payer", ctx.lock.payer);
|
|
4601
4591
|
if (!deadlinePassed(ctx)) {
|
|
4602
4592
|
throw new Error(`escrow claim-expired: the work window is still open (until ${expiryIso(ctx)}). The worker can still submit; the chain will reject this claim until then.`);
|
|
@@ -4614,7 +4604,7 @@ async function claimExpiredHandler(delegationId, opts) {
|
|
|
4614
4604
|
}
|
|
4615
4605
|
async function disputeOpenHandler(delegationId, opts) {
|
|
4616
4606
|
const ctx = await setup("escrow dispute open", delegationId, opts);
|
|
4617
|
-
requireState(ctx, [
|
|
4607
|
+
requireState(ctx, [import_sdk14.LockStates.SUBMITTED], "Disputes open against SUBMITTED work, inside the review window.");
|
|
4618
4608
|
requireSigner(ctx, "payer", ctx.lock.payer);
|
|
4619
4609
|
if (deadlinePassed(ctx)) {
|
|
4620
4610
|
throw new Error(
|
|
@@ -4629,7 +4619,7 @@ async function disputeOpenHandler(delegationId, opts) {
|
|
|
4629
4619
|
}
|
|
4630
4620
|
async function disputeCloseHandler(delegationId, opts) {
|
|
4631
4621
|
const ctx = await setup("escrow dispute close", delegationId, opts);
|
|
4632
|
-
requireState(ctx, [
|
|
4622
|
+
requireState(ctx, [import_sdk14.LockStates.DISPUTING], "Close only applies to an OPEN dispute that the operator never resolved.");
|
|
4633
4623
|
const me = ctx.keypair.publicKey.toBase58();
|
|
4634
4624
|
if (me !== ctx.lock.payer && me !== ctx.lock.payee) {
|
|
4635
4625
|
throw new Error(`escrow dispute close: your settlement key ${me} is neither party of this lock.`);
|
|
@@ -4654,7 +4644,7 @@ async function disputeCloseHandler(delegationId, opts) {
|
|
|
4654
4644
|
}
|
|
4655
4645
|
async function disputeResolveHandler(delegationId, opts) {
|
|
4656
4646
|
const ctx = await setup("escrow dispute resolve", delegationId, opts);
|
|
4657
|
-
requireState(ctx, [
|
|
4647
|
+
requireState(ctx, [import_sdk14.LockStates.DISPUTING], "Resolve only applies to an OPEN dispute, inside the dispute window.");
|
|
4658
4648
|
if (deadlinePassed(ctx)) {
|
|
4659
4649
|
throw new Error(
|
|
4660
4650
|
`escrow dispute resolve: the dispute window lapsed at ${expiryIso(ctx)} \u2014 the chain rejects late rulings (LockExpired); either party can now \`escrow dispute close\` instead.`
|
|
@@ -4695,7 +4685,12 @@ async function showHandler(delegationId, opts) {
|
|
|
4695
4685
|
const normalised = normaliseDelegationId(delegationId);
|
|
4696
4686
|
const fetched = await fetchLockAccount(conn, programId, normalised);
|
|
4697
4687
|
if (!fetched) {
|
|
4698
|
-
jsonOut({
|
|
4688
|
+
jsonOut({
|
|
4689
|
+
delegation_id: normalised.slice("del_".length),
|
|
4690
|
+
lock_exists: false,
|
|
4691
|
+
lock_pda: deriveLockPda(programId, (0, import_sdk14.deriveLockId)(normalised)).toBase58(),
|
|
4692
|
+
rpc_url: redactRpcUrl(rpcUrl)
|
|
4693
|
+
});
|
|
4699
4694
|
return;
|
|
4700
4695
|
}
|
|
4701
4696
|
const l = fetched.lock;
|
|
@@ -4714,7 +4709,7 @@ async function showHandler(delegationId, opts) {
|
|
|
4714
4709
|
fee_bps_at_lock: l.feeBpsAtLock,
|
|
4715
4710
|
worker_stake_at_lock: l.workerStakeAtLock.toString(),
|
|
4716
4711
|
operator_dispute_fee_at_lock: l.operatorDisputeFeeAtLock.toString(),
|
|
4717
|
-
rpc_url: rpcUrl
|
|
4712
|
+
rpc_url: redactRpcUrl(rpcUrl)
|
|
4718
4713
|
});
|
|
4719
4714
|
}
|
|
4720
4715
|
function sharedFlags(cmd) {
|
|
@@ -4770,7 +4765,7 @@ function registerEscrowCommands(root) {
|
|
|
4770
4765
|
"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)."
|
|
4771
4766
|
).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").option(
|
|
4772
4767
|
"--currency <s>",
|
|
4773
|
-
`Asset identifier bound into the hash: shorthand (${
|
|
4768
|
+
`Asset identifier bound into the hash: shorthand (${import_sdk15.WELL_KNOWN_ASSET_KEYS.join("|")}) OR raw CAIP-19 string. Optional but must match the offer's --currency.`
|
|
4774
4769
|
).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) => {
|
|
4775
4770
|
await runDeriveConditionHash(opts);
|
|
4776
4771
|
});
|
|
@@ -4849,7 +4844,7 @@ async function runDeriveConditionHash(opts) {
|
|
|
4849
4844
|
progress(opts.json, import_chalk12.default.dim(`Server: ${api.serverUrl}`));
|
|
4850
4845
|
progress(opts.json, import_chalk12.default.dim(`Signer: ${sender.did}`));
|
|
4851
4846
|
const subset = projectDelegationForHash(cmdName, opts, delegationId);
|
|
4852
|
-
const hashBytes = (0,
|
|
4847
|
+
const hashBytes = (0, import_sdk15.deriveDelegationConditionHash)(subset);
|
|
4853
4848
|
const hex = (0, import_utils2.bytesToHex)(hashBytes);
|
|
4854
4849
|
if (opts.json) {
|
|
4855
4850
|
jsonOut({
|
|
@@ -4931,6 +4926,7 @@ async function runRecoverSequence(opts) {
|
|
|
4931
4926
|
}
|
|
4932
4927
|
|
|
4933
4928
|
// src/commands/events.ts
|
|
4929
|
+
var import_sdk16 = require("@heyanon-arp/sdk");
|
|
4934
4930
|
var import_chalk13 = __toESM(require("chalk"));
|
|
4935
4931
|
init_api();
|
|
4936
4932
|
function registerEventsCommand(root) {
|
|
@@ -4949,7 +4945,7 @@ function registerEventsCommand(root) {
|
|
|
4949
4945
|
false
|
|
4950
4946
|
).option(
|
|
4951
4947
|
"--full-ids",
|
|
4952
|
-
"Print eventId, DIDs and serverEventHash in full (no truncation). The eventId column in the default human row is truncated to `evt_<head>...<tail>` \u2014 pass --full-ids to read the full UUID for `heyarp envelope <evt-id>`. NOTE: `serverEventHash` is the hash-chain anchor, NOT the value `receipt propose
|
|
4948
|
+
"Print eventId, DIDs and serverEventHash in full (no truncation). The eventId column in the default human row is truncated to `evt_<head>...<tail>` \u2014 pass --full-ids to read the full UUID for `heyarp envelope <evt-id>`. NOTE: `serverEventHash` is the hash-chain anchor, NOT the value `receipt propose` requires \u2014 that wants `requestHash` / `responseHash`, which are exposed by `heyarp receipts <rel-id> --full-ids`, or auto-derived via `receipt propose --auto-hashes --rel-id --request-id`.",
|
|
4953
4949
|
false
|
|
4954
4950
|
).option(
|
|
4955
4951
|
"--success-only",
|
|
@@ -4981,8 +4977,8 @@ async function runEvents(relationshipId, opts) {
|
|
|
4981
4977
|
}
|
|
4982
4978
|
const query = { limit };
|
|
4983
4979
|
if (since !== void 0) query.since = since;
|
|
4984
|
-
if (opts.successOnly) query.readModelStatus =
|
|
4985
|
-
else if (opts.rejectedOnly) query.readModelStatus =
|
|
4980
|
+
if (opts.successOnly) query.readModelStatus = import_sdk16.ReadModelStatuses.MATERIALIZED;
|
|
4981
|
+
else if (opts.rejectedOnly) query.readModelStatus = import_sdk16.ReadModelStatuses.REJECTED;
|
|
4986
4982
|
const signer = makeSigner(sender);
|
|
4987
4983
|
const events = await api.listEvents(relationshipId, signer, query);
|
|
4988
4984
|
if (opts.json) {
|
|
@@ -5001,8 +4997,7 @@ async function runEvents(relationshipId, opts) {
|
|
|
5001
4997
|
printVerbose(events, "Full event envelopes:", (ev) => ({
|
|
5002
4998
|
primary: `#${ev.relationshipEventIndex} ${ev.type ?? "<unknown>"}`,
|
|
5003
4999
|
// Surface BOTH the full eventId (used by `heyarp envelope
|
|
5004
|
-
// <evt-id>`) and the full serverEventHash
|
|
5005
|
-
// `receipt propose` / `receipt cosign`).
|
|
5000
|
+
// <evt-id>`) and the full serverEventHash.
|
|
5006
5001
|
secondary: `eventId=${ev.eventId} serverEventHash=${ev.serverEventHash}`
|
|
5007
5002
|
}));
|
|
5008
5003
|
}
|
|
@@ -5018,8 +5013,8 @@ function formatEventLine(ev, selfDid, opts = {}) {
|
|
|
5018
5013
|
const hash = opts.fullIds ? ev.serverEventHash : hashHead(ev.serverEventHash);
|
|
5019
5014
|
const extra = extraDetail(ev);
|
|
5020
5015
|
const tail = extra ? ` ${import_chalk13.default.dim(`(${extra})`)}` : "";
|
|
5021
|
-
const status = ev.readModelStatus ??
|
|
5022
|
-
const statusGlyph = status ===
|
|
5016
|
+
const status = ev.readModelStatus ?? import_sdk16.ReadModelStatuses.MATERIALIZED;
|
|
5017
|
+
const statusGlyph = status === import_sdk16.ReadModelStatuses.REJECTED ? `${import_chalk13.default.red("\u2717")} ` : " ";
|
|
5023
5018
|
return `${statusGlyph}${idx} ${import_chalk13.default.cyan(eventId)} ${type} ${direction} ${import_chalk13.default.cyan(hash)}${tail}`;
|
|
5024
5019
|
}
|
|
5025
5020
|
function eventIdHead(eventId) {
|
|
@@ -5310,7 +5305,7 @@ var GUIDE_SECTIONS = [
|
|
|
5310
5305
|
title: "Identity",
|
|
5311
5306
|
body: [
|
|
5312
5307
|
" \u2022 Each agent has a `did:arp:<base58btc>` DID + an Ed25519 identity key.",
|
|
5313
|
-
" \u2022 Keys live in `~/.
|
|
5308
|
+
" \u2022 Keys live in `~/.heyarp/agents.json` (or $HEYARP_HOME/agents.json).",
|
|
5314
5309
|
" \u2022 State file = identity. If two agents share one home dir, EITHER can sign as",
|
|
5315
5310
|
" the other. For multi-agent setups on one host, set HEYARP_HOME per agent:",
|
|
5316
5311
|
" HEYARP_HOME=/tmp/agent-alice heyarp register \u2026",
|
|
@@ -5337,8 +5332,8 @@ var GUIDE_SECTIONS = [
|
|
|
5337
5332
|
"",
|
|
5338
5333
|
" (There is no separate terms-agreement step \u2014 the delegation offer",
|
|
5339
5334
|
" carries the agreed terms inline and the condition_hash binds them.",
|
|
5340
|
-
"
|
|
5341
|
-
"
|
|
5335
|
+
" The escrow lock is funded AFTER acceptance via `delegation fund`,",
|
|
5336
|
+
" so an un-accepted offer never strands funds.)",
|
|
5342
5337
|
" Skipping any stage = the next stage rejects with a state-machine error.",
|
|
5343
5338
|
" On-chain claim_work_payment is the closure: the buyer approves, or the",
|
|
5344
5339
|
" worker self-claims after the review window expires."
|
|
@@ -5350,13 +5345,13 @@ var GUIDE_SECTIONS = [
|
|
|
5350
5345
|
overview: true,
|
|
5351
5346
|
title: "Escrow (how funds actually move)",
|
|
5352
5347
|
body: [
|
|
5353
|
-
"
|
|
5348
|
+
" The buyer funds escrow AFTER the worker accepts.",
|
|
5354
5349
|
" `delegation offer` carries terms only (no lock). Once the worker",
|
|
5355
5350
|
" accepts, `delegation fund` attaches the signed Solana `create_lock`",
|
|
5356
5351
|
" tx blob; the worker waits for the lock to confirm (LOCKED) before",
|
|
5357
5352
|
" starting work, knowing the cash is locked. An un-accepted offer has",
|
|
5358
5353
|
" no lock to strand.",
|
|
5359
|
-
" Payment consent is ON-CHAIN
|
|
5354
|
+
" Payment consent is ON-CHAIN: the payer sends claim_work_payment to",
|
|
5360
5355
|
" approve, or the payee self-claims after the review window. Exit paths:",
|
|
5361
5356
|
" \u2022 cancel_lock \u2014 buyer cancels while the lock is unaccepted",
|
|
5362
5357
|
" \u2022 claim_expired_work \u2014 buyer reclaims if the work window lapses",
|
|
@@ -5557,7 +5552,7 @@ var GUIDE_SECTIONS = [
|
|
|
5557
5552
|
" relationship : status <rel> --json \u2192 .relationshipState (NOT .state)",
|
|
5558
5553
|
" delegation : status <rel> --json \u2192 .latestDelegation.state",
|
|
5559
5554
|
" (or delegations <rel> --json \u2192 .[].state)",
|
|
5560
|
-
" receipt : receipts <rel> --json \u2192 .[].state / .
|
|
5555
|
+
" receipt : receipts <rel> --json \u2192 .[].state / .verdictProposed / .receiptEventHash",
|
|
5561
5556
|
" incoming events : inbox --json \u2192 .[].body.type / .eventId / .serverTimestamp /",
|
|
5562
5557
|
" .senderDid (NOT .signer)",
|
|
5563
5558
|
" worker pay key : did-doc <worker-did> --field settlementPublicKey",
|
|
@@ -6067,9 +6062,9 @@ async function runInbox(positionalDid, opts) {
|
|
|
6067
6062
|
printVerbose(events, "Full event envelopes:", (ev) => ({
|
|
6068
6063
|
primary: `#${ev.relationshipEventIndex} ${ev.type ?? "<unknown>"}`,
|
|
6069
6064
|
// eventId is the cursor used by `--before-event-id`;
|
|
6070
|
-
// serverEventHash is what `receipt propose`
|
|
6071
|
-
//
|
|
6072
|
-
//
|
|
6065
|
+
// serverEventHash is what `receipt propose` requires.
|
|
6066
|
+
// Surface BOTH inline so a copy-paste workflow doesn't have
|
|
6067
|
+
// to dig into the JSON below.
|
|
6073
6068
|
secondary: `eventId=${ev.eventId} serverEventHash=${ev.serverEventHash}`
|
|
6074
6069
|
}));
|
|
6075
6070
|
}
|
|
@@ -6194,7 +6189,7 @@ function parseLimit4(raw) {
|
|
|
6194
6189
|
// src/commands/keys.ts
|
|
6195
6190
|
var import_node_crypto2 = require("crypto");
|
|
6196
6191
|
var import_node_fs7 = require("fs");
|
|
6197
|
-
var
|
|
6192
|
+
var import_sdk17 = require("@heyanon-arp/sdk");
|
|
6198
6193
|
var import_chalk17 = __toESM(require("chalk"));
|
|
6199
6194
|
function writeSecretFile(path, body) {
|
|
6200
6195
|
const tmp = `${path}.tmp.${(0, import_node_crypto2.randomBytes)(8).toString("hex")}`;
|
|
@@ -6221,25 +6216,22 @@ function writeSecretFile(path, body) {
|
|
|
6221
6216
|
}
|
|
6222
6217
|
function registerKeysCommand(root) {
|
|
6223
6218
|
const keys = root.command("keys").description("Local key utilities");
|
|
6224
|
-
keys.command("gen").description("Generate a fresh identity + settlement keypair (no save by default)").
|
|
6225
|
-
const identity = (0,
|
|
6226
|
-
const settlement = (0,
|
|
6219
|
+
keys.command("gen").description("Generate a fresh identity + settlement keypair (no save by default)").action(() => {
|
|
6220
|
+
const identity = (0, import_sdk17.generateKeyPair)();
|
|
6221
|
+
const settlement = (0, import_sdk17.generateKeyPair)();
|
|
6227
6222
|
const out = [
|
|
6228
6223
|
import_chalk17.default.bold("Identity key (Ed25519)"),
|
|
6229
|
-
` public (base58btc): ${import_chalk17.default.cyan((0,
|
|
6224
|
+
` public (base58btc): ${import_chalk17.default.cyan((0, import_sdk17.base58btcEncode)(identity.publicKey))}`,
|
|
6230
6225
|
` secret (base64) : ${import_chalk17.default.yellow(Buffer.from(identity.secretKey).toString("base64"))}`,
|
|
6231
6226
|
"",
|
|
6232
6227
|
import_chalk17.default.bold("Settlement key (Ed25519)"),
|
|
6233
|
-
` public (base58btc): ${import_chalk17.default.cyan((0,
|
|
6228
|
+
` public (base58btc): ${import_chalk17.default.cyan((0, import_sdk17.base58btcEncode)(settlement.publicKey))}`,
|
|
6234
6229
|
` secret (base64) : ${import_chalk17.default.yellow(Buffer.from(settlement.secretKey).toString("base64"))}`,
|
|
6235
6230
|
"",
|
|
6236
6231
|
import_chalk17.default.bold("Resulting DID"),
|
|
6237
|
-
` ${import_chalk17.default.cyan((0,
|
|
6232
|
+
` ${import_chalk17.default.cyan((0, import_sdk17.formatDid)(identity.publicKey))}`
|
|
6238
6233
|
];
|
|
6239
6234
|
console.log(out.join("\n"));
|
|
6240
|
-
if (opts.save) {
|
|
6241
|
-
console.log(import_chalk17.default.yellow("\nNote: --save is not yet implemented. Capture the secret keys above before they scroll off-screen."));
|
|
6242
|
-
}
|
|
6243
6235
|
});
|
|
6244
6236
|
keys.command("export <did>").description("Export an agent\u2019s key backup bundle (SECRETS) so you can recover it on another machine with `heyarp recover`. Writes to --out (mode 0600) or stdout.").option("--server <url>", "Override ARP server base URL").option("--out <file>", "Write the bundle to this file (mode 0600) instead of stdout").action((did, opts) => {
|
|
6245
6237
|
const agent = loadAgentOrThrow(opts.server, did);
|
|
@@ -6256,10 +6248,10 @@ function registerKeysCommand(root) {
|
|
|
6256
6248
|
});
|
|
6257
6249
|
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) => {
|
|
6258
6250
|
const seed = decodeSeed(secretKeyB64);
|
|
6259
|
-
const pub = (0,
|
|
6260
|
-
const did = (0,
|
|
6251
|
+
const pub = (0, import_sdk17.getPublicKey)(seed);
|
|
6252
|
+
const did = (0, import_sdk17.formatDid)(pub);
|
|
6261
6253
|
console.log(`${import_chalk17.default.bold("DID")}: ${import_chalk17.default.cyan(did)}`);
|
|
6262
|
-
console.log(`${import_chalk17.default.bold("Identity public key (base58btc)")}: ${import_chalk17.default.cyan((0,
|
|
6254
|
+
console.log(`${import_chalk17.default.bold("Identity public key (base58btc)")}: ${import_chalk17.default.cyan((0, import_sdk17.base58btcEncode)(pub))}`);
|
|
6263
6255
|
});
|
|
6264
6256
|
}
|
|
6265
6257
|
function decodeSeed(b64) {
|
|
@@ -6278,8 +6270,12 @@ function decodeSeed(b64) {
|
|
|
6278
6270
|
// src/commands/list.ts
|
|
6279
6271
|
var import_chalk18 = __toESM(require("chalk"));
|
|
6280
6272
|
function registerListCommand(root) {
|
|
6281
|
-
root.command("list").description("List agents registered locally (~/.
|
|
6273
|
+
root.command("list").description("List agents registered locally (~/.heyarp/agents.json)").option("--json", "Machine-readable mode \u2014 emit the local agents as a single JSON array on stdout ({serverUrl, did, name, tags, registeredAt}[]).", false).action((opts) => {
|
|
6282
6274
|
const rows = listAgents();
|
|
6275
|
+
if (opts.json) {
|
|
6276
|
+
jsonOut(rows.map(({ serverUrl, agent }) => ({ serverUrl, did: agent.did, name: agent.name ?? null, tags: agent.tags ?? [], registeredAt: agent.registeredAt })));
|
|
6277
|
+
return;
|
|
6278
|
+
}
|
|
6283
6279
|
if (rows.length === 0) {
|
|
6284
6280
|
console.log(import_chalk18.default.dim(`No local agents. State file: ${stateFilePath()}`));
|
|
6285
6281
|
return;
|
|
@@ -6418,12 +6414,12 @@ function registerLogoutCommand(root) {
|
|
|
6418
6414
|
}
|
|
6419
6415
|
|
|
6420
6416
|
// src/commands/profile.ts
|
|
6421
|
-
var
|
|
6417
|
+
var import_sdk18 = require("@heyanon-arp/sdk");
|
|
6422
6418
|
var import_chalk21 = __toESM(require("chalk"));
|
|
6423
6419
|
init_api();
|
|
6424
6420
|
function registerProfileCommand(root) {
|
|
6425
6421
|
root.command("profile").description("Composed agent profile: public fields + reputation + liveness, in one read.").argument("<did>", "did:arp:<base58btc> identifier").option("--server <url>", "Override ARP server base URL").option("--json", "Emit the profile as JSON on stdout.", false).action(async (did, opts) => {
|
|
6426
|
-
if (!(0,
|
|
6422
|
+
if (!(0, import_sdk18.isValidDid)(did)) {
|
|
6427
6423
|
throw new Error(`'${did}' is not a syntactically valid did:arp identifier`);
|
|
6428
6424
|
}
|
|
6429
6425
|
const api = new ArpApiClient(opts.server);
|
|
@@ -6451,45 +6447,16 @@ function registerProfileCommand(root) {
|
|
|
6451
6447
|
}
|
|
6452
6448
|
|
|
6453
6449
|
// src/commands/receipt.ts
|
|
6454
|
-
var
|
|
6450
|
+
var import_sdk19 = require("@heyanon-arp/sdk");
|
|
6455
6451
|
var import_shield2 = require("@heyanon-arp/shield");
|
|
6456
6452
|
var import_chalk22 = __toESM(require("chalk"));
|
|
6457
6453
|
init_api();
|
|
6458
6454
|
function registerReceiptCommands(root) {
|
|
6459
|
-
const cmd = root.command("receipt").description("Receipt envelopes \u2014 the payee proposes a delivery record (
|
|
6455
|
+
const cmd = root.command("receipt").description("Receipt envelopes \u2014 the payee proposes a delivery record (payment consent is on-chain via claim_work_payment)");
|
|
6460
6456
|
registerPropose(cmd);
|
|
6461
6457
|
}
|
|
6462
|
-
var POST_COMMIT_ERROR_CODES2 = /* @__PURE__ */ new Set([
|
|
6463
|
-
"RECEIPT_ALREADY_EXISTS",
|
|
6464
|
-
"RECEIPT_DELEGATION_NOT_FOUND",
|
|
6465
|
-
"RECEIPT_DELEGATION_NOT_ACTIVE",
|
|
6466
|
-
"RECEIPT_RELATIONSHIP_MISMATCH",
|
|
6467
|
-
"RECEIPT_ISSUER_IS_CALLER",
|
|
6468
|
-
"RECEIPT_NOT_FOUND",
|
|
6469
|
-
"RECEIPT_INVALID_STATE",
|
|
6470
|
-
// M6 lock-at-accept: the receipt-propose handler's LOCKED gate emits
|
|
6471
|
-
// `DELEGATION_PENDING_LOCK` (409) when the delegation is funded but
|
|
6472
|
-
// the on-chain lock isn't confirmed yet (state
|
|
6473
|
-
// `pending_lock_finalization`). It fires from the body handler AFTER
|
|
6474
|
-
// the event row is committed — same lifecycle as
|
|
6475
|
-
// `RECEIPT_DELEGATION_NOT_ACTIVE` — so the CLI must advance
|
|
6476
|
-
// `lastSenderSequence`, otherwise a retry once the lock confirms
|
|
6477
|
-
// reuses the consumed sequence and trips `ENV_SEQUENCE_BACKWARDS`.
|
|
6478
|
-
"DELEGATION_PENDING_LOCK",
|
|
6479
|
-
// response_hash / request_hash / deliverable_hash content
|
|
6480
|
-
// verification. Server commits the receipt envelope row BEFORE
|
|
6481
|
-
// running the canonical-hash lookup, so a rejection here still
|
|
6482
|
-
// consumes the sender sequence — must be in the allowlist or
|
|
6483
|
-
// a retry after fixing the hashes would trip
|
|
6484
|
-
// `ENV_SEQUENCE_BACKWARDS`.
|
|
6485
|
-
"RECEIPT_RESPONSE_HASH_NOT_FOUND",
|
|
6486
|
-
"RECEIPT_REQUEST_HASH_NOT_FOUND",
|
|
6487
|
-
"RECEIPT_DELIVERABLE_HASH_MISMATCH"
|
|
6488
|
-
]);
|
|
6489
|
-
var VERDICT_VALUES = ["accepted", "accepted_with_notes", "rejected"];
|
|
6490
|
-
var SHA256_RE = /^sha256:[0-9a-f]{64}$/;
|
|
6491
6458
|
function registerPropose(parent) {
|
|
6492
|
-
parent.command("propose").description("Send a receipt envelope as the PAYEE. Row lands PROPOSED;
|
|
6459
|
+
parent.command("propose").description("Send a receipt envelope as the PAYEE. Row lands PROPOSED; the buyer approves payment on-chain via claim_work_payment.").argument("<recipient-did>", "Recipient agent DID (= caller / offerer of the parent delegation)").argument("<delegation-id>", "Parent delegation id (UUID, must be ACCEPTED)").argument("[request-hash]", "sha256:<64 hex> \u2014 SHA-256 of the work_request payload being settled. Omit when --auto-hashes is set.").argument("[response-hash]", "sha256:<64 hex> \u2014 SHA-256 of the work_response payload being settled. Omit when --auto-hashes is set.").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("--verdict <s>", "verdict_proposed (accepted | accepted_with_notes | rejected)", "accepted").option("--notes-hash <sha256>", "Optional sha256:<64 hex> notes hash").option("--deliverable-hash <sha256>", "Optional sha256:<64 hex> deliverable hash").option("--input-tokens <n>", "Optional usage.input_tokens").option("--output-tokens <n>", "Optional usage.output_tokens").option("--latency-ms <n>", "Optional usage.latency_ms").option("--model <name>", "Optional usage.model").option("--computed-amount <s>", "Optional usage.computed_amount").option("--ttl <seconds>", "Envelope TTL in seconds", "3600").option(
|
|
6493
6460
|
"--auto-hashes",
|
|
6494
6461
|
"Compute request_hash + response_hash automatically from the work-log row at (--rel-id, <delegation-id>, --request-id). Positional <request-hash>/<response-hash> become optional; if supplied they must match the computed values (consistency check). --rel-id and --request-id auto-resolve from local state when unambiguous \u2014 pass them explicitly only if the lookup finds multiple candidates.",
|
|
6495
6462
|
false
|
|
@@ -6630,7 +6597,7 @@ async function assertSenderIsReceiptPayee(api, sender, relationshipId, delegatio
|
|
|
6630
6597
|
}
|
|
6631
6598
|
if (delegation.offererDid === sender.did) {
|
|
6632
6599
|
throw new Error(
|
|
6633
|
-
`receipt propose: ${sender.did} is the CALLER on delegation ${delegationId} (matches delegation.offererDid). Only the PAYEE \u2014 the counterparty who accepted the delegation offer \u2014 can issue a receipt; as the caller your role is to
|
|
6600
|
+
`receipt propose: ${sender.did} is the CALLER on delegation ${delegationId} (matches delegation.offererDid). Only the PAYEE \u2014 the counterparty who accepted the delegation offer \u2014 can issue a receipt; as the caller your role is to approve payment ON-CHAIN via claim_work_payment once the payee proposes the receipt. Wait for it via \`heyarp status ${relationshipId} --wait --until receipt.proposed\`.`
|
|
6634
6601
|
);
|
|
6635
6602
|
}
|
|
6636
6603
|
return delegation;
|
|
@@ -6687,7 +6654,7 @@ async function computeWorkLogHashes(api, sender, relationshipId, delegationId, r
|
|
|
6687
6654
|
`receipt propose --auto-hashes: no work-log row found for (relationshipId=${relationshipId}, delegationId=${delegationId}, requestId=${requestId}). Did the work_request envelope land yet?`
|
|
6688
6655
|
);
|
|
6689
6656
|
}
|
|
6690
|
-
if (workLog.state !==
|
|
6657
|
+
if (workLog.state !== import_sdk19.WorkLogStates.RESPONDED) {
|
|
6691
6658
|
throw new Error(
|
|
6692
6659
|
`receipt propose --auto-hashes: work-log row ${requestId} is in state '${workLog.state}', not 'responded'. The payee must \`work respond\` before a receipt can be proposed.`
|
|
6693
6660
|
);
|
|
@@ -6703,27 +6670,27 @@ async function computeWorkLogHashes(api, sender, relationshipId, delegationId, r
|
|
|
6703
6670
|
};
|
|
6704
6671
|
const responseBody = workLog.responseOutput !== void 0 ? { type: "work_response", content: { delegation_id: workLog.delegationId, request_id: workLog.requestId, output: workLog.responseOutput } } : { type: "work_response", content: { delegation_id: workLog.delegationId, request_id: workLog.requestId, error: workLog.responseError } };
|
|
6705
6672
|
return {
|
|
6706
|
-
requestHash: (0,
|
|
6707
|
-
responseHash: (0,
|
|
6673
|
+
requestHash: (0, import_sdk19.canonicalSha256Hex)(requestBody),
|
|
6674
|
+
responseHash: (0, import_sdk19.canonicalSha256Hex)(responseBody)
|
|
6708
6675
|
};
|
|
6709
6676
|
}
|
|
6710
6677
|
async function sendReceiptEnvelope(args) {
|
|
6711
6678
|
const nextSequence = (args.sender.lastSenderSequence ?? 0) + 1;
|
|
6712
6679
|
const protectedBlock = {
|
|
6713
|
-
protocol_version:
|
|
6714
|
-
purpose:
|
|
6715
|
-
message_id: (0,
|
|
6680
|
+
protocol_version: import_sdk19.CURRENT_PROTOCOL_VERSION,
|
|
6681
|
+
purpose: import_sdk19.Purpose.ENVELOPE,
|
|
6682
|
+
message_id: (0, import_sdk19.uuidV4)(),
|
|
6716
6683
|
sender_did: args.sender.did,
|
|
6717
6684
|
recipient_did: args.recipientDid,
|
|
6718
6685
|
relationship_id: null,
|
|
6719
6686
|
sender_sequence: nextSequence,
|
|
6720
|
-
sender_nonce: (0,
|
|
6721
|
-
timestamp: (0,
|
|
6722
|
-
expires_at: (0,
|
|
6687
|
+
sender_nonce: (0, import_sdk19.senderNonce)(),
|
|
6688
|
+
timestamp: (0, import_sdk19.rfc3339)(),
|
|
6689
|
+
expires_at: (0, import_sdk19.expiresAt)(args.ttlSeconds),
|
|
6723
6690
|
delivery_id: null
|
|
6724
6691
|
};
|
|
6725
6692
|
const signer = makeSigner(args.sender);
|
|
6726
|
-
const envelope = (0,
|
|
6693
|
+
const envelope = (0, import_sdk19.signEnvelope)({
|
|
6727
6694
|
protected: protectedBlock,
|
|
6728
6695
|
body: args.body,
|
|
6729
6696
|
identitySecretKey: signer.identitySecretKey,
|
|
@@ -6738,7 +6705,7 @@ async function sendReceiptEnvelope(args) {
|
|
|
6738
6705
|
updateAgentLocal(args.server, args.sender.did, { lastSenderSequence: nextSequence });
|
|
6739
6706
|
return result;
|
|
6740
6707
|
} catch (err) {
|
|
6741
|
-
if (err instanceof ApiError &&
|
|
6708
|
+
if (err instanceof ApiError && (0, import_sdk19.isPostCommitErrorCode)(err.payload.code)) {
|
|
6742
6709
|
updateAgentLocal(args.server, args.sender.did, { lastSenderSequence: nextSequence });
|
|
6743
6710
|
}
|
|
6744
6711
|
throw err;
|
|
@@ -6753,9 +6720,9 @@ function printIngestResult2(result) {
|
|
|
6753
6720
|
console.log(`${import_chalk22.default.bold("Server event hash")}: ${import_chalk22.default.cyan(result.serverEventHash)}`);
|
|
6754
6721
|
}
|
|
6755
6722
|
function parseVerdict(cmdName, raw) {
|
|
6756
|
-
if (raw === void 0 || raw === "") return
|
|
6757
|
-
if (!
|
|
6758
|
-
throw new Error(`${cmdName}: --verdict must be one of ${
|
|
6723
|
+
if (raw === void 0 || raw === "") return import_sdk19.ReceiptVerdicts.ACCEPTED;
|
|
6724
|
+
if (!import_sdk19.RECEIPT_VERDICTS.includes(raw)) {
|
|
6725
|
+
throw new Error(`${cmdName}: --verdict must be one of ${import_sdk19.RECEIPT_VERDICTS.join(" | ")} (got '${raw}')`);
|
|
6759
6726
|
}
|
|
6760
6727
|
return raw;
|
|
6761
6728
|
}
|
|
@@ -6792,7 +6759,7 @@ function requireUuid3(cmdName, raw, label) {
|
|
|
6792
6759
|
requireUuid(cmdName, raw, label);
|
|
6793
6760
|
}
|
|
6794
6761
|
function requireSha256(cmdName, raw, label) {
|
|
6795
|
-
if (!
|
|
6762
|
+
if (!(0, import_sdk19.isSha256Hex)(raw)) {
|
|
6796
6763
|
throw new Error(`${cmdName}: ${label} must match 'sha256:<64 lowercase hex>' (got '${raw}')`);
|
|
6797
6764
|
}
|
|
6798
6765
|
}
|
|
@@ -6803,21 +6770,17 @@ function requireDid2(cmdName, did, label) {
|
|
|
6803
6770
|
}
|
|
6804
6771
|
|
|
6805
6772
|
// src/commands/receipts.ts
|
|
6773
|
+
var import_sdk20 = require("@heyanon-arp/sdk");
|
|
6806
6774
|
var import_chalk23 = __toESM(require("chalk"));
|
|
6807
6775
|
init_api();
|
|
6808
|
-
var ALLOWED_STATES2 = /* @__PURE__ */ new Set(["proposed", "cosigned"]);
|
|
6809
6776
|
function registerReceiptsCommand(root) {
|
|
6810
6777
|
root.command("receipts").description(
|
|
6811
6778
|
"List receipts for a relationship (one row per (delegationId, requestHash, responseHash), oldest-first). Receipt rows expose the chain hash as `receiptEventHash` (NOT `serverEventHash` \u2014 that field is the wider envelope identifier on `heyarp events` rows). Scripts using `--json | jq .receiptEventHash` get the value; `jq .serverEventHash` silently returns null because the row doesn't carry that key."
|
|
6812
|
-
).argument("<relationship-id>", "Relationship UUID").option("--server <url>", "Override ARP server base URL").option("--
|
|
6779
|
+
).argument("<relationship-id>", "Relationship UUID").option("--server <url>", "Override ARP server base URL").option("--delegation-id <uuid>", "Narrow to receipts under a specific delegation id").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(
|
|
6813
6780
|
"--verbose",
|
|
6814
|
-
'After the one-line summaries, print a framed "Full receipt payloads (N rows)" block \u2014 each row labelled with full delegationId + requestHash + responseHash
|
|
6815
|
-
false
|
|
6816
|
-
).option("--json", "Machine-readable mode \u2014 emit a single JSON array of receipt rows, no chalk, no summary. Pipe-safe. Mutually exclusive with --verbose.", false).option(
|
|
6817
|
-
"--full-ids",
|
|
6818
|
-
"Print delegationId / requestHash / responseHash / DIDs in full (no truncation). REQUIRED for piping into `receipt cosign` which needs the full sha256s.",
|
|
6781
|
+
'After the one-line summaries, print a framed "Full receipt payloads (N rows)" block \u2014 each row labelled with full delegationId + requestHash + responseHash and dumped as JSON. Mutually exclusive with --json.',
|
|
6819
6782
|
false
|
|
6820
|
-
).action(async (relationshipId, opts) => {
|
|
6783
|
+
).option("--json", "Machine-readable mode \u2014 emit a single JSON array of receipt rows, no chalk, no summary. Pipe-safe. Mutually exclusive with --verbose.", false).option("--full-ids", "Print delegationId / requestHash / responseHash / DIDs in full (no truncation).", false).action(async (relationshipId, opts) => {
|
|
6821
6784
|
await runReceipts(relationshipId, opts);
|
|
6822
6785
|
});
|
|
6823
6786
|
}
|
|
@@ -6828,7 +6791,6 @@ async function runReceipts(relationshipId, opts) {
|
|
|
6828
6791
|
);
|
|
6829
6792
|
}
|
|
6830
6793
|
const limit = parseLimit5(opts.limit);
|
|
6831
|
-
const state = parseState2(opts.state);
|
|
6832
6794
|
const api = new ArpApiClient(opts.server);
|
|
6833
6795
|
const sender = resolveSenderAgent("receipts", opts.server, opts.fromDid);
|
|
6834
6796
|
if (!opts.json) {
|
|
@@ -6837,7 +6799,6 @@ async function runReceipts(relationshipId, opts) {
|
|
|
6837
6799
|
console.log(import_chalk23.default.dim(`Relationship: ${relationshipId}`));
|
|
6838
6800
|
}
|
|
6839
6801
|
const query = { limit };
|
|
6840
|
-
if (state) query.state = state;
|
|
6841
6802
|
if (opts.delegationId) query.delegationId = opts.delegationId;
|
|
6842
6803
|
if (opts.after) query.after = opts.after;
|
|
6843
6804
|
const signer = makeSigner(sender);
|
|
@@ -6856,11 +6817,10 @@ async function runReceipts(relationshipId, opts) {
|
|
|
6856
6817
|
}
|
|
6857
6818
|
if (opts.verbose) {
|
|
6858
6819
|
printVerbose(rows, "Full receipt payloads:", (r) => ({
|
|
6859
|
-
primary: `
|
|
6860
|
-
//
|
|
6861
|
-
//
|
|
6862
|
-
//
|
|
6863
|
-
// dropping into `--json | jq`.
|
|
6820
|
+
primary: `verdict=${r.verdictProposed}`,
|
|
6821
|
+
// Surface the FULL `requestHash` + `responseHash` + `delegationId`
|
|
6822
|
+
// on one inline label so the receipt-correlation flow stays
|
|
6823
|
+
// grep-able without dropping into `--json | jq`.
|
|
6864
6824
|
secondary: `delegationId=${r.delegationId} requestHash=${r.requestHash} responseHash=${r.responseHash}`
|
|
6865
6825
|
}));
|
|
6866
6826
|
}
|
|
@@ -6872,34 +6832,21 @@ function formatReceiptLine(r, selfDid, opts = {}) {
|
|
|
6872
6832
|
const delegationPart = opts.fullIds ? r.delegationId : idHead2(r.delegationId);
|
|
6873
6833
|
const requestHashPart = opts.fullIds ? r.requestHash : hashHead3(r.requestHash);
|
|
6874
6834
|
const id = import_chalk23.default.bold(`${delegationPart}/${requestHashPart}`);
|
|
6875
|
-
const state = colorState2(r.state).padEnd(stateColumnWidth2());
|
|
6876
6835
|
const callerHead = opts.fullIds ? r.callerDid : didHead3(r.callerDid);
|
|
6877
6836
|
const payeeHead = opts.fullIds ? r.payeeDid : didHead3(r.payeeDid);
|
|
6878
6837
|
const direction = r.payeeDid === selfDid ? `${import_chalk23.default.bold("me")}(payee) \u2192 ${import_chalk23.default.dim(callerHead)}` : `${import_chalk23.default.dim(payeeHead)}(payee) \u2192 ${import_chalk23.default.bold("me")}`;
|
|
6879
6838
|
const verdict = formatVerdict(r);
|
|
6880
6839
|
const responseTail = opts.fullIds ? `
|
|
6881
6840
|
${import_chalk23.default.dim("responseHash:")} ${import_chalk23.default.cyan(r.responseHash)}` : "";
|
|
6882
|
-
return `${id} ${
|
|
6883
|
-
}
|
|
6884
|
-
function colorState2(s) {
|
|
6885
|
-
switch (s) {
|
|
6886
|
-
case "proposed":
|
|
6887
|
-
return import_chalk23.default.yellow("proposed");
|
|
6888
|
-
case "cosigned":
|
|
6889
|
-
return import_chalk23.default.green("cosigned");
|
|
6890
|
-
}
|
|
6891
|
-
}
|
|
6892
|
-
function stateColumnWidth2() {
|
|
6893
|
-
return 8;
|
|
6841
|
+
return `${id} ${direction} ${verdict}${responseTail}`;
|
|
6894
6842
|
}
|
|
6895
6843
|
function formatVerdict(r) {
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
case "accepted":
|
|
6844
|
+
switch (r.verdictProposed) {
|
|
6845
|
+
case import_sdk20.ReceiptVerdicts.ACCEPTED:
|
|
6899
6846
|
return import_chalk23.default.green("accepted");
|
|
6900
|
-
case
|
|
6847
|
+
case import_sdk20.ReceiptVerdicts.ACCEPTED_WITH_NOTES:
|
|
6901
6848
|
return import_chalk23.default.yellow("accepted_with_notes");
|
|
6902
|
-
case
|
|
6849
|
+
case import_sdk20.ReceiptVerdicts.REJECTED:
|
|
6903
6850
|
return import_chalk23.default.red("rejected");
|
|
6904
6851
|
}
|
|
6905
6852
|
}
|
|
@@ -6915,13 +6862,6 @@ function didHead3(did) {
|
|
|
6915
6862
|
if (did.length <= 20) return did;
|
|
6916
6863
|
return `${did.slice(0, 20)}...`;
|
|
6917
6864
|
}
|
|
6918
|
-
function parseState2(raw) {
|
|
6919
|
-
if (raw === void 0) return void 0;
|
|
6920
|
-
if (!ALLOWED_STATES2.has(raw)) {
|
|
6921
|
-
throw new Error(`receipts: --state must be one of proposed|cosigned (got '${raw}')`);
|
|
6922
|
-
}
|
|
6923
|
-
return raw;
|
|
6924
|
-
}
|
|
6925
6865
|
function parseLimit5(raw) {
|
|
6926
6866
|
if (raw === void 0) return 20;
|
|
6927
6867
|
const n = Number(raw);
|
|
@@ -6933,7 +6873,7 @@ function parseLimit5(raw) {
|
|
|
6933
6873
|
|
|
6934
6874
|
// src/commands/recover.ts
|
|
6935
6875
|
var import_node_fs8 = require("fs");
|
|
6936
|
-
var
|
|
6876
|
+
var import_sdk21 = require("@heyanon-arp/sdk");
|
|
6937
6877
|
var import_chalk24 = __toESM(require("chalk"));
|
|
6938
6878
|
init_api();
|
|
6939
6879
|
function registerRecoverCommand(root) {
|
|
@@ -6954,18 +6894,18 @@ async function runRecover(opts) {
|
|
|
6954
6894
|
}
|
|
6955
6895
|
const bundle = validateKeyBundle(raw);
|
|
6956
6896
|
const identitySecret = new Uint8Array(Buffer.from(bundle.identitySecretKeyB64, "base64"));
|
|
6957
|
-
const identityPub = (0,
|
|
6958
|
-
const derivedDid = (0,
|
|
6897
|
+
const identityPub = (0, import_sdk21.base58btcDecode)(bundle.identityPublicKeyB58);
|
|
6898
|
+
const derivedDid = (0, import_sdk21.formatDid)(identityPub);
|
|
6959
6899
|
if (derivedDid !== bundle.did) {
|
|
6960
6900
|
throw new Error(`recover: key file is inconsistent \u2014 its DID (${bundle.did}) does not match its identity public key (${derivedDid})`);
|
|
6961
6901
|
}
|
|
6962
6902
|
const probe2 = new TextEncoder().encode("heyarp-recover-keycheck");
|
|
6963
|
-
if (!(0,
|
|
6903
|
+
if (!(0, import_sdk21.verify)((0, import_sdk21.sign)(probe2, identitySecret), probe2, identityPub)) {
|
|
6964
6904
|
throw new Error("recover: the identity secret key does not match the public key in this bundle");
|
|
6965
6905
|
}
|
|
6966
6906
|
const settlementSecret = new Uint8Array(Buffer.from(bundle.settlementSecretKeyB64, "base64"));
|
|
6967
|
-
const settlementPub = (0,
|
|
6968
|
-
if (!(0,
|
|
6907
|
+
const settlementPub = (0, import_sdk21.base58btcDecode)(bundle.settlementPublicKeyB58);
|
|
6908
|
+
if (!(0, import_sdk21.verify)((0, import_sdk21.sign)(probe2, settlementSecret), probe2, settlementPub)) {
|
|
6969
6909
|
throw new Error("recover: the settlement secret key does not match the settlement public key in this bundle");
|
|
6970
6910
|
}
|
|
6971
6911
|
if (bundle.ownerWallet && bundle.ownerWallet !== credential.wallet) {
|
|
@@ -6987,8 +6927,10 @@ async function runRecover(opts) {
|
|
|
6987
6927
|
lastSenderSequence = seq.lastSenderSequence;
|
|
6988
6928
|
} catch (err) {
|
|
6989
6929
|
process.stderr.write(
|
|
6990
|
-
import_chalk24.default.yellow(
|
|
6991
|
-
|
|
6930
|
+
import_chalk24.default.yellow(
|
|
6931
|
+
`\u26A0 could not read the sender sequence from the server (${err.message}). If sends are rejected as backwards, run \`heyarp escrow recover-sequence --apply\`.
|
|
6932
|
+
`
|
|
6933
|
+
)
|
|
6992
6934
|
);
|
|
6993
6935
|
}
|
|
6994
6936
|
const state = {
|
|
@@ -7019,7 +6961,7 @@ async function runRecover(opts) {
|
|
|
7019
6961
|
// src/commands/register.ts
|
|
7020
6962
|
var import_node_crypto4 = require("crypto");
|
|
7021
6963
|
var import_node_fs9 = require("fs");
|
|
7022
|
-
var
|
|
6964
|
+
var import_sdk22 = require("@heyanon-arp/sdk");
|
|
7023
6965
|
var import_chalk25 = __toESM(require("chalk"));
|
|
7024
6966
|
var import_prompts2 = __toESM(require("prompts"));
|
|
7025
6967
|
init_api();
|
|
@@ -7040,13 +6982,12 @@ function registerRegisterCommand(root) {
|
|
|
7040
6982
|
// logged by CI runners (e.g. GitHub Actions echoes the
|
|
7041
6983
|
// full command). Safer alternatives:
|
|
7042
6984
|
// • interactive prompt (default) — never written anywhere
|
|
7043
|
-
// • `HEYARP_PASSWORD` env var (
|
|
7044
|
-
//
|
|
7045
|
-
// scripts
|
|
6985
|
+
// • `HEYARP_PASSWORD` env var (tracked) reads from env so
|
|
6986
|
+
// the secret stays out of argv even in scripts
|
|
7046
6987
|
// Treat `--password <s>` as a one-off local-dev affordance;
|
|
7047
6988
|
// do NOT use it in CI pipelines without a secret-redacting
|
|
7048
6989
|
// runner.
|
|
7049
|
-
"Owner password \u2014 MUST be at least 8 characters. REQUIRED when --yes is set; otherwise the CLI prompts for it. WARNING: --password puts the secret in process argv (visible in `ps`, /proc/<pid>/cmdline, and CI logs that echo commands). Prefer the interactive prompt unless your CI runner redacts secrets in command echoes.
|
|
6990
|
+
"Owner password \u2014 MUST be at least 8 characters. REQUIRED when --yes is set; otherwise the CLI prompts for it. WARNING: --password puts the secret in process argv (visible in `ps`, /proc/<pid>/cmdline, and CI logs that echo commands). Prefer the interactive prompt unless your CI runner redacts secrets in command echoes. HEYARP_PASSWORD env var support is tracked."
|
|
7050
6991
|
).option(
|
|
7051
6992
|
"--yes",
|
|
7052
6993
|
"Strict non-interactive: fail if any required field is still missing after merging flags. With --yes, --password must be supplied explicitly (>= 8 chars).",
|
|
@@ -7057,7 +6998,6 @@ function registerRegisterCommand(root) {
|
|
|
7057
6998
|
// success prints. Output shape:
|
|
7058
6999
|
// {did, settlementPublicKeyB58, identityPublicKeyB58,
|
|
7059
7000
|
// localStatePath, didDocument}
|
|
7060
|
-
// (V1-alpha: `accountId` parked from the output.)
|
|
7061
7001
|
// REQUIRES --yes (interactive prompts would corrupt the
|
|
7062
7002
|
// single-doc contract).
|
|
7063
7003
|
"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).",
|
|
@@ -7083,41 +7023,39 @@ async function runRegister(opts, deps = defaultRegisterDeps) {
|
|
|
7083
7023
|
warnIfOrphanHomesPresent();
|
|
7084
7024
|
}
|
|
7085
7025
|
const keys = opts.fromKeys ? loadKeysFromFile(opts.fromKeys) : freshKeys();
|
|
7086
|
-
const did = (0,
|
|
7026
|
+
const did = (0, import_sdk22.formatDid)(keys.identityPublicKey);
|
|
7087
7027
|
if (!opts.json) console.log(import_chalk25.default.dim(`DID will be: ${did}`));
|
|
7088
7028
|
const answers = await mergeAnswers(opts);
|
|
7089
7029
|
const scryptSalt = (0, import_node_crypto4.randomBytes)(16);
|
|
7090
7030
|
if (!opts.json) console.log(import_chalk25.default.dim("Deriving scrypt key, this may take a moment..."));
|
|
7091
|
-
const scryptKey = (0,
|
|
7031
|
+
const scryptKey = (0, import_sdk22.deriveScryptKey)(answers.password, new Uint8Array(scryptSalt));
|
|
7092
7032
|
const challenge = await api.issueChallenge("register");
|
|
7093
7033
|
const challengeBytes = base64UrlNoPadDecode(challenge.challengeB64);
|
|
7094
7034
|
if (challengeBytes.length !== 32) {
|
|
7095
7035
|
throw new Error(`Server returned a ${challengeBytes.length}-byte challenge; expected 32`);
|
|
7096
7036
|
}
|
|
7097
|
-
const challengeSig = (0,
|
|
7098
|
-
const identityPublicKeyB58 = (0,
|
|
7037
|
+
const challengeSig = (0, import_sdk22.signChallenge)(challengeBytes, keys.identitySecretKey);
|
|
7038
|
+
const identityPublicKeyB58 = (0, import_sdk22.base58btcEncode)(keys.identityPublicKey);
|
|
7099
7039
|
await api.submitChallengeResponse({
|
|
7100
7040
|
challengeId: challenge.challengeId,
|
|
7101
7041
|
identityPublicKey: identityPublicKeyB58,
|
|
7102
7042
|
signature: Buffer.from(challengeSig).toString("base64")
|
|
7103
7043
|
});
|
|
7104
|
-
const settlementPublicKeyB58 = (0,
|
|
7105
|
-
const scryptSaltId = (0,
|
|
7044
|
+
const settlementPublicKeyB58 = (0, import_sdk22.base58btcEncode)(keys.settlementPublicKey);
|
|
7045
|
+
const scryptSaltId = (0, import_sdk22.uuidV4)();
|
|
7106
7046
|
const payload = {
|
|
7107
|
-
purpose:
|
|
7047
|
+
purpose: import_sdk22.Purpose.KEY_LINK,
|
|
7108
7048
|
agent_did: did,
|
|
7109
7049
|
identity_public_key: identityPublicKeyB58,
|
|
7110
7050
|
settlement_public_key: settlementPublicKeyB58,
|
|
7111
|
-
owner_signing_method:
|
|
7051
|
+
owner_signing_method: import_sdk22.OWNER_SIGNING_METHODS[0],
|
|
7112
7052
|
link_method: "manual",
|
|
7113
|
-
created_at: (0,
|
|
7114
|
-
nonce: (0,
|
|
7053
|
+
created_at: (0, import_sdk22.rfc3339)(),
|
|
7054
|
+
nonce: (0, import_sdk22.senderNonce)()
|
|
7115
7055
|
};
|
|
7116
|
-
const attestation = (0,
|
|
7056
|
+
const attestation = (0, import_sdk22.signKeyLinkAttestation)({ payload, scryptKey, scryptSaltId });
|
|
7117
7057
|
const body = {
|
|
7118
7058
|
challengeId: challenge.challengeId,
|
|
7119
|
-
// V1-alpha: accountId parked — server's DTO no longer accepts it.
|
|
7120
|
-
// accountId: answers.accountId,
|
|
7121
7059
|
identityPublicKey: identityPublicKeyB58,
|
|
7122
7060
|
settlementPublicKey: settlementPublicKeyB58,
|
|
7123
7061
|
ownerAttestation: {
|
|
@@ -7158,7 +7096,7 @@ async function runRegister(opts, deps = defaultRegisterDeps) {
|
|
|
7158
7096
|
try {
|
|
7159
7097
|
result = await api.register(body, credential.token);
|
|
7160
7098
|
} catch (err) {
|
|
7161
|
-
if (err instanceof ApiError && (err.payload.code ===
|
|
7099
|
+
if (err instanceof ApiError && (err.payload.code === import_sdk22.CliAuthTokenErrorCodes.INVALID || err.payload.code === import_sdk22.CliAuthTokenErrorCodes.REQUIRED)) {
|
|
7162
7100
|
throw new Error(
|
|
7163
7101
|
`register: the stored login token for ${api.serverUrl} was rejected (${err.payload.message}) \u2014 run 'heyarp login' again. Your keys are saved locally; re-running register with --from-keys is safe.`
|
|
7164
7102
|
);
|
|
@@ -7210,20 +7148,11 @@ Local state saved to ${arpHomeDir()}/agents.json (mode 0600).`));
|
|
|
7210
7148
|
}
|
|
7211
7149
|
}
|
|
7212
7150
|
async function mergeAnswers(opts) {
|
|
7213
|
-
const need = opts.yes ? {
|
|
7214
|
-
password: false,
|
|
7215
|
-
name: false,
|
|
7216
|
-
description: false,
|
|
7217
|
-
tagsCsv: false
|
|
7218
|
-
/* accountId parked V1-alpha */
|
|
7219
|
-
} : {
|
|
7151
|
+
const need = opts.yes ? { password: false, name: false, description: false, tagsCsv: false } : {
|
|
7220
7152
|
password: opts.password === void 0,
|
|
7221
7153
|
name: opts.name === void 0,
|
|
7222
7154
|
description: opts.description === void 0,
|
|
7223
7155
|
tagsCsv: opts.tag === void 0 || opts.tag.length === 0
|
|
7224
|
-
// V1-alpha: accountId parked. Uncomment + restore the prompt
|
|
7225
|
-
// below when launchpad↔ARP join lands.
|
|
7226
|
-
// accountId: opts.accountId === undefined,
|
|
7227
7156
|
};
|
|
7228
7157
|
if (opts.yes) {
|
|
7229
7158
|
const missing = [];
|
|
@@ -7275,7 +7204,6 @@ async function mergeAnswers(opts) {
|
|
|
7275
7204
|
password: opts.password ?? prompted.password,
|
|
7276
7205
|
name: opts.name ?? prompted.name,
|
|
7277
7206
|
description: opts.description ?? prompted.description ?? "",
|
|
7278
|
-
// accountId, // V1-alpha parked — see RegisterAnswers above.
|
|
7279
7207
|
tags
|
|
7280
7208
|
};
|
|
7281
7209
|
}
|
|
@@ -7311,7 +7239,7 @@ function warnIfAgentsAlreadyRegistered(serverOverride) {
|
|
|
7311
7239
|
const existing = listAgents().filter((row) => row.serverUrl === targetServer);
|
|
7312
7240
|
if (existing.length === 0) return;
|
|
7313
7241
|
const list = existing.map((row) => ` \u2022 ${import_chalk25.default.cyan(row.agent.did)}${row.agent.name ? import_chalk25.default.dim(` (${row.agent.name})`) : ""}`).join("\n");
|
|
7314
|
-
console.log(import_chalk25.default.yellow("\n\u26A0 ~/.
|
|
7242
|
+
console.log(import_chalk25.default.yellow("\n\u26A0 ~/.heyarp/agents.json already has agent(s) for this server:"));
|
|
7315
7243
|
console.log(list);
|
|
7316
7244
|
console.log(
|
|
7317
7245
|
import_chalk25.default.dim(
|
|
@@ -7324,8 +7252,8 @@ function parseTagsCsv(raw) {
|
|
|
7324
7252
|
return raw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
7325
7253
|
}
|
|
7326
7254
|
function freshKeys() {
|
|
7327
|
-
const identity = (0,
|
|
7328
|
-
const settlement = (0,
|
|
7255
|
+
const identity = (0, import_sdk22.generateKeyPair)();
|
|
7256
|
+
const settlement = (0, import_sdk22.generateKeyPair)();
|
|
7329
7257
|
return {
|
|
7330
7258
|
identityPublicKey: identity.publicKey,
|
|
7331
7259
|
identitySecretKey: identity.secretKey,
|
|
@@ -7351,9 +7279,9 @@ function loadKeysFromFile(path) {
|
|
|
7351
7279
|
if (identitySecret.length !== 32) throw new Error("--from-keys: identitySecretKeyB64 is not a 32-byte Ed25519 seed");
|
|
7352
7280
|
if (settlementSecret.length !== 32) throw new Error("--from-keys: settlementSecretKeyB64 is not a 32-byte Ed25519 seed");
|
|
7353
7281
|
return {
|
|
7354
|
-
identityPublicKey: (0,
|
|
7282
|
+
identityPublicKey: (0, import_sdk22.getPublicKey)(identitySecret),
|
|
7355
7283
|
identitySecretKey: identitySecret,
|
|
7356
|
-
settlementPublicKey: (0,
|
|
7284
|
+
settlementPublicKey: (0, import_sdk22.getPublicKey)(settlementSecret),
|
|
7357
7285
|
settlementSecretKey: settlementSecret
|
|
7358
7286
|
};
|
|
7359
7287
|
}
|
|
@@ -7369,19 +7297,29 @@ function assertJsonRequiresYes(opts) {
|
|
|
7369
7297
|
}
|
|
7370
7298
|
|
|
7371
7299
|
// src/commands/relationships.ts
|
|
7300
|
+
var import_sdk23 = require("@heyanon-arp/sdk");
|
|
7372
7301
|
var import_chalk26 = __toESM(require("chalk"));
|
|
7373
7302
|
init_api();
|
|
7374
|
-
var
|
|
7303
|
+
var ALLOWED_STATES2 = new Set(import_sdk23.RELATIONSHIP_STATE_NAMES);
|
|
7375
7304
|
function registerRelationshipsCommand(root) {
|
|
7376
7305
|
root.command("relationships").description(
|
|
7377
7306
|
"List relationships <did> belongs to (live + closed pairs, ordered by lastEventAt desc). DID can be passed positionally OR via --from-did; if both are omitted the resolver picks the sole local agent for the server (see `heyarp config set server` + multi-DID disambiguation rules)."
|
|
7378
|
-
).argument("[did]", "Agent DID (did:arp:...) \u2014 must have local state. Optional when --from-did is given OR exactly one agent is registered for the resolved server.").option("--server <url>", "Override ARP server base URL").option("--state <s>", "Filter by relationship state (pending|active|paused|closed)").option("--limit <n>", "Max relationships to return (1..100)", "20").option("--verbose", "Print the full JSON for each relationship in addition to the table
|
|
7307
|
+
).argument("[did]", "Agent DID (did:arp:...) \u2014 must have local state. Optional when --from-did is given OR exactly one agent is registered for the resolved server.").option("--server <url>", "Override ARP server base URL").option("--state <s>", "Filter by relationship state (pending|active|paused|closed)").option("--limit <n>", "Max relationships to return (1..100)", "20").option("--verbose", "Print the full JSON for each relationship in addition to the table. Mutually exclusive with --json.", false).option(
|
|
7308
|
+
"--json",
|
|
7309
|
+
"Machine-readable mode \u2014 emit the relationships as a single JSON array on stdout (RelationshipPublic[]). Prelude + table move to stderr. Mutually exclusive with --verbose.",
|
|
7310
|
+
false
|
|
7311
|
+
).option("--from-did <did>", "Alias for the positional <did> argument \u2014 accepted for consistency with other signed commands. Must match the positional if both are given.").action(async (did, opts) => {
|
|
7379
7312
|
await runRelationships(did, opts);
|
|
7380
7313
|
});
|
|
7381
7314
|
}
|
|
7382
7315
|
async function runRelationships(positionalDid, opts) {
|
|
7316
|
+
if (opts.verbose && opts.json) {
|
|
7317
|
+
throw new Error(
|
|
7318
|
+
"relationships: --verbose and --json are mutually exclusive. --json emits the rows as a clean array; --verbose adds per-row dumps that would break `--json | jq`."
|
|
7319
|
+
);
|
|
7320
|
+
}
|
|
7383
7321
|
const limit = parseLimit6(opts.limit);
|
|
7384
|
-
const state =
|
|
7322
|
+
const state = parseState2(opts.state);
|
|
7385
7323
|
if (positionalDid !== void 0 && opts.fromDid !== void 0 && positionalDid !== opts.fromDid) {
|
|
7386
7324
|
throw new Error(`relationships: positional <did> (${positionalDid}) and --from-did (${opts.fromDid}) disagree \u2014 pass only one`);
|
|
7387
7325
|
}
|
|
@@ -7389,12 +7327,16 @@ async function runRelationships(positionalDid, opts) {
|
|
|
7389
7327
|
const local = explicitDid !== void 0 ? loadAgentOrThrow(opts.server, explicitDid) : resolveSenderAgent("relationships", opts.server, void 0);
|
|
7390
7328
|
const did = local.did;
|
|
7391
7329
|
const api = new ArpApiClient(opts.server);
|
|
7392
|
-
|
|
7393
|
-
|
|
7330
|
+
progress(opts.json, import_chalk26.default.dim(`Server: ${api.serverUrl}`));
|
|
7331
|
+
progress(opts.json, import_chalk26.default.dim(`Signer: ${local.did}`));
|
|
7394
7332
|
const query = { limit };
|
|
7395
7333
|
if (state) query.state = state;
|
|
7396
7334
|
const signer = makeSigner(local);
|
|
7397
7335
|
const rows = await api.listRelationships(did, signer, query);
|
|
7336
|
+
if (opts.json) {
|
|
7337
|
+
jsonOut(rows);
|
|
7338
|
+
return;
|
|
7339
|
+
}
|
|
7398
7340
|
if (rows.length === 0) {
|
|
7399
7341
|
console.log(import_chalk26.default.dim("\n(no relationships)"));
|
|
7400
7342
|
return;
|
|
@@ -7422,9 +7364,9 @@ function otherPair(r, selfDid) {
|
|
|
7422
7364
|
if (r.pairDidB === selfDid) return r.pairDidA;
|
|
7423
7365
|
return `${r.pairDidA} \u2194 ${r.pairDidB}`;
|
|
7424
7366
|
}
|
|
7425
|
-
function
|
|
7367
|
+
function parseState2(raw) {
|
|
7426
7368
|
if (raw === void 0) return void 0;
|
|
7427
|
-
if (!
|
|
7369
|
+
if (!ALLOWED_STATES2.has(raw)) {
|
|
7428
7370
|
throw new Error(`relationships: --state must be one of pending|active|paused|closed (got '${raw}')`);
|
|
7429
7371
|
}
|
|
7430
7372
|
return raw;
|
|
@@ -7439,12 +7381,12 @@ function parseLimit6(raw) {
|
|
|
7439
7381
|
}
|
|
7440
7382
|
|
|
7441
7383
|
// src/commands/reputation.ts
|
|
7442
|
-
var
|
|
7384
|
+
var import_sdk24 = require("@heyanon-arp/sdk");
|
|
7443
7385
|
var import_chalk27 = __toESM(require("chalk"));
|
|
7444
7386
|
init_api();
|
|
7445
7387
|
function registerReputationCommand(root) {
|
|
7446
7388
|
root.command("reputation").description("Show an agent's informational reputation (composite + component scores + raw counters). Public, no auth. NOT a money/eligibility gate.").argument("<did>", "did:arp:<base58btc> identifier").option("--server <url>", "Override ARP server base URL").option("--json", "Emit the reputation as JSON on stdout.", false).action(async (did, opts) => {
|
|
7447
|
-
if (!(0,
|
|
7389
|
+
if (!(0, import_sdk24.isValidDid)(did)) {
|
|
7448
7390
|
throw new Error(`'${did}' is not a syntactically valid did:arp identifier`);
|
|
7449
7391
|
}
|
|
7450
7392
|
const api = new ArpApiClient(opts.server);
|
|
@@ -7486,44 +7428,53 @@ function bar(score) {
|
|
|
7486
7428
|
}
|
|
7487
7429
|
|
|
7488
7430
|
// src/commands/send-handshake.ts
|
|
7489
|
-
var
|
|
7431
|
+
var import_sdk25 = require("@heyanon-arp/sdk");
|
|
7490
7432
|
var import_chalk28 = __toESM(require("chalk"));
|
|
7491
7433
|
init_api();
|
|
7492
7434
|
function registerSendHandshakeCommand(root) {
|
|
7493
|
-
root.command("send-handshake").description("Send a handshake envelope to <recipient-did>. Server creates the relationship row on first contact.").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("--greeting <s>", "Optional greeting text included in body.content").option("--intent <s>", "Optional intent text included in body.content").option("--ttl <seconds>",
|
|
7435
|
+
root.command("send-handshake").description("Send a handshake envelope to <recipient-did>. Server creates the relationship row on first contact.").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("--greeting <s>", "Optional greeting text included in body.content").option("--intent <s>", "Optional intent text included in body.content").option("--ttl <seconds>", `Envelope TTL in seconds (max ${import_sdk25.MAX_ENVELOPE_TTL_SECONDS} = 24h)`, "3600").option("--verbose", "Print the full envelope before sending and the full server response. Mutually exclusive with --json.", false).option(
|
|
7436
|
+
"--json",
|
|
7437
|
+
"Machine-readable mode \u2014 emit a single JSON object on stdout ({ok, eventId, relationshipId, relationshipEventIndex, serverTimestamp, signedMessageHash, serverEventHash, prevServerEventHash, senderSequence}). The Server/Sender/Recipient prelude moves to stderr so `--json | jq` stays byte-pure. Mutually exclusive with --verbose.",
|
|
7438
|
+
false
|
|
7439
|
+
).action(async (recipientDid, opts) => {
|
|
7494
7440
|
await runSendHandshake(recipientDid, opts);
|
|
7495
7441
|
});
|
|
7496
7442
|
}
|
|
7497
7443
|
async function runSendHandshake(recipientDid, opts) {
|
|
7498
|
-
if (!
|
|
7499
|
-
throw new Error(`send-handshake: <recipient-did> must
|
|
7444
|
+
if (!(0, import_sdk25.isValidDid)(recipientDid)) {
|
|
7445
|
+
throw new Error(`send-handshake: <recipient-did> must be a valid did:arp identifier (base58btc-encoded 32-byte Ed25519 pubkey) (got '${recipientDid}')`);
|
|
7446
|
+
}
|
|
7447
|
+
if (opts.verbose && opts.json) {
|
|
7448
|
+
throw new Error(
|
|
7449
|
+
"send-handshake: --verbose and --json are mutually exclusive. --json emits the structured server response; --verbose adds envelope + response dumps that would break `--json | jq`."
|
|
7450
|
+
);
|
|
7500
7451
|
}
|
|
7501
7452
|
const ttlSeconds = parseTtl3(opts.ttl);
|
|
7502
7453
|
const api = new ArpApiClient(opts.server);
|
|
7503
|
-
|
|
7454
|
+
progress(opts.json, import_chalk28.default.dim(`Server: ${api.serverUrl}`));
|
|
7504
7455
|
const sender = resolveSenderAgent("send-handshake", opts.server, opts.fromDid);
|
|
7505
|
-
|
|
7506
|
-
|
|
7456
|
+
progress(opts.json, import_chalk28.default.dim(`Sender: ${sender.did}`));
|
|
7457
|
+
progress(opts.json, import_chalk28.default.dim(`Recipient: ${recipientDid}`));
|
|
7507
7458
|
const content = {};
|
|
7508
7459
|
if (opts.greeting) content.greeting = opts.greeting;
|
|
7509
7460
|
if (opts.intent) content.intent = opts.intent;
|
|
7510
7461
|
const body = { type: "handshake", content };
|
|
7511
7462
|
const nextSequence = (sender.lastSenderSequence ?? 0) + 1;
|
|
7512
7463
|
const protectedBlock = {
|
|
7513
|
-
protocol_version:
|
|
7514
|
-
purpose:
|
|
7515
|
-
message_id: (0,
|
|
7464
|
+
protocol_version: import_sdk25.CURRENT_PROTOCOL_VERSION,
|
|
7465
|
+
purpose: import_sdk25.Purpose.ENVELOPE,
|
|
7466
|
+
message_id: (0, import_sdk25.uuidV4)(),
|
|
7516
7467
|
sender_did: sender.did,
|
|
7517
7468
|
recipient_did: recipientDid,
|
|
7518
7469
|
relationship_id: null,
|
|
7519
7470
|
sender_sequence: nextSequence,
|
|
7520
|
-
sender_nonce: (0,
|
|
7521
|
-
timestamp: (0,
|
|
7522
|
-
expires_at: (0,
|
|
7471
|
+
sender_nonce: (0, import_sdk25.senderNonce)(),
|
|
7472
|
+
timestamp: (0, import_sdk25.rfc3339)(),
|
|
7473
|
+
expires_at: (0, import_sdk25.expiresAt)(ttlSeconds),
|
|
7523
7474
|
delivery_id: null
|
|
7524
7475
|
};
|
|
7525
7476
|
const signer = makeSigner(sender);
|
|
7526
|
-
const envelope = (0,
|
|
7477
|
+
const envelope = (0, import_sdk25.signEnvelope)({
|
|
7527
7478
|
protected: protectedBlock,
|
|
7528
7479
|
body,
|
|
7529
7480
|
identitySecretKey: signer.identitySecretKey
|
|
@@ -7534,6 +7485,21 @@ async function runSendHandshake(recipientDid, opts) {
|
|
|
7534
7485
|
}
|
|
7535
7486
|
const result = await api.ingest(envelope);
|
|
7536
7487
|
updateAgentLocal(opts.server, sender.did, { lastSenderSequence: nextSequence });
|
|
7488
|
+
if (opts.json) {
|
|
7489
|
+
jsonOut({
|
|
7490
|
+
ok: true,
|
|
7491
|
+
action: "handshake",
|
|
7492
|
+
eventId: result.eventId,
|
|
7493
|
+
relationshipId: result.relationshipId,
|
|
7494
|
+
relationshipEventIndex: result.relationshipEventIndex,
|
|
7495
|
+
serverTimestamp: result.serverTimestamp,
|
|
7496
|
+
signedMessageHash: result.signedMessageHash,
|
|
7497
|
+
serverEventHash: result.serverEventHash,
|
|
7498
|
+
prevServerEventHash: result.prevServerEventHash ?? null,
|
|
7499
|
+
senderSequence: nextSequence
|
|
7500
|
+
});
|
|
7501
|
+
return;
|
|
7502
|
+
}
|
|
7537
7503
|
console.log(import_chalk28.default.green("\nDelivered."));
|
|
7538
7504
|
console.log(`${import_chalk28.default.bold("Event id")}: ${import_chalk28.default.cyan(result.eventId)}`);
|
|
7539
7505
|
console.log(`${import_chalk28.default.bold("Relationship id")}: ${import_chalk28.default.cyan(result.relationshipId)}`);
|
|
@@ -7561,15 +7527,12 @@ function parseTtl3(raw) {
|
|
|
7561
7527
|
}
|
|
7562
7528
|
return n;
|
|
7563
7529
|
}
|
|
7564
|
-
function isDid(s) {
|
|
7565
|
-
return typeof s === "string" && s.startsWith("did:arp:") && s.length > "did:arp:".length;
|
|
7566
|
-
}
|
|
7567
7530
|
|
|
7568
7531
|
// src/commands/send-handshake-response.ts
|
|
7569
|
-
var
|
|
7532
|
+
var import_sdk26 = require("@heyanon-arp/sdk");
|
|
7570
7533
|
var import_chalk29 = __toESM(require("chalk"));
|
|
7571
7534
|
init_api();
|
|
7572
|
-
var ALLOWED_DECISIONS =
|
|
7535
|
+
var ALLOWED_DECISIONS = new Set(import_sdk26.HANDSHAKE_DECISIONS);
|
|
7573
7536
|
function registerSendHandshakeResponseCommand(root) {
|
|
7574
7537
|
root.command("send-handshake-response").description("Accept or decline an inbound handshake from <recipient-did>. Server reuses the existing relationship row.").argument("<recipient-did>", "Recipient agent DID (did:arp:...) \u2014 the original handshake sender").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("--decision <s>", "REQUIRED: accept or decline").option("--notes <s>", "Optional notes included in body.content").option(
|
|
7575
7538
|
"--reason <code>",
|
|
@@ -7577,8 +7540,8 @@ function registerSendHandshakeResponseCommand(root) {
|
|
|
7577
7540
|
// We don't use commander's requiredOption because it
|
|
7578
7541
|
// would fire for the accept path too; validate manually
|
|
7579
7542
|
// after decision is parsed.
|
|
7580
|
-
`When --decision=decline: required reason code (one of: ${
|
|
7581
|
-
).option("--reason-detail <s>", "Optional free-text elaboration alongside --reason (max 512 chars).").option("--ttl <seconds>",
|
|
7543
|
+
`When --decision=decline: required reason code (one of: ${import_sdk26.DECLINE_REASONS.join(", ")}). Carried in body.content.reason.`
|
|
7544
|
+
).option("--reason-detail <s>", "Optional free-text elaboration alongside --reason (max 512 chars).").option("--ttl <seconds>", `Envelope TTL in seconds (max ${import_sdk26.MAX_ENVELOPE_TTL_SECONDS} = 24h)`, "3600").option("--verbose", "Print the full envelope before sending and the full server response. Mutually exclusive with --json.", false).option(
|
|
7582
7545
|
"--json",
|
|
7583
7546
|
"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.",
|
|
7584
7547
|
false
|
|
@@ -7596,13 +7559,13 @@ async function runSendHandshakeResponse(recipientDid, opts) {
|
|
|
7596
7559
|
"send-handshake-response: --verbose and --json are mutually exclusive. --json emits the structured server response; --verbose adds envelope + response dumps that would break `--json | jq`."
|
|
7597
7560
|
);
|
|
7598
7561
|
}
|
|
7599
|
-
if (!
|
|
7600
|
-
throw new Error(`send-handshake-response: <recipient-did> must
|
|
7562
|
+
if (!(0, import_sdk26.isValidDid)(recipientDid)) {
|
|
7563
|
+
throw new Error(`send-handshake-response: <recipient-did> must be a valid did:arp identifier (base58btc-encoded 32-byte Ed25519 pubkey) (got '${recipientDid}')`);
|
|
7601
7564
|
}
|
|
7602
7565
|
const decision = parseDecision(opts.decision);
|
|
7603
7566
|
const ttlSeconds = parseTtl4(opts.ttl);
|
|
7604
7567
|
let declinePayload = null;
|
|
7605
|
-
if (decision ===
|
|
7568
|
+
if (decision === import_sdk26.HandshakeDecisions.DECLINE) {
|
|
7606
7569
|
const reason = parseDeclineReason("send-handshake-response", opts.reason);
|
|
7607
7570
|
const detail = parseReasonDetail("send-handshake-response", opts.reasonDetail);
|
|
7608
7571
|
declinePayload = detail ? { reason, reasonDetail: detail } : { reason };
|
|
@@ -7628,6 +7591,7 @@ async function runSendHandshakeResponse(recipientDid, opts) {
|
|
|
7628
7591
|
if (opts.json) {
|
|
7629
7592
|
jsonOut({
|
|
7630
7593
|
ok: true,
|
|
7594
|
+
action: "handshake_response",
|
|
7631
7595
|
idempotent: true,
|
|
7632
7596
|
decision,
|
|
7633
7597
|
eventId: previousResponseFromMe.eventId,
|
|
@@ -7670,19 +7634,19 @@ async function runSendHandshakeResponse(recipientDid, opts) {
|
|
|
7670
7634
|
const body = { type: "handshake_response", content };
|
|
7671
7635
|
const nextSequence = (sender.lastSenderSequence ?? 0) + 1;
|
|
7672
7636
|
const protectedBlock = {
|
|
7673
|
-
protocol_version:
|
|
7674
|
-
purpose:
|
|
7675
|
-
message_id: (0,
|
|
7637
|
+
protocol_version: import_sdk26.CURRENT_PROTOCOL_VERSION,
|
|
7638
|
+
purpose: import_sdk26.Purpose.ENVELOPE,
|
|
7639
|
+
message_id: (0, import_sdk26.uuidV4)(),
|
|
7676
7640
|
sender_did: sender.did,
|
|
7677
7641
|
recipient_did: recipientDid,
|
|
7678
7642
|
relationship_id: null,
|
|
7679
7643
|
sender_sequence: nextSequence,
|
|
7680
|
-
sender_nonce: (0,
|
|
7681
|
-
timestamp: (0,
|
|
7682
|
-
expires_at: (0,
|
|
7644
|
+
sender_nonce: (0, import_sdk26.senderNonce)(),
|
|
7645
|
+
timestamp: (0, import_sdk26.rfc3339)(),
|
|
7646
|
+
expires_at: (0, import_sdk26.expiresAt)(ttlSeconds),
|
|
7683
7647
|
delivery_id: null
|
|
7684
7648
|
};
|
|
7685
|
-
const envelope = (0,
|
|
7649
|
+
const envelope = (0, import_sdk26.signEnvelope)({
|
|
7686
7650
|
protected: protectedBlock,
|
|
7687
7651
|
body,
|
|
7688
7652
|
identitySecretKey: signer.identitySecretKey
|
|
@@ -7696,6 +7660,7 @@ async function runSendHandshakeResponse(recipientDid, opts) {
|
|
|
7696
7660
|
if (opts.json) {
|
|
7697
7661
|
jsonOut({
|
|
7698
7662
|
ok: true,
|
|
7663
|
+
action: "handshake_response",
|
|
7699
7664
|
decision,
|
|
7700
7665
|
eventId: result.eventId,
|
|
7701
7666
|
relationshipId: result.relationshipId,
|
|
@@ -7744,13 +7709,10 @@ function parseTtl4(raw) {
|
|
|
7744
7709
|
}
|
|
7745
7710
|
return n;
|
|
7746
7711
|
}
|
|
7747
|
-
function isDid2(s) {
|
|
7748
|
-
return typeof s === "string" && s.startsWith("did:arp:") && s.length > "did:arp:".length;
|
|
7749
|
-
}
|
|
7750
7712
|
function classifyIdempotencyOutcome(decision, existing) {
|
|
7751
7713
|
if (!existing) return { kind: "proceed" };
|
|
7752
|
-
if (existing.state ===
|
|
7753
|
-
if (decision ===
|
|
7714
|
+
if (existing.state === import_sdk26.RelationshipStates.ACTIVE) {
|
|
7715
|
+
if (decision === import_sdk26.HandshakeDecisions.DECLINE) {
|
|
7754
7716
|
return {
|
|
7755
7717
|
kind: "error",
|
|
7756
7718
|
message: `send-handshake-response: relationship ${existing.relationshipId} is already 'active' from a previous ACCEPT. A decline cannot reverse that \u2014 the active relationship is now under FSM control. If you want to terminate, close the relationship explicitly; pass --force to ignore this check and send the decline (which the server will reject with DOM_INVALID_TRANSITION).`
|
|
@@ -7758,7 +7720,7 @@ function classifyIdempotencyOutcome(decision, existing) {
|
|
|
7758
7720
|
}
|
|
7759
7721
|
return { kind: "short-circuit" };
|
|
7760
7722
|
}
|
|
7761
|
-
if (existing.state ===
|
|
7723
|
+
if (existing.state === import_sdk26.RelationshipStates.CLOSED) {
|
|
7762
7724
|
return {
|
|
7763
7725
|
kind: "error",
|
|
7764
7726
|
message: `send-handshake-response: relationship ${existing.relationshipId} is CLOSED. Cannot respond to handshake on a terminated relationship \u2014 start a fresh handshake instead.`
|
|
@@ -7883,8 +7845,7 @@ function registerWhoamiCommand(root) {
|
|
|
7883
7845
|
// `--payer-settlement-pubkey` / on-chain ops without a
|
|
7884
7846
|
// server round-trip. `--local` short-circuits the signed
|
|
7885
7847
|
// GET, prints just the resolved DID + pubkeys from local
|
|
7886
|
-
// state.
|
|
7887
|
-
// output.)
|
|
7848
|
+
// state.
|
|
7888
7849
|
"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.",
|
|
7889
7850
|
false
|
|
7890
7851
|
).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) => {
|
|
@@ -7897,10 +7858,6 @@ function registerWhoamiCommand(root) {
|
|
|
7897
7858
|
did: local.did,
|
|
7898
7859
|
settlementPublicKeyB58: local.settlementPublicKeyB58,
|
|
7899
7860
|
identityPublicKeyB58: local.identityPublicKeyB58,
|
|
7900
|
-
// V1-alpha: accountId parked — local-state type no
|
|
7901
|
-
// longer carries the field. Uncomment when launchpad↔ARP
|
|
7902
|
-
// join lands.
|
|
7903
|
-
// accountId: local.accountId,
|
|
7904
7861
|
name: local.name
|
|
7905
7862
|
};
|
|
7906
7863
|
if (opts.local) {
|
|
@@ -7953,7 +7910,7 @@ function registerWhoamiCommand(root) {
|
|
|
7953
7910
|
}
|
|
7954
7911
|
|
|
7955
7912
|
// src/commands/work.ts
|
|
7956
|
-
var
|
|
7913
|
+
var import_sdk27 = require("@heyanon-arp/sdk");
|
|
7957
7914
|
var import_chalk32 = __toESM(require("chalk"));
|
|
7958
7915
|
init_api();
|
|
7959
7916
|
function registerWorkCommands(root) {
|
|
@@ -7961,25 +7918,6 @@ function registerWorkCommands(root) {
|
|
|
7961
7918
|
registerRequest(cmd);
|
|
7962
7919
|
registerRespond(cmd);
|
|
7963
7920
|
}
|
|
7964
|
-
var POST_COMMIT_ERROR_CODES3 = /* @__PURE__ */ new Set([
|
|
7965
|
-
"WORK_DELEGATION_NOT_FOUND",
|
|
7966
|
-
"WORK_DELEGATION_NOT_ACTIVE",
|
|
7967
|
-
"WORK_RELATIONSHIP_MISMATCH",
|
|
7968
|
-
"WORK_REQUESTER_NOT_OFFERER",
|
|
7969
|
-
"WORK_REQUEST_ALREADY_EXISTS",
|
|
7970
|
-
"WORK_REQUEST_NOT_FOUND",
|
|
7971
|
-
"WORK_RESPONDER_IS_CALLER",
|
|
7972
|
-
"WORK_INVALID_STATE",
|
|
7973
|
-
// M6 lock-at-accept: the work handler's LOCKED gate emits
|
|
7974
|
-
// `DELEGATION_PENDING_LOCK` (409) when the delegation is funded but
|
|
7975
|
-
// the on-chain lock isn't confirmed yet (state
|
|
7976
|
-
// `pending_lock_finalization`). It fires from the body handler AFTER
|
|
7977
|
-
// the event row is committed — same lifecycle as
|
|
7978
|
-
// `WORK_DELEGATION_NOT_ACTIVE` — so the CLI must advance
|
|
7979
|
-
// `lastSenderSequence`, otherwise a retry once the lock confirms
|
|
7980
|
-
// reuses the consumed sequence and trips `ENV_SEQUENCE_BACKWARDS`.
|
|
7981
|
-
"DELEGATION_PENDING_LOCK"
|
|
7982
|
-
]);
|
|
7983
7921
|
function registerRequest(parent) {
|
|
7984
7922
|
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(
|
|
7985
7923
|
"--params <json>",
|
|
@@ -7987,11 +7925,20 @@ function registerRequest(parent) {
|
|
|
7987
7925
|
).option(
|
|
7988
7926
|
"--params-file <path>",
|
|
7989
7927
|
"Read the JSON payload from a file. Mutually exclusive with --params. Use this for any payload large or quote-heavy enough that `--params '{...}'` would fight your shell."
|
|
7990
|
-
).option("--ttl <seconds>",
|
|
7928
|
+
).option("--ttl <seconds>", `Envelope TTL in seconds (max ${import_sdk27.MAX_ENVELOPE_TTL_SECONDS} = 24h)`, "3600").option("--verbose", "Print the full envelope before sending and the full server response. Mutually exclusive with --json.", false).option(
|
|
7929
|
+
"--json",
|
|
7930
|
+
'Machine-readable mode \u2014 emit a single JSON object on stdout ({ok, action:"work_request", requestId, delegationId, eventId, relationshipId, relationshipEventIndex, serverTimestamp, serverEventHash}). Prelude + reply hints move to stderr; on failure stderr carries `{code, message}`. Mutually exclusive with --verbose.',
|
|
7931
|
+
false
|
|
7932
|
+
).action(async (recipientDid, delegationId, opts) => {
|
|
7991
7933
|
await runRequest(recipientDid, delegationId, opts);
|
|
7992
7934
|
});
|
|
7993
7935
|
}
|
|
7994
7936
|
async function runRequest(recipientDid, delegationId, opts) {
|
|
7937
|
+
if (opts.verbose && opts.json) {
|
|
7938
|
+
throw new Error(
|
|
7939
|
+
"work request: --verbose and --json are mutually exclusive. --json emits the structured server response; --verbose adds dumps that would break `--json | jq`."
|
|
7940
|
+
);
|
|
7941
|
+
}
|
|
7995
7942
|
requireDid3("work request", recipientDid, "<recipient-did>");
|
|
7996
7943
|
delegationId = requireUuidNormalised3("work request", delegationId, "<delegation-id>");
|
|
7997
7944
|
const ttlSeconds = parseTtl5("work request", opts.ttl);
|
|
@@ -8005,27 +7952,51 @@ async function runRequest(recipientDid, delegationId, opts) {
|
|
|
8005
7952
|
params
|
|
8006
7953
|
};
|
|
8007
7954
|
const body = { type: "work_request", content };
|
|
8008
|
-
|
|
8009
|
-
|
|
8010
|
-
|
|
8011
|
-
|
|
8012
|
-
|
|
7955
|
+
progress(opts.json, import_chalk32.default.dim(`Server: ${api.serverUrl}`));
|
|
7956
|
+
progress(opts.json, import_chalk32.default.dim(`Sender: ${sender.did}`));
|
|
7957
|
+
progress(opts.json, import_chalk32.default.dim(`Recipient: ${recipientDid}`));
|
|
7958
|
+
progress(opts.json, import_chalk32.default.dim(`Delegation: ${delegationId}`));
|
|
7959
|
+
progress(opts.json, import_chalk32.default.dim(`Request id: ${requestId}`));
|
|
8013
7960
|
const result = await sendWorkEnvelope({ api, sender, recipientDid, body, ttlSeconds, verbose: opts.verbose, server: opts.server });
|
|
8014
|
-
|
|
8015
|
-
|
|
7961
|
+
if (opts.json) {
|
|
7962
|
+
jsonOut({
|
|
7963
|
+
ok: true,
|
|
7964
|
+
action: "work_request",
|
|
7965
|
+
requestId,
|
|
7966
|
+
delegationId,
|
|
7967
|
+
eventId: result.eventId,
|
|
7968
|
+
relationshipId: result.relationshipId,
|
|
7969
|
+
relationshipEventIndex: result.relationshipEventIndex,
|
|
7970
|
+
serverTimestamp: result.serverTimestamp,
|
|
7971
|
+
serverEventHash: result.serverEventHash,
|
|
7972
|
+
prevServerEventHash: result.prevServerEventHash ?? null
|
|
7973
|
+
});
|
|
7974
|
+
} else {
|
|
7975
|
+
printIngestResult3(result);
|
|
7976
|
+
console.log(import_chalk32.default.dim(`
|
|
8016
7977
|
The payee can reply with:`));
|
|
8017
|
-
|
|
8018
|
-
|
|
7978
|
+
console.log(import_chalk32.default.dim(` heyarp work respond ${result.relationshipId} ${delegationId} ${requestId} --output '<json>'`));
|
|
7979
|
+
console.log(import_chalk32.default.dim(` heyarp work respond ${result.relationshipId} ${delegationId} ${requestId} --error CODE:message`));
|
|
7980
|
+
}
|
|
8019
7981
|
}
|
|
8020
7982
|
function registerRespond(parent) {
|
|
8021
7983
|
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(
|
|
8022
7984
|
"--output-file <path>",
|
|
8023
7985
|
"Read the success payload from a file. Mutually exclusive with --output and --error. Use this for any payload large or quote-heavy enough to fight your shell."
|
|
8024
|
-
).option("--error <code:message>", "Failure payload as `CODE:message`. Mutually exclusive with --output / --output-file.").option("--ttl <seconds>", "Envelope TTL in seconds", "3600").option("--verbose", "Print the full envelope before sending and the full server response", false).
|
|
7986
|
+
).option("--error <code:message>", "Failure payload as `CODE:message`. Mutually exclusive with --output / --output-file.").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(
|
|
7987
|
+
"--json",
|
|
7988
|
+
'Machine-readable mode \u2014 emit a single JSON object on stdout ({ok, action:"work_response", requestId, delegationId, eventId, relationshipId, relationshipEventIndex, serverTimestamp, serverEventHash}). Prelude moves to stderr; on failure stderr carries `{code, message}`. Mutually exclusive with --verbose.',
|
|
7989
|
+
false
|
|
7990
|
+
).action(async (relationshipId, delegationId, requestId, opts) => {
|
|
8025
7991
|
await runRespond(relationshipId, delegationId, requestId, opts);
|
|
8026
7992
|
});
|
|
8027
7993
|
}
|
|
8028
7994
|
async function runRespond(relationshipId, delegationId, requestId, opts) {
|
|
7995
|
+
if (opts.verbose && opts.json) {
|
|
7996
|
+
throw new Error(
|
|
7997
|
+
"work respond: --verbose and --json are mutually exclusive. --json emits the structured server response; --verbose adds dumps that would break `--json | jq`."
|
|
7998
|
+
);
|
|
7999
|
+
}
|
|
8029
8000
|
relationshipId = requireUuidNormalised3("work respond", relationshipId, "<relationship-id>");
|
|
8030
8001
|
delegationId = requireUuidNormalised3("work respond", delegationId, "<delegation-id>");
|
|
8031
8002
|
const ttlSeconds = parseTtl5("work respond", opts.ttl);
|
|
@@ -8040,33 +8011,48 @@ async function runRespond(relationshipId, delegationId, requestId, opts) {
|
|
|
8040
8011
|
...responsePayload
|
|
8041
8012
|
};
|
|
8042
8013
|
const body = { type: "work_response", content };
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
|
|
8046
|
-
|
|
8047
|
-
|
|
8048
|
-
|
|
8049
|
-
|
|
8014
|
+
progress(opts.json, import_chalk32.default.dim(`Server: ${api.serverUrl}`));
|
|
8015
|
+
progress(opts.json, import_chalk32.default.dim(`Sender: ${sender.did}`));
|
|
8016
|
+
progress(opts.json, import_chalk32.default.dim(`Recipient: ${recipientDid}`));
|
|
8017
|
+
progress(opts.json, import_chalk32.default.dim(`Relationship: ${relationshipId}`));
|
|
8018
|
+
progress(opts.json, import_chalk32.default.dim(`Delegation: ${delegationId}`));
|
|
8019
|
+
progress(opts.json, import_chalk32.default.dim(`Request id: ${requestId}`));
|
|
8020
|
+
progress(opts.json, import_chalk32.default.dim(`Outcome: ${responsePayload.output ? "success" : "error"}`));
|
|
8050
8021
|
const result = await sendWorkEnvelope({ api, sender, recipientDid, body, ttlSeconds, verbose: opts.verbose, server: opts.server });
|
|
8051
|
-
|
|
8022
|
+
if (opts.json) {
|
|
8023
|
+
jsonOut({
|
|
8024
|
+
ok: true,
|
|
8025
|
+
action: "work_response",
|
|
8026
|
+
requestId,
|
|
8027
|
+
delegationId,
|
|
8028
|
+
eventId: result.eventId,
|
|
8029
|
+
relationshipId: result.relationshipId,
|
|
8030
|
+
relationshipEventIndex: result.relationshipEventIndex,
|
|
8031
|
+
serverTimestamp: result.serverTimestamp,
|
|
8032
|
+
serverEventHash: result.serverEventHash,
|
|
8033
|
+
prevServerEventHash: result.prevServerEventHash ?? null
|
|
8034
|
+
});
|
|
8035
|
+
} else {
|
|
8036
|
+
printIngestResult3(result);
|
|
8037
|
+
}
|
|
8052
8038
|
}
|
|
8053
8039
|
async function sendWorkEnvelope(args) {
|
|
8054
8040
|
const nextSequence = (args.sender.lastSenderSequence ?? 0) + 1;
|
|
8055
8041
|
const protectedBlock = {
|
|
8056
|
-
protocol_version:
|
|
8057
|
-
purpose:
|
|
8058
|
-
message_id: (0,
|
|
8042
|
+
protocol_version: import_sdk27.CURRENT_PROTOCOL_VERSION,
|
|
8043
|
+
purpose: import_sdk27.Purpose.ENVELOPE,
|
|
8044
|
+
message_id: (0, import_sdk27.uuidV4)(),
|
|
8059
8045
|
sender_did: args.sender.did,
|
|
8060
8046
|
recipient_did: args.recipientDid,
|
|
8061
8047
|
relationship_id: null,
|
|
8062
8048
|
sender_sequence: nextSequence,
|
|
8063
|
-
sender_nonce: (0,
|
|
8064
|
-
timestamp: (0,
|
|
8065
|
-
expires_at: (0,
|
|
8049
|
+
sender_nonce: (0, import_sdk27.senderNonce)(),
|
|
8050
|
+
timestamp: (0, import_sdk27.rfc3339)(),
|
|
8051
|
+
expires_at: (0, import_sdk27.expiresAt)(args.ttlSeconds),
|
|
8066
8052
|
delivery_id: null
|
|
8067
8053
|
};
|
|
8068
8054
|
const signer = makeSigner(args.sender);
|
|
8069
|
-
const envelope = (0,
|
|
8055
|
+
const envelope = (0, import_sdk27.signEnvelope)({
|
|
8070
8056
|
protected: protectedBlock,
|
|
8071
8057
|
body: args.body,
|
|
8072
8058
|
identitySecretKey: signer.identitySecretKey
|
|
@@ -8080,7 +8066,7 @@ async function sendWorkEnvelope(args) {
|
|
|
8080
8066
|
updateAgentLocal(args.server, args.sender.did, { lastSenderSequence: nextSequence });
|
|
8081
8067
|
return result;
|
|
8082
8068
|
} catch (err) {
|
|
8083
|
-
if (err instanceof ApiError &&
|
|
8069
|
+
if (err instanceof ApiError && (0, import_sdk27.isPostCommitErrorCode)(err.payload.code)) {
|
|
8084
8070
|
updateAgentLocal(args.server, args.sender.did, { lastSenderSequence: nextSequence });
|
|
8085
8071
|
}
|
|
8086
8072
|
throw err;
|
|
@@ -8092,7 +8078,7 @@ async function resolveResponseRecipient(cmdName, api, signer, args) {
|
|
|
8092
8078
|
const page = await api.listWorkLogs(args.relationshipId, signer, { delegationId: args.delegationId, limit: 100, after });
|
|
8093
8079
|
const row = page.find((w) => w.requestId === args.requestId);
|
|
8094
8080
|
if (row) {
|
|
8095
|
-
if (row.state !==
|
|
8081
|
+
if (row.state !== import_sdk27.WorkLogStates.REQUESTED) {
|
|
8096
8082
|
throw new Error(
|
|
8097
8083
|
`${cmdName}: work_request ${args.requestId} for delegation ${args.delegationId} is already in state '${row.state}' \u2014 only requests in state 'requested' can be responded to.`
|
|
8098
8084
|
);
|
|
@@ -8209,7 +8195,7 @@ function parseTtl5(cmdName, raw) {
|
|
|
8209
8195
|
}
|
|
8210
8196
|
function parseRequestId(cmdName, raw) {
|
|
8211
8197
|
if (raw === void 0 || raw === "") {
|
|
8212
|
-
return (0,
|
|
8198
|
+
return (0, import_sdk27.uuidV4)();
|
|
8213
8199
|
}
|
|
8214
8200
|
if (raw.length === 0) {
|
|
8215
8201
|
throw new Error(`${cmdName}: --request-id must be a non-empty string`);
|
|
@@ -8230,9 +8216,10 @@ function requireDid3(cmdName, did, label) {
|
|
|
8230
8216
|
}
|
|
8231
8217
|
|
|
8232
8218
|
// src/commands/work-list.ts
|
|
8219
|
+
var import_sdk28 = require("@heyanon-arp/sdk");
|
|
8233
8220
|
var import_chalk33 = __toESM(require("chalk"));
|
|
8234
8221
|
init_api();
|
|
8235
|
-
var
|
|
8222
|
+
var ALLOWED_STATES3 = new Set(import_sdk28.WORK_LOG_STATES);
|
|
8236
8223
|
function registerWorkListCommand(root) {
|
|
8237
8224
|
root.command("work-list").description("List work-log rows for a relationship (one row per (delegationId, requestId), oldest-first)").argument("<relationship-id>", "Relationship UUID").option("--server <url>", "Override ARP server base URL").option("--state <s>", "Filter by exact state (requested|responded)").option("--delegation-id <uuid>", "Narrow to work-logs operating under a specific delegation id").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(
|
|
8238
8225
|
"--verbose",
|
|
@@ -8253,7 +8240,7 @@ async function runWorkList(relationshipId, opts) {
|
|
|
8253
8240
|
);
|
|
8254
8241
|
}
|
|
8255
8242
|
const limit = parseLimit7(opts.limit);
|
|
8256
|
-
const state =
|
|
8243
|
+
const state = parseState3(opts.state);
|
|
8257
8244
|
const api = new ArpApiClient(opts.server);
|
|
8258
8245
|
const sender = resolveSenderAgent("work-list", opts.server, opts.fromDid);
|
|
8259
8246
|
if (!opts.json) {
|
|
@@ -8296,26 +8283,26 @@ function formatWorkLogLine(w, selfDid, opts = {}) {
|
|
|
8296
8283
|
const delegationPart = opts.fullIds ? w.delegationId : idHead3(w.delegationId);
|
|
8297
8284
|
const requestPart = opts.fullIds ? w.requestId : truncate3(w.requestId, 16);
|
|
8298
8285
|
const id = import_chalk33.default.bold(`${delegationPart}/${requestPart}`);
|
|
8299
|
-
const state =
|
|
8286
|
+
const state = colorState2(w.state).padEnd(stateColumnWidth2());
|
|
8300
8287
|
const peerCallerHead = opts.fullIds ? w.callerDid : didHead5(w.callerDid);
|
|
8301
8288
|
const peerPayeeHead = opts.fullIds ? w.payeeDid : didHead5(w.payeeDid);
|
|
8302
8289
|
const direction = w.callerDid === selfDid ? `${import_chalk33.default.bold("me")} \u2192 ${import_chalk33.default.dim(peerPayeeHead)}` : `${import_chalk33.default.dim(peerCallerHead)} \u2192 ${import_chalk33.default.bold("me")}`;
|
|
8303
8290
|
const outcome = formatOutcome(w);
|
|
8304
8291
|
return `${id} ${state} ${direction} ${outcome}`;
|
|
8305
8292
|
}
|
|
8306
|
-
function
|
|
8293
|
+
function colorState2(s) {
|
|
8307
8294
|
switch (s) {
|
|
8308
|
-
case
|
|
8295
|
+
case import_sdk28.WorkLogStates.REQUESTED:
|
|
8309
8296
|
return import_chalk33.default.yellow("requested");
|
|
8310
|
-
case
|
|
8297
|
+
case import_sdk28.WorkLogStates.RESPONDED:
|
|
8311
8298
|
return import_chalk33.default.green("responded");
|
|
8312
8299
|
}
|
|
8313
8300
|
}
|
|
8314
|
-
function
|
|
8301
|
+
function stateColumnWidth2() {
|
|
8315
8302
|
return 9;
|
|
8316
8303
|
}
|
|
8317
8304
|
function formatOutcome(w) {
|
|
8318
|
-
if (w.state ===
|
|
8305
|
+
if (w.state === import_sdk28.WorkLogStates.REQUESTED) return import_chalk33.default.dim("(in flight)");
|
|
8319
8306
|
if (w.responseError) return import_chalk33.default.red(`error ${w.responseError.code}: ${truncate3(w.responseError.message, 32)}`);
|
|
8320
8307
|
if (w.responseOutput) return import_chalk33.default.cyan("ok");
|
|
8321
8308
|
return import_chalk33.default.dim("(empty response)");
|
|
@@ -8332,9 +8319,9 @@ function truncate3(s, max) {
|
|
|
8332
8319
|
if (s.length <= max) return s;
|
|
8333
8320
|
return `${s.slice(0, max - 3)}...`;
|
|
8334
8321
|
}
|
|
8335
|
-
function
|
|
8322
|
+
function parseState3(raw) {
|
|
8336
8323
|
if (raw === void 0) return void 0;
|
|
8337
|
-
if (!
|
|
8324
|
+
if (!ALLOWED_STATES3.has(raw)) {
|
|
8338
8325
|
throw new Error(`work-list: --state must be one of requested|responded (got '${raw}')`);
|
|
8339
8326
|
}
|
|
8340
8327
|
return raw;
|
|
@@ -8348,6 +8335,43 @@ function parseLimit7(raw) {
|
|
|
8348
8335
|
return n;
|
|
8349
8336
|
}
|
|
8350
8337
|
|
|
8338
|
+
// src/option-hint.ts
|
|
8339
|
+
function unknownOptionHint(program, err) {
|
|
8340
|
+
try {
|
|
8341
|
+
if (err.code !== "commander.unknownOption" || !err.message) return null;
|
|
8342
|
+
const m = /unknown option '([^']+)'/.exec(err.message);
|
|
8343
|
+
if (!m) return null;
|
|
8344
|
+
const raw = m[1];
|
|
8345
|
+
const flag = raw.split("=")[0];
|
|
8346
|
+
const owners = [];
|
|
8347
|
+
const walk = (cmd, path) => {
|
|
8348
|
+
for (const opt of cmd.options) {
|
|
8349
|
+
if (opt.long === flag || opt.short === flag) owners.push(path.join(" "));
|
|
8350
|
+
}
|
|
8351
|
+
for (const sub of cmd.commands) walk(sub, [...path, sub.name()]);
|
|
8352
|
+
};
|
|
8353
|
+
for (const sub of program.commands) walk(sub, [sub.name()]);
|
|
8354
|
+
if (owners.length === 0) return null;
|
|
8355
|
+
owners.sort((a, b) => a.split(" ").length - b.split(" ").length);
|
|
8356
|
+
const argv = process.argv.slice(2);
|
|
8357
|
+
const commandNames = /* @__PURE__ */ new Set();
|
|
8358
|
+
for (const c of program.commands) {
|
|
8359
|
+
commandNames.add(c.name());
|
|
8360
|
+
for (const a of c.aliases()) commandNames.add(a);
|
|
8361
|
+
}
|
|
8362
|
+
const flagIdx = argv.findIndex((t) => t === raw || t.split("=")[0] === flag);
|
|
8363
|
+
const cmdIdx = argv.findIndex((t) => commandNames.has(t));
|
|
8364
|
+
const placedBeforeCommand = cmdIdx === -1 || flagIdx !== -1 && flagIdx < cmdIdx;
|
|
8365
|
+
if (placedBeforeCommand) {
|
|
8366
|
+
return `'${flag}' is a command option, not a global one \u2014 put it AFTER the command (e.g. \`heyarp ${owners[0]} ${flag} \u2026\`), not before it.`;
|
|
8367
|
+
}
|
|
8368
|
+
const list = owners.slice(0, 4).join(", ") + (owners.length > 4 ? ", \u2026" : "");
|
|
8369
|
+
return `'${flag}' isn't valid for that command \u2014 it's an option of: ${list}.`;
|
|
8370
|
+
} catch {
|
|
8371
|
+
return null;
|
|
8372
|
+
}
|
|
8373
|
+
}
|
|
8374
|
+
|
|
8351
8375
|
// src/cli.ts
|
|
8352
8376
|
async function checkForUpdates() {
|
|
8353
8377
|
if (package_default.private === true) return;
|
|
@@ -8362,7 +8386,7 @@ async function checkForUpdates() {
|
|
|
8362
8386
|
async function main() {
|
|
8363
8387
|
void checkForUpdates();
|
|
8364
8388
|
const program = new import_commander.Command();
|
|
8365
|
-
program.name("heyarp").description("ARP \u2014 Agent Relationship Protocol CLI (talks to apps/arp-server)").version(package_default.version).option("--trace", "Surface stack traces and error details on failure.", false);
|
|
8389
|
+
program.name("heyarp").description("ARP \u2014 Agent Relationship Protocol CLI (talks to apps/arp-server)").version(package_default.version).usage("[global-options] <command> [options]").option("--trace", "Surface stack traces and error details on failure.", false);
|
|
8366
8390
|
program.exitOverride();
|
|
8367
8391
|
program.configureOutput({
|
|
8368
8392
|
writeErr: (str) => {
|
|
@@ -8370,6 +8394,7 @@ async function main() {
|
|
|
8370
8394
|
process.stderr.write(str);
|
|
8371
8395
|
}
|
|
8372
8396
|
});
|
|
8397
|
+
program.addHelpText("after", () => optionPlacementHelpNote());
|
|
8373
8398
|
program.addHelpText("after", () => onboardingHelpFooter());
|
|
8374
8399
|
registerConfigCommand(program);
|
|
8375
8400
|
registerGuideCommand(program);
|
|
@@ -8417,10 +8442,13 @@ async function main() {
|
|
|
8417
8442
|
const json = process.argv.includes("--json");
|
|
8418
8443
|
const verbose = process.argv.includes("--trace");
|
|
8419
8444
|
const exitCode = typeof cerr.exitCode === "number" && cerr.exitCode !== 0 ? cerr.exitCode : 1;
|
|
8445
|
+
const hint = unknownOptionHint(program, cerr);
|
|
8420
8446
|
if (isCommanderError && !json) {
|
|
8447
|
+
if (hint) process.stderr.write(`hint: ${hint}
|
|
8448
|
+
`);
|
|
8421
8449
|
process.exit(exitCode);
|
|
8422
8450
|
}
|
|
8423
|
-
emitError(err, { json, verbose });
|
|
8451
|
+
emitError(err, { json, verbose, hint: hint ?? void 0 });
|
|
8424
8452
|
process.exit(exitCode);
|
|
8425
8453
|
}
|
|
8426
8454
|
}
|