@arp-protocol/cli 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.
@@ -15,6 +15,19 @@ import {
15
15
  } from "node:fs";
16
16
  import { dirname, join as join2 } from "node:path";
17
17
 
18
+ // ../../packages/protocol/src/share-offer.ts
19
+ var SHARE_OFFER = {
20
+ question: "Share this machine's skill failures as public lines?",
21
+ sends: `When a skill fails here, one public line on Intuition testnet: the skill's public address, its category when known, "failed" and a closed reason (missing-key, missing-program, missing-file).`,
22
+ signer: "Each line is signed by this machine's own ARP address, created on the first send: it is public and the same on every line, so this machine's lines can be told apart from others' and linked together.",
23
+ never: "Never your prompts, your files, your paths, your code or your keys.",
24
+ service: "Sending goes through ARP's sponsoring service, which is not online yet: until it is, nothing is sent and `arp share send` says so.",
25
+ later: "Off by default. Change it any time with `arp share on` or `arp share off`."
26
+ };
27
+ function shareOfferLines() {
28
+ return [SHARE_OFFER.sends, SHARE_OFFER.signer, SHARE_OFFER.never, SHARE_OFFER.service, SHARE_OFFER.later];
29
+ }
30
+
18
31
  // src/install/targets.ts
19
32
  import { existsSync, readFileSync } from "node:fs";
20
33
  import { join } from "node:path";
@@ -52,10 +65,21 @@ var CLAUDE_HOOKS = {
52
65
  shape: "nested",
53
66
  events: [
54
67
  { name: "PostToolUse", matcher: "*" },
55
- { name: "PostToolUseFailure", matcher: "*" }
68
+ { name: "PostToolUseFailure", matcher: "*", sync: true },
69
+ { name: "PreToolUse", matcher: "Bash", sync: true, timeout: 15, if: "Bash(*skills*)" },
70
+ { name: "Stop", sync: true, timeout: 10 }
56
71
  ],
57
- entry: (command) => ({ type: "command", command, async: true, timeout: 10 })
72
+ entry: (command, event) => ({
73
+ type: "command",
74
+ command,
75
+ ...event.if === undefined ? {} : { if: event.if },
76
+ ...event.sync === true ? {} : { async: true },
77
+ timeout: event.timeout ?? 10
78
+ })
58
79
  };
80
+ function eventNames(spec) {
81
+ return [...new Set(spec.events.map((event) => event.name))];
82
+ }
59
83
  function isOurs(hook, marker) {
60
84
  return isRecord(hook) && typeof hook.command === "string" && hook.command.includes(marker);
61
85
  }
@@ -85,16 +109,18 @@ function ours(list, spec, marker) {
85
109
  }
