@garydevenay/emporion 0.0.1 → 0.0.3

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.
Files changed (57) hide show
  1. package/README.md +230 -10
  2. package/dist/src/cli.js +1626 -300
  3. package/dist/src/cli.js.map +1 -1
  4. package/dist/src/context-store.d.ts +22 -0
  5. package/dist/src/context-store.js +133 -0
  6. package/dist/src/context-store.js.map +1 -0
  7. package/dist/src/daemon.d.ts +76 -0
  8. package/dist/src/daemon.js +341 -0
  9. package/dist/src/daemon.js.map +1 -0
  10. package/dist/src/errors.d.ts +8 -0
  11. package/dist/src/errors.js +8 -0
  12. package/dist/src/errors.js.map +1 -1
  13. package/dist/src/experience/deals-store.d.ts +37 -0
  14. package/dist/src/experience/deals-store.js +96 -0
  15. package/dist/src/experience/deals-store.js.map +1 -0
  16. package/dist/src/handshake.js +69 -45
  17. package/dist/src/handshake.js.map +1 -1
  18. package/dist/src/index.d.ts +6 -0
  19. package/dist/src/index.js +5 -0
  20. package/dist/src/index.js.map +1 -1
  21. package/dist/src/transport.d.ts +6 -1
  22. package/dist/src/transport.js +17 -1
  23. package/dist/src/transport.js.map +1 -1
  24. package/dist/src/wallet/config-store.d.ts +16 -0
  25. package/dist/src/wallet/config-store.js +180 -0
  26. package/dist/src/wallet/config-store.js.map +1 -0
  27. package/dist/src/wallet/index.d.ts +3 -0
  28. package/dist/src/wallet/index.js +4 -0
  29. package/dist/src/wallet/index.js.map +1 -0
  30. package/dist/src/wallet/ledger.d.ts +50 -0
  31. package/dist/src/wallet/ledger.js +340 -0
  32. package/dist/src/wallet/ledger.js.map +1 -0
  33. package/dist/src/wallet/nostr-nwc-adapter.d.ts +40 -0
  34. package/dist/src/wallet/nostr-nwc-adapter.js +506 -0
  35. package/dist/src/wallet/nostr-nwc-adapter.js.map +1 -0
  36. package/dist/src/wallet/nwc-adapter.d.ts +20 -0
  37. package/dist/src/wallet/nwc-adapter.js +233 -0
  38. package/dist/src/wallet/nwc-adapter.js.map +1 -0
  39. package/dist/src/wallet/service.d.ts +42 -0
  40. package/dist/src/wallet/service.js +390 -0
  41. package/dist/src/wallet/service.js.map +1 -0
  42. package/dist/src/wallet/types.d.ts +140 -0
  43. package/dist/src/wallet/types.js +2 -0
  44. package/dist/src/wallet/types.js.map +1 -0
  45. package/dist/test/cli.test.js +76 -15
  46. package/dist/test/cli.test.js.map +1 -1
  47. package/dist/test/experience.test.d.ts +1 -0
  48. package/dist/test/experience.test.js +232 -0
  49. package/dist/test/experience.test.js.map +1 -0
  50. package/dist/test/integration.test.js +37 -31
  51. package/dist/test/integration.test.js.map +1 -1
  52. package/dist/test/unit.test.js +26 -0
  53. package/dist/test/unit.test.js.map +1 -1
  54. package/dist/test/wallet.test.d.ts +1 -0
  55. package/dist/test/wallet.test.js +853 -0
  56. package/dist/test/wallet.test.js.map +1 -0
  57. package/package.json +2 -1
package/dist/src/cli.js CHANGED
@@ -1,11 +1,24 @@
1
1
  #!/usr/bin/env node
2
+ import { AsyncLocalStorage } from "node:async_hooks";
3
+ import { spawn } from "node:child_process";
4
+ import { randomUUID } from "node:crypto";
2
5
  import { once } from "node:events";
6
+ import { closeSync } from "node:fs";
7
+ import { readFile } from "node:fs/promises";
8
+ import path from "node:path";
9
+ import { fileURLToPath, pathToFileURL } from "node:url";
10
+ import { AgentDaemon, cleanupStaleDaemonArtifacts, daemonOptionsRecordToMap, daemonRequestFromParsed, daemonRequestOptionsToRecord, ensureDaemonRuntimeDir, getDaemonLogPath, getLocalControlEndpoint, openDaemonLogFd, probeDaemonStatus, sendDaemonCommand } from "./daemon.js";
3
11
  import { AgentTransport } from "./transport.js";
4
12
  import { createLogger } from "./logger.js";
5
13
  import { loadPersistentIdentityMaterial } from "./persistent-agent.js";
6
14
  import * as Protocol from "./protocol/index.js";
7
15
  import { sha256Hex } from "./protocol/shared.js";
8
16
  import { TransportStorage } from "./storage.js";
