@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.
@@ -1,9 +1,12 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ domain_aliases_default
4
+ } from "./cli-00qp1sj2.js";
2
5
  import {
3
6
  arpHome,
4
7
  loadOrCreateKeyFile,
5
8
  stateFile
6
- } from "./cli-tyey74p3.js";
9
+ } from "./cli-n4qagybd.js";
7
10
  import {
8
11
  ARP_DOMAIN_ATTESTORS,
9
12
  HAS_TAG,
@@ -12,6 +15,8 @@ import {
12
15
  bundleMatchesReceipt,
13
16
  contentIndexOf,
14
17
  contentRequestTypedData,
18
+ contentsFileOf,
19
+ contentsLines,
15
20
  createErc8004Client,
16
21
  createPublicClient,
17
22
  createWalletClient,
@@ -23,12 +28,17 @@ import {
23
28
  intuitionToolAtomLookup,
24
29
  multiVaultDeclareAbi,
25
30
  multiVaultReads,
31
+ paymentAtomLabel,
32
+ readDomainTree,
33
+ readSkillContents,
26
34
  receiptMatchesContent,
27
35
  recoverContentRequester,
28
36
  renderSkillPrompt,
37
+ resolveDomain,
29
38
  reviewSkill,
30
39
  serveContent,
31
40
  serveSkillBundle,
41
+ signCategoryRequest,
32
42
  signLineRequest,
33
43
  signListToolRequest,
34
44
  signMintGasRequest,
@@ -36,19 +46,19 @@ import {
36
46
  skillRefFromUrl,
37
47
  sponsorProofDigest,
38
48
  verifyContentReceipt
39
- } from "./cli-74hv4etk.js";
49
+ } from "./cli-wev0jgf7.js";
40
50
  import {
41
51
  encodeFunctionData
42
- } from "./cli-c8568g4c.js";
52
+ } from "./cli-0gsbnq5w.js";
43
53
  import {
44
54
  privateKeyToAccount
45
- } from "./cli-v446mw5v.js";
55
+ } from "./cli-fbwpcfvp.js";
46
56
  import {
47
57
  getAddress,
48
58
  isHex,
49
59
  keccak256,
50
60
  stringToBytes
51
- } from "./cli-0hqxver6.js";
61
+ } from "./cli-mhzh9z8k.js";
52
62
 
53
63
  // ../mcp/src/content.ts
54
64
  import { existsSync, writeFileSync } from "node:fs";
