@arp-protocol/cli 0.0.1 → 0.0.4

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 (31) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/LICENSE +26 -0
  3. package/README.md +22 -8
  4. package/dist/chunks/backfill-5zmh0t43.js +1050 -0
  5. package/dist/chunks/{ccip-qmytgn8e.js → ccip-zxtpc4fx.js} +4 -4
  6. package/dist/chunks/check-k4qnc9ee.js +2011 -0
  7. package/dist/chunks/cli-00qp1sj2.js +116 -0
  8. package/dist/chunks/{cli-c8568g4c.js → cli-0gsbnq5w.js} +72 -72
  9. package/dist/chunks/{cli-zpft011j.js → cli-64fx73kx.js} +3 -3
  10. package/dist/chunks/cli-7c393v47.js +173 -0
  11. package/dist/chunks/{cli-0z6f3603.js → cli-8m56jsfx.js} +239 -183
  12. package/dist/chunks/{cli-v446mw5v.js → cli-fbwpcfvp.js} +34 -34
  13. package/dist/chunks/cli-kda5hz4w.js +23 -0
  14. package/dist/chunks/{cli-0hqxver6.js → cli-mhzh9z8k.js} +33 -33
  15. package/dist/chunks/{cli-tyey74p3.js → cli-n4qagybd.js} +6 -2
  16. package/dist/chunks/{cli-74hv4etk.js → cli-wer26exb.js} +4939 -4105
  17. package/dist/chunks/cli-zehr6kdx.js +719 -0
  18. package/dist/chunks/{commands-dnk10vze.js → commands-t5a0w785.js} +88 -17
  19. package/dist/chunks/{env-s4585w6j.js → env-n5ac8d1z.js} +1 -0
  20. package/dist/chunks/{index-3a3ssef1.js → index-jrs2shcc.js} +3 -3
  21. package/dist/chunks/line-kvm1ew55.js +215 -0
  22. package/dist/chunks/{node-init-4v2m2vj0.js → node-init-amedveja.js} +7 -6
  23. package/dist/chunks/{node-0ydee9hy.js → node-x35ncwhq.js} +7 -6
  24. package/dist/chunks/server-ek7fet5m.js +6924 -0
  25. package/dist/chunks/{share-g8v5qe6r.js → share-ax2k8805.js} +10 -5
  26. package/dist/cli.js +71 -19
  27. package/dist/hook.js +4178 -270
  28. package/package.json +10 -3
  29. package/dist/chunks/backfill-j4hxt1w1.js +0 -262
  30. package/dist/chunks/line-zy1tc2np.js +0 -45
  31. package/dist/chunks/server-jmyjttvv.js +0 -3170
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@arp-protocol/cli",
3
- "version": "0.0.1",
3
+ "version": "0.0.4",
4
+ "mcpName": "io.github.Wieedze/arp",
4
5
  "description": "ARP's agent-side programs in one binary: the MCP server, the runtime hook that records what an agent used, and the opt-in hire node. Runs on Node 22 or later.",
5
6
  "homepage": "https://github.com/intuition-box/ARP",
6
7
  "repository": {
@@ -15,14 +16,20 @@
15
16
  },
16
17
  "files": [
17
18
  "dist",
18
- "README.md"
19
+ "README.md",
20
+ "CHANGELOG.md",
21
+ "LICENSE"
19
22
  ],
23
+ "config": {
24
+ "sponsorDefault": "https://sponsor.arp.intuition.box"
25
+ },
20
26
  "engines": {
21
27
  "node": ">=22"
22
28
  },