17
+ import { WalletService } from "./wallet/index.js";
18
+ import { ContextStore } from "./context-store.js";
19
+ import { DealsStore } from "./experience/deals-store.js";
20
+ const DEFAULT_DAEMON_PROXY_TIMEOUT_MS = 5_000;
21
+ const WALLET_DAEMON_PROXY_TIMEOUT_MS = 30_000;
9
22
  const DEFAULT_IO = {
10
23
  stdout(message) {
11
24
  process.stdout.write(message);
@@ -14,6 +27,7 @@ const DEFAULT_IO = {
14
27
  process.stderr.write(message);
15
28
  }
16
29
  };
30
+ const CLI_CONTEXT_STORAGE = new AsyncLocalStorage();
17
31
  function parseArgs(argv) {
18
32
  const commandPath = [];
19
33
  const options = new Map();
@@ -43,6 +57,12 @@ function parseArgs(argv) {
43
57
  function commandMatches(commandPath, ...expected) {
44
58
  return commandPath.length === expected.length && expected.every((value, index) => commandPath[index] === value);
45
59
  }
60
+ function isContextCommand(commandPath) {
61
+ return commandPath[0] === "context";
62
+ }
63
+ function isWalletCommand(commandPath) {
64
+ return commandPath[0] === "wallet";
65
+ }
46
66
  function getOptionValues(args, name) {
47
67
  return args.options.get(name) ?? [];
48
68
  }
@@ -84,6 +104,17 @@ function parseOptionalNonNegativeInteger(args, name) {
84
104
  const value = getOptionalOption(args, name);
85
105
  return value === undefined ? undefined : parseNonNegativeInteger(value, `--${name}`);
86
106
  }
107
+ function parseOptionalIsoTimestamp(args, name) {
108
+ const value = getOptionalOption(args, name);
109
+ if (value === undefined) {
110
+ return undefined;
111
+ }
112
+ const epochMs = Date.parse(value);
113
+ if (Number.isNaN(epochMs)) {
114
+ throw new Error(`--${name} must be a valid ISO-8601 timestamp`);
115
+ }
116
+ return new Date(epochMs).toISOString();
117
+ }
87
118
  function parseEnum(value, fieldName, allowed) {
88
119
  if (allowed.includes(value)) {
89
120
  return value;
@@ -182,27 +213,190 @@ function getUniqueOptionValues(args, name) {
182
213
  function writeJson(io, value) {
183
214
  io.stdout(`${JSON.stringify(value, null, 2)}\n`);
184
215
  }
185
- async function withCliContext(dataDir, fn) {
216
+ function normalizeDataDirPath(dataDir) {
217
+ return path.resolve(dataDir);
218
+ }
219
+ function parsedArgsFromCommand(commandPath, options) {
220
+ const map = new Map();
221
+ for (const [name, value] of Object.entries(options)) {
222
+ if (value === undefined) {
223
+ continue;
224
+ }
225
+ if (typeof value === "boolean") {
226
+ if (value) {
227
+ map.set(name, ["true"]);
228
+ }
229
+ continue;
230
+ }
231
+ if (Array.isArray(value)) {
232
+ map.set(name, value.map((entry) => `${entry}`));
233
+ continue;
234
+ }
235
+ map.set(name, [`${value}`]);
236
+ }
237
+ return {
238
+ commandPath: [...commandPath],
239
+ options: map
240
+ };
241
+ }
242
+ async function runNestedCommand(commandPath, options) {
243
+ return executeCapturedInDaemon(parsedArgsFromCommand(commandPath, options));
244
+ }
245
+ function ensureObjectIdPrefix(objectId, prefixes) {
246
+ const trimmed = objectId.trim();
247
+ if (!prefixes.some((prefix) => trimmed.startsWith(prefix))) {
248
+ throw new Error(`Unsupported object id: ${trimmed}`);
249
+ }
250
+ return trimmed;
251
+ }
252
+ function stageOrder(stage) {
253
+ switch (stage) {
254
+ case "draft":
255
+ return 0;
256
+ case "negotiating":
257
+ return 1;
258
+ case "agreed":
259
+ return 2;
260
+ case "in_progress":
261
+ return 3;
262
+ case "proof_submitted":
263
+ return 4;
264
+ case "proof_accepted":
265
+ return 5;
266
+ case "settlement_pending":
267
+ return 6;
268
+ case "settled":
269
+ return 7;
270
+ case "closed":
271
+ return 8;
272
+ }
273
+ }
274
+ function stageAtLeast(current, expected) {
275
+ return stageOrder(current) >= stageOrder(expected);
276
+ }
277
+ function toChangedObjects(record) {
278
+ const entries = [];
279
+ if (record.rootObjectKind && record.rootObjectId)
280
+ entries.push({ kind: record.rootObjectKind, id: record.rootObjectId });
281
+ if (record.proposalKind && record.proposalId)
282
+ entries.push({ kind: record.proposalKind, id: record.proposalId });
283
+ if (record.agreementId)
284
+ entries.push({ kind: "agreement", id: record.agreementId });
285
+ if (record.contractId)
286
+ entries.push({ kind: "contract", id: record.contractId });
287
+ if (record.evidenceId)
288
+ entries.push({ kind: "evidence-bundle", id: record.evidenceId });
289
+ if (record.invoiceId)
290
+ entries.push({ kind: "invoice", id: record.invoiceId });
291
+ if (record.paymentId)
292
+ entries.push({ kind: "payment", id: record.paymentId });
293
+ return entries;
294
+ }
295
+ function nextActionsForStage(stage) {
296
+ switch (stage) {
297
+ case "draft":
298
+ case "negotiating":
299
+ return ["deal.propose", "deal.accept"];
300
+ case "agreed":
301
+ return ["deal.start"];
302
+ case "in_progress":
303
+ return ["proof.submit"];
304
+ case "proof_submitted":
305
+ return ["proof.accept"];
306
+ case "proof_accepted":
307
+ return ["settlement.invoice.create", "settlement.pay"];
308
+ case "settlement_pending":
309
+ return ["settlement.pay", "settlement.status"];
310
+ case "settled":
311
+ return ["deal.status"];
312
+ case "closed":
313
+ return [];
314
+ }
315
+ }
316
+ function writeDealResponse(io, command, deal, overrides) {
317
+ writeJson(io, {
318
+ command,
319
+ dealId: deal.dealId,
320
+ stage: deal.stage,
321
+ changedObjects: toChangedObjects(deal),
322
+ nextActions: nextActionsForStage(deal.stage),
323
+ safety: {
324
+ policy: "proof-gated",
325
+ earlySettlementAllowed: overrides?.safety?.earlySettlementAllowed ?? false
326
+ }
327
+ });
328
+ }
329
+ function asRecord(value, label) {
330
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
331
+ throw new Error(`Expected ${label} to be an object`);
332
+ }
333
+ return value;
334
+ }
335
+ function readString(value, fieldName) {
336
+ if (typeof value !== "string" || value.trim().length === 0) {
337
+ throw new Error(`Expected ${fieldName} to be a non-empty string`);
338
+ }
339
+ return value;
340
+ }
341
+ function inferIntentFromRootObjectKind(kind) {
342
+ return kind === "request" ? "sell" : "buy";
343
+ }
344
+ function inferRootObjectKindFromId(objectId) {
345
+ if (objectId.startsWith("emporion:request:")) {
346
+ return "request";
347
+ }
348
+ if (objectId.startsWith("emporion:listing:")) {
349
+ return "listing";
350
+ }
351
+ throw new Error(`Unsupported target object id for proposal: ${objectId}`);
352
+ }
353
+ function inferProposalKindFromId(proposalId) {
354
+ if (proposalId.startsWith("emporion:offer:")) {
355
+ return "offer";
356
+ }
357
+ if (proposalId.startsWith("emporion:bid:")) {
358
+ return "bid";
359
+ }
360
+ throw new Error(`Unsupported proposal id: ${proposalId}`);
361
+ }
362
+ async function openCliContext(dataDir) {
186
363
  const identityMaterial = await loadPersistentIdentityMaterial(dataDir);
187
364
  const repository = await Protocol.ProtocolRepository.create(dataDir);
188
365
  const transportStorage = await TransportStorage.create(dataDir, identityMaterial.storagePrimaryKey, createLogger("error"));
366
+ const walletService = await WalletService.create({ dataDir });
189
367
  await transportStorage.initializeDefaults();
368
+ return {
369
+ dataDir,
370
+ identityMaterial,
371
+ repository,
372
+ transportStorage,
373
+ walletService,
374
+ signer: {
375
+ did: identityMaterial.agentIdentity.did,
376
+ publicKey: identityMaterial.transportKeyPair.publicKey,
377
+ secretKey: identityMaterial.transportKeyPair.secretKey
378
+ }
379
+ };
380
+ }
381
+ async function closeCliContext(context) {
382
+ await context.walletService.close();
383
+ await context.transportStorage.close();
384
+ await context.repository.close();
385
+ }
386
+ async function withCliContext(dataDir, fn) {
387
+ const sharedContext = CLI_CONTEXT_STORAGE.getStore();
388
+ if (sharedContext) {
389
+ if (normalizeDataDirPath(sharedContext.dataDir) !== normalizeDataDirPath(dataDir)) {
390
+ throw new Error(`Daemon context is bound to ${sharedContext.dataDir}, not ${dataDir}`);
391
+ }
392
+ return fn(sharedContext);
393
+ }
394
+ const context = await openCliContext(dataDir);
190
395
  try {
191
- return await fn({
192
- dataDir,
193
- identityMaterial,
194
- repository,
195
- transportStorage,
196
- signer: {
197
- did: identityMaterial.agentIdentity.did,
198
- publicKey: identityMaterial.transportKeyPair.publicKey,
199
- secretKey: identityMaterial.transportKeyPair.secretKey
200
- }
201
- });
396
+ return await CLI_CONTEXT_STORAGE.run(context, async () => fn(context));
202
397
  }
203
398
  finally {
204
- await transportStorage.close();
205
- await repository.close();
399
+ await closeCliContext(context);
206
400
  }
207
401
  }
208
402
  async function readRequiredState(repository, objectKind, objectId) {
@@ -1745,101 +1939,640 @@ async function handleMarketList(args, io) {
1745
1939
  });
1746
1940
  });
1747
1941
  }
1748
- async function handleServe(args, io) {
1942
+ async function handleContextAdd(args, io) {
1943
+ const name = requireOption(args, "name");
1749
1944
  const dataDir = requireOption(args, "data-dir");
1750
- const logLevel = parseEnum(getOptionalOption(args, "log-level") ?? "info", "--log-level", [
1751
- "debug",
1752
- "info",
1753
- "warn",
1754
- "error"
1755
- ]);
1756
- const bootstrap = getCsvOptionValues(args, "bootstrap");
1757
- const marketplaces = getCsvOptionValues(args, "marketplace");
1758
- const companyIds = getCsvOptionValues(args, "company");
1759
- const connectDids = getCsvOptionValues(args, "connect-did");
1760
- const connectNoiseKeys = getCsvOptionValues(args, "connect-noise-key");
1761
- const exitAfterMs = getOptionalOption(args, "exit-after-ms");
1762
- const watchProtocol = !hasFlag(args, "no-watch-protocol");
1763
- const transportConfig = {
1764
- dataDir,
1765
- logLevel,
1766
- ...(bootstrap.length > 0 ? { bootstrap } : {})
1767
- };
1768
- const transport = await AgentTransport.create(transportConfig);
1769
- const topics = [];
1770
- const seenControlLengths = new Map();
1771
- const discoveryIntervalMs = 2_000;
1772
- let discoveryInterval;
1773
- try {
1774
- await transport.start();
1775
- if (hasFlag(args, "agent-topic")) {
1776
- topics.push({ kind: "agent", agentDid: transport.identity.did });
1777
- }
1778
- for (const marketplaceId of marketplaces) {
1779
- topics.push({ kind: "marketplace", marketplaceId });
1780
- }
1781
- for (const companyId of companyIds) {
1782
- topics.push({ kind: "company", companyId });
1783
- }
1784
- for (const topic of topics) {
1785
- await transport.joinTopic(topic);
1786
- }
1787
- for (const did of connectDids) {
1788
- await transport.connectToDid(did);
1789
- }
1790
- for (const publicKey of connectNoiseKeys) {
1791
- await transport.connectToNoiseKey(publicKey);
1945
+ const makeActive = hasFlag(args, "make-active");
1946
+ const store = new ContextStore();
1947
+ const snapshot = await store.add(name, dataDir, makeActive);
1948
+ writeJson(io, {
1949
+ command: "context.add",
1950
+ activeContext: snapshot.activeContext,
1951
+ contexts: snapshot.contexts
1952
+ });
1953
+ }
1954
+ async function handleContextUse(args, io) {
1955
+ const name = requireOption(args, "name");
1956
+ const store = new ContextStore();
1957
+ const snapshot = await store.use(name);
1958
+ writeJson(io, {
1959
+ command: "context.use",
1960
+ activeContext: snapshot.activeContext,
1961
+ contexts: snapshot.contexts
1962
+ });
1963
+ }
1964
+ async function handleContextList(io) {
1965
+ const store = new ContextStore();
1966
+ const snapshot = await store.snapshot();
1967
+ writeJson(io, {
1968
+ command: "context.list",
1969
+ activeContext: snapshot.activeContext,
1970
+ contexts: snapshot.contexts
1971
+ });
1972
+ }
1973
+ async function handleContextShow(io) {
1974
+ const store = new ContextStore();
1975
+ const snapshot = await store.snapshot();
1976
+ const active = snapshot.activeContext
1977
+ ? snapshot.contexts.find((entry) => entry.name === snapshot.activeContext)
1978
+ : undefined;
1979
+ writeJson(io, {
1980
+ command: "context.show",
1981
+ activeContext: snapshot.activeContext,
1982
+ active: active ?? null
1983
+ });
1984
+ }
1985
+ async function handleContextRemove(args, io) {
1986
+ const name = requireOption(args, "name");
1987
+ const store = new ContextStore();
1988
+ const snapshot = await store.remove(name);
1989
+ writeJson(io, {
1990
+ command: "context.remove",
1991
+ activeContext: snapshot.activeContext,
1992
+ contexts: snapshot.contexts
1993
+ });
1994
+ }
1995
+ function applyWalletRuntimeKeyFromArgs(context, args) {
1996
+ const walletKey = getOptionalOption(args, "wallet-key");
1997
+ if (walletKey && walletKey.trim().length > 0) {
1998
+ context.walletService.setRuntimeKey(walletKey);
1999
+ }
2000
+ }
2001
+ async function handleWalletConnectNwc(args, io) {
2002
+ const dataDir = requireOption(args, "data-dir");
2003
+ const connectionUri = requireOption(args, "connection-uri");
2004
+ const publishEndpoint = hasFlag(args, "publish-payment-endpoint");
2005
+ await withCliContext(dataDir, async (context) => {
2006
+ applyWalletRuntimeKeyFromArgs(context, args);
2007
+ const connected = await context.walletService.connect(connectionUri);
2008
+ let paymentEndpointEventId;
2009
+ if (publishEndpoint) {
2010
+ await ensureAgentProfileExists(context);
2011
+ const capabilities = getCsvOptionValues(args, "payment-capability");
2012
+ const accountId = getOptionalOption(args, "payment-account-id");
2013
+ const endpoint = {
2014
+ id: getOptionalOption(args, "payment-endpoint-id") ?? "wallet-nwc",
2015
+ network: "bitcoin",
2016
+ custodial: true,
2017
+ capabilities: capabilities.length > 0 ? capabilities : ["invoice.create", "invoice.pay", "auto-settle"],
2018
+ ...(accountId ? { accountId } : {}),
2019
+ nodeUri: connected.endpoint
2020
+ };
2021
+ const result = await appendEnvelope(context, {
2022
+ objectKind: "agent-profile",
2023
+ objectId: context.identityMaterial.agentIdentity.did,
2024
+ eventKind: "agent-profile.payment-endpoint-added",
2025
+ subjectId: context.identityMaterial.agentIdentity.did,
2026
+ payload: Protocol.paymentEndpointToJson(endpoint)
2027
+ });
2028
+ paymentEndpointEventId = result.envelope.eventId;
1792
2029
  }
1793
2030
  writeJson(io, {
1794
- command: "serve",
1795
- identity: transport.identity,
1796
- topics,
1797
- connectedPeers: [...transport.getPeerSessions().values()]
2031
+ command: "wallet.connect.nwc",
2032
+ wallet: connected.status,
2033
+ endpoint: connected.endpoint,
2034
+ ...(paymentEndpointEventId ? { paymentEndpointEventId } : {})
1798
2035
  });
1799
- if (watchProtocol) {
1800
- discoveryInterval = setInterval(() => {
1801
- void (async () => {
1802
- for (const session of transport.getPeerSessions().values()) {
1803
- const remoteFeed = transport.getRemoteFeed(session.remoteControlFeedKey);
1804
- if (!remoteFeed) {
1805
- continue;
1806
- }
1807
- await remoteFeed.update({ wait: false });
1808
- const seenLength = seenControlLengths.get(session.remoteControlFeedKey) ?? 0;
1809
- if (remoteFeed.length <= seenLength) {
1810
- continue;
1811
- }
1812
- for (let index = seenLength; index < remoteFeed.length; index += 1) {
1813
- const entry = await remoteFeed.get(index);
1814
- if (Protocol.isProtocolAnnouncement(entry)) {
1815
- io.stdout(`${JSON.stringify({ command: "serve.discovery", remoteDid: session.remoteDid, announcement: entry }, null, 2)}\n`);
1816
- }
1817
- }
1818
- seenControlLengths.set(session.remoteControlFeedKey, remoteFeed.length);
1819
- }
1820
- })().catch((error) => {
1821
- io.stderr(`${error instanceof Error ? error.message : String(error)}\n`);
1822
- });
1823
- }, discoveryIntervalMs);
1824
- }
1825
- if (exitAfterMs) {
1826
- await sleep(parsePositiveInteger(exitAfterMs, "--exit-after-ms"));
1827
- return;
1828
- }
1829
- await Promise.race([once(process, "SIGINT"), once(process, "SIGTERM")]);
2036
+ });
2037
+ }
2038
+ async function handleWalletDisconnect(args, io) {
2039
+ const dataDir = requireOption(args, "data-dir");
2040
+ await withCliContext(dataDir, async (context) => {
2041
+ applyWalletRuntimeKeyFromArgs(context, args);
2042
+ const wallet = await context.walletService.disconnect();
2043
+ writeJson(io, {
2044
+ command: "wallet.disconnect",
2045
+ wallet
2046
+ });
2047
+ });
2048
+ }
2049
+ async function handleWalletStatus(args, io) {
2050
+ const dataDir = requireOption(args, "data-dir");
2051
+ await withCliContext(dataDir, async (context) => {
2052
+ applyWalletRuntimeKeyFromArgs(context, args);
2053
+ writeJson(io, {
2054
+ command: "wallet.status",
2055
+ wallet: await context.walletService.status()
2056
+ });
2057
+ });
2058
+ }
2059
+ async function handleWalletUnlock(args, io) {
2060
+ if (process.env.EMPORION_DAEMON !== "1") {
2061
+ throw new Error("wallet unlock requires a running daemon for this data-dir");
1830
2062
  }
1831
- finally {
1832
- if (discoveryInterval) {
1833
- clearInterval(discoveryInterval);
1834
- }
1835
- await transport.stop();
2063
+ const dataDir = requireOption(args, "data-dir");
2064
+ const walletKey = requireOption(args, "wallet-key");
2065
+ if (walletKey.trim().length === 0) {
2066
+ throw new Error("--wallet-key must not be blank");
1836
2067
  }
2068
+ await withCliContext(dataDir, async (context) => {
2069
+ context.walletService.setRuntimeKey(walletKey);
2070
+ writeJson(io, {
2071
+ command: "wallet.unlock",
2072
+ wallet: await context.walletService.status()
2073
+ });
2074
+ });
2075
+ }
2076
+ async function handleWalletLock(args, io) {
2077
+ if (process.env.EMPORION_DAEMON !== "1") {
2078
+ throw new Error("wallet lock requires a running daemon for this data-dir");
2079
+ }
2080
+ const dataDir = requireOption(args, "data-dir");
2081
+ await withCliContext(dataDir, async (context) => {
2082
+ context.walletService.setRuntimeKey(null);
2083
+ writeJson(io, {
2084
+ command: "wallet.lock",
2085
+ wallet: await context.walletService.status()
2086
+ });
2087
+ });
2088
+ }
2089
+ async function handleWalletInvoiceCreate(args, io) {
2090
+ const dataDir = requireOption(args, "data-dir");
2091
+ const amountSats = parsePositiveInteger(requireOption(args, "amount-sats"), "--amount-sats");
2092
+ const memo = getOptionalOption(args, "memo");
2093
+ const expiresAt = parseOptionalIsoTimestamp(args, "expires-at");
2094
+ await withCliContext(dataDir, async (context) => {
2095
+ applyWalletRuntimeKeyFromArgs(context, args);
2096
+ const created = await context.walletService.createInvoice({
2097
+ amountSats,
2098
+ ...(memo ? { memo } : {}),
2099
+ ...(expiresAt ? { expiresAt } : {})
2100
+ });
2101
+ writeJson(io, {
2102
+ command: "wallet.invoice.create",
2103
+ invoice: created.invoice,
2104
+ bolt11: created.bolt11
2105
+ });
2106
+ });
2107
+ }
2108
+ async function handleWalletPayBolt11(args, io) {
2109
+ const dataDir = requireOption(args, "data-dir");
2110
+ const invoice = requireOption(args, "invoice");
2111
+ const sourceRef = getOptionalOption(args, "source-ref");
2112
+ await withCliContext(dataDir, async (context) => {
2113
+ applyWalletRuntimeKeyFromArgs(context, args);
2114
+ const paid = await context.walletService.payInvoice({
2115
+ invoice,
2116
+ ...(sourceRef ? { sourceRef } : {})
2117
+ });
2118
+ writeJson(io, {
2119
+ command: "wallet.pay.bolt11",
2120
+ payment: paid.payment
2121
+ });
2122
+ });
2123
+ }
2124
+ async function handleWalletLedgerList(args, io) {
2125
+ const dataDir = requireOption(args, "data-dir");
2126
+ const kindValue = getOptionalOption(args, "kind");
2127
+ const status = getOptionalOption(args, "status");
2128
+ const kind = kindValue ? parseEnum(kindValue, "--kind", ["invoice", "payment"]) : undefined;
2129
+ await withCliContext(dataDir, async (context) => {
2130
+ applyWalletRuntimeKeyFromArgs(context, args);
2131
+ const filters = {
2132
+ ...(kind ? { kind } : {}),
2133
+ ...(status ? { status } : {})
2134
+ };
2135
+ writeJson(io, {
2136
+ command: "wallet.ledger.list",
2137
+ kind: kind ?? null,
2138
+ status: status ?? null,
2139
+ entries: await context.walletService.listLedger(filters)
2140
+ });
2141
+ });
2142
+ }
2143
+ async function handleWalletKeyRotate(args, io) {
2144
+ const dataDir = requireOption(args, "data-dir");
2145
+ const newKey = requireOption(args, "new-key");
2146
+ await withCliContext(dataDir, async (context) => {
2147
+ applyWalletRuntimeKeyFromArgs(context, args);
2148
+ await context.walletService.rotateKey(newKey);
2149
+ writeJson(io, {
2150
+ command: "wallet.key.rotate",
2151
+ rotated: true
2152
+ });
2153
+ });
2154
+ }
2155
+ async function handleDealOpen(args, io) {
2156
+ const dataDir = requireOption(args, "data-dir");
2157
+ const intent = parseEnum(requireOption(args, "intent"), "--intent", ["buy", "sell"]);
2158
+ const marketplaceId = requireOption(args, "marketplace");
2159
+ const title = requireOption(args, "title");
2160
+ const amountSats = parsePositiveInteger(requireOption(args, "amount-sats"), "--amount-sats");
2161
+ const dealId = getOptionalOption(args, "deal-id") ?? `deal:${randomUUID()}`;
2162
+ await withCliContext(dataDir, async () => {
2163
+ const deals = await DealsStore.create(dataDir);
2164
+ if (deals.get(dealId)) {
2165
+ throw new Error(`Deal already exists: ${dealId}`);
2166
+ }
2167
+ const created = await runNestedCommand(["market", intent === "buy" ? "request" : "listing", "publish"], {
2168
+ "data-dir": dataDir,
2169
+ marketplace: marketplaceId,
2170
+ title,
2171
+ "amount-sats": `${amountSats}`
2172
+ });
2173
+ const createdRecord = asRecord(created, `${intent}.publish result`);
2174
+ const objectId = readString(createdRecord.objectId, "objectId");
2175
+ const nowIso = now();
2176
+ const deal = {
2177
+ dealId,
2178
+ stage: "negotiating",
2179
+ intent,
2180
+ marketplaceId,
2181
+ title,
2182
+ amountSats,
2183
+ rootObjectKind: intent === "buy" ? "request" : "listing",
2184
+ rootObjectId: objectId,
2185
+ createdAt: nowIso,
2186
+ updatedAt: nowIso
2187
+ };
2188
+ await deals.save(deal);
2189
+ writeDealResponse(io, "deal.open", deal);
2190
+ });
2191
+ }
2192
+ async function handleDealPropose(args, io) {
2193
+ const dataDir = requireOption(args, "data-dir");
2194
+ const targetId = requireOption(args, "target-id");
2195
+ const amountSats = parsePositiveInteger(requireOption(args, "amount-sats"), "--amount-sats");
2196
+ const proposalId = getOptionalOption(args, "proposal-id");
2197
+ const proposerDid = getOptionalOption(args, "proposer-did");
2198
+ await withCliContext(dataDir, async (context) => {
2199
+ const rootObjectKind = inferRootObjectKindFromId(targetId);
2200
+ const deals = await DealsStore.create(dataDir);
2201
+ const targetState = rootObjectKind === "request"
2202
+ ? await readRequiredState(context.repository, "request", targetId)
2203
+ : await readRequiredState(context.repository, "listing", targetId);
2204
+ const proposalKind = rootObjectKind === "request" ? "offer" : "bid";
2205
+ const proposed = await runNestedCommand(["market", proposalKind, "submit"], {
2206
+ "data-dir": dataDir,
2207
+ marketplace: targetState.marketplaceId,
2208
+ "target-object-id": targetId,
2209
+ "amount-sats": `${amountSats}`,
2210
+ ...(proposerDid ? { "proposer-did": proposerDid } : {}),
2211
+ ...(proposalId ? { id: proposalId } : {})
2212
+ });
2213
+ const proposedRecord = asRecord(proposed, `${proposalKind}.submit result`);
2214
+ const resolvedProposalId = readString(proposedRecord.objectId, "objectId");
2215
+ const existing = deals.findByRootObjectId(targetId);
2216
+ const nowIso = now();
2217
+ const deal = existing
2218
+ ? await deals.update(existing.dealId, (current) => ({
2219
+ ...current,
2220
+ stage: "negotiating",
2221
+ proposalKind,
2222
+ proposalId: resolvedProposalId,
2223
+ amountSats,
2224
+ updatedAt: nowIso
2225
+ }))
2226
+ : await deals.save({
2227
+ dealId: `deal:${randomUUID()}`,
2228
+ stage: "negotiating",
2229
+ intent: inferIntentFromRootObjectKind(rootObjectKind),
2230
+ marketplaceId: targetState.marketplaceId,
2231
+ title: "proposal-only-deal",
2232
+ amountSats,
2233
+ rootObjectKind,
2234
+ rootObjectId: targetId,
2235
+ proposalKind,
2236
+ proposalId: resolvedProposalId,
2237
+ createdAt: nowIso,
2238
+ updatedAt: nowIso
2239
+ });
2240
+ writeDealResponse(io, "deal.propose", deal);
2241
+ });
2242
+ }
2243
+ async function handleDealAccept(args, io) {
2244
+ const dataDir = requireOption(args, "data-dir");
2245
+ const proposalId = requireOption(args, "proposal-id");
2246
+ const proposalKind = inferProposalKindFromId(proposalId);
2247
+ await withCliContext(dataDir, async () => {
2248
+ const deals = await DealsStore.create(dataDir);
2249
+ await runNestedCommand(["market", proposalKind, "accept"], {
2250
+ "data-dir": dataDir,
2251
+ id: proposalId
2252
+ });
2253
+ const existing = deals.findByProposalId(proposalId);
2254
+ const nowIso = now();
2255
+ const deal = existing
2256
+ ? await deals.update(existing.dealId, (current) => ({
2257
+ ...current,
2258
+ stage: "agreed",
2259
+ proposalKind,
2260
+ proposalId,
2261
+ updatedAt: nowIso
2262
+ }))
2263
+ : await deals.save({
2264
+ dealId: `deal:${randomUUID()}`,
2265
+ stage: "agreed",
2266
+ proposalKind,
2267
+ proposalId,
2268
+ createdAt: nowIso,
2269
+ updatedAt: nowIso
2270
+ });
2271
+ writeDealResponse(io, "deal.accept", deal);
2272
+ });
2273
+ }
2274
+ async function handleDealStart(args, io) {
2275
+ const dataDir = requireOption(args, "data-dir");
2276
+ const proposalId = requireOption(args, "proposal-id");
2277
+ const scope = requireOption(args, "scope");
2278
+ const milestoneId = requireOption(args, "milestone-id");
2279
+ const milestoneTitle = requireOption(args, "milestone-title");
2280
+ const deadline = parseOptionalIsoTimestamp(args, "deadline");
2281
+ if (!deadline) {
2282
+ throw new Error("--deadline is required");
2283
+ }
2284
+ const deliverableKind = parseEnum(requireOption(args, "deliverable-kind"), "--deliverable-kind", ["artifact", "generic", "oracle-claim"]);
2285
+ const requiredArtifactKinds = getCsvOptionValues(args, "required-artifact-kind");
2286
+ if (requiredArtifactKinds.length === 0) {
2287
+ throw new Error("--required-artifact-kind must include at least one value");
2288
+ }
2289
+ const proposalKind = inferProposalKindFromId(proposalId);
2290
+ await withCliContext(dataDir, async (context) => {
2291
+ const deals = await DealsStore.create(dataDir);
2292
+ const proposalState = proposalKind === "offer"
2293
+ ? await readRequiredState(context.repository, "offer", proposalId)
2294
+ : await readRequiredState(context.repository, "bid", proposalId);
2295
+ if (proposalState.status !== "accepted") {
2296
+ throw new Error(`Proposal must be accepted before deal start: ${proposalId}`);
2297
+ }
2298
+ const agreement = await runNestedCommand(["market", "agreement", "create"], {
2299
+ "data-dir": dataDir,
2300
+ "source-kind": proposalKind,
2301
+ "source-id": proposalId,
2302
+ deliverable: [milestoneTitle],
2303
+ counterparty: [context.identityMaterial.agentIdentity.did, proposalState.proposerDid],
2304
+ "amount-sats": `${proposalState.paymentTerms.amountSats}`
2305
+ });
2306
+ const agreementRecord = asRecord(agreement, "agreement.create result");
2307
+ const agreementId = readString(agreementRecord.objectId, "objectId");
2308
+ const milestonesJson = JSON.stringify([{
2309
+ milestoneId,
2310
+ title: milestoneTitle,
2311
+ deliverableSchema: {
2312
+ kind: deliverableKind,
2313
+ requiredArtifactKinds
2314
+ },
2315
+ proofPolicy: {
2316
+ allowedModes: ["artifact-verifiable"],
2317
+ verifierRefs: [],
2318
+ minArtifacts: 1,
2319
+ requireCounterpartyAcceptance: true
2320
+ },
2321
+ settlementAdapters: []
2322
+ }]);
2323
+ const contract = await runNestedCommand(["contract", "create"], {
2324
+ "data-dir": dataDir,
2325
+ "origin-kind": "agreement",
2326
+ "origin-id": agreementId,
2327
+ party: [context.identityMaterial.agentIdentity.did, proposalState.proposerDid],
2328
+ scope,
2329
+ "milestones-json": milestonesJson,
2330
+ "deliverable-schema-json": JSON.stringify({ kind: deliverableKind, requiredArtifactKinds }),
2331
+ "proof-policy-json": JSON.stringify({
2332
+ allowedModes: ["artifact-verifiable"],
2333
+ verifierRefs: [],
2334
+ minArtifacts: 1,
2335
+ requireCounterpartyAcceptance: true
2336
+ }),
2337
+ "resolution-policy-json": JSON.stringify({ mode: "mutual", deterministicVerifierIds: [] }),
2338
+ "settlement-policy-json": JSON.stringify({ adapters: [], releaseCondition: "contract-completed" }),
2339
+ "deadline-policy-json": JSON.stringify({ milestoneDeadlines: { [milestoneId]: deadline } })
2340
+ });
2341
+ const contractRecord = asRecord(contract, "contract.create result");
2342
+ const contractId = readString(contractRecord.objectId, "objectId");
2343
+ await runNestedCommand(["contract", "open-milestone"], {
2344
+ "data-dir": dataDir,
2345
+ id: contractId,
2346
+ "milestone-id": milestoneId
2347
+ });
2348
+ const existing = deals.findByProposalId(proposalId);
2349
+ const nowIso = now();
2350
+ const deal = existing
2351
+ ? await deals.update(existing.dealId, (current) => ({
2352
+ ...current,
2353
+ stage: "in_progress",
2354
+ proposalKind,
2355
+ proposalId,
2356
+ agreementId,
2357
+ contractId,
2358
+ milestoneId,
2359
+ updatedAt: nowIso
2360
+ }))
2361
+ : await deals.save({
2362
+ dealId: `deal:${randomUUID()}`,
2363
+ stage: "in_progress",
2364
+ proposalKind,
2365
+ proposalId,
2366
+ agreementId,
2367
+ contractId,
2368
+ milestoneId,
2369
+ createdAt: nowIso,
2370
+ updatedAt: nowIso
2371
+ });
2372
+ writeDealResponse(io, "deal.start", deal);
2373
+ });
2374
+ }
2375
+ async function handleDealStatus(args, io) {
2376
+ const dataDir = requireOption(args, "data-dir");
2377
+ const dealId = requireOption(args, "deal-id");
2378
+ const deals = await DealsStore.create(dataDir);
2379
+ const deal = deals.get(dealId);
2380
+ if (!deal) {
2381
+ throw new Error(`Unknown deal: ${dealId}`);
2382
+ }
2383
+ writeDealResponse(io, "deal.status", deal);
2384
+ }
2385
+ async function handleProofSubmit(args, io) {
2386
+ const dataDir = requireOption(args, "data-dir");
2387
+ const dealId = requireOption(args, "deal-id");
2388
+ const milestoneId = requireOption(args, "milestone-id");
2389
+ const preset = parseEnum(requireOption(args, "proof-preset"), "--proof-preset", ["simple-artifact"]);
2390
+ if (preset !== "simple-artifact") {
2391
+ throw new Error(`Unsupported --proof-preset: ${preset}`);
2392
+ }
2393
+ const artifactId = requireOption(args, "artifact-id");
2394
+ const artifactHash = requireOption(args, "artifact-hash");
2395
+ const repro = getOptionalOption(args, "repro");
2396
+ await withCliContext(dataDir, async () => {
2397
+ const deals = await DealsStore.create(dataDir);
2398
+ const existing = deals.get(dealId);
2399
+ if (!existing) {
2400
+ throw new Error(`Unknown deal: ${dealId}`);
2401
+ }
2402
+ if (!existing.contractId) {
2403
+ throw new Error("Deal has no contract yet; run deal start first");
2404
+ }
2405
+ if (!stageAtLeast(existing.stage, "in_progress")) {
2406
+ throw new Error("Deal is not ready for proof submission");
2407
+ }
2408
+ const evidence = await runNestedCommand(["evidence", "record"], {
2409
+ "data-dir": dataDir,
2410
+ "contract-id": existing.contractId,
2411
+ "milestone-id": milestoneId,
2412
+ "proof-mode": "artifact-verifiable",
2413
+ "artifact-json": JSON.stringify([{ artifactId, hash: artifactHash }]),
2414
+ "verifier-json": JSON.stringify([{ verifierId: "simple-artifact-check", verifierKind: "human-review" }]),
2415
+ ...(repro ? { repro } : {})
2416
+ });
2417
+ const evidenceRecord = asRecord(evidence, "evidence.record result");
2418
+ const evidenceId = readString(evidenceRecord.objectId, "objectId");
2419
+ await runNestedCommand(["contract", "submit-milestone"], {
2420
+ "data-dir": dataDir,
2421
+ id: existing.contractId,
2422
+ "milestone-id": milestoneId,
2423
+ "evidence-bundle-id": evidenceId
2424
+ });
2425
+ const nowIso = now();
2426
+ const deal = await deals.update(dealId, (current) => ({
2427
+ ...current,
2428
+ stage: "proof_submitted",
2429
+ milestoneId,
2430
+ evidenceId,
2431
+ updatedAt: nowIso
2432
+ }));
2433
+ writeDealResponse(io, "proof.submit", deal);
2434
+ });
2435
+ }
2436
+ async function handleProofAccept(args, io) {
2437
+ const dataDir = requireOption(args, "data-dir");
2438
+ const dealId = requireOption(args, "deal-id");
2439
+ const milestoneId = requireOption(args, "milestone-id");
2440
+ await withCliContext(dataDir, async () => {
2441
+ const deals = await DealsStore.create(dataDir);
2442
+ const existing = deals.get(dealId);
2443
+ if (!existing) {
2444
+ throw new Error(`Unknown deal: ${dealId}`);
2445
+ }
2446
+ if (!existing.contractId) {
2447
+ throw new Error("Deal has no contract yet");
2448
+ }
2449
+ if (!stageAtLeast(existing.stage, "proof_submitted")) {
2450
+ throw new Error("Deal proof must be submitted before acceptance");
2451
+ }
2452
+ await runNestedCommand(["contract", "accept-milestone"], {
2453
+ "data-dir": dataDir,
2454
+ id: existing.contractId,
2455
+ "milestone-id": milestoneId
2456
+ });
2457
+ const nowIso = now();
2458
+ const deal = await deals.update(dealId, (current) => ({
2459
+ ...current,
2460
+ stage: "proof_accepted",
2461
+ milestoneId,
2462
+ updatedAt: nowIso
2463
+ }));
2464
+ writeDealResponse(io, "proof.accept", deal);
2465
+ });
2466
+ }
2467
+ async function handleSettlementInvoiceCreate(args, io) {
2468
+ const dataDir = requireOption(args, "data-dir");
2469
+ const dealId = requireOption(args, "deal-id");
2470
+ const amountSats = parsePositiveInteger(requireOption(args, "amount-sats"), "--amount-sats");
2471
+ const memo = getOptionalOption(args, "memo");
2472
+ const expiresAt = parseOptionalIsoTimestamp(args, "expires-at");
2473
+ const allowEarlySettlement = hasFlag(args, "allow-early-settlement");
2474
+ await withCliContext(dataDir, async (context) => {
2475
+ const deals = await DealsStore.create(dataDir);
2476
+ const existing = deals.get(dealId);
2477
+ if (!existing) {
2478
+ throw new Error(`Unknown deal: ${dealId}`);
2479
+ }
2480
+ if (!allowEarlySettlement && !stageAtLeast(existing.stage, "proof_accepted")) {
2481
+ throw new Error("Settlement invoice creation is proof-gated. Use --allow-early-settlement to override.");
2482
+ }
2483
+ applyWalletRuntimeKeyFromArgs(context, args);
2484
+ const created = await context.walletService.createInvoice({
2485
+ amountSats,
2486
+ ...(memo ? { memo } : {}),
2487
+ ...(expiresAt ? { expiresAt } : {})
2488
+ });
2489
+ const nowIso = now();
2490
+ const deal = await deals.update(dealId, (current) => ({
2491
+ ...current,
2492
+ stage: "settlement_pending",
2493
+ invoiceId: created.invoice.id,
2494
+ invoiceBolt11: created.bolt11,
2495
+ updatedAt: nowIso
2496
+ }));
2497
+ writeDealResponse(io, "settlement.invoice.create", deal, {
2498
+ safety: { earlySettlementAllowed: allowEarlySettlement }
2499
+ });
2500
+ });
2501
+ }
2502
+ async function handleSettlementPay(args, io) {
2503
+ const dataDir = requireOption(args, "data-dir");
2504
+ const dealId = requireOption(args, "deal-id");
2505
+ const invoice = requireOption(args, "invoice");
2506
+ const allowEarlySettlement = hasFlag(args, "allow-early-settlement");
2507
+ await withCliContext(dataDir, async (context) => {
2508
+ const deals = await DealsStore.create(dataDir);
2509
+ const existing = deals.get(dealId);
2510
+ if (!existing) {
2511
+ throw new Error(`Unknown deal: ${dealId}`);
2512
+ }
2513
+ if (!allowEarlySettlement && !stageAtLeast(existing.stage, "proof_accepted")) {
2514
+ throw new Error("Settlement payment is proof-gated. Use --allow-early-settlement to override.");
2515
+ }
2516
+ applyWalletRuntimeKeyFromArgs(context, args);
2517
+ const sourceRef = existing.contractId
2518
+ ? `${existing.contractId}:${existing.milestoneId ?? "milestone"}`
2519
+ : dealId;
2520
+ const paid = await context.walletService.payInvoice({
2521
+ invoice,
2522
+ sourceRef
2523
+ });
2524
+ const nowIso = now();
2525
+ const deal = await deals.update(dealId, (current) => ({
2526
+ ...current,
2527
+ stage: "settled",
2528
+ paymentId: paid.payment.id,
2529
+ updatedAt: nowIso
2530
+ }));
2531
+ writeDealResponse(io, "settlement.pay", deal, {
2532
+ safety: { earlySettlementAllowed: allowEarlySettlement }
2533
+ });
2534
+ });
2535
+ }
2536
+ async function handleSettlementStatus(args, io) {
2537
+ const dataDir = requireOption(args, "data-dir");
2538
+ const dealId = requireOption(args, "deal-id");
2539
+ const deals = await DealsStore.create(dataDir);
2540
+ const deal = deals.get(dealId);
2541
+ if (!deal) {
2542
+ throw new Error(`Unknown deal: ${dealId}`);
2543
+ }
2544
+ writeDealResponse(io, "settlement.status", deal);
1837
2545
  }
1838
2546
  function usage() {
1839
2547
  return [
1840
2548
  "Usage:",
2549
+ " Global data-dir resolution: --data-dir <path> > --context <name> > active context",
2550
+ " emporion context add --name <context> --data-dir <path> [--make-active]",
2551
+ " emporion context use --name <context>",
2552
+ " emporion context list",
2553
+ " emporion context show",
2554
+ " emporion context remove --name <context>",
1841
2555
  " emporion agent init --data-dir <path> [--display-name <name>] [--bio <text>]",
1842
2556
  " emporion agent show --data-dir <path>",
2557
+ " emporion wallet connect nwc --data-dir <path> --connection-uri <uri> [--publish-payment-endpoint]",
2558
+ " emporion wallet disconnect --data-dir <path>",
2559
+ " emporion wallet status --data-dir <path>",
2560
+ " emporion wallet unlock [--data-dir <path>|--context <name>] --wallet-key <key-material>",
2561
+ " emporion wallet lock [--data-dir <path>|--context <name>]",
2562
+ " emporion wallet invoice create --data-dir <path> --amount-sats <n> [--memo <text>] [--expires-at <iso>]",
2563
+ " emporion wallet pay bolt11 --data-dir <path> --invoice <bolt11>",
2564
+ " emporion wallet ledger list --data-dir <path> [--kind <invoice|payment>] [--status <status>]",
2565
+ " emporion wallet key rotate --data-dir <path> --new-key <key-material>",
2566
+ " emporion deal open [--data-dir <path>|--context <name>] --intent <buy|sell> --marketplace <id> --title <text> --amount-sats <n> [--deal-id <id>]",
2567
+ " emporion deal propose [--data-dir <path>|--context <name>] --target-id <object-id> --amount-sats <n> [--proposal-id <id>] [--proposer-did <did>]",
2568
+ " emporion deal accept [--data-dir <path>|--context <name>] --proposal-id <offer-or-bid-id>",
2569
+ " emporion deal start [--data-dir <path>|--context <name>] --proposal-id <offer-or-bid-id> --scope <text> --milestone-id <id> --milestone-title <text> --deadline <iso> --deliverable-kind <artifact|generic|oracle-claim> --required-artifact-kind <kind>[,<kind>...]",
2570
+ " emporion deal status [--data-dir <path>|--context <name>] --deal-id <id>",
2571
+ " emporion proof submit [--data-dir <path>|--context <name>] --deal-id <id> --milestone-id <id> --proof-preset <simple-artifact> --artifact-id <id> --artifact-hash <hex> [--repro <text>]",
2572
+ " emporion proof accept [--data-dir <path>|--context <name>] --deal-id <id> --milestone-id <id>",
2573
+ " emporion settlement invoice create [--data-dir <path>|--context <name>] --deal-id <id> --amount-sats <n> [--memo <text>] [--expires-at <iso>]",
2574
+ " emporion settlement pay [--data-dir <path>|--context <name>] --deal-id <id> --invoice <bolt11>",
2575
+ " emporion settlement status [--data-dir <path>|--context <name>] --deal-id <id>",
1843
2576
  " emporion agent payment-endpoint add --data-dir <path> --id <id> --capability <cap>[,<cap>...]",
1844
2577
  " emporion agent wallet-attestation add --data-dir <path> --attestation-id <id> --wallet-account-id <id> --balance-sats <n> --expires-at <iso>",
1845
2578
  " emporion agent feedback add --data-dir <path> --credential-id <id> --issuer-did <did> --contract-id <id> --agreement-id <id> --score <n> --max-score <n>",
@@ -1861,238 +2594,831 @@ function usage() {
1861
2594
  " emporion message send --data-dir <path> --space-id <id> --body <text>",
1862
2595
  " emporion market list --data-dir <path> --marketplace <id>",
1863
2596
  " emporion object show --data-dir <path> --kind <kind> --id <id>",
1864
- " emporion serve --data-dir <path> [--marketplace <id>] [--company <did>] [--agent-topic]"
2597
+ " emporion daemon start --data-dir <path> [--marketplace <id>] [--company <did>] [--agent-topic]",
2598
+ " emporion daemon status --data-dir <path>",
2599
+ " emporion daemon stop --data-dir <path>",
2600
+ " emporion daemon logs --data-dir <path> [--tail <n>] [--follow]"
1865
2601
  ].join("\n");
1866
2602
  }
1867
- export async function runCli(argv, io = DEFAULT_IO) {
1868
- try {
1869
- const args = parseArgs(argv);
1870
- if (args.commandPath.length === 0 || hasFlag(args, "help")) {
1871
- io.stdout(`${usage()}\n`);
1872
- return 0;
1873
- }
1874
- if (commandMatches(args.commandPath, "agent", "init"))
1875
- return await handleAgentInit(args, io).then(() => 0);
1876
- if (commandMatches(args.commandPath, "agent", "show"))
1877
- return await handleAgentShow(args, io).then(() => 0);
1878
- if (commandMatches(args.commandPath, "agent", "payment-endpoint", "add")) {
1879
- return await handleAgentPaymentEndpointAdd(args, io).then(() => 0);
1880
- }
1881
- if (commandMatches(args.commandPath, "agent", "payment-endpoint", "remove")) {
1882
- return await handleAgentPaymentEndpointRemove(args, io).then(() => 0);
1883
- }
1884
- if (commandMatches(args.commandPath, "agent", "wallet-attestation", "add")) {
1885
- return await handleAgentWalletAttestationAdd(args, io).then(() => 0);
1886
- }
1887
- if (commandMatches(args.commandPath, "agent", "wallet-attestation", "remove")) {
1888
- return await handleAgentWalletAttestationRemove(args, io).then(() => 0);
1889
- }
1890
- if (commandMatches(args.commandPath, "agent", "feedback", "add")) {
1891
- return await handleAgentFeedbackAdd(args, io).then(() => 0);
1892
- }
1893
- if (commandMatches(args.commandPath, "agent", "feedback", "remove")) {
1894
- return await handleAgentFeedbackRemove(args, io).then(() => 0);
1895
- }
1896
- if (commandMatches(args.commandPath, "company", "create"))
1897
- return await handleCompanyCreate(args, io).then(() => 0);
1898
- if (commandMatches(args.commandPath, "company", "show"))
1899
- return await handleCompanyShow(args, io).then(() => 0);
1900
- if (commandMatches(args.commandPath, "company", "update"))
1901
- return await handleCompanyUpdate(args, io).then(() => 0);
1902
- if (commandMatches(args.commandPath, "company", "grant-role")) {
1903
- return await handleCompanyRoleChange(args, io, "grant").then(() => 0);
1904
- }
1905
- if (commandMatches(args.commandPath, "company", "revoke-role")) {
1906
- return await handleCompanyRoleChange(args, io, "revoke").then(() => 0);
1907
- }
1908
- if (commandMatches(args.commandPath, "company", "join-market")) {
1909
- return await handleCompanyMarketMembership(args, io, "join").then(() => 0);
1910
- }
1911
- if (commandMatches(args.commandPath, "company", "leave-market")) {
1912
- return await handleCompanyMarketMembership(args, io, "leave").then(() => 0);
1913
- }
1914
- if (commandMatches(args.commandPath, "company", "treasury-attest")) {
1915
- return await handleCompanyTreasuryAttest(args, io).then(() => 0);
1916
- }
1917
- if (commandMatches(args.commandPath, "company", "treasury-reserve")) {
1918
- return await handleCompanyTreasuryReserve(args, io).then(() => 0);
1919
- }
1920
- if (commandMatches(args.commandPath, "company", "treasury-release")) {
1921
- return await handleCompanyTreasuryRelease(args, io).then(() => 0);
1922
- }
1923
- if (commandMatches(args.commandPath, "market", "product", "create")) {
1924
- return await handleMarketProductCreate(args, io).then(() => 0);
1925
- }
1926
- if (commandMatches(args.commandPath, "market", "product", "update")) {
1927
- return await handleMarketProductUpdate(args, io).then(() => 0);
1928
- }
1929
- if (commandMatches(args.commandPath, "market", "product", "publish")) {
1930
- return await handleMarketProductStateChange(args, io, "product.published").then(() => 0);
1931
- }
1932
- if (commandMatches(args.commandPath, "market", "product", "unpublish")) {
1933
- return await handleMarketProductStateChange(args, io, "product.unpublished").then(() => 0);
1934
- }
1935
- if (commandMatches(args.commandPath, "market", "product", "retire")) {
1936
- return await handleMarketProductStateChange(args, io, "product.retired").then(() => 0);
1937
- }
1938
- if (commandMatches(args.commandPath, "market", "listing", "publish")) {
1939
- return await handleMarketListingPublish(args, io).then(() => 0);
1940
- }
1941
- if (commandMatches(args.commandPath, "market", "listing", "revise")) {
1942
- return await handleMarketListingRevise(args, io).then(() => 0);
1943
- }
1944
- if (commandMatches(args.commandPath, "market", "listing", "withdraw")) {
1945
- return await handleSimpleMarketStateChange(args, io, "listing", "listing.withdrawn").then(() => 0);
1946
- }
1947
- if (commandMatches(args.commandPath, "market", "listing", "expire")) {
1948
- return await handleSimpleMarketStateChange(args, io, "listing", "listing.expired").then(() => 0);
1949
- }
1950
- if (commandMatches(args.commandPath, "market", "request", "publish")) {
1951
- return await handleMarketRequestPublish(args, io).then(() => 0);
1952
- }
1953
- if (commandMatches(args.commandPath, "market", "request", "revise")) {
1954
- return await handleMarketRequestRevise(args, io).then(() => 0);
1955
- }
1956
- if (commandMatches(args.commandPath, "market", "request", "close")) {
1957
- return await handleSimpleMarketStateChange(args, io, "request", "request.closed").then(() => 0);
1958
- }
1959
- if (commandMatches(args.commandPath, "market", "request", "expire")) {
1960
- return await handleSimpleMarketStateChange(args, io, "request", "request.expired").then(() => 0);
1961
- }
1962
- if (commandMatches(args.commandPath, "market", "offer", "submit")) {
1963
- return await handleMarketOfferSubmit(args, io).then(() => 0);
1964
- }
1965
- if (commandMatches(args.commandPath, "market", "offer", "counter")) {
1966
- return await handleMarketOfferCounter(args, io).then(() => 0);
1967
- }
1968
- if (commandMatches(args.commandPath, "market", "offer", "accept")) {
1969
- return await handleSimpleMarketStateChange(args, io, "offer", "offer.accepted").then(() => 0);
1970
- }
1971
- if (commandMatches(args.commandPath, "market", "offer", "reject")) {
1972
- return await handleSimpleMarketStateChange(args, io, "offer", "offer.rejected").then(() => 0);
1973
- }
1974
- if (commandMatches(args.commandPath, "market", "offer", "cancel")) {
1975
- return await handleSimpleMarketStateChange(args, io, "offer", "offer.canceled").then(() => 0);
1976
- }
1977
- if (commandMatches(args.commandPath, "market", "offer", "expire")) {
1978
- return await handleSimpleMarketStateChange(args, io, "offer", "offer.expired").then(() => 0);
1979
- }
1980
- if (commandMatches(args.commandPath, "market", "bid", "submit")) {
1981
- return await handleMarketBidSubmit(args, io).then(() => 0);
1982
- }
1983
- if (commandMatches(args.commandPath, "market", "bid", "counter")) {
1984
- return await handleMarketBidCounter(args, io).then(() => 0);
1985
- }
1986
- if (commandMatches(args.commandPath, "market", "bid", "accept")) {
1987
- return await handleSimpleMarketStateChange(args, io, "bid", "bid.accepted").then(() => 0);
1988
- }
1989
- if (commandMatches(args.commandPath, "market", "bid", "reject")) {
1990
- return await handleSimpleMarketStateChange(args, io, "bid", "bid.rejected").then(() => 0);
1991
- }
1992
- if (commandMatches(args.commandPath, "market", "bid", "cancel")) {
1993
- return await handleSimpleMarketStateChange(args, io, "bid", "bid.canceled").then(() => 0);
1994
- }
1995
- if (commandMatches(args.commandPath, "market", "bid", "expire")) {
1996
- return await handleSimpleMarketStateChange(args, io, "bid", "bid.expired").then(() => 0);
2603
+ function getDataDirFromArgs(args) {
2604
+ return getOptionalOption(args, "data-dir");
2605
+ }
2606
+ async function withResolvedDataDir(args) {
2607
+ if (isContextCommand(args.commandPath)) {
2608
+ return args;
2609
+ }
2610
+ if (getOptionalOption(args, "data-dir")) {
2611
+ return args;
2612
+ }
2613
+ const requestedContext = getOptionalOption(args, "context");
2614
+ const store = new ContextStore();
2615
+ const resolved = await store.resolveDataDir(requestedContext);
2616
+ if (requestedContext && !resolved) {
2617
+ throw new Error(`Unknown context: ${requestedContext}`);
2618
+ }
2619
+ if (!resolved) {
2620
+ return args;
2621
+ }
2622
+ const options = new Map(args.options);
2623
+ options.set("data-dir", [resolved]);
2624
+ return {
2625
+ commandPath: [...args.commandPath],
2626
+ options
2627
+ };
2628
+ }
2629
+ function withDaemonWalletKeyForwarding(args) {
2630
+ if (!isWalletCommand(args.commandPath)) {
2631
+ return args;
2632
+ }
2633
+ if (getOptionalOption(args, "wallet-key")) {
2634
+ return args;
2635
+ }
2636
+ const walletKey = process.env.EMPORION_WALLET_KEY;
2637
+ if (!walletKey || walletKey.trim().length === 0) {
2638
+ return args;
2639
+ }
2640
+ const options = new Map(args.options);
2641
+ options.set("wallet-key", [walletKey]);
2642
+ return {
2643
+ commandPath: [...args.commandPath],
2644
+ options
2645
+ };
2646
+ }
2647
+ function getDaemonProxyTimeoutMs(commandPath) {
2648
+ if (isWalletCommand(commandPath) || commandPath[0] === "settlement") {
2649
+ return WALLET_DAEMON_PROXY_TIMEOUT_MS;
2650
+ }
2651
+ return DEFAULT_DAEMON_PROXY_TIMEOUT_MS;
2652
+ }
2653
+ function createCaptureIo() {
2654
+ const stdout = [];
2655
+ const stderr = [];
2656
+ return {
2657
+ stdout,
2658
+ stderr,
2659
+ io: {
2660
+ stdout(message) {
2661
+ stdout.push(message);
2662
+ },
2663
+ stderr(message) {
2664
+ stderr.push(message);
2665
+ }
1997
2666
  }
1998
- if (commandMatches(args.commandPath, "market", "agreement", "create")) {
1999
- return await handleMarketAgreementCreate(args, io).then(() => 0);
2667
+ };
2668
+ }
2669
+ function buildTransportConfigFromArgs(args) {
2670
+ const dataDir = requireOption(args, "data-dir");
2671
+ const logLevel = parseEnum(getOptionalOption(args, "log-level") ?? "info", "--log-level", [
2672
+ "debug",
2673
+ "info",
2674
+ "warn",
2675
+ "error"
2676
+ ]);
2677
+ const bootstrap = getCsvOptionValues(args, "bootstrap");
2678
+ return {
2679
+ dataDir,
2680
+ logLevel,
2681
+ ...(bootstrap.length > 0 ? { bootstrap } : {})
2682
+ };
2683
+ }
2684
+ function buildTopicsFromArgs(args, agentDid) {
2685
+ const topics = [];
2686
+ if (hasFlag(args, "agent-topic")) {
2687
+ topics.push({ kind: "agent", agentDid });
2688
+ }
2689
+ for (const marketplaceId of getCsvOptionValues(args, "marketplace")) {
2690
+ topics.push({ kind: "marketplace", marketplaceId });
2691
+ }
2692
+ for (const companyId of getCsvOptionValues(args, "company")) {
2693
+ topics.push({ kind: "company", companyId });
2694
+ }
2695
+ return topics;
2696
+ }
2697
+ function getDaemonStartupOptions(args) {
2698
+ return {
2699
+ topics: [],
2700
+ connectDids: getCsvOptionValues(args, "connect-did"),
2701
+ connectNoiseKeys: getCsvOptionValues(args, "connect-noise-key"),
2702
+ watchProtocol: !hasFlag(args, "no-watch-protocol")
2703
+ };
2704
+ }
2705
+ function sanitizeExecArgv() {
2706
+ return process.execArgv.filter((value) => value !== "--test" && !value.startsWith("--test-"));
2707
+ }
2708
+ function buildDaemonRunArgv(args) {
2709
+ const forwarded = new Set([
2710
+ "data-dir",
2711
+ "log-level",
2712
+ "bootstrap",
2713
+ "marketplace",
2714
+ "company",
2715
+ "connect-did",
2716
+ "connect-noise-key",
2717
+ "agent-topic",
2718
+ "no-watch-protocol"
2719
+ ]);
2720
+ const argv = [...sanitizeExecArgv()];
2721
+ argv.push(fileURLToPath(import.meta.url), "daemon", "run");
2722
+ for (const [name, values] of args.options.entries()) {
2723
+ if (!forwarded.has(name)) {
2724
+ continue;
2725
+ }
2726
+ for (const value of values) {
2727
+ argv.push(`--${name}`);
2728
+ if (value !== "true") {
2729
+ argv.push(value);
2730
+ }
2000
2731
  }
2001
- if (commandMatches(args.commandPath, "market", "agreement", "complete")) {
2002
- return await handleSimpleMarketStateChange(args, io, "agreement", "agreement.completed").then(() => 0);
2732
+ }
2733
+ return argv;
2734
+ }
2735
+ function tailText(input, lineCount) {
2736
+ if (lineCount <= 0) {
2737
+ return "";
2738
+ }
2739
+ const lines = input.split("\n");
2740
+ const slice = lines.slice(Math.max(lines.length - lineCount - 1, 0)).join("\n");
2741
+ return slice.length > 0 && !slice.endsWith("\n") ? `${slice}\n` : slice;
2742
+ }
2743
+ async function readDaemonLogTail(dataDir, lineCount) {
2744
+ try {
2745
+ return tailText(await readFile(getDaemonLogPath(dataDir), "utf8"), lineCount);
2746
+ }
2747
+ catch {
2748
+ return "";
2749
+ }
2750
+ }
2751
+ async function waitForDaemonReady(dataDir, timeoutMs) {
2752
+ const deadline = Date.now() + timeoutMs;
2753
+ let lastError;
2754
+ while (Date.now() < deadline) {
2755
+ try {
2756
+ const status = await probeDaemonStatus(dataDir, 1_000);
2757
+ if (status) {
2758
+ return status;
2759
+ }
2003
2760
  }
2004
- if (commandMatches(args.commandPath, "market", "agreement", "cancel")) {
2005
- return await handleSimpleMarketStateChange(args, io, "agreement", "agreement.canceled").then(() => 0);
2761
+ catch (error) {
2762
+ lastError = error instanceof Error ? error : new Error(String(error));
2006
2763
  }
2007
- if (commandMatches(args.commandPath, "market", "agreement", "dispute")) {
2008
- return await handleSimpleMarketStateChange(args, io, "agreement", "agreement.disputed").then(() => 0);
2764
+ await sleep(100);
2765
+ }
2766
+ if (lastError) {
2767
+ throw lastError;
2768
+ }
2769
+ throw new Error(`Daemon for ${dataDir} did not become ready within ${timeoutMs}ms`);
2770
+ }
2771
+ async function waitForDaemonExit(dataDir, timeoutMs) {
2772
+ const deadline = Date.now() + timeoutMs;
2773
+ while (Date.now() < deadline) {
2774
+ try {
2775
+ const status = await probeDaemonStatus(dataDir, 1_000);
2776
+ if (!status) {
2777
+ return;
2778
+ }
2009
2779
  }
2010
- if (commandMatches(args.commandPath, "market", "list"))
2011
- return await handleMarketList(args, io).then(() => 0);
2012
- if (commandMatches(args.commandPath, "contract", "create"))
2013
- return await handleContractCreate(args, io).then(() => 0);
2014
- if (commandMatches(args.commandPath, "contract", "open-milestone")) {
2015
- return await handleContractMilestoneAction(args, io, "contract.milestone-opened").then(() => 0);
2780
+ catch {
2781
+ // The daemon may tear down the socket before the pid disappears.
2016
2782
  }
2017
- if (commandMatches(args.commandPath, "contract", "submit-milestone")) {
2018
- return await handleContractMilestoneAction(args, io, "contract.milestone-submitted").then(() => 0);
2783
+ await sleep(100);
2784
+ }
2785
+ throw new Error(`Daemon for ${dataDir} did not stop within ${timeoutMs}ms`);
2786
+ }
2787
+ async function proxyParsedArgsToDaemon(args, io) {
2788
+ const dataDir = getDataDirFromArgs(args);
2789
+ if (!dataDir) {
2790
+ return 1;
2791
+ }
2792
+ const response = await sendDaemonCommand(dataDir, daemonRequestFromParsed(args.commandPath, daemonRequestOptionsToRecord(args.options)), getDaemonProxyTimeoutMs(args.commandPath));
2793
+ if (!response.ok) {
2794
+ throw new Error(response.error ?? "Daemon command failed");
2795
+ }
2796
+ if (response.result !== undefined) {
2797
+ writeJson(io, response.result);
2798
+ }
2799
+ return 0;
2800
+ }
2801
+ async function executeCapturedInDaemon(args) {
2802
+ const capture = createCaptureIo();
2803
+ const exitCode = await executeParsedArgs(args, capture.io, { allowDaemonProxy: false });
2804
+ if (exitCode !== 0) {
2805
+ throw new Error(capture.stderr.join("").trim() || "Daemon command failed");
2806
+ }
2807
+ const output = capture.stdout.join("").trim();
2808
+ return output.length === 0 ? null : JSON.parse(output);
2809
+ }
2810
+ async function handleDaemonStart(args, io) {
2811
+ const dataDir = requireOption(args, "data-dir");
2812
+ const existing = await probeDaemonStatus(dataDir, 1_000);
2813
+ if (existing) {
2814
+ writeJson(io, {
2815
+ command: "daemon.start",
2816
+ alreadyRunning: true,
2817
+ status: existing
2818
+ });
2819
+ return;
2820
+ }
2821
+ await ensureDaemonRuntimeDir(dataDir);
2822
+ const logFd = openDaemonLogFd(dataDir);
2823
+ let childError;
2824
+ try {
2825
+ const child = spawn(process.execPath, buildDaemonRunArgv(args), {
2826
+ detached: true,
2827
+ stdio: ["ignore", logFd, logFd],
2828
+ env: {
2829
+ ...process.env,
2830
+ EMPORION_DAEMON: "1"
2831
+ }
2832
+ });
2833
+ child.once("error", (error) => {
2834
+ childError = error;
2835
+ });
2836
+ child.unref();
2837
+ }
2838
+ finally {
2839
+ closeSync(logFd);
2840
+ }
2841
+ if (childError) {
2842
+ throw childError;
2843
+ }
2844
+ const status = await waitForDaemonReady(dataDir, 10_000);
2845
+ writeJson(io, {
2846
+ command: "daemon.start",
2847
+ alreadyRunning: false,
2848
+ status
2849
+ });
2850
+ }
2851
+ async function handleDaemonStatus(args, io) {
2852
+ const dataDir = requireOption(args, "data-dir");
2853
+ const status = await probeDaemonStatus(dataDir, 1_000);
2854
+ if (!status) {
2855
+ throw new Error(`No daemon is running for ${dataDir}`);
2856
+ }
2857
+ writeJson(io, {
2858
+ command: "daemon.status",
2859
+ status
2860
+ });
2861
+ }
2862
+ async function handleDaemonStop(args, io) {
2863
+ const dataDir = requireOption(args, "data-dir");
2864
+ const status = await probeDaemonStatus(dataDir, 1_000);
2865
+ if (!status) {
2866
+ await cleanupStaleDaemonArtifacts(dataDir);
2867
+ writeJson(io, {
2868
+ command: "daemon.stop",
2869
+ stopped: true,
2870
+ alreadyStopped: true
2871
+ });
2872
+ return;
2873
+ }
2874
+ const response = await sendDaemonCommand(dataDir, {
2875
+ commandPath: ["daemon", "stop"],
2876
+ options: {}
2877
+ });
2878
+ if (!response.ok) {
2879
+ throw new Error(response.error ?? "Daemon stop request failed");
2880
+ }
2881
+ await waitForDaemonExit(dataDir, 10_000);
2882
+ writeJson(io, {
2883
+ command: "daemon.stop",
2884
+ stopped: true,
2885
+ pid: status.pid
2886
+ });
2887
+ }
2888
+ async function handleDaemonLogs(args, io) {
2889
+ const dataDir = requireOption(args, "data-dir");
2890
+ const tail = parseNonNegativeInteger(getOptionalOption(args, "tail") ?? "100", "--tail");
2891
+ const follow = hasFlag(args, "follow");
2892
+ const logPath = getDaemonLogPath(dataDir);
2893
+ let currentContents = await readDaemonLogTail(dataDir, tail);
2894
+ if (currentContents.length > 0) {
2895
+ io.stdout(currentContents);
2896
+ }
2897
+ if (!follow) {
2898
+ return;
2899
+ }
2900
+ let stopped = false;
2901
+ const stop = () => {
2902
+ stopped = true;
2903
+ };
2904
+ process.once("SIGINT", stop);
2905
+ process.once("SIGTERM", stop);
2906
+ try {
2907
+ try {
2908
+ currentContents = await readFile(logPath, "utf8");
2019
2909
  }
2020
- if (commandMatches(args.commandPath, "contract", "accept-milestone")) {
2021
- return await handleContractMilestoneAction(args, io, "contract.milestone-accepted").then(() => 0);
2910
+ catch {
2911
+ currentContents = "";
2022
2912
  }
2023
- if (commandMatches(args.commandPath, "contract", "reject-milestone")) {
2024
- return await handleContractMilestoneAction(args, io, "contract.milestone-rejected").then(() => 0);
2913
+ let lastLength = currentContents.length;
2914
+ while (!stopped) {
2915
+ await sleep(500);
2916
+ if (stopped) {
2917
+ break;
2918
+ }
2919
+ try {
2920
+ const next = await readFile(logPath, "utf8");
2921
+ if (next.length > lastLength) {
2922
+ io.stdout(next.slice(lastLength));
2923
+ }
2924
+ lastLength = next.length;
2925
+ }
2926
+ catch {
2927
+ // Keep polling while the operator waits for logs to appear.
2928
+ }
2025
2929
  }
2026
- if (commandMatches(args.commandPath, "contract", "pause")) {
2027
- return await handleContractStateChange(args, io, "contract.paused").then(() => 0);
2930
+ }
2931
+ finally {
2932
+ process.off("SIGINT", stop);
2933
+ process.off("SIGTERM", stop);
2934
+ }
2935
+ }
2936
+ async function buildDaemonSharedContext(dataDir, transport, walletService) {
2937
+ const identityMaterial = transport.getIdentityMaterial();
2938
+ const repository = await Protocol.ProtocolRepository.create(dataDir);
2939
+ return {
2940
+ dataDir,
2941
+ identityMaterial,
2942
+ repository,
2943
+ transportStorage: transport.getStorage(),
2944
+ walletService,
2945
+ signer: {
2946
+ did: identityMaterial.agentIdentity.did,
2947
+ publicKey: identityMaterial.transportKeyPair.publicKey,
2948
+ secretKey: identityMaterial.transportKeyPair.secretKey
2028
2949
  }
2029
- if (commandMatches(args.commandPath, "contract", "resume")) {
2030
- return await handleContractStateChange(args, io, "contract.resumed").then(() => 0);
2950
+ };
2951
+ }
2952
+ function buildDaemonStatus(dataDir, startedAt, transport, walletStatus) {
2953
+ return {
2954
+ dataDir: normalizeDataDirPath(dataDir),
2955
+ pid: process.pid,
2956
+ startedAt,
2957
+ identity: transport.identity,
2958
+ runtimeEndpoint: getLocalControlEndpoint(dataDir).path,
2959
+ logPath: getDaemonLogPath(dataDir),
2960
+ topics: transport.getJoinedTopics(),
2961
+ connectedPeers: [...transport.getPeerSessions().values()],
2962
+ wallet: walletStatus,
2963
+ healthy: true
2964
+ };
2965
+ }
2966
+ function collectAutoSettleCandidates(snapshot) {
2967
+ const candidates = [];
2968
+ for (const offer of snapshot.offers.values()) {
2969
+ if (offer.status !== "accepted") {
2970
+ continue;
2971
+ }
2972
+ for (const lightningRef of offer.lightningRefs) {
2973
+ candidates.push({
2974
+ triggerObjectKind: "offer",
2975
+ triggerObjectId: offer.objectId,
2976
+ eventId: offer.latestEventId,
2977
+ lightningRef,
2978
+ amountSats: offer.paymentTerms.amountSats
2979
+ });
2031
2980
  }
2032
- if (commandMatches(args.commandPath, "contract", "complete")) {
2033
- return await handleContractStateChange(args, io, "contract.completed").then(() => 0);
2981
+ }
2982
+ for (const bid of snapshot.bids.values()) {
2983
+ if (bid.status !== "accepted") {
2984
+ continue;
2985
+ }
2986
+ for (const lightningRef of bid.lightningRefs) {
2987
+ candidates.push({
2988
+ triggerObjectKind: "bid",
2989
+ triggerObjectId: bid.objectId,
2990
+ eventId: bid.latestEventId,
2991
+ lightningRef,
2992
+ amountSats: bid.paymentTerms.amountSats
2993
+ });
2034
2994
  }
2035
- if (commandMatches(args.commandPath, "contract", "cancel")) {
2036
- return await handleContractStateChange(args, io, "contract.canceled").then(() => 0);
2995
+ }
2996
+ for (const agreement of snapshot.agreements.values()) {
2997
+ if (agreement.status !== "active") {
2998
+ continue;
2999
+ }
3000
+ for (const lightningRef of agreement.lightningRefs) {
3001
+ candidates.push({
3002
+ triggerObjectKind: "agreement",
3003
+ triggerObjectId: agreement.objectId,
3004
+ eventId: agreement.latestEventId,
3005
+ lightningRef,
3006
+ amountSats: agreement.paymentTerms.amountSats
3007
+ });
2037
3008
  }
2038
- if (commandMatches(args.commandPath, "contract", "dispute")) {
2039
- return await handleContractStateChange(args, io, "contract.disputed").then(() => 0);
3009
+ }
3010
+ return candidates;
3011
+ }
3012
+ async function runProtocolDiscoveryWatcher(transport, seenControlLengths, io) {
3013
+ for (const session of transport.getPeerSessions().values()) {
3014
+ const remoteFeed = transport.getRemoteFeed(session.remoteControlFeedKey);
3015
+ if (!remoteFeed) {
3016
+ continue;
3017
+ }
3018
+ await remoteFeed.update({ wait: false });
3019
+ const seenLength = seenControlLengths.get(session.remoteControlFeedKey) ?? 0;
3020
+ if (remoteFeed.length <= seenLength) {
3021
+ continue;
3022
+ }
3023
+ for (let index = seenLength; index < remoteFeed.length; index += 1) {
3024
+ const entry = await remoteFeed.get(index);
3025
+ if (Protocol.isProtocolAnnouncement(entry)) {
3026
+ io.stdout(`${JSON.stringify({ command: "daemon.discovery", remoteDid: session.remoteDid, announcement: entry })}\n`);
3027
+ }
2040
3028
  }
2041
- if (commandMatches(args.commandPath, "contract", "entries")) {
2042
- return await handleContractEntries(args, io).then(() => 0);
3029
+ seenControlLengths.set(session.remoteControlFeedKey, remoteFeed.length);
3030
+ }
3031
+ }
3032
+ async function waitForProcessSignal() {
3033
+ await Promise.race([
3034
+ once(process, "SIGINT").then(() => undefined),
3035
+ once(process, "SIGTERM").then(() => undefined)
3036
+ ]);
3037
+ }
3038
+ async function handleDaemonRun(args, io) {
3039
+ const dataDir = requireOption(args, "data-dir");
3040
+ const startup = getDaemonStartupOptions(args);
3041
+ const walletService = await WalletService.create({
3042
+ dataDir,
3043
+ logger: createLogger("warn")
3044
+ });
3045
+ walletService.setRuntimeKey(process.env.EMPORION_WALLET_KEY ?? null);
3046
+ let transport;
3047
+ let sharedContext;
3048
+ let daemon;
3049
+ let discoveryInterval;
3050
+ let walletPollInterval;
3051
+ let autoSettleInterval;
3052
+ let walletStatus = await walletService.daemonStatus();
3053
+ let walletPollRunning = false;
3054
+ let autoSettleRunning = false;
3055
+ const seenControlLengths = new Map();
3056
+ const startedAt = new Date().toISOString();
3057
+ try {
3058
+ transport = await AgentTransport.create(buildTransportConfigFromArgs(args));
3059
+ await transport.start();
3060
+ const activeTransport = transport;
3061
+ sharedContext = await buildDaemonSharedContext(dataDir, activeTransport, walletService);
3062
+ const topics = buildTopicsFromArgs(args, activeTransport.identity.did);
3063
+ startup.topics.push(...topics);
3064
+ for (const topic of startup.topics) {
3065
+ await activeTransport.joinTopic(topic);
2043
3066
  }
2044
- if (commandMatches(args.commandPath, "evidence", "record"))
2045
- return await handleEvidenceRecord(args, io).then(() => 0);
2046
- if (commandMatches(args.commandPath, "oracle", "attest"))
2047
- return await handleOracleAttest(args, io).then(() => 0);
2048
- if (commandMatches(args.commandPath, "dispute", "open"))
2049
- return await handleDisputeOpen(args, io).then(() => 0);
2050
- if (commandMatches(args.commandPath, "dispute", "add-evidence")) {
2051
- return await handleDisputeAddEvidence(args, io).then(() => 0);
3067
+ for (const did of startup.connectDids) {
3068
+ await activeTransport.connectToDid(did);
2052
3069
  }
2053
- if (commandMatches(args.commandPath, "dispute", "request-oracle")) {
2054
- return await handleDisputeRequestOracle(args, io).then(() => 0);
3070
+ for (const publicKey of startup.connectNoiseKeys) {
3071
+ await activeTransport.connectToNoiseKey(publicKey);
2055
3072
  }
2056
- if (commandMatches(args.commandPath, "dispute", "rule"))
2057
- return await handleDisputeRule(args, io).then(() => 0);
2058
- if (commandMatches(args.commandPath, "dispute", "close"))
2059
- return await handleDisputeClose(args, io).then(() => 0);
2060
- if (commandMatches(args.commandPath, "space", "create"))
2061
- return await handleSpaceCreate(args, io).then(() => 0);
2062
- if (commandMatches(args.commandPath, "space", "add-member")) {
2063
- return await handleSpaceMembershipAction(args, io, "space-membership.member-added").then(() => 0);
3073
+ if (startup.watchProtocol) {
3074
+ discoveryInterval = setInterval(() => {
3075
+ void runProtocolDiscoveryWatcher(activeTransport, seenControlLengths, io).catch((error) => {
3076
+ io.stderr(`${error instanceof Error ? error.message : String(error)}\n`);
3077
+ });
3078
+ }, 2_000);
2064
3079
  }
2065
- if (commandMatches(args.commandPath, "space", "remove-member")) {
2066
- return await handleSpaceMembershipAction(args, io, "space-membership.member-removed").then(() => 0);
3080
+ walletPollInterval = setInterval(() => {
3081
+ if (walletPollRunning) {
3082
+ return;
3083
+ }
3084
+ walletPollRunning = true;
3085
+ void (async () => {
3086
+ try {
3087
+ await walletService.pollUpdates();
3088
+ walletStatus = await walletService.daemonStatus();
3089
+ }
3090
+ catch (error) {
3091
+ io.stderr(`${error instanceof Error ? error.message : String(error)}\n`);
3092
+ }
3093
+ finally {
3094
+ walletPollRunning = false;
3095
+ }
3096
+ })();
3097
+ }, 3_000);
3098
+ autoSettleInterval = setInterval(() => {
3099
+ if (autoSettleRunning || !sharedContext) {
3100
+ return;
3101
+ }
3102
+ autoSettleRunning = true;
3103
+ void (async () => {
3104
+ try {
3105
+ const candidates = collectAutoSettleCandidates(sharedContext.repository.getSnapshot());
3106
+ for (const candidate of candidates) {
3107
+ await walletService.attemptAutoSettle(candidate);
3108
+ }
3109
+ walletStatus = await walletService.daemonStatus();
3110
+ }
3111
+ catch (error) {
3112
+ io.stderr(`${error instanceof Error ? error.message : String(error)}\n`);
3113
+ }
3114
+ finally {
3115
+ autoSettleRunning = false;
3116
+ }
3117
+ })();
3118
+ }, 2_000);
3119
+ daemon = new AgentDaemon({
3120
+ dataDir,
3121
+ statusProvider: () => buildDaemonStatus(dataDir, startedAt, activeTransport, walletStatus),
3122
+ commandHandler: async (request) => {
3123
+ if (!sharedContext) {
3124
+ throw new Error("Daemon context is not available");
3125
+ }
3126
+ const result = await CLI_CONTEXT_STORAGE.run(sharedContext, async () => executeCapturedInDaemon({
3127
+ commandPath: request.commandPath,
3128
+ options: daemonOptionsRecordToMap(request.options)
3129
+ }));
3130
+ walletStatus = await walletService.daemonStatus();
3131
+ return result;
3132
+ },
3133
+ onShutdown: async () => {
3134
+ if (discoveryInterval) {
3135
+ clearInterval(discoveryInterval);
3136
+ discoveryInterval = undefined;
3137
+ }
3138
+ if (walletPollInterval) {
3139
+ clearInterval(walletPollInterval);
3140
+ walletPollInterval = undefined;
3141
+ }
3142
+ if (autoSettleInterval) {
3143
+ clearInterval(autoSettleInterval);
3144
+ autoSettleInterval = undefined;
3145
+ }
3146
+ if (sharedContext) {
3147
+ await sharedContext.repository.close();
3148
+ sharedContext = undefined;
3149
+ }
3150
+ await walletService.close();
3151
+ await activeTransport.stop();
3152
+ }
3153
+ });
3154
+ await daemon.start();
3155
+ io.stdout(`${JSON.stringify({ command: "daemon.run", pid: process.pid, endpoint: getLocalControlEndpoint(dataDir).path })}\n`);
3156
+ await Promise.race([daemon.waitForShutdown(), waitForProcessSignal()]);
3157
+ }
3158
+ finally {
3159
+ if (daemon) {
3160
+ await daemon.stop();
2067
3161
  }
2068
- if (commandMatches(args.commandPath, "space", "mute-member")) {
2069
- return await handleSpaceMembershipAction(args, io, "space-membership.member-muted").then(() => 0);
3162
+ else {
3163
+ if (discoveryInterval) {
3164
+ clearInterval(discoveryInterval);
3165
+ }
3166
+ if (walletPollInterval) {
3167
+ clearInterval(walletPollInterval);
3168
+ }
3169
+ if (autoSettleInterval) {
3170
+ clearInterval(autoSettleInterval);
3171
+ }
3172
+ if (sharedContext) {
3173
+ await sharedContext.repository.close();
3174
+ }
3175
+ await walletService.close();
3176
+ if (transport) {
3177
+ await transport.stop();
3178
+ }
2070
3179
  }
2071
- if (commandMatches(args.commandPath, "space", "set-role")) {
2072
- return await handleSpaceMembershipAction(args, io, "space-membership.member-role-updated").then(() => 0);
3180
+ }
3181
+ }
3182
+ async function executeParsedArgs(args, io, options) {
3183
+ if (args.commandPath.length === 0 || hasFlag(args, "help")) {
3184
+ io.stdout(`${usage()}\n`);
3185
+ return 0;
3186
+ }
3187
+ const resolvedArgs = await withResolvedDataDir(args);
3188
+ if (commandMatches(resolvedArgs.commandPath, "daemon", "start"))
3189
+ return await handleDaemonStart(resolvedArgs, io).then(() => 0);
3190
+ if (commandMatches(resolvedArgs.commandPath, "daemon", "status"))
3191
+ return await handleDaemonStatus(resolvedArgs, io).then(() => 0);
3192
+ if (commandMatches(resolvedArgs.commandPath, "daemon", "stop"))
3193
+ return await handleDaemonStop(resolvedArgs, io).then(() => 0);
3194
+ if (commandMatches(resolvedArgs.commandPath, "daemon", "logs"))
3195
+ return await handleDaemonLogs(resolvedArgs, io).then(() => 0);
3196
+ if (commandMatches(resolvedArgs.commandPath, "daemon", "run"))
3197
+ return await handleDaemonRun(resolvedArgs, io).then(() => 0);
3198
+ if (options.allowDaemonProxy) {
3199
+ const dataDir = getDataDirFromArgs(resolvedArgs);
3200
+ if (dataDir) {
3201
+ const activeDaemon = await probeDaemonStatus(dataDir, 1_000);
3202
+ if (activeDaemon) {
3203
+ return proxyParsedArgsToDaemon(withDaemonWalletKeyForwarding(resolvedArgs), io);
3204
+ }
2073
3205
  }
2074
- if (commandMatches(args.commandPath, "space", "entries"))
2075
- return await handleSpaceEntries(args, io).then(() => 0);
2076
- if (commandMatches(args.commandPath, "message", "send"))
2077
- return await handleMessageSend(args, io).then(() => 0);
2078
- if (commandMatches(args.commandPath, "message", "edit"))
2079
- return await handleMessageEdit(args, io).then(() => 0);
2080
- if (commandMatches(args.commandPath, "message", "delete"))
2081
- return await handleMessageDelete(args, io).then(() => 0);
2082
- if (commandMatches(args.commandPath, "message", "react"))
2083
- return await handleMessageReact(args, io).then(() => 0);
2084
- if (commandMatches(args.commandPath, "object", "show"))
2085
- return await handleObjectShow(args, io).then(() => 0);
2086
- if (commandMatches(args.commandPath, "serve"))
2087
- return await handleServe(args, io).then(() => 0);
2088
- throw new Error(`Unknown command: ${args.commandPath.join(" ")}`);
3206
+ }
3207
+ if (commandMatches(resolvedArgs.commandPath, "context", "add"))
3208
+ return await handleContextAdd(resolvedArgs, io).then(() => 0);
3209
+ if (commandMatches(resolvedArgs.commandPath, "context", "use"))
3210
+ return await handleContextUse(resolvedArgs, io).then(() => 0);
3211
+ if (commandMatches(resolvedArgs.commandPath, "context", "list"))
3212
+ return await handleContextList(io).then(() => 0);
3213
+ if (commandMatches(resolvedArgs.commandPath, "context", "show"))
3214
+ return await handleContextShow(io).then(() => 0);
3215
+ if (commandMatches(resolvedArgs.commandPath, "context", "remove"))
3216
+ return await handleContextRemove(resolvedArgs, io).then(() => 0);
3217
+ if (commandMatches(resolvedArgs.commandPath, "agent", "init"))
3218
+ return await handleAgentInit(resolvedArgs, io).then(() => 0);
3219
+ if (commandMatches(resolvedArgs.commandPath, "wallet", "connect", "nwc"))
3220
+ return await handleWalletConnectNwc(resolvedArgs, io).then(() => 0);
3221
+ if (commandMatches(resolvedArgs.commandPath, "wallet", "disconnect"))
3222
+ return await handleWalletDisconnect(resolvedArgs, io).then(() => 0);
3223
+ if (commandMatches(resolvedArgs.commandPath, "wallet", "status"))
3224
+ return await handleWalletStatus(resolvedArgs, io).then(() => 0);
3225
+ if (commandMatches(resolvedArgs.commandPath, "wallet", "unlock"))
3226
+ return await handleWalletUnlock(resolvedArgs, io).then(() => 0);
3227
+ if (commandMatches(resolvedArgs.commandPath, "wallet", "lock"))
3228
+ return await handleWalletLock(resolvedArgs, io).then(() => 0);
3229
+ if (commandMatches(resolvedArgs.commandPath, "wallet", "invoice", "create"))
3230
+ return await handleWalletInvoiceCreate(resolvedArgs, io).then(() => 0);
3231
+ if (commandMatches(resolvedArgs.commandPath, "wallet", "pay", "bolt11"))
3232
+ return await handleWalletPayBolt11(resolvedArgs, io).then(() => 0);
3233
+ if (commandMatches(resolvedArgs.commandPath, "wallet", "ledger", "list"))
3234
+ return await handleWalletLedgerList(resolvedArgs, io).then(() => 0);
3235
+ if (commandMatches(resolvedArgs.commandPath, "wallet", "key", "rotate"))
3236
+ return await handleWalletKeyRotate(resolvedArgs, io).then(() => 0);
3237
+ if (commandMatches(resolvedArgs.commandPath, "deal", "open"))
3238
+ return await handleDealOpen(resolvedArgs, io).then(() => 0);
3239
+ if (commandMatches(resolvedArgs.commandPath, "deal", "propose"))
3240
+ return await handleDealPropose(resolvedArgs, io).then(() => 0);
3241
+ if (commandMatches(resolvedArgs.commandPath, "deal", "accept"))
3242
+ return await handleDealAccept(resolvedArgs, io).then(() => 0);
3243
+ if (commandMatches(resolvedArgs.commandPath, "deal", "start"))
3244
+ return await handleDealStart(resolvedArgs, io).then(() => 0);
3245
+ if (commandMatches(resolvedArgs.commandPath, "deal", "status"))
3246
+ return await handleDealStatus(resolvedArgs, io).then(() => 0);
3247
+ if (commandMatches(resolvedArgs.commandPath, "proof", "submit"))
3248
+ return await handleProofSubmit(resolvedArgs, io).then(() => 0);
3249
+ if (commandMatches(resolvedArgs.commandPath, "proof", "accept"))
3250
+ return await handleProofAccept(resolvedArgs, io).then(() => 0);
3251
+ if (commandMatches(resolvedArgs.commandPath, "settlement", "invoice", "create"))
3252
+ return await handleSettlementInvoiceCreate(resolvedArgs, io).then(() => 0);
3253
+ if (commandMatches(resolvedArgs.commandPath, "settlement", "pay"))
3254
+ return await handleSettlementPay(resolvedArgs, io).then(() => 0);
3255
+ if (commandMatches(resolvedArgs.commandPath, "settlement", "status"))
3256
+ return await handleSettlementStatus(resolvedArgs, io).then(() => 0);
3257
+ if (commandMatches(resolvedArgs.commandPath, "agent", "show"))
3258
+ return await handleAgentShow(resolvedArgs, io).then(() => 0);
3259
+ if (commandMatches(resolvedArgs.commandPath, "agent", "payment-endpoint", "add"))
3260
+ return await handleAgentPaymentEndpointAdd(resolvedArgs, io).then(() => 0);
3261
+ if (commandMatches(resolvedArgs.commandPath, "agent", "payment-endpoint", "remove"))
3262
+ return await handleAgentPaymentEndpointRemove(resolvedArgs, io).then(() => 0);
3263
+ if (commandMatches(resolvedArgs.commandPath, "agent", "wallet-attestation", "add"))
3264
+ return await handleAgentWalletAttestationAdd(resolvedArgs, io).then(() => 0);
3265
+ if (commandMatches(resolvedArgs.commandPath, "agent", "wallet-attestation", "remove"))
3266
+ return await handleAgentWalletAttestationRemove(resolvedArgs, io).then(() => 0);
3267
+ if (commandMatches(resolvedArgs.commandPath, "agent", "feedback", "add"))
3268
+ return await handleAgentFeedbackAdd(resolvedArgs, io).then(() => 0);
3269
+ if (commandMatches(resolvedArgs.commandPath, "agent", "feedback", "remove"))
3270
+ return await handleAgentFeedbackRemove(resolvedArgs, io).then(() => 0);
3271
+ if (commandMatches(resolvedArgs.commandPath, "company", "create"))
3272
+ return await handleCompanyCreate(resolvedArgs, io).then(() => 0);
3273
+ if (commandMatches(resolvedArgs.commandPath, "company", "show"))
3274
+ return await handleCompanyShow(resolvedArgs, io).then(() => 0);
3275
+ if (commandMatches(resolvedArgs.commandPath, "company", "update"))
3276
+ return await handleCompanyUpdate(resolvedArgs, io).then(() => 0);
3277
+ if (commandMatches(resolvedArgs.commandPath, "company", "grant-role"))
3278
+ return await handleCompanyRoleChange(resolvedArgs, io, "grant").then(() => 0);
3279
+ if (commandMatches(resolvedArgs.commandPath, "company", "revoke-role"))
3280
+ return await handleCompanyRoleChange(resolvedArgs, io, "revoke").then(() => 0);
3281
+ if (commandMatches(resolvedArgs.commandPath, "company", "join-market"))
3282
+ return await handleCompanyMarketMembership(resolvedArgs, io, "join").then(() => 0);
3283
+ if (commandMatches(resolvedArgs.commandPath, "company", "leave-market"))
3284
+ return await handleCompanyMarketMembership(resolvedArgs, io, "leave").then(() => 0);
3285
+ if (commandMatches(resolvedArgs.commandPath, "company", "treasury-attest"))
3286
+ return await handleCompanyTreasuryAttest(resolvedArgs, io).then(() => 0);
3287
+ if (commandMatches(resolvedArgs.commandPath, "company", "treasury-reserve"))
3288
+ return await handleCompanyTreasuryReserve(resolvedArgs, io).then(() => 0);
3289
+ if (commandMatches(resolvedArgs.commandPath, "company", "treasury-release"))
3290
+ return await handleCompanyTreasuryRelease(resolvedArgs, io).then(() => 0);
3291
+ if (commandMatches(resolvedArgs.commandPath, "market", "product", "create"))
3292
+ return await handleMarketProductCreate(resolvedArgs, io).then(() => 0);
3293
+ if (commandMatches(resolvedArgs.commandPath, "market", "product", "update"))
3294
+ return await handleMarketProductUpdate(resolvedArgs, io).then(() => 0);
3295
+ if (commandMatches(resolvedArgs.commandPath, "market", "product", "publish"))
3296
+ return await handleMarketProductStateChange(resolvedArgs, io, "product.published").then(() => 0);
3297
+ if (commandMatches(resolvedArgs.commandPath, "market", "product", "unpublish"))
3298
+ return await handleMarketProductStateChange(resolvedArgs, io, "product.unpublished").then(() => 0);
3299
+ if (commandMatches(resolvedArgs.commandPath, "market", "product", "retire"))
3300
+ return await handleMarketProductStateChange(resolvedArgs, io, "product.retired").then(() => 0);
3301
+ if (commandMatches(resolvedArgs.commandPath, "market", "listing", "publish"))
3302
+ return await handleMarketListingPublish(resolvedArgs, io).then(() => 0);
3303
+ if (commandMatches(resolvedArgs.commandPath, "market", "listing", "revise"))
3304
+ return await handleMarketListingRevise(resolvedArgs, io).then(() => 0);
3305
+ if (commandMatches(resolvedArgs.commandPath, "market", "listing", "withdraw"))
3306
+ return await handleSimpleMarketStateChange(resolvedArgs, io, "listing", "listing.withdrawn").then(() => 0);
3307
+ if (commandMatches(resolvedArgs.commandPath, "market", "listing", "expire"))
3308
+ return await handleSimpleMarketStateChange(resolvedArgs, io, "listing", "listing.expired").then(() => 0);
3309
+ if (commandMatches(resolvedArgs.commandPath, "market", "request", "publish"))
3310
+ return await handleMarketRequestPublish(resolvedArgs, io).then(() => 0);
3311
+ if (commandMatches(resolvedArgs.commandPath, "market", "request", "revise"))
3312
+ return await handleMarketRequestRevise(resolvedArgs, io).then(() => 0);
3313
+ if (commandMatches(resolvedArgs.commandPath, "market", "request", "close"))
3314
+ return await handleSimpleMarketStateChange(resolvedArgs, io, "request", "request.closed").then(() => 0);
3315
+ if (commandMatches(resolvedArgs.commandPath, "market", "request", "expire"))
3316
+ return await handleSimpleMarketStateChange(resolvedArgs, io, "request", "request.expired").then(() => 0);
3317
+ if (commandMatches(resolvedArgs.commandPath, "market", "offer", "submit"))
3318
+ return await handleMarketOfferSubmit(resolvedArgs, io).then(() => 0);
3319
+ if (commandMatches(resolvedArgs.commandPath, "market", "offer", "counter"))
3320
+ return await handleMarketOfferCounter(resolvedArgs, io).then(() => 0);
3321
+ if (commandMatches(resolvedArgs.commandPath, "market", "offer", "accept"))
3322
+ return await handleSimpleMarketStateChange(resolvedArgs, io, "offer", "offer.accepted").then(() => 0);
3323
+ if (commandMatches(resolvedArgs.commandPath, "market", "offer", "reject"))
3324
+ return await handleSimpleMarketStateChange(resolvedArgs, io, "offer", "offer.rejected").then(() => 0);
3325
+ if (commandMatches(resolvedArgs.commandPath, "market", "offer", "cancel"))
3326
+ return await handleSimpleMarketStateChange(resolvedArgs, io, "offer", "offer.canceled").then(() => 0);
3327
+ if (commandMatches(resolvedArgs.commandPath, "market", "offer", "expire"))
3328
+ return await handleSimpleMarketStateChange(resolvedArgs, io, "offer", "offer.expired").then(() => 0);
3329
+ if (commandMatches(resolvedArgs.commandPath, "market", "bid", "submit"))
3330
+ return await handleMarketBidSubmit(resolvedArgs, io).then(() => 0);
3331
+ if (commandMatches(resolvedArgs.commandPath, "market", "bid", "counter"))
3332
+ return await handleMarketBidCounter(resolvedArgs, io).then(() => 0);
3333
+ if (commandMatches(resolvedArgs.commandPath, "market", "bid", "accept"))
3334
+ return await handleSimpleMarketStateChange(resolvedArgs, io, "bid", "bid.accepted").then(() => 0);
3335
+ if (commandMatches(resolvedArgs.commandPath, "market", "bid", "reject"))
3336
+ return await handleSimpleMarketStateChange(resolvedArgs, io, "bid", "bid.rejected").then(() => 0);
3337
+ if (commandMatches(resolvedArgs.commandPath, "market", "bid", "cancel"))
3338
+ return await handleSimpleMarketStateChange(resolvedArgs, io, "bid", "bid.canceled").then(() => 0);
3339
+ if (commandMatches(resolvedArgs.commandPath, "market", "bid", "expire"))
3340
+ return await handleSimpleMarketStateChange(resolvedArgs, io, "bid", "bid.expired").then(() => 0);
3341
+ if (commandMatches(resolvedArgs.commandPath, "market", "agreement", "create"))
3342
+ return await handleMarketAgreementCreate(resolvedArgs, io).then(() => 0);
3343
+ if (commandMatches(resolvedArgs.commandPath, "market", "agreement", "complete"))
3344
+ return await handleSimpleMarketStateChange(resolvedArgs, io, "agreement", "agreement.completed").then(() => 0);
3345
+ if (commandMatches(resolvedArgs.commandPath, "market", "agreement", "cancel"))
3346
+ return await handleSimpleMarketStateChange(resolvedArgs, io, "agreement", "agreement.canceled").then(() => 0);
3347
+ if (commandMatches(resolvedArgs.commandPath, "market", "agreement", "dispute"))
3348
+ return await handleSimpleMarketStateChange(resolvedArgs, io, "agreement", "agreement.disputed").then(() => 0);
3349
+ if (commandMatches(resolvedArgs.commandPath, "market", "list"))
3350
+ return await handleMarketList(resolvedArgs, io).then(() => 0);
3351
+ if (commandMatches(resolvedArgs.commandPath, "contract", "create"))
3352
+ return await handleContractCreate(resolvedArgs, io).then(() => 0);
3353
+ if (commandMatches(resolvedArgs.commandPath, "contract", "open-milestone"))
3354
+ return await handleContractMilestoneAction(resolvedArgs, io, "contract.milestone-opened").then(() => 0);
3355
+ if (commandMatches(resolvedArgs.commandPath, "contract", "submit-milestone"))
3356
+ return await handleContractMilestoneAction(resolvedArgs, io, "contract.milestone-submitted").then(() => 0);
3357
+ if (commandMatches(resolvedArgs.commandPath, "contract", "accept-milestone"))
3358
+ return await handleContractMilestoneAction(resolvedArgs, io, "contract.milestone-accepted").then(() => 0);
3359
+ if (commandMatches(resolvedArgs.commandPath, "contract", "reject-milestone"))
3360
+ return await handleContractMilestoneAction(resolvedArgs, io, "contract.milestone-rejected").then(() => 0);
3361
+ if (commandMatches(resolvedArgs.commandPath, "contract", "pause"))
3362
+ return await handleContractStateChange(resolvedArgs, io, "contract.paused").then(() => 0);
3363
+ if (commandMatches(resolvedArgs.commandPath, "contract", "resume"))
3364
+ return await handleContractStateChange(resolvedArgs, io, "contract.resumed").then(() => 0);
3365
+ if (commandMatches(resolvedArgs.commandPath, "contract", "complete"))
3366
+ return await handleContractStateChange(resolvedArgs, io, "contract.completed").then(() => 0);
3367
+ if (commandMatches(resolvedArgs.commandPath, "contract", "cancel"))
3368
+ return await handleContractStateChange(resolvedArgs, io, "contract.canceled").then(() => 0);
3369
+ if (commandMatches(resolvedArgs.commandPath, "contract", "dispute"))
3370
+ return await handleContractStateChange(resolvedArgs, io, "contract.disputed").then(() => 0);
3371
+ if (commandMatches(resolvedArgs.commandPath, "contract", "entries"))
3372
+ return await handleContractEntries(resolvedArgs, io).then(() => 0);
3373
+ if (commandMatches(resolvedArgs.commandPath, "evidence", "record"))
3374
+ return await handleEvidenceRecord(resolvedArgs, io).then(() => 0);
3375
+ if (commandMatches(resolvedArgs.commandPath, "oracle", "attest"))
3376
+ return await handleOracleAttest(resolvedArgs, io).then(() => 0);
3377
+ if (commandMatches(resolvedArgs.commandPath, "dispute", "open"))
3378
+ return await handleDisputeOpen(resolvedArgs, io).then(() => 0);
3379
+ if (commandMatches(resolvedArgs.commandPath, "dispute", "add-evidence"))
3380
+ return await handleDisputeAddEvidence(resolvedArgs, io).then(() => 0);
3381
+ if (commandMatches(resolvedArgs.commandPath, "dispute", "request-oracle"))
3382
+ return await handleDisputeRequestOracle(resolvedArgs, io).then(() => 0);
3383
+ if (commandMatches(resolvedArgs.commandPath, "dispute", "rule"))
3384
+ return await handleDisputeRule(resolvedArgs, io).then(() => 0);
3385
+ if (commandMatches(resolvedArgs.commandPath, "dispute", "close"))
3386
+ return await handleDisputeClose(resolvedArgs, io).then(() => 0);
3387
+ if (commandMatches(resolvedArgs.commandPath, "space", "create"))
3388
+ return await handleSpaceCreate(resolvedArgs, io).then(() => 0);
3389
+ if (commandMatches(resolvedArgs.commandPath, "space", "add-member"))
3390
+ return await handleSpaceMembershipAction(resolvedArgs, io, "space-membership.member-added").then(() => 0);
3391
+ if (commandMatches(resolvedArgs.commandPath, "space", "remove-member"))
3392
+ return await handleSpaceMembershipAction(resolvedArgs, io, "space-membership.member-removed").then(() => 0);
3393
+ if (commandMatches(resolvedArgs.commandPath, "space", "mute-member"))
3394
+ return await handleSpaceMembershipAction(resolvedArgs, io, "space-membership.member-muted").then(() => 0);
3395
+ if (commandMatches(resolvedArgs.commandPath, "space", "set-role"))
3396
+ return await handleSpaceMembershipAction(resolvedArgs, io, "space-membership.member-role-updated").then(() => 0);
3397
+ if (commandMatches(resolvedArgs.commandPath, "space", "entries"))
3398
+ return await handleSpaceEntries(resolvedArgs, io).then(() => 0);
3399
+ if (commandMatches(resolvedArgs.commandPath, "message", "send"))
3400
+ return await handleMessageSend(resolvedArgs, io).then(() => 0);
3401
+ if (commandMatches(resolvedArgs.commandPath, "message", "edit"))
3402
+ return await handleMessageEdit(resolvedArgs, io).then(() => 0);
3403
+ if (commandMatches(resolvedArgs.commandPath, "message", "delete"))
3404
+ return await handleMessageDelete(resolvedArgs, io).then(() => 0);
3405
+ if (commandMatches(resolvedArgs.commandPath, "message", "react"))
3406
+ return await handleMessageReact(resolvedArgs, io).then(() => 0);
3407
+ if (commandMatches(resolvedArgs.commandPath, "object", "show"))
3408
+ return await handleObjectShow(resolvedArgs, io).then(() => 0);
3409
+ throw new Error(`Unknown command: ${resolvedArgs.commandPath.join(" ")}`);
3410
+ }
3411
+ export async function runCli(argv, io = DEFAULT_IO) {
3412
+ try {
3413
+ const args = parseArgs(argv);
3414
+ return await executeParsedArgs(args, io, { allowDaemonProxy: true });
2089
3415
  }
2090
3416
  catch (error) {
2091
3417
  io.stderr(`${error instanceof Error ? error.message : String(error)}\n`);
2092
3418
  return 1;
2093
3419
  }
2094
3420
  }
2095
- const executedPath = process.argv[1] ? new URL(`file://${process.argv[1]}`).href : "";
3421
+ const executedPath = process.argv[1] ? pathToFileURL(process.argv[1]).href : "";
2096
3422
  if (import.meta.url === executedPath) {
2097
3423
  void runCli(process.argv.slice(2)).then((exitCode) => {
2098
3424
  process.exitCode = exitCode;