86
110
  function withHooks(settings, spec, command, marker) {
87
111
  const hooks = isRecord(settings.hooks) ? { ...settings.hooks } : {};
88
- for (const event of spec.events) {
89
- const kept = strip(hooks[event.name], spec, marker);
112
+ for (const name of eventNames(spec)) {
113
+ const kept = strip(hooks[name], spec, marker);
90
114
  if (command !== null) {
91
- const entry = spec.entry(command);
92
- kept.push(spec.shape === "flat" ? entry : { ...event.matcher === undefined ? {} : { matcher: event.matcher }, hooks: [entry] });
115
+ for (const event of spec.events.filter((row) => row.name === name)) {
116
+ const entry = spec.entry(command, event);
117
+ kept.push(spec.shape === "flat" ? entry : { ...event.matcher === undefined ? {} : { matcher: event.matcher }, hooks: [entry] });
118
+ }
93
119
  }
94
120
  if (kept.length > 0)
95
- hooks[event.name] = kept;
121
+ hooks[name] = kept;
96
122
  else
97
- delete hooks[event.name];
123
+ delete hooks[name];
98
124
  }
99
125
  const next = { ...settings, hooks };
100
126
  if (Object.keys(hooks).length === 0) {
@@ -110,16 +136,19 @@ function hooksOf(settings, spec, marker) {
110
136
  if (!isRecord(settings.hooks))
111
137
  return {};
112
138
  const out = {};
113
- for (const event of spec.events) {
114
- const found = ours(settings.hooks[event.name], spec, marker);
139
+ for (const name of eventNames(spec)) {
140
+ const found = ours(settings.hooks[name], spec, marker);
115
141
  if (found.length > 0)
116
- out[event.name] = found;
142
+ out[name] = found;
117
143
  }
118
144
  return out;
119
145
  }
120
146
  function hasHooks(settings, spec, marker) {
121
147
  const found = hooksOf(settings, spec, marker);
122
- return spec.events.every((event) => (event.name in found));
148
+ return eventNames(spec).every((name) => {
149
+ const list = found[name];
150
+ return Array.isArray(list) && list.length >= spec.events.filter((row) => row.name === name).length;
151
+ });
123
152
  }
124
153
  var TOML_BEGIN = "# >>> arp — managed by `arp init`, removed by `arp uninstall` >>>";
125
154
  var TOML_END = "# <<< arp <<<";
@@ -328,7 +357,7 @@ function claudeMcpSteps(env, commands, project, remove, allowCli) {
328
357
  ];
329
358
  }
330
359
  function claudeHookStep(env, commands, project, remove) {
331
- return jsonStep(claudeFiles(env, project).settings, (settings) => withHooks(settings, CLAUDE_HOOKS, remove ? null : commands.hook, commands.hookFile), remove ? "hooks: arp removed from PostToolUse and PostToolUseFailure" : "hooks: arp on PostToolUse and PostToolUseFailure, async", (settings) => hooksOf(settings, CLAUDE_HOOKS, commands.hookFile));
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));
332
361
  }
333
362
  var CLAUDE_HOOKS_SEEN_FROM = [2, 1, 316];
334
363
  function claudeVersionCheck(env) {
@@ -361,7 +390,7 @@ function claudeChecks(env, commands, project) {
361
390
  viaCli || SERVER_NAME in servers
362
391
  ],
363
392
  [
364
- `hook on PostToolUse and PostToolUseFailure (${files.settings})`,
393
+ `hook on ${eventNames(CLAUDE_HOOKS).join(", ")} (${files.settings})`,
365
394
  !(settings instanceof Error) && hasHooks(settings, CLAUDE_HOOKS, commands.hookFile)
366
395
  ],
367
396
  ...claudeVersionCheck(env)
@@ -424,7 +453,7 @@ function noHook(path, spec, commands) {
424
453
  return removal.kind === "file" && removal.before !== removal.after ? removal : NO_HOOK;
425
454
  }
426
455
  function hookStep(path, spec, commands, remove) {
427
- return jsonStep(path, (settings) => withHooks(settings, spec, remove ? null : commands.hook, commands.hookFile), remove ? `hooks: arp removed (${spec.events.map((e) => e.name).join(", ")})` : `hooks: arp on ${spec.events.map((e) => e.name).join(", ")}${commands.capture ? ", capturing" : ""}`, (settings) => hooksOf(settings, spec, commands.hookFile));
456
+ return jsonStep(path, (settings) => withHooks(settings, spec, remove ? null : commands.hook, commands.hookFile), remove ? `hooks: arp removed (${eventNames(spec).join(", ")})` : `hooks: arp on ${eventNames(spec).join(", ")}${commands.capture ? ", capturing" : ""}`, (settings) => hooksOf(settings, spec, commands.hookFile));
428
457
  }
429
458
  function jsonMcpRuntime(options) {
430
459
  const { id, name, dir, file, typed, binary, hook } = options;
@@ -558,6 +587,7 @@ function parseFlags(args) {
558
587
  print: false,
559
588
  capture: false,
560
589
  sponsored: false,
590
+ share: null,
561
591
  force: false,
562
592
  runtimes: null
563
593
  };
@@ -579,6 +609,10 @@ function parseFlags(args) {
579
609
  flags.sponsored = true;
580
610
  else if (arg === "--force")
581
611
  flags.force = true;
612
+ else if (arg === "--share")
613
+ flags.share = true;
614
+ else if (arg === "--no-share")
615
+ flags.share = false;
582
616
  else if (arg === "--runtime") {
583
617
  const list = (args[i + 1] ?? "").split(",").filter((name) => name.length > 0);
584
618
  i += 1;
@@ -733,7 +767,34 @@ async function init(env, flags, cliPath) {
733
767
  if (installsHook(runtimes, flags.capture)) {
734
768
  plan.push({ runtime: "hook", steps: [hookFileStep(env, commands, false)] });
735
769
  }
736
- return change(env, flags, plan, "Install");
770
+ const code = await change(env, flags, plan, "Install");
771
+ if ((code === EXIT.ok || code === EXIT.partly) && !flags.dryRun)
772
+ await offerSharing(env, flags);
773
+ return code;
774
+ }
775
+ async function offerSharing(env, flags) {
776
+ const home = env.arpHome ?? join2(env.home, ".arp");
777
+ const file = join2(home, "sharing.json");
778
+ const save = (on) => {
779
+ mkdirSync(home, { recursive: true });
780
+ writeFileSync(file, `${JSON.stringify({ failures: on })}
781
+ `);
782
+ };
783
+ if (flags.share !== null) {
784
+ save(flags.share);
785
+ env.out(`Sharing ${flags.share ? "on" : "off"}. ${SHARE_OFFER.later}`);
786
+ return;
787
+ }
788
+ if (existsSync2(file))
789
+ return;
790
+ if (flags.yes || flags.noInput || !env.interactive) {
791
+ env.out(`Sharing is off. ${SHARE_OFFER.later}`);
792
+ return;
793
+ }
794
+ env.out("");
795
+ for (const line of shareOfferLines())
796
+ env.out(line);
797
+ save(await env.ask(SHARE_OFFER.question));
737
798
  }
738
799
  async function uninstall(env, flags, cliPath) {
739
800
  const commands = commandsFor(env, cliPath);
@@ -20,6 +20,7 @@ function which(command) {
20
20
  function processEnv(dist, version) {
21
21
  return {
22
22
  home: homedir(),
23
+ arpHome: process.env["ARP_HOME"]?.trim() || join(homedir(), ".arp"),
23
24
  cwd: process.cwd(),
24
25
  which,
25
26
  run: (argv, stdin, timeoutMs = 15000) => {
@@ -20,11 +20,11 @@ import {
20
20
  refuseMovedState,
21
21
  serveFetch,
22
22
  stateFile
23
- } from "./cli-tyey74p3.js";
23
+ } from "./cli-n4qagybd.js";
24
24
  import {
25
25
  ERC8004_REGISTRY
26
- } from "./cli-v446mw5v.js";
27
- import"./cli-0hqxver6.js";
26
+ } from "./cli-fbwpcfvp.js";
27
+ import"./cli-mhzh9z8k.js";
28
28
  import"./cli-d3dy7cxe.js";
29
29
  import"./cli-3kbbwfbp.js";
30
30
  import"./cli-g5zvr10v.js";
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ agentFor,
4
+ chainId,
5
+ domainWalk,
6
+ domainWords,
7
+ loopConfig,
8
+ sponsorApproval,
9
+ writeLine
10
+ } from "./cli-10wacn76.js";
11
+ import"./cli-00qp1sj2.js";
12
+ import"./cli-n4qagybd.js";
13
+ import {
14
+ USAGE_REASONS,
15
+ X402_DEFAULT_NETWORKS,
16
+ chainIdOfNetwork,
17
+ isCategoryIn,
18
+ isExactEndpointUrl,
19
+ isTransactionAbsent,
20
+ isUsageOutcome,
21
+ isUsageReason,
22
+ needShapeProblem,
23
+ normaliseCategoryLabel,
24
+ readDomainTree,
25
+ resolveDomain,
26
+ transfersIn,
27
+ x402NetworksFrom,
28
+ x402Reading
29
+ } from "./cli-wev0jgf7.js";
30
+ import"./cli-0gsbnq5w.js";
31
+ import"./cli-fbwpcfvp.js";
32
+ import"./cli-mhzh9z8k.js";
33
+ import"./cli-d3dy7cxe.js";
34
+ import"./cli-3kbbwfbp.js";
35
+ import"./cli-g5zvr10v.js";
36
+
37
+ // ../mcp/src/line.ts
38
+ function lineNetworks(env = process.env) {
39
+ return x402NetworksFrom(X402_DEFAULT_NETWORKS, env);
40
+ }
41
+ function lineFromArgs(args, env = process.env) {
42
+ if (args.parent === undefined !== (args.why === undefined))
43
+ return new Error("--parent and --why go together");
44
+ if (args.paymentTx === undefined !== (args.network === undefined))
45
+ return new Error("--payment-tx and --network go together");
46
+ if (args.network !== undefined && chainIdOfNetwork(args.network) === null) {
47
+ return new Error("--network is a CAIP-2 EVM network, such as eip155:8453 for Base");
48
+ }
49
+ if (args.network !== undefined) {
50
+ let networks;
51
+ try {
52
+ networks = lineNetworks(env);
53
+ } catch (error) {
54
+ return error instanceof Error ? error : new Error(String(error));
55
+ }
56
+ if (!networks.has(args.network))
57
+ return new Error(`--network must be one of ${[...networks.keys()].join(", ")}`);
58
+ }
59
+ if (args.paymentTx !== undefined && !/^0x[0-9a-fA-F]{64}$/.test(args.paymentTx))
60
+ 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");
63
+ }
64
+ if (args.parent !== undefined && args.domain === undefined)
65
+ return new Error("--parent and --why add a --domain");
66
+ if (args.why !== undefined && args.why.trim() === "")
67
+ return new Error("--why says why no category fits");
68
+ if (!isUsageOutcome(args.outcome))
69
+ return new Error("--outcome must be worked or failed");
70
+ if (args.reason !== undefined && !isUsageReason(args.reason)) {
71
+ return new Error(`--reason must be one of ${USAGE_REASONS.join(", ")}`);
72
+ }
73
+ if (args.reason !== undefined && args.outcome !== "failed")
74
+ return new Error("a reason only follows --outcome failed");
75
+ if (args.needs !== undefined && args.outcome !== "failed")
76
+ return new Error("--needs follows --outcome failed; a worked line takes --with");
77
+ if (args.with !== undefined && args.outcome !== "worked")
78
+ return new Error("--with follows --outcome worked; a failure takes --needs");
79
+ const need = args.needs ?? args.with;
80
+ if (need !== undefined) {
81
+ const problem = needShapeProblem({ need, outcome: args.outcome, reason: args.reason ?? "" });
82
+ if (problem !== null)
83
+ return new Error(problem);
84
+ if (args.paymentTx !== undefined)
85
+ return new Error("--needs and --with name a skill's word; a paid endpoint has no SKILL.md");
86
+ }
87
+ return {
88
+ toolUrl: args.toolUrl,
89
+ outcome: args.outcome,
90
+ ...args.domain === undefined ? {} : { domain: args.domain },
91
+ ...args.reason === undefined ? {} : { reason: args.reason },
92
+ ...args.parent === undefined || args.why === undefined ? {} : { newDomain: { parent: args.parent, why: args.why.trim() } },
93
+ ...args.paymentTx === undefined || args.network === undefined ? {} : { payment: { network: args.network, txHash: args.paymentTx.toLowerCase() } },
94
+ ...need === undefined ? {} : { need }
95
+ };
96
+ }
97
+ async function readLinePayment(input, reading) {
98
+ if (input.payment === undefined)
99
+ return null;
100
+ const { network, txHash } = input.payment;
101
+ const assets = reading.policy.get(network)?.assets ?? [];
102
+ const reads = reading.readsFor(network);
103
+ let receipt;
104
+ try {
105
+ receipt = await reads.getTransactionReceipt({ hash: txHash });
106
+ } catch (error) {
107
+ if (isTransactionAbsent(error))
108
+ throw new Error(`transaction ${txHash} not found on ${network}`);
109
+ throw new Error(`${network} could not be read: ${error instanceof Error ? error.message.split(`
110
+ `)[0] : String(error)}`);
111
+ }
112
+ if (receipt.status !== "success")
113
+ throw new Error(`the payment ${txHash} reverted on ${network}`);
114
+ const transfers = transfersIn(receipt.logs, assets);
115
+ if (transfers.length === 0)
116
+ throw new Error(`${txHash} on ${network} moves no accepted asset (${assets.join(", ")})`);
117
+ return { network, txHash, transfers: transfers.map((transfer) => ({ ...transfer, amount: transfer.amount.toString() })) };
118
+ }
119
+ async function writeLineChecked(steps, input) {
120
+ let line = input;
121
+ let payment = null;
122
+ if (input.payment !== undefined) {
123
+ if (!isExactEndpointUrl(input.toolUrl))
124
+ throw new Error(`${input.toolUrl} is not the exact http(s) URL that was paid`);
125
+ if (steps.payments === undefined)
126
+ throw new Error("this build reads no payment network");
127
+ payment = await readLinePayment(input, steps.payments);
128
+ }
129
+ if (input.domain !== undefined) {
130
+ const domain = normaliseCategoryLabel(input.domain);
131
+ const resolved = await steps.isCategory(domain) ? null : await steps.resolve?.(domain);
132
+ if (resolved === null || resolved?.status === "node") {
133
+ const { newDomain: _unused, ...rest } = input;
134
+ line = { ...rest, domain: resolved?.status === "node" ? resolved.label : domain };
135
+ } else if (input.newDomain === undefined) {
136
+ const closest = resolved?.status === "unknown" && resolved.suggestions.length > 0 ? ` Closest: ${resolved.suggestions.join(", ")}.` : "";
137
+ throw new Error(`"${input.domain}" is not a category under tool.${closest} Pick one, or add it with --parent <category> --why <reason>`);
138
+ } else {
139
+ const parent = normaliseCategoryLabel(input.newDomain.parent);
140
+ if (!await steps.isCategory(parent))
141
+ throw new Error(`--parent "${input.newDomain.parent}" is not a category under tool`);
142
+ line = { ...input, domain, newDomain: { parent, why: input.newDomain.why } };
143
+ }
144
+ }
145
+ await sponsorApproval(steps.config);
146
+ const written = await (steps.write ?? writeLine)(steps.config, steps.agent(), line);
147
+ return payment === null ? written : { ...written, payment };
148
+ }
149
+ async function writeLineHere(input) {
150
+ if (chainId !== 13579)
151
+ throw new Error("a line is written on testnet 13579 only for now");
152
+ return writeLineChecked({
153
+ config: loopConfig(),
154
+ isCategory: async (label) => isCategoryIn(await readDomainTree(domainWalk()))(label),
155
+ resolve: async (word) => resolveDomain(word, domainWords(await readDomainTree(domainWalk()))),
156
+ agent: agentFor,
157
+ payments: x402Reading(lineNetworks(), process.env)
158
+ }, input);
159
+ }
160
+ export {
161
+ writeLineHere,
162
+ writeLineChecked,
163
+ readLinePayment,
164
+ lineNetworks,
165
+ lineFromArgs
166
+ };
@@ -1,20 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  resolveNodeKey
4
- } from "./cli-zpft011j.js";
5
- import"./cli-tyey74p3.js";
4
+ } from "./cli-64fx73kx.js";
5
+ import"./cli-n4qagybd.js";
6
6
  import {
7
7
  asEndpointBinding,
8
8
  endpointBindingDocument,
9
9
  normaliseEndpoint,
10
10
  parseAgentRegistry,
11
11
  validateHandshakeRequest
12
- } from "./cli-74hv4etk.js";
13
- import"./cli-c8568g4c.js";
14
- import"./cli-v446mw5v.js";
12
+ } from "./cli-wev0jgf7.js";
13
+ import"./cli-0gsbnq5w.js";
14
+ import"./cli-fbwpcfvp.js";
15
15
  import {
16
16
  isHex
17
- } from "./cli-0hqxver6.js";
17
+ } from "./cli-mhzh9z8k.js";
18
18
  import"./cli-d3dy7cxe.js";
19
19
  import"./cli-3kbbwfbp.js";
20
20
  import"./cli-g5zvr10v.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  resolveNodeKey
4
- } from "./cli-zpft011j.js";
4
+ } from "./cli-64fx73kx.js";
5
5
  import {
6
6
  appendJsonLine,
7
7
  bigintSafe,
@@ -9,7 +9,7 @@ import {
9
9
  readJsonLines,
10
10
  serveFetch,
11
11
  stateFile
12
- } from "./cli-tyey74p3.js";
12
+ } from "./cli-n4qagybd.js";
13
13
  import {
14
14
  HIRE_RECEIPT_VERSION,
15
15
  createPublicClient,
@@ -18,15 +18,15 @@ import {
18
18
  normaliseEndpoint,
19
19
  parseEther,
20
20
  signHireReceipt
21
- } from "./cli-74hv4etk.js";
22
- import"./cli-c8568g4c.js";
21
+ } from "./cli-wev0jgf7.js";
22
+ import"./cli-0gsbnq5w.js";
23
23
  import {
24
24
  ERC8004_REGISTRY
25
- } from "./cli-v446mw5v.js";
25
+ } from "./cli-fbwpcfvp.js";
26
26
  import {
27
27
  formatEther,
28
28
  isAddress
29
- } from "./cli-0hqxver6.js";
29
+ } from "./cli-mhzh9z8k.js";
30
30
  import"./cli-d3dy7cxe.js";
31
31
  import"./cli-3kbbwfbp.js";
32
32
  import"./cli-g5zvr10v.js";