@arp-protocol/cli 0.0.3 → 0.0.5

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.
@@ -13,7 +13,7 @@ import {
13
13
  statSync,
14
14
  writeFileSync
15
15
  } from "node:fs";
16
- import { dirname, join as join2 } from "node:path";
16
+ import { dirname as dirname2, join as join2 } from "node:path";
17
17
 
18
18
  // ../../packages/protocol/src/share-offer.ts
19
19
  var SHARE_OFFER = {
@@ -28,9 +28,15 @@ function shareOfferLines() {
28
28
  return [SHARE_OFFER.sends, SHARE_OFFER.signer, SHARE_OFFER.never, SHARE_OFFER.service, SHARE_OFFER.later];
29
29
  }
30
30
 
31
+ // ../../packages/protocol/src/sharing-state.ts
32
+ function sharingFileText(on, how, at) {
33
+ return `${JSON.stringify({ failures: on, at: at.toISOString(), how })}
34
+ `;
35
+ }
36
+
31
37
  // src/install/targets.ts
32
38
  import { existsSync, readFileSync } from "node:fs";
33
- import { join } from "node:path";
39
+ import { dirname, join } from "node:path";
34
40
 
35
41
  // src/install/edits.ts
36
42
  function isRecord(value) {
@@ -64,10 +70,12 @@ function withMcpServer(config, name, entry) {
64
70
  var CLAUDE_HOOKS = {
65
71
  shape: "nested",
66
72
  events: [
67
- { name: "PostToolUse", matcher: "*" },
73
+ { name: "PostToolUse", matcher: "^(?!Skill$)" },
74
+ { name: "PostToolUse", matcher: "Skill", sync: true, timeout: 5 },
68
75
  { name: "PostToolUseFailure", matcher: "*", sync: true },
69
76
  { name: "PreToolUse", matcher: "Bash", sync: true, timeout: 15, if: "Bash(*skills*)" },
70
- { name: "Stop", sync: true, timeout: 10 }
77
+ { name: "Stop", sync: true, timeout: 10 },
78
+ { name: "SessionEnd", sync: true, timeout: 5 }
71
79
  ],
72
80
  entry: (command, event) => ({
73
81
  type: "command",
@@ -243,10 +251,10 @@ function read(path) {
243
251
  return null;
244
252
  }
245
253
  }
246
- function npxLaunch(pkg) {
254
+ function npxLaunch(pkg, command = "mcp") {
247
255
  return {
248
256
  command: "sh",
249
- args: ["-c", `export ARP_PROJECT_DIR="$PWD"; cd / && exec npx --prefix / -y --fetch-retries=0 --fetch-timeout=5000 ${pkg} mcp`]
257
+ args: ["-c", `export ARP_PROJECT_DIR="$PWD"; cd / && exec npx --prefix / -y --fetch-retries=0 --fetch-timeout=5000 ${pkg} ${command}`]
250
258
  };
251
259
  }
252
260
  var SERVER_ENV = { ARP_TARGET: "testnet" };
@@ -265,7 +273,8 @@ function commandsFor(env, cliPath, capture = false, sponsored = false, force = f
265
273
  env: sponsored ? SPONSORED_ENV : SERVER_ENV
266
274
  };
267
275
  const hook = `"${env.node}" "${hookFile}"${capture ? " --capture" : ""}`;
268
- return { mcp, hook, hookFile, cliPath, capture, force };
276
+ const send = fromNpx ? npxLaunch(`@arp-protocol/cli@${env.version}`, "share send --auto") : { command: env.node, args: [cliPath, "share", "send", "--auto"] };
277
+ return { mcp, hook, hookFile, sendFile: join(dirname(hookFile), "send.json"), send, cliPath, capture, force };
269
278
  }
270
279
  function diffLines(before, after) {
271
280
  const lines = (value, sign) => value === undefined || typeof value === "object" && value !== null && Object.keys(value).length === 0 ? [] : JSON.stringify(value, null, 2).split(`
@@ -356,8 +365,17 @@ function claudeMcpSteps(env, commands, project, remove, allowCli) {
356
365
  jsonStep(files.mcp, (config) => withMcpServer(config, SERVER_NAME, remove ? null : mcpEntry(commands.mcp, false)), remove ? "mcpServers.arp removed" : `mcpServers.arp → ${commands.mcp.command} ${commands.mcp.args.join(" ")}`, (config) => serversOf(config)[SERVER_NAME])
357
366
  ];
358
367
  }
368
+ function waitedLabel(event) {
369
+ if (event.sync !== true)
370
+ return "";
371
+ if (event.if !== undefined)
372
+ return " (waited on, Bash skills installs only)";
373
+ if (event.matcher !== undefined && event.matcher !== "*")
374
+ return ` (waited on, ${event.matcher} calls only)`;
375
+ return " (waited on)";
376
+ }
359
377
  function claudeHookStep(env, commands, project, remove) {
360
- return jsonStep(claudeFiles(env, project).settings, (settings) => withHooks(settings, CLAUDE_HOOKS, remove ? null : commands.hook, commands.hookFile), remove ? `hooks: arp removed from ${eventNames(CLAUDE_HOOKS).join(", ")}` : `hooks: arp on ${CLAUDE_HOOKS.events.map((event) => `${event.name}${event.sync !== true ? "" : event.if === undefined ? " (waited on)" : " (waited on, Bash skills installs only)"}`).join(", ")}`, (settings) => hooksOf(settings, CLAUDE_HOOKS, commands.hookFile));
378
+ return jsonStep(claudeFiles(env, project).settings, (settings) => withHooks(settings, CLAUDE_HOOKS, remove ? null : commands.hook, commands.hookFile), remove ? `hooks: arp removed from ${eventNames(CLAUDE_HOOKS).join(", ")}` : `hooks: arp on ${CLAUDE_HOOKS.events.map((event) => `${event.name}${waitedLabel(event)}`).join(", ")}`, (settings) => hooksOf(settings, CLAUDE_HOOKS, commands.hookFile));
361
379
  }
362
380
  var CLAUDE_HOOKS_SEEN_FROM = [2, 1, 316];
363
381
  function claudeVersionCheck(env) {
@@ -659,7 +677,7 @@ function apply(env, step) {
659
677
  rmSync(step.path, { force: true });
660
678
  return null;
661
679
  }
662
- mkdirSync(dirname(step.path), { recursive: true });
680
+ mkdirSync(dirname2(step.path), { recursive: true });
663
681
  const mode = step.before === null ? undefined : statSync(step.path).mode & 511;
664
682
  const temp = `${step.path}.arp-tmp`;
665
683
  writeFileSync(temp, step.after, mode === undefined ? undefined : { mode });
@@ -690,6 +708,19 @@ function hookFileStep(env, commands, remove) {
690
708
  diff: []
691
709
  };
692
710
  }
711
+ function sendFileStep(commands, remove) {
712
+ const before = read2(commands.sendFile);
713
+ const after = remove ? null : `${JSON.stringify(commands.send, null, 2)}
714
+ `;
715
+ return {
716
+ kind: "file",
717
+ path: commands.sendFile,
718
+ before,
719
+ after,
720
+ summary: remove ? "removed" : "how the hook starts `arp share send --auto` when sharing is on",
721
+ diff: []
722
+ };
723
+ }
693
724
  async function consent(env, flags, question) {
694
725
  if (flags.yes)
695
726
  return true;
@@ -765,7 +796,7 @@ async function init(env, flags, cliPath) {
765
796
  steps: runtime.install(env, commands, flags.project)
766
797
  }));
767
798
  if (installsHook(runtimes, flags.capture)) {
768
- plan.push({ runtime: "hook", steps: [hookFileStep(env, commands, false)] });
799
+ plan.push({ runtime: "hook", steps: [hookFileStep(env, commands, false), sendFileStep(commands, false)] });
769
800
  }
770
801
  const code = await change(env, flags, plan, "Install");
771
802
  if ((code === EXIT.ok || code === EXIT.partly) && !flags.dryRun)
@@ -777,8 +808,7 @@ async function offerSharing(env, flags) {
777
808
  const file = join2(home, "sharing.json");
778
809
  const save = (on) => {
779
810
  mkdirSync(home, { recursive: true });
780
- writeFileSync(file, `${JSON.stringify({ failures: on })}
781
- `);
811
+ writeFileSync(file, sharingFileText(on, "init", env.now()));
782
812
  };
783
813
  if (flags.share !== null) {
784
814
  save(flags.share);
@@ -803,7 +833,7 @@ async function uninstall(env, flags, cliPath) {
803
833
  runtime: runtime.name,
804
834
  steps: runtime.remove(env, commands, flags.project)
805
835
  }));
806
- plan.push({ runtime: "hook", steps: [hookFileStep(env, commands, true)] });
836
+ plan.push({ runtime: "hook", steps: [hookFileStep(env, commands, true), sendFileStep(commands, true)] });
807
837
  const code = await change(env, flags, plan, "Remove");
808
838
  const captures = join2(env.home, ".arp", "captures");
809
839
  if (existsSync2(captures))
@@ -7,7 +7,8 @@ import {
7
7
  loopConfig,
8
8
  sponsorApproval,
9
9
  writeLine
10
- } from "./cli-10wacn76.js";
10
+ } from "./cli-jew4syvf.js";
11
+ import"./cli-7c393v47.js";
11
12
  import"./cli-00qp1sj2.js";
12
13
  import"./cli-n4qagybd.js";
13
14
  import {
@@ -22,11 +23,13 @@ import {
22
23
  needShapeProblem,
23
24
  normaliseCategoryLabel,
24
25
  readDomainTree,
26
+ redactEndpointUrl,
25
27
  resolveDomain,
26
28
  transfersIn,
27
29
  x402NetworksFrom,
28
30
  x402Reading
29
- } from "./cli-wev0jgf7.js";
31
+ } from "./cli-g3q6fxm0.js";
32
+ import"./cli-zehr6kdx.js";
30
33
  import"./cli-0gsbnq5w.js";
31
34
  import"./cli-fbwpcfvp.js";
32
35
  import"./cli-mhzh9z8k.js";
@@ -58,8 +61,22 @@ function lineFromArgs(args, env = process.env) {
58
61
  }
59
62
  if (args.paymentTx !== undefined && !/^0x[0-9a-fA-F]{64}$/.test(args.paymentTx))
60
63
  return new Error("--payment-tx is a 32-byte transaction hash");
61
- if (args.paymentTx !== undefined && !isExactEndpointUrl(args.toolUrl)) {
62
- return new Error("with --payment-tx, --tool is the exact URL you paid, scheme included, as you called it");
64
+ const terms = termsFromArgs(args);
65
+ if (terms instanceof Error)
66
+ return terms;
67
+ if (args.reason === "no-delivery" && args.paymentTx === undefined) {
68
+ return new Error("--reason no-delivery means paid, nothing received: it takes --payment-tx and --network");
69
+ }
70
+ let toolUrl = args.toolUrl;
71
+ if (args.paymentTx !== undefined) {
72
+ if (!isExactEndpointUrl(args.toolUrl)) {
73
+ return new Error("with --payment-tx, --tool is the exact URL you paid, scheme included, as you called it");
74
+ }
75
+ try {
76
+ toolUrl = redactEndpointUrl(args.toolUrl);
77
+ } catch (error) {
78
+ return error instanceof Error ? error : new Error(String(error));
79
+ }
63
80
  }
64
81
  if (args.parent !== undefined && args.domain === undefined)
65
82
  return new Error("--parent and --why add a --domain");
@@ -85,15 +102,42 @@ function lineFromArgs(args, env = process.env) {
85
102
  return new Error("--needs and --with name a skill's word; a paid endpoint has no SKILL.md");
86
103
  }
87
104
  return {
88
- toolUrl: args.toolUrl,
105
+ toolUrl,
89
106
  outcome: args.outcome,
90
107
  ...args.domain === undefined ? {} : { domain: args.domain },
91
108
  ...args.reason === undefined ? {} : { reason: args.reason },
92
109
  ...args.parent === undefined || args.why === undefined ? {} : { newDomain: { parent: args.parent, why: args.why.trim() } },
93
110
  ...args.paymentTx === undefined || args.network === undefined ? {} : { payment: { network: args.network, txHash: args.paymentTx.toLowerCase() } },
111
+ ...terms === null ? {} : { terms },
94
112
  ...need === undefined ? {} : { need }
95
113
  };
96
114
  }
115
+ var TERMS_FLAGS = ["--pay-to", "--asset", "--price", "--terms-at"];
116
+ function termsFromArgs(args) {
117
+ const given = [args.payTo, args.asset, args.price, args.termsAt];
118
+ if (args.paymentTx === undefined) {
119
+ return given.every((value) => value === undefined) ? null : new Error(`${TERMS_FLAGS.join(", ")} follow --payment-tx`);
120
+ }
121
+ if (given.some((value) => value === undefined)) {
122
+ return new Error(`with --payment-tx, name the terms the 402 gave and you paid against: ${TERMS_FLAGS.join(" ")}`);
123
+ }
124
+ const { payTo, asset, price, termsAt } = args;
125
+ if (!/^0x[0-9a-fA-F]{40}$/.test(payTo))
126
+ return new Error("--pay-to is the 402's payTo address");
127
+ if (!/^0x[0-9a-fA-F]{40}$/.test(asset))
128
+ return new Error("--asset is the 402's asset address");
129
+ if (!/^[1-9][0-9]{0,77}$/.test(price))
130
+ return new Error("--price is the 402's amount, a positive integer in the asset's smallest unit");
131
+ const receivedAt = /^[0-9]+$/.test(termsAt) ? Number(termsAt) : Math.floor(Date.parse(termsAt) / 1000);
132
+ if (!Number.isSafeInteger(receivedAt) || receivedAt <= 0)
133
+ return new Error("--terms-at is when the 402 was received: unix seconds or ISO 8601");
134
+ if (receivedAt > Math.floor(Date.now() / 1000) + 300)
135
+ return new Error("--terms-at is in the future");
136
+ return { payTo, asset, price, receivedAt };
137
+ }
138
+ function matchesTerms(payment, terms) {
139
+ return payment.transfers.some((transfer) => transfer.payTo.toLowerCase() === terms.payTo.toLowerCase() && transfer.asset.toLowerCase() === terms.asset.toLowerCase() && BigInt(transfer.amount) >= BigInt(terms.price));
140
+ }
97
141
  async function readLinePayment(input, reading) {
98
142
  if (input.payment === undefined)
99
143
  return null;
@@ -125,6 +169,11 @@ async function writeLineChecked(steps, input) {
125
169
  if (steps.payments === undefined)
126
170
  throw new Error("this build reads no payment network");
127
171
  payment = await readLinePayment(input, steps.payments);
172
+ if (input.terms === undefined)
173
+ throw new Error("a line with a payment names the terms it paid against");
174
+ if (payment !== null && !matchesTerms(payment, input.terms)) {
175
+ throw new Error(`no transfer in ${input.payment.txHash} pays ${input.terms.payTo} at least ${input.terms.price} of ${input.terms.asset}: check --pay-to, --asset and --price`);
176
+ }
128
177
  }
129
178
  if (input.domain !== undefined) {
130
179
  const domain = normaliseCategoryLabel(input.domain);
@@ -18,7 +18,8 @@ import {
18
18
  normaliseEndpoint,
19
19
  parseEther,
20
20
  signHireReceipt
21
- } from "./cli-wev0jgf7.js";
21
+ } from "./cli-g3q6fxm0.js";
22
+ import"./cli-zehr6kdx.js";
22
23
  import"./cli-0gsbnq5w.js";
23
24
  import {
24
25
  ERC8004_REGISTRY
@@ -9,7 +9,8 @@ import {
9
9
  normaliseEndpoint,
10
10
  parseAgentRegistry,
11
11
  validateHandshakeRequest
12
- } from "./cli-wev0jgf7.js";
12
+ } from "./cli-g3q6fxm0.js";
13
+ import"./cli-zehr6kdx.js";
13
14
  import"./cli-0gsbnq5w.js";
14
15
  import"./cli-fbwpcfvp.js";
15
16
  import {