23
29
  "scripts": {
24
- "build": "bun run --cwd ../../packages/protocol build && rm -rf dist && bun build src/cli.ts --outdir dist --target=node --format=esm --splitting --entry-naming '[name].js' --chunk-naming 'chunks/[name]-[hash].js' --external ./hook.js --external @metamask/smart-accounts-kit --banner '#!/usr/bin/env node' && bun build ../hook/src/hook.ts --outfile dist/hook.js --target=node --format=esm --external viem --external 'viem/*'",
30
+ "build": "bun run --cwd ../../packages/protocol build && rm -rf dist && ARP_SPONSOR_DEFAULT=$npm_package_config_sponsorDefault bun build src/cli.ts --env 'ARP_SPONSOR_DEFAULT*' --outdir dist --target=node --format=esm --splitting --entry-naming '[name].js' --chunk-naming 'chunks/[name]-[hash].js' --external ./hook.js --external @metamask/smart-accounts-kit --banner '#!/usr/bin/env node' && bun build ../hook/src/hook.ts --outfile dist/hook.js --target=node --format=esm --external viem --external 'viem/*'",
25
31
  "test": "bun run build && bun test",
32
+ "prepublishOnly": "bun run build",
26
33
  "lint": "bun run --cwd ../../packages/protocol build && tsc --noEmit -p tsconfig.json",
27
34
  "check:node": "bun run build && bash scripts/check-node.sh",
28
35
  "packages": "bun run build && bun scripts/packages.ts"
@@ -1,262 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- SKILL_DOMAINS,
4
- classifySkill,
5
- inferDomain,
6
- readRequires
7
- } from "./cli-74hv4etk.js";
8
- import"./cli-c8568g4c.js";
9
- import"./cli-v446mw5v.js";
10
- import"./cli-0hqxver6.js";
11
- import"./cli-d3dy7cxe.js";
12
- import"./cli-3kbbwfbp.js";
13
- import"./cli-g5zvr10v.js";
14
-
15
- // ../hook/src/backfill.ts
16
- import { readdirSync, readFileSync as readFileSync2, statSync } from "node:fs";
17
- import { homedir as homedir2 } from "node:os";
18
- import { basename, join as join2 } from "node:path";
19
-
20
- // ../hook/src/classify.ts
21
- var CANCELLED = /\b(interrupted|cancel(l)?ed|aborted) by (the )?user\b|\buser (rejected|denied|declined)\b|\bpermission denied by user\b|\buser doesn't want to (proceed|take this action)\b/i;
22
- function isCancelled(text) {
23
- return CANCELLED.test(text);
24
- }
25
-
26
- // ../hook/src/append.ts
27
- import { closeSync, constants, lstatSync, openSync, writeSync } from "node:fs";
28
- import { dirname } from "node:path";
29
- function appendOwn(path, text) {
30
- if (lstatSync(dirname(path)).isSymbolicLink())
31
- throw new Error(`${dirname(path)} is a symbolic link`);
32
- const flags = constants.O_WRONLY | constants.O_APPEND | constants.O_CREAT | (constants.O_NOFOLLOW ?? 0);
33
- const fd = openSync(path, flags, 384);
34
- try {
35
- writeSync(fd, text);
36
- } finally {
37
- closeSync(fd);
38
- }
39
- }
40
-
41
- // ../hook/src/skill-failure.ts
42
- import { existsSync, mkdirSync, readFileSync } from "node:fs";
43
- import { homedir } from "node:os";
44
- import { dirname as dirname2, join } from "node:path";
45
- var MISSING_KEY = /\b(api[ _-]?key|access[ _-]?token|credentials?)\b.{0,40}\b(missing|not set|not found|required|invalid|expired)\b|(?<!\bnot )\b(missing|no|invalid|expired)\b.{0,20}\b(api[ _-]?key|access[ _-]?token|credentials?)\b|\b(HTTP|status)(\/[\d.]+| code)?[ :=]*(401|403)\b|\b(401|403) (unauthori[sz]ed|forbidden)\b|\bunauthori[sz]ed\b|not authenticated|not logged in/i;
46
- var MISSING_KEY_VARIABLE = /\b[A-Z0-9_]*(API_KEY|TOKEN|SECRET)\b.{0,30}\b(not set|missing|required|unset)\b/;
47
- var MISSING_PROGRAM = /command not found|executable file not found|\bnot installed\b|No module named|spawn \S+ ENOENT|Cannot find module/i;
48
- var MISSING_FILE = /No such file or directory|\bENOENT\b|file not found|\b(file|path|directory|folder)\b(?!\.).{0,80}\bdoes not exist\b/i;
49
- function failureReason(text) {
50
- if (MISSING_PROGRAM.test(text))
51
- return "missing-program";
52
- if (MISSING_KEY.test(text) || MISSING_KEY_VARIABLE.test(text))
53
- return "missing-key";
54
- if (MISSING_FILE.test(text))
55
- return "missing-file";
56
- return null;
57
- }
58
- var RUNNERS = new Set(["bash", "bun", "bunx", "node", "npx", "python", "python3", "sh", "uv", "uvx", "zsh"]);
59
- function programsOf(command) {
60
- const found = [];
61
- for (const part of command.split(/&&|\|\||[;|\n]/)) {
62
- const words = part.trim().split(/\s+/).filter((word) => !/^[A-Za-z_][A-Za-z0-9_]*=/.test(word));
63
- const first = words[0];
64
- if (first === undefined || first === "")
65
- continue;
66
- const program = first.split("/").pop() ?? first;
67
- if (!RUNNERS.has(program)) {
68
- found.push(program);
69
- continue;
70
- }
71
- const target = words.slice(1).find((word) => !word.startsWith("-") && word !== "run");
72
- if (target !== undefined)
73
- found.push(target.split("/").pop() ?? target);
74
- }
75
- return found;
76
- }
77
- var escaped = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
78
- function askedBySkill(command, markdown, directory) {
79
- if (new RegExp(`${escaped(directory)}(?=[/\\s'"]|$)`).test(command))
80
- return true;
81
- const { cli, runs } = readRequires(markdown);
82
- const named = new Set([...cli, ...runs]);
83
- return programsOf(command).some((program) => named.has(program) || program.includes(".") && new RegExp(`(?:^|[\\s\`'"/(])${escaped(program)}(?=[\\s\`'")]|$)`, "m").test(markdown));
84
- }
85
- var DOMAINS = [...new Set(SKILL_DOMAINS.map((rule) => rule.domain))];
86
- function contextOf(words, skill, description) {
87
- return inferDomain(words, DOMAINS) ?? classifySkill(skill.split(":").pop() ?? skill, description ?? "");
88
- }
89
- function skillDirectory(name, cwd) {
90
- const roots = [join(cwd, ".claude", "skills"), join(homedir(), ".claude", "skills")];
91
- const given = process.env["ARP_SKILLS_DIR"];
92
- if (given !== undefined && given !== "")
93
- roots.unshift(given);
94
- return roots.map((root) => join(root, name)).find((dir) => existsSync(join(dir, "SKILL.md"))) ?? null;
95
- }
96
- function arpHome() {
97
- const given = process.env["ARP_HOME"]?.trim();
98
- return given !== undefined && given !== "" ? given : join(homedir(), ".arp");
99
- }
100
- function appendFailure(line) {
101
- const path = join(arpHome(), "failures.jsonl");
102
- const seen = existsSync(path) ? readFileSync(path, "utf8") : "";
103
- const key = `"session":${JSON.stringify(line.session)},"skill":${JSON.stringify(line.skill)},`;
104
- if (seen.split(`
105
- `).some((row) => row.includes(key) && row.includes(`"reason":"${line.reason}"`)))
106
- return false;
107
- mkdirSync(dirname2(path), { recursive: true });
108
- appendOwn(path, `${JSON.stringify(line)}
109
- `);
110
- return true;
111
- }
112
-
113
- // ../hook/src/backfill.ts
114
- var BODY = /^Base directory for this skill: ([^\n]+)/;
115
- function blocksOf(content) {
116
- return Array.isArray(content) ? content : [];
117
- }
118
- function textOf(content) {
119
- if (typeof content === "string")
120
- return content;
121
- return blocksOf(content).map((block) => typeof block?.text === "string" ? block.text : "").join(`
122
- `);
123
- }
124
- var lastPart = (name) => name.split(":").pop() ?? name;
125
- function fromDisk(name, cwd) {
126
- try {
127
- const directory = skillDirectory(name, cwd);
128
- return directory === null ? null : { name, directory, markdown: readFileSync2(join2(directory, "SKILL.md"), "utf8") };
129
- } catch {
130
- return null;
131
- }
132
- }
133
- function readTranscript(text, fallback) {
134
- const active = [];
135
- const commands = new Map;
136
- const pending = new Map;
137
- const failures = [];
138
- let uses = 0;
139
- let words = "";
140
- for (const line of text.split(`
141
- `)) {
142
- if (!/tool_use|tool_result|Base directory for this skill|"user"/.test(line))
143
- continue;
144
- let entry;
145
- try {
146
- entry = JSON.parse(line);
147
- } catch {
148
- continue;
149
- }
150
- if (entry["isSidechain"] === true)
151
- continue;
152
- const message = entry["message"];
153
- const content = message?.content;
154
- const session = typeof entry["sessionId"] === "string" ? entry["sessionId"] : fallback;
155
- const cwd = typeof entry["cwd"] === "string" ? entry["cwd"] : "";
156
- const at = typeof entry["timestamp"] === "string" ? entry["timestamp"] : "";
157
- if (entry["type"] === "assistant") {
158
- for (const block of blocksOf(content)) {
159
- if (block?.type !== "tool_use" || typeof block.id !== "string")
160
- continue;
161
- const input = block.input ?? {};
162
- if (block.name === "Skill" && typeof input["skill"] === "string") {
163
- uses += 1;
164
- pending.set(block.id, input["skill"]);
165
- const known = fromDisk(input["skill"], cwd);
166
- if (known !== null)
167
- active.unshift(known);
168
- } else if (block.name === "Bash" && typeof input["command"] === "string") {
169
- commands.set(block.id, input["command"]);
170
- }
171
- }
172
- continue;
173
- }
174
- if (entry["type"] !== "user")
175
- continue;
176
- const typed = textOf(content);
177
- if (entry["isMeta"] !== true && typed.trim() !== "" && !typed.trimStart().startsWith("<"))
178
- words = typed;
179
- const directory = BODY.exec(textOf(content))?.[1]?.trim();
180
- if (entry["isMeta"] === true && directory !== undefined) {
181
- const folder = basename(directory);
182
- const call = [...pending].find(([, name2]) => lastPart(name2) === folder);
183
- if (call === undefined)
184
- uses += 1;
185
- else
186
- pending.delete(call[0]);
187
- const name = call?.[1] ?? folder;
188
- const known = active.findIndex((skill) => skill.name === name);
189
- if (known !== -1)
190
- active.splice(known, 1);
191
- active.unshift({ name, directory, markdown: textOf(content) });
192
- continue;
193
- }
194
- for (const block of blocksOf(content)) {
195
- if (block?.type !== "tool_result" || typeof block.tool_use_id !== "string")
196
- continue;
197
- if (block.is_error === true)
198
- pending.delete(block.tool_use_id);
199
- if (block.is_error !== true)
200
- continue;
201
- const command = commands.get(block.tool_use_id);
202
- if (command === undefined)
203
- continue;
204
- const output = textOf(block.content).slice(0, 4000);
205
- if (isCancelled(output))
206
- continue;
207
- const reason = failureReason(output);
208
- if (reason === null)
209
- continue;
210
- const skill = active.find((candidate) => askedBySkill(command, candidate.markdown, candidate.directory));
211
- if (skill === undefined)
212
- continue;
213
- const domain = contextOf(words, skill.name, readRequires(skill.markdown).description);
214
- failures.push({ at, session, skill: skill.name, source: null, ...domain === null ? {} : { domain }, reason });
215
- }
216
- }
217
- return { uses, failures };
218
- }
219
- function transcripts(root) {
220
- const found = [];
221
- let names;
222
- try {
223
- names = readdirSync(root);
224
- } catch {
225
- return found;
226
- }
227
- for (const name of names) {
228
- const path = join2(root, name);
229
- try {
230
- if (statSync(path).isDirectory())
231
- found.push(...transcripts(path));
232
- else if (name.endsWith(".jsonl"))
233
- found.push(path);
234
- } catch {
235
- continue;
236
- }
237
- }
238
- return found;
239
- }
240
- function backfill(root = join2(homedir2(), ".claude", "projects")) {
241
- const files = transcripts(root);
242
- let uses = 0;
243
- let added = 0;
244
- for (const file of files) {
245
- let text;
246
- try {
247
- text = readFileSync2(file, "utf8");
248
- } catch {
249
- continue;
250
- }
251
- const read = readTranscript(text, basename(file, ".jsonl"));
252
- uses += read.uses;
253
- for (const failure of read.failures)
254
- if (appendFailure(failure))
255
- added += 1;
256
- }
257
- return { transcripts: files.length, uses, added, file: join2(arpHome(), "failures.jsonl") };
258
- }
259
- export {
260
- readTranscript,
261
- backfill
262
- };
@@ -1,45 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- agentFor,
4
- chainId,
5
- loopConfig,
6
- writeLine
7
- } from "./cli-0z6f3603.js";
8
- import"./cli-tyey74p3.js";
9
- import {
10
- USAGE_REASONS,
11
- isUsageOutcome,
12
- isUsageReason
13
- } from "./cli-74hv4etk.js";
14
- import"./cli-c8568g4c.js";
15
- import"./cli-v446mw5v.js";
16
- import"./cli-0hqxver6.js";
17
- import"./cli-d3dy7cxe.js";
18
- import"./cli-3kbbwfbp.js";
19
- import"./cli-g5zvr10v.js";
20
-
21
- // ../mcp/src/line.ts
22
- function lineFromArgs(args) {
23
- if (!isUsageOutcome(args.outcome))
24
- return new Error("--outcome must be worked or failed");
25
- if (args.reason !== undefined && !isUsageReason(args.reason)) {
26
- return new Error(`--reason must be one of ${USAGE_REASONS.join(", ")}`);
27
- }
28
- if (args.reason !== undefined && args.outcome !== "failed")
29
- return new Error("a reason only follows --outcome failed");
30
- return {
31
- toolUrl: args.toolUrl,
32
- outcome: args.outcome,
33
- ...args.domain === undefined ? {} : { domain: args.domain },
34
- ...args.reason === undefined ? {} : { reason: args.reason }
35
- };
36
- }
37
- async function writeLineHere(input) {
38
- if (chainId !== 13579)
39
- throw new Error("a line is written on testnet 13579 only for now");
40
- return writeLine(loopConfig(), agentFor(), input);
41
- }
42
- export {
43
- writeLineHere,
44
- lineFromArgs
45
- };