@@ -183,8 +193,9 @@ var _13579_default = {
183
193
  domainScopeEnforcer: "0x516B82E29e3Ca46Ca810FC2EEf348932b198f7f9",
184
194
  trustStakeCapEnforcer: "0x19e07280F1483Aa9aCDb1a7F8C61bfd20c7879Bc",
185
195
  identityRegistry: "0xC165A2AD2E540A4069E02834009161E2b4490d5A",
186
- sponsor: "0xE596096F4176b682E300d73963e7B04B383C1AA1",
187
- _sponsorNote: "The address ARP's sponsoring service sends from on 13579 (apps/sponsor, ADR 0092, 0094): its GET / answered this on 2026-11-02. /tools counts every deposit it sent in Usage: the proof-gated ones, the edge creations it paid for, and, because the address is also ARP's deployer, 3 deposits written before the service existed (scripts/measure/sponsor-deposit-provenance.ts, 2026-11-03). Not a contract: an EOA whose key is in a 0600 file the agent never reads.",
196
+ sponsor: "0x77939A4c276f3E549276CEB3A09CDa1fF58b7ec9",
197
+ sponsorV1: "0xE596096F4176b682E300d73963e7B04B383C1AA1",
198
+ _sponsorNote: "The address ARP's sponsoring service sends from on 13579 (apps/sponsor, ADR 0092, 0094). Since 2026-11-09 a key of its own, for the hosted service (task 78, ADR 0149): sponsorV1 is the deployer's key, which sent for it before and also holds mainnet funds. Not a contract: an EOA whose key is a file mounted into the service's container, never read by an agent. Before 2026-11-09: The address ARP's sponsoring service sends from on 13579 (apps/sponsor, ADR 0092, 0094): its GET / answered this on 2026-11-02. /tools counts every deposit it sent in Usage: the proof-gated ones, the edge creations it paid for, and, because the address is also ARP's deployer, 3 deposits written before the service existed (scripts/measure/sponsor-deposit-provenance.ts, 2026-11-03). Not a contract: an EOA whose key is in a 0600 file the agent never reads.",
188
199
  deployedTxs: {
189
200
  moduleRegistry: "0x6ee4f4d76dd55c69ea434f8c3c204cb6149df1c588910b1792db1650681fbc2e",
190
201
  moduleRegistryV1: "0x02abe9a0c598df131995bc40927a4e0be0b86e7bdbfe747cfdf37fb19b069634",
@@ -6740,10 +6751,69 @@ function text(id, value) {
6740
6751
  var isTermId = (value) => typeof value === "string" && /^0x[0-9a-fA-F]{64}$/.test(value);
6741
6752
  var isAddress = (value) => typeof value === "string" && /^0x[0-9a-fA-F]{40}$/.test(value);
6742
6753
 
6754
+ // ../mcp/src/hosted.ts
6755
+ var HOSTED_FLAG = "ARP_HOSTED";
6756
+ var HOST_NAME = /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z0-9-]{0,61}[a-z0-9]$/;
6757
+ var REFUSED_WHEN_HOSTED = [
6758
+ "ARP_RECEIPT_KEY",
6759
+ "ARP_AGENT_PRIVATE_KEY",
6760
+ "ARP_ALLOW_PRIVATE_PROBE"
6761
+ ];
6762
+ function isHosted(env = process.env) {
6763
+ return env[HOSTED_FLAG] === "1";
6764
+ }
6765
+ function publicHost(env = process.env) {
6766
+ const name = (env["ARP_PUBLIC_HOST"] ?? "").trim().toLowerCase();
6767
+ return name === "" ? null : name;
6768
+ }
6769
+ function hostedRefusal(env = process.env) {
6770
+ const name = publicHost(env);
6771
+ const flag = env[HOSTED_FLAG];
6772
+ if (flag !== undefined && flag !== "" && flag !== "1") {
6773
+ return `${HOSTED_FLAG} must be 1 or unset, not ${flag}`;
6774
+ }
6775
+ if (!isHosted(env)) {
6776
+ return name === null ? null : `ARP_PUBLIC_HOST needs ${HOSTED_FLAG}=1: only the hosted read-only server answers a public name`;
6777
+ }
6778
+ if (env["ARP_TOOLS"] !== "read") {
6779
+ return `${HOSTED_FLAG}=1 serves ARP_TOOLS=read only, not ${env["ARP_TOOLS"] ?? "every tool"}`;
6780
+ }
6781
+ if (env["ARP_TARGET"] !== "testnet") {
6782
+ return `${HOSTED_FLAG}=1 reads testnet 13579 only: set ARP_TARGET=testnet`;
6783
+ }
6784
+ if (env["ARP_TRANSPORT"] === "stdio") {
6785
+ return `${HOSTED_FLAG}=1 serves HTTP; stdio is for a client on the same machine`;
6786
+ }
6787
+ for (const variable of REFUSED_WHEN_HOSTED) {
6788
+ if ((env[variable] ?? "") !== "")
6789
+ return `${HOSTED_FLAG}=1 refuses ${variable}: a hosted server holds no key and probes no private address`;
6790
+ }
6791
+ if (name === null) {
6792
+ return `${HOSTED_FLAG}=1 needs ARP_PUBLIC_HOST, the name the proxy forwards`;
6793
+ }
6794
+ if (!HOST_NAME.test(name)) {
6795
+ return `ARP_PUBLIC_HOST must be a bare host name such as mcp.example.org, not ${env["ARP_PUBLIC_HOST"]}`;
6796
+ }
6797
+ return null;
6798
+ }
6799
+ function loggable(error, message, env = process.env) {
6800
+ if (!isHosted(env))
6801
+ return message;
6802
+ return error instanceof Error ? error.name : "error";
6803
+ }
6804
+
6743
6805
  // ../mcp/src/config.ts
6744
6806
  import { resolve } from "node:path";
6807
+ var refusedHosting = hostedRefusal(process.env);
6808
+ if (refusedHosting !== null) {
6809
+ console.error(refusedHosting);
6810
+ process.exit(1);
6811
+ }
6812
+ var HOSTED = isHosted(process.env);
6745
6813
  var PORT = Number(process.env.PORT ?? 3002);
6746
6814
  var RECEIPT_SIGNER = (() => {
6815
+ if (HOSTED)
6816
+ return null;
6747
6817
  try {
6748
6818
  return receiptSigner();
6749
6819
  } catch (error) {
@@ -6758,7 +6828,8 @@ function inProject(path, projectDir = process.env["ARP_PROJECT_DIR"]) {
6758
6828
  var SKILLS_DIR = process.env["ARP_SKILLS_DIR"] === undefined ? undefined : inProject(process.env["ARP_SKILLS_DIR"]);
6759
6829
  var READ_ONLY = process.env["ARP_TOOLS"] === "read" || DESKTOP;
6760
6830
  var CONTENT_URL = process.env["ARP_CONTENT"] ?? "http://127.0.0.1:3002";
6761
- var SPONSOR_URL = process.env["ARP_SPONSOR"] ?? "http://127.0.0.1:3200";
6831
+ var SPONSOR_DEFAULT = "http://127.0.0.1:3200";
6832
+ var SPONSOR_URL = process.env["ARP_SPONSOR"] ?? SPONSOR_DEFAULT;
6762
6833
  var SESSION_LOG = inProject(process.env["ARP_SESSION_LOG"] ?? ".arp/session.jsonl");
6763
6834
  var RECEIPTS_LOG = inProject(process.env["ARP_RECEIPTS"] ?? ".arp/receipts.jsonl");
6764
6835
  var TARGETS = { mainnet: 1155, testnet: 13579 };
@@ -6793,6 +6864,12 @@ function required(value, what) {
6793
6864
  return value;
6794
6865
  }
6795
6866
  var GRAPHQL = required(GRAPHQL_URL, "a graphqlUrl");
6867
+ var SKILLS_INDEX = (() => {
6868
+ const raw2 = process.env.ARP_SKILLS_INDEX_URL?.trim() ?? "";
6869
+ if (raw2 === "off")
6870
+ return null;
6871
+ return raw2 === "" ? "https://skills.sh" : raw2;
6872
+ })();
6796
6873
  var RPC = required(RPC_URL, "an rpcUrl");
6797
6874
  var MULTI_VAULT = required(MULTI_VAULT_RAW, "a multiVault");
6798
6875
  var reads = multiVaultReads({ address: MULTI_VAULT, chainId, rpcUrl: RPC });
@@ -6836,7 +6913,8 @@ var IN_CONTEXT_OF = (() => {
6836
6913
  })();
6837
6914
  var LINE_PAYERS = (() => {
6838
6915
  const arp = record.arp;
6839
- return typeof arp?.deployer === "string" ? [arp.deployer] : [];
6916
+ const wallets = [arp?.deployer, arp?.sponsor].filter((wallet) => typeof wallet === "string");
6917
+ return [...new Set(wallets)];
6840
6918
  })();
6841
6919
  function domainWalk() {
6842
6920
  return {
@@ -6848,6 +6926,11 @@ function domainWalk() {
6848
6926
  };
6849
6927
  }
6850
6928
 
6929
+ // ../mcp/src/domain-words.ts
6930
+ function domainWords(tree, root = "tool") {
6931
+ return { labels: tree.subtree(root), aliases: domain_aliases_default.aliases };
6932
+ }
6933
+
6851
6934
  // ../mcp/src/client-session.ts
6852
6935
  var ASK_TIMEOUT_MS = 15 * 60 * 1000;
6853
6936
  var declaredElicitation = false;
@@ -6978,7 +7061,7 @@ import { dirname as dirname2, join as join2 } from "node:path";
6978
7061
  var SAFE_SKILL_NAME = /^[a-z0-9][a-z0-9._-]*$/i;
6979
7062
  function installPrompt(receipt, files, installPath, context) {
6980
7063
  const ref = skillRefFromUrl(receipt.receipt.url);
6981
- return renderSkillPrompt(reviewSkill({
7064
+ const prompt = renderSkillPrompt(reviewSkill({
6982
7065
  repo: ref?.repo ?? receipt.receipt.url,
6983
7066
  skill: ref?.skill ?? receipt.receipt.url,
6984
7067
  path: receipt.receipt.path,
@@ -6987,6 +7070,9 @@ function installPrompt(receipt, files, installPath, context) {
6987
7070
  files,
6988
7071
  thirdParty: context.thirdParty
6989
7072
  }), installPath);
7073
+ const contains = contentsLines(readSkillContents(files.map((file) => contentsFileOf(file.path, file.bytes))), 3);
7074
+ return { ...prompt, message: [prompt.message, "", "What its files contain:", ...contains].join(`
7075
+ `) };
6990
7076
  }
6991
7077
  function installFiles(skillsDir, skill, files) {
6992
7078
  if (!SAFE_SKILL_NAME.test(skill))
@@ -7153,7 +7239,8 @@ async function getSkill(config, agent, atom) {
7153
7239
  const line = { atom: signed.receipt.atom, url: signed.receipt.url, skill: ref?.skill ?? null, receipt: signed };
7154
7240
  appendFileSync2(join4(config.stateDir, "receipts.jsonl"), `${JSON.stringify(line)}
7155
7241
  `);
7156
- const served = { atom: signed.receipt.atom, url: signed.receipt.url, name: ref?.skill ?? signed.receipt.url, text: new TextDecoder().decode(skillMd.bytes) };
7242
+ const contains = contentsLines(readSkillContents(files.map((file) => contentsFileOf(file.path, file.bytes))));
7243
+ const served = { atom: signed.receipt.atom, url: signed.receipt.url, name: ref?.skill ?? signed.receipt.url, text: new TextDecoder().decode(skillMd.bytes), contains };
7157
7244
  if (config.skillsDir === undefined || ref === null || !SAFE_SKILL_NAME.test(ref.skill))
7158
7245
  return served;
7159
7246
  const prompt = installPrompt(signed, files, join4(config.skillsDir, ref.skill), config.reviewContext?.(signed.receipt.url) ?? { committedAt: null, thirdParty: [] });
@@ -7230,9 +7317,26 @@ async function writeLine(config, agent, input) {
7230
7317
  const listing2 = await signListToolRequest({ address: agent.address, url: input.toolUrl, nonce: keccak256(stringToBytes(`list-${input.toolUrl}-${Date.now()}`)), expiresAt: expiresAt() }, DESKTOP_CHAIN_ID, agent.account);
7231
7318
  const listed = await post(config, "/list-tool", { url: input.toolUrl, request: listing2 });
7232
7319
  if (listed.status !== 200 || typeof listed.body["atomId"] !== "string") {
7233
- throw new Error(`the tool could not be listed (${listed.status}): ${String(listed.body["error"] ?? "")}`);
7320
+ const rule = typeof listed.body["rule"] === "string" ? `, ${listed.body["rule"]}` : "";
7321
+ throw new Error(`the sponsor would not list ${input.toolUrl} (${listed.status}${rule}): ${String(listed.body["error"] ?? "")}`);
7234
7322
  }
7235
7323
  const tool = listed.body["atomId"];
7324
+ if (input.newDomain !== undefined && input.domain !== undefined) {
7325
+ const category = await signCategoryRequest({
7326
+ agent: agent.address,
7327
+ ...ref,
7328
+ label: input.domain,
7329
+ parent: input.newDomain.parent,
7330
+ subject: tool,
7331
+ reason: input.newDomain.why,
7332
+ nonce: keccak256(stringToBytes(`category-${tool}-${input.domain}-${Date.now()}`)),
7333
+ expiresAt: expiresAt()
7334
+ }, DESKTOP_CHAIN_ID, agent.account);
7335
+ const filed = await post(config, "/propose-category", { request: category });
7336
+ if (filed.status !== 200) {
7337
+ throw new Error(`the domain "${input.domain}" could not be added (${filed.status}): ${String(filed.body["error"] ?? "")}`);
7338
+ }
7339
+ }
7236
7340
  const wait = config.sleep ?? ((ms) => new Promise((done) => setTimeout(done, ms)));
7237
7341
  const deadline = Date.now() + (config.indexerWaitMs ?? 120000);
7238
7342
  let approved = false;
@@ -7244,6 +7348,9 @@ async function writeLine(config, agent, input) {
7244
7348
  domain: input.domain ?? "",
7245
7349
  outcome: input.outcome,
7246
7350
  reason: input.reason ?? "",
7351
+ payment: input.payment === undefined ? "" : paymentAtomLabel(input.payment),
7352
+ endpoint: input.payment === undefined ? "" : input.toolUrl,
7353
+ need: input.need ?? "",
7247
7354
  nonce: keccak256(stringToBytes(`line-${tool}-${Date.now()}`)),
7248
7355
  expiresAt: expiresAt()
7249
7356
  }, DESKTOP_CHAIN_ID, agent.account);
@@ -7258,7 +7365,7 @@ async function writeLine(config, agent, input) {
7258
7365
  };
7259
7366
  }
7260
7367
  const error = String(answer.body["error"] ?? "");
7261
- const notIndexedYet = answer.status === 403 && /no identity atom on the graph/.test(error) || answer.status === 422 && answer.body["retry"] === true || answer.status === 503 && answer.body["retry"] === true;
7368
+ const notIndexedYet = answer.status === 403 && /no identity atom on the graph/.test(error) || answer.status === 422 && answer.body["retry"] === true || answer.status === 503 && answer.body["retry"] === true || answer.status === 422 && answer.body["rule"] === "unknown_domain" && input.newDomain !== undefined;
7262
7369
  if (notIndexedYet && Date.now() < deadline) {
7263
7370
  await wait(5000);
7264
7371
  continue;
@@ -7354,7 +7461,7 @@ Use when: "make a game", "build a browser game",…`, installs: 26583 }, "github
7354
7461
  real-time WebSockets, scheduled tasks, tool integration, and chat capabilities.
7355
7462
  Generates production-ready agent code…`, installs: 3702 }, "github.com/cloudflare/skills?skill=building-mcp-server-on-cloudflare": { description: `Builds remote MCP (Model Context Protocol) servers on Cloudflare Workers
7356
7463
  with tools, OAuth authentication, and production deployment. Generates
7357
- server code, configures auth providers, and deploys to…`, installs: 3424 }, "github.com/easyzoom/aix-skills?skill=webpage-to-markdown": { description: "Use when converting a public webpage URL into clean Markdown content", installs: null }, "github.com/JimLiu/baoyu-skills?skill=baoyu-url-to-markdown": { description: "Fetch any URL and convert to markdown using baoyu-fetch CLI (Chrome CDP with site-specific adapters). Built-in adapters for X/Twitter, YouTube transcripts, Hacker News threads, and generic pages via…", installs: null }, "github.com/WyattBlue/auto-editor?skill=auto-editor": { description: "Record a microphone or edit video/audio with the auto-editor CLI — cut out dead space (silence, motionlessness, black frames), set the pace, make manual cuts, and render the result. Use when someone…", installs: null }, "github.com/Melvynx/video-agent-skills?skill=short-editing": { description: "Edits a talking-head take end to end into a paper-style motion design short (1080x1920, also 1:1 and 16:9) with its own render engine (Node + Chrome + ffmpeg, free, offline). Use when the user says…", installs: null }, "github.com/clueso-ai/skills?skill=remove-filler-words": { description: `Strip ums, ahs, false starts, and rambling from a video's narration - clean the transcript, regenerate the voiceover, and keep the visuals aligned to the new timing. Use when the user says "remove…`, installs: null }, "github.com/browser-use/video-use?skill=video-use": { description: "Edit any video by conversation. Transcribe, cut, color grade, generate overlay animations, burn subtitles — for talking heads, montages, tutorials, travel, interviews. No presets, no menus. Ask…", installs: null }, "github.com/louisedesadeleer/cut-video?skill=cut-video": { description: "Tighten a long recording aggressively — remove silences, fillers, hedges, false starts, and repetitions while preserving laughs and comedic pauses. Cuts are driven by Montreal Forced Aligner (MFA)…", installs: null }, "github.com/softaworks/agent-toolkit?skill=web-to-markdown": { description: "Use ONLY when the user explicitly says: 'use the skill web-to-markdown ...' (or 'use a skill web-to-markdown ...'). Converts webpage URLs to clean Markdown by calling the local web2md CLI (Puppeteer…", installs: null }, "github.com/Wieedze/ARP?skill=arp": { description: "Find the skill, MCP server or API that fits a task, before you pick one yourself, and see which ones other agents actually used. Use whenever a task needs a tool you do not already have — search the…", installs: null } }
7464
+ server code, configures auth providers, and deploys to…`, installs: 3424 }, "github.com/easyzoom/aix-skills?skill=webpage-to-markdown": { description: "Use when converting a public webpage URL into clean Markdown content", installs: null }, "github.com/JimLiu/baoyu-skills?skill=baoyu-url-to-markdown": { description: "Fetch any URL and convert to markdown using baoyu-fetch CLI (Chrome CDP with site-specific adapters). Built-in adapters for X/Twitter, YouTube transcripts, Hacker News threads, and generic pages via…", installs: null }, "github.com/WyattBlue/auto-editor?skill=auto-editor": { description: "Record a microphone or edit video/audio with the auto-editor CLI — cut out dead space (silence, motionlessness, black frames), set the pace, make manual cuts, and render the result. Use when someone…", installs: null }, "github.com/Melvynx/video-agent-skills?skill=short-editing": { description: "Edits a talking-head take end to end into a paper-style motion design short (1080x1920, also 1:1 and 16:9) with its own render engine (Node + Chrome + ffmpeg, free, offline). Use when the user says…", installs: null }, "github.com/clueso-ai/skills?skill=remove-filler-words": { description: `Strip ums, ahs, false starts, and rambling from a video's narration - clean the transcript, regenerate the voiceover, and keep the visuals aligned to the new timing. Use when the user says "remove…`, installs: null }, "github.com/browser-use/video-use?skill=video-use": { description: "Edit any video by conversation. Transcribe, cut, color grade, generate overlay animations, burn subtitles — for talking heads, montages, tutorials, travel, interviews. No presets, no menus. Ask…", installs: null }, "github.com/louisedesadeleer/cut-video?skill=cut-video": { description: "Tighten a long recording aggressively — remove silences, fillers, hedges, false starts, and repetitions while preserving laughs and comedic pauses. Cuts are driven by Montreal Forced Aligner (MFA)…", installs: null }, "github.com/softaworks/agent-toolkit?skill=web-to-markdown": { description: "Use ONLY when the user explicitly says: 'use the skill web-to-markdown ...' (or 'use a skill web-to-markdown ...'). Converts webpage URLs to clean Markdown by calling the local web2md CLI (Puppeteer…", installs: null }, "github.com/Wieedze/ARP?skill=arp": { description: "Check a skill, MCP server or API before you install or pay for it, and see what other agents declared about it. Use whenever a task needs a tool you do not already have — search the registry by the…", installs: null }, "github.com/intuition-box/ARP?skill=arp": { description: "Check a skill, MCP server or API before you install or pay for it, and see what other agents declared about it. Use whenever a task needs a tool you do not already have — search the registry by the…", installs: null } }
7358
7465
  };
7359
7466
 
7360
7467
  // ../mcp/src/tool-search.ts
@@ -7455,6 +7562,68 @@ function pageOf(entries, offset, limit) {
7455
7562
  nextOffset: offset + limit < entries.length ? offset + limit : null
7456
7563
  };
7457
7564
  }
7565
+ function mergeIndexed(matched, indexed, options) {
7566
+ const out = [];
7567
+ const taken = new Set;
7568
+ const add = (entry) => {
7569
+ const key = options.keyOf(entry);
7570
+ if (key !== null) {
7571
+ if (taken.has(key))
7572
+ return;
7573
+ taken.add(key);
7574
+ }
7575
+ out.push(entry);
7576
+ };
7577
+ for (const entry of matched)
7578
+ if (options.leads(entry))
7579
+ add(entry);
7580
+ indexed.forEach(({ key }, index) => add(options.held(key) ?? options.make(index)));
7581
+ for (const entry of matched)
7582
+ add(entry);
7583
+ return out;
7584
+ }
7585
+
7586
+ // ../mcp/src/desktop/file-category.ts
7587
+ function isRecord(value) {
7588
+ return typeof value === "object" && value !== null;
7589
+ }
7590
+ async function fileCategory(args, deps) {
7591
+ let request;
7592
+ try {
7593
+ request = await deps.sign({
7594
+ label: args.category,
7595
+ parent: args.parent ?? "",
7596
+ subject: args.id,
7597
+ reason: args.reason ?? ""
7598
+ });
7599
+ } catch (error) {
7600
+ return { error: `this agent could not sign the request: ${error instanceof Error ? error.message : String(error)}` };
7601
+ }
7602
+ return submitCategory(request, deps);
7603
+ }
7604
+ async function submitCategory(request, deps) {
7605
+ let response;
7606
+ try {
7607
+ response = await deps.fetch(`${deps.sponsorUrl}/propose-category`, {
7608
+ method: "POST",
7609
+ headers: { "content-type": "application/json" },
7610
+ body: JSON.stringify({ request })
7611
+ });
7612
+ } catch {
7613
+ return { error: "ARP's sponsor is not reachable, so the category was not filed." };
7614
+ }
7615
+ const answer = await response.json().catch(() => null);
7616
+ if (response.ok && isRecord(answer) && typeof answer["label"] === "string") {
7617
+ return {
7618
+ filed: answer["label"],
7619
+ new_category: answer["newCategory"] === true,
7620
+ written: answer["created"] === true
7621
+ };
7622
+ }
7623
+ const rule = isRecord(answer) && typeof answer["rule"] === "string" ? answer["rule"] : "unknown";
7624
+ const detail = isRecord(answer) && typeof answer["error"] === "string" ? answer["error"] : `HTTP ${response.status}`;
7625
+ return { refused: rule, detail };
7626
+ }
7458
7627
 
7459
7628
  // ../mcp/src/desktop/handlers.ts
7460
7629
  var agent;
@@ -7495,6 +7664,49 @@ async function desktopTool(id, name, args) {
7495
7664
  if (typeof atom !== "string" || !/^0x[0-9a-fA-F]{64}$/.test(atom)) {
7496
7665
  return fail(id, -32602, "id must be the 0x + 32 bytes id from arp_find_tools");
7497
7666
  }
7667
+ if (name === "arp_file_category") {
7668
+ const asked = args["category"];
7669
+ if (typeof asked !== "string" || asked.trim().length === 0) {
7670
+ return fail(id, -32602, "category must be a category name");
7671
+ }
7672
+ const parent = typeof args["parent"] === "string" ? args["parent"] : undefined;
7673
+ let category = asked;
7674
+ const resolved = resolveDomain(asked, domainWords(await readDomainTree(domainWalk())));
7675
+ if (resolved.status === "node")
7676
+ category = resolved.label;
7677
+ else if (parent === undefined) {
7678
+ return text(id, {
7679
+ refused: "unknown_category",
7680
+ detail: `"${asked}" is not a category.${resolved.suggestions.length === 0 ? "" : ` Closest: ${resolved.suggestions.join(", ")}.`} Pick one, or pass parent and reason to add it.`
7681
+ });
7682
+ }
7683
+ const reason = typeof args["reason"] === "string" ? args["reason"] : undefined;
7684
+ return text(id, await fileCategory({
7685
+ id: atom,
7686
+ category,
7687
+ ...parent === undefined ? {} : { parent },
7688
+ ...reason === undefined ? {} : { reason }
7689
+ }, {
7690
+ sponsorUrl: SPONSOR_URL,
7691
+ fetch: globalThis.fetch.bind(globalThis),
7692
+ sign: async (fields) => {
7693
+ const sender = agentFor();
7694
+ const config = loopConfig();
7695
+ const state = await ensureAgent(config, sender);
7696
+ return signCategoryRequest({
7697
+ agent: sender.address,
7698
+ agentRegistry: registryRef(config),
7699
+ agentId: state.agentId,
7700
+ label: fields.label,
7701
+ parent: fields.parent,
7702
+ subject: atom,
7703
+ reason: fields.reason,
7704
+ nonce: keccak256(stringToBytes(`category-${atom}-${Date.now()}`)),
7705
+ expiresAt: Math.floor(Date.now() / 1000) + 600
7706
+ }, DESKTOP_CHAIN_ID, sender.account);
7707
+ }
7708
+ }));
7709
+ }
7498
7710
  const loop = loopConfig();
7499
7711
  try {
7500
7712
  if (name === "arp_get_skill") {
@@ -7502,6 +7714,8 @@ async function desktopTool(id, name, args) {
7502
7714
  return text(id, {
7503
7715
  name: skill.name,
7504
7716
  source: skill.url,
7717
+ contains: skill.contains,
7718
+ show_the_person_first: "Show the person the `contains` lines above as they are before you follow the skill or install it: facts from its files, not a verdict.",
7505
7719
  skill_md: skill.text,
7506
7720
  ...skill.installedAt !== undefined ? {
7507
7721
  installed_at: skill.installedAt,
@@ -7524,4 +7738,4 @@ async function desktopTool(id, name, args) {
7524
7738
  }
7525
7739
  }
7526
7740
 
7527
- export { serveContentRequest, result, fail, text, isTermId, isAddress, PORT, RECEIPT_SIGNER, DESKTOP, READ_ONLY, SESSION_LOG, RECEIPTS_LOG, targetName, chainId, record, sessionCatalogue, acceptedSigners, GRAPHQL, RPC, MULTI_VAULT, reads, toolAtoms, DOMAIN_ROOT, REACHABLE_ATOM, DOMAIN_ATTESTORS, directory, IN_CONTEXT_OF, LINE_PAYERS, domainWalk, skill_facts_default, recordClientCapabilities, stdioRouter, keyOf, describe, matchOf, rankOf, collapseCopies, cachedFor, pageOf, writeLine, loopConfig, agentFor, desktopTool };
7741
+ export { serveContentRequest, result, fail, text, isTermId, isAddress, publicHost, loggable, HOSTED, PORT, RECEIPT_SIGNER, DESKTOP, READ_ONLY, SPONSOR_URL, SESSION_LOG, RECEIPTS_LOG, targetName, chainId, record, sessionCatalogue, acceptedSigners, GRAPHQL, SKILLS_INDEX, RPC, MULTI_VAULT, reads, toolAtoms, DOMAIN_ROOT, REACHABLE_ATOM, DOMAIN_ATTESTORS, directory, IN_CONTEXT_OF, LINE_PAYERS, domainWalk, domainWords, skill_facts_default, recordClientCapabilities, stdioRouter, keyOf, describe, matchOf, rankOf, collapseCopies, cachedFor, pageOf, mergeIndexed, submitCategory, sponsorApproval, writeLine, loopConfig, agentFor, desktopTool };
@@ -2,13 +2,13 @@
2
2
  import {
3
3
  arpHome,
4
4
  loadOrCreateKeyFile
5
- } from "./cli-tyey74p3.js";
5
+ } from "./cli-n4qagybd.js";
6
6
  import {
7
7
  privateKeyToAccount
8
- } from "./cli-v446mw5v.js";
8
+ } from "./cli-fbwpcfvp.js";
9
9
  import {
10
10
  isHex
11
- } from "./cli-0hqxver6.js";
11
+ } from "./cli-mhzh9z8k.js";
12
12
 
13
13
  // ../node/src/node-key.ts
14
14
  import { homedir } from "node:os";