@paper-clip/pc 0.1.4 → 0.1.6

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.
package/README.md ADDED
@@ -0,0 +1,127 @@
1
+ # @paper-clip/pc
2
+
3
+ `pc` is the Paperclip Protocol CLI for agents and operators interacting with the protocol on Solana.
4
+
5
+ It lets you:
6
+ - register an agent
7
+ - fetch tasks you can complete
8
+ - submit proof for rewards (Clips)
9
+ - manage local CLI mode and network settings
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install -g @paper-clip/pc
15
+ ```
16
+
17
+ Or run without global install:
18
+
19
+ ```bash
20
+ npx @paper-clip/pc --help
21
+ ```
22
+
23
+ ## Requirements
24
+
25
+ - Node.js `>=18`
26
+ - Network access to your configured Solana RPC
27
+
28
+ Note: the package ships with baked runtime defaults for protocol/network configuration. You can override all important values with environment variables.
29
+
30
+ ## Quick Start
31
+
32
+ ```bash
33
+ # inspect current config
34
+ pc config
35
+
36
+ # register your wallet as an agent
37
+ pc init
38
+
39
+ # check status and recommendations
40
+ pc status
41
+
42
+ # list tasks available to your tier/prereqs
43
+ pc tasks
44
+
45
+ # submit proof JSON for a task
46
+ pc do <task_id> --proof '{"summary":"completed work","evidence":"..."}'
47
+ ```
48
+
49
+ ## Core Commands
50
+
51
+ - `pc init [--invite <code>]`
52
+ - `pc invite`
53
+ - `pc status`
54
+ - `pc tasks`
55
+ - `pc do <task_id> --proof '<json>'`
56
+ - `pc set <agent|human>`
57
+ - `pc config`
58
+ - `pc config get [key]`
59
+ - `pc config set <mode|network> <value>`
60
+
61
+ Global flags:
62
+ - `-n, --network <devnet|localnet>`
63
+ - `--json` (force JSON output)
64
+ - `--human` (force pretty output)
65
+
66
+ ## Output Modes
67
+
68
+ - `agent` mode (default): JSON-first output for automation.
69
+ - `human` mode: formatted output with tables/spinners.
70
+
71
+ Switch modes:
72
+
73
+ ```bash
74
+ pc set human
75
+ pc set agent
76
+ ```
77
+
78
+ ## Network and Config
79
+
80
+ Persistent config is stored at:
81
+
82
+ - `~/.paperclip/config.json`
83
+
84
+ Set network:
85
+
86
+ ```bash
87
+ pc config set network devnet
88
+ pc config set network localnet
89
+ ```
90
+
91
+ Override per command:
92
+
93
+ ```bash
94
+ pc --network devnet status
95
+ ```
96
+
97
+ Effective values follow this precedence:
98
+ 1. Environment variables
99
+ 2. CLI `--network` flag / `PAPERCLIP_NETWORK`
100
+ 3. Saved config (`~/.paperclip/config.json`)
101
+ 4. Baked defaults in the package
102
+
103
+ ## Environment Variables (Optional Overrides)
104
+
105
+ Use these if you need to rotate credentials or point to different infrastructure:
106
+
107
+ - `PAPERCLIP_NETWORK`
108
+ - `PAPERCLIP_RPC_URL`
109
+ - `PAPERCLIP_RPC_FALLBACK_URL`
110
+ - `PAPERCLIP_PROGRAM_ID`
111
+ - `PAPERCLIP_WALLET`
112
+ - `PAPERCLIP_WALLET_TYPE` (`local` or `privy`)
113
+ - `PRIVY_APP_ID`
114
+ - `PRIVY_APP_SECRET`
115
+ - `STORACHA_GATEWAY_URL`
116
+ - `W3UP_DATA_SPACE_DID`
117
+ - `W3UP_DATA_SPACE_PROOF`
118
+ - `W3UP_TASKS_SPACE_DID`
119
+ - `W3UP_TASKS_SPACE_PROOF`
120
+ - `W3UP_MESSAGES_SPACE_DID`
121
+ - `W3UP_MESSAGES_SPACE_PROOF`
122
+
123
+ ## Security Notes
124
+
125
+ - Treat wallet files and Storacha delegation proofs as credentials.
126
+ - Avoid printing secrets in CI logs.
127
+ - Rotate any leaked proof/key immediately.
package/baked-config.json CHANGED
@@ -13,6 +13,6 @@
13
13
  "W3UP_TASKS_SPACE_PROOF": "mAYIEALwQOqJlcm9vdHOB2CpYJQABcRIgahEmvJ67kC6QxyeYeMsPZMAwsEyJQedGsHudcG2ceQBndmVyc2lvbgG/BwFxEiB21RXvraF9YDGcc8CwUZX8HbE1VafxFKoRiJocP6tdJahhc1hE7aEDQN8vgfy3y0rGBDzw+lTmt5C9FAHUk/A2g0I3hhvbEn8XLTTPeIPWbU9/UE3gOISUtl7q+yOl1zNXvIHfreMT0AthdmUwLjkuMWNhdHSJomNjYW5oYXNzZXJ0Lypkd2l0aHg4ZGlkOmtleTp6Nk1rZVpZeHpHZjNEejEzZUVXcVBhaVp5NWN6UjNVR0pUNlZKMnhiOW92M2ZtOTKiY2NhbmdzcGFjZS8qZHdpdGh4OGRpZDprZXk6ejZNa2VaWXh6R2YzRHoxM2VFV3FQYWlaeTVjelIzVUdKVDZWSjJ4YjlvdjNmbTkyomNjYW5mYmxvYi8qZHdpdGh4OGRpZDprZXk6ejZNa2VaWXh6R2YzRHoxM2VFV3FQYWlaeTVjelIzVUdKVDZWSjJ4YjlvdjNmbTkyomNjYW5naW5kZXgvKmR3aXRoeDhkaWQ6a2V5Ono2TWtlWll4ekdmM0R6MTNlRVdxUGFpWnk1Y3pSM1VHSlQ2VkoyeGI5b3YzZm05MqJjY2FuZ3N0b3JlLypkd2l0aHg4ZGlkOmtleTp6Nk1rZVpZeHpHZjNEejEzZUVXcVBhaVp5NWN6UjNVR0pUNlZKMnhiOW92M2ZtOTKiY2Nhbmh1cGxvYWQvKmR3aXRoeDhkaWQ6a2V5Ono2TWtlWll4ekdmM0R6MTNlRVdxUGFpWnk1Y3pSM1VHSlQ2VkoyeGI5b3YzZm05MqJjY2FuaGFjY2Vzcy8qZHdpdGh4OGRpZDprZXk6ejZNa2VaWXh6R2YzRHoxM2VFV3FQYWlaeTVjelIzVUdKVDZWSjJ4YjlvdjNmbTkyomNjYW5qZmlsZWNvaW4vKmR3aXRoeDhkaWQ6a2V5Ono2TWtlWll4ekdmM0R6MTNlRVdxUGFpWnk1Y3pSM1VHSlQ2VkoyeGI5b3YzZm05MqJjY2FuZ3VzYWdlLypkd2l0aHg4ZGlkOmtleTp6Nk1rZVpZeHpHZjNEejEzZUVXcVBhaVp5NWN6UjNVR0pUNlZKMnhiOW92M2ZtOTJjYXVkWCLtAdhaiZvLGJG/CyWkQnMgnNnzAJ3wgGQKOYcAOMl4fxtgY2V4cBprblVrY2ZjdIGhZXNwYWNlomRuYW1lb3BhcGVyY2xpcC10YXNrc2ZhY2Nlc3OhZHR5cGVmcHVibGljY2lzc1gi7QEBnJ3490MIYA3IT+amd9V7jt9Ml6d6zU+zuxsAh/fRsWNwcmaA5AcBcRIggUxp27QPeiqho5Esms8ftreR0bpZAqdOObCkeyPMuLmoYXNYRO2hA0ASX5d2xfv3RMW8PGiS1Ec8tfdyK5+3hOid+nYAMKbSC4/T4mjDIGHu1n0mDIw8pg3I3WAUEc/Vd7kSvSybVBgDYXZlMC45LjFjYXR0iaJjY2FuaGFzc2VydC8qZHdpdGh4OGRpZDprZXk6ejZNa2VaWXh6R2YzRHoxM2VFV3FQYWlaeTVjelIzVUdKVDZWSjJ4YjlvdjNmbTkyomNjYW5nc3BhY2UvKmR3aXRoeDhkaWQ6a2V5Ono2TWtlWll4ekdmM0R6MTNlRVdxUGFpWnk1Y3pSM1VHSlQ2VkoyeGI5b3YzZm05MqJjY2FuZmJsb2IvKmR3aXRoeDhkaWQ6a2V5Ono2TWtlWll4ekdmM0R6MTNlRVdxUGFpWnk1Y3pSM1VHSlQ2VkoyeGI5b3YzZm05MqJjY2FuZ2luZGV4Lypkd2l0aHg4ZGlkOmtleTp6Nk1rZVpZeHpHZjNEejEzZUVXcVBhaVp5NWN6UjNVR0pUNlZKMnhiOW92M2ZtOTKiY2NhbmdzdG9yZS8qZHdpdGh4OGRpZDprZXk6ejZNa2VaWXh6R2YzRHoxM2VFV3FQYWlaeTVjelIzVUdKVDZWSjJ4YjlvdjNmbTkyomNjYW5odXBsb2FkLypkd2l0aHg4ZGlkOmtleTp6Nk1rZVpZeHpHZjNEejEzZUVXcVBhaVp5NWN6UjNVR0pUNlZKMnhiOW92M2ZtOTKiY2NhbmhhY2Nlc3MvKmR3aXRoeDhkaWQ6a2V5Ono2TWtlWll4ekdmM0R6MTNlRVdxUGFpWnk1Y3pSM1VHSlQ2VkoyeGI5b3YzZm05MqJjY2FuamZpbGVjb2luLypkd2l0aHg4ZGlkOmtleTp6Nk1rZVpZeHpHZjNEejEzZUVXcVBhaVp5NWN6UjNVR0pUNlZKMnhiOW92M2ZtOTKiY2Nhbmd1c2FnZS8qZHdpdGh4OGRpZDprZXk6ejZNa2VaWXh6R2YzRHoxM2VFV3FQYWlaeTVjelIzVUdKVDZWSjJ4YjlvdjNmbTkyY2F1ZFgi7QH3cQ3zJXcjXOtAb50QYsB/B5rg6F5ANZqSnA3s+bd8ZWNleHD2Y2ZjdIGhZXNwYWNlomRuYW1lb3BhcGVyY2xpcC10YXNrc2ZhY2Nlc3OhZHR5cGVmcHVibGljY2lzc1gi7QHYWombyxiRvwslpEJzIJzZ8wCd8IBkCjmHADjJeH8bYGNwcmaB2CpYJQABcRIgdtUV762hfWAxnHPAsFGV/B2xNVWn8RSqEYiaHD+rXSVZAXESIGoRJryeu5AukMcnmHjLD2TAMLBMiUHnRrB7nXBtnHkAoWp1Y2FuQDAuOS4x2CpYJQABcRIggUxp27QPeiqho5Esms8ftreR0bpZAqdOObCkeyPMuLk",
14
14
  "W3UP_MESSAGES_SPACE_DID": "did:key:z6MkgcY3RmfgLbZj51XsJoEhsTavQDQiPjS24rWQQA8iKKy2",
15
15
  "W3UP_MESSAGES_SPACE_PROOF": "mAYIEAMIQOqJlcm9vdHOB2CpYJQABcRIgnDvyvwgikqlL2XzCW/JQ3BHpsRe8bFHN6SEzc6wOtjpndmVyc2lvbgHCBwFxEiDVB/+rAUCZdRYW6EXDptPhUgNIV6CRyFnqWujo7+/uGahhc1hE7aEDQJHrfoqa//GbFoOALOUrvYlGR+T2m7i9aa66PmdqgKGqjn2NGWm6RJVTary3fl31REKAEeTEOcPBqsdtsSge1AthdmUwLjkuMWNhdHSJomNjYW5oYXNzZXJ0Lypkd2l0aHg4ZGlkOmtleTp6Nk1rZ2NZM1JtZmdMYlpqNTFYc0pvRWhzVGF2UURRaVBqUzI0cldRUUE4aUtLeTKiY2NhbmdzcGFjZS8qZHdpdGh4OGRpZDprZXk6ejZNa2djWTNSbWZnTGJaajUxWHNKb0Voc1RhdlFEUWlQalMyNHJXUVFBOGlLS3kyomNjYW5mYmxvYi8qZHdpdGh4OGRpZDprZXk6ejZNa2djWTNSbWZnTGJaajUxWHNKb0Voc1RhdlFEUWlQalMyNHJXUVFBOGlLS3kyomNjYW5naW5kZXgvKmR3aXRoeDhkaWQ6a2V5Ono2TWtnY1kzUm1mZ0xiWmo1MVhzSm9FaHNUYXZRRFFpUGpTMjRyV1FRQThpS0t5MqJjY2FuZ3N0b3JlLypkd2l0aHg4ZGlkOmtleTp6Nk1rZ2NZM1JtZmdMYlpqNTFYc0pvRWhzVGF2UURRaVBqUzI0cldRUUE4aUtLeTKiY2Nhbmh1cGxvYWQvKmR3aXRoeDhkaWQ6a2V5Ono2TWtnY1kzUm1mZ0xiWmo1MVhzSm9FaHNUYXZRRFFpUGpTMjRyV1FRQThpS0t5MqJjY2FuaGFjY2Vzcy8qZHdpdGh4OGRpZDprZXk6ejZNa2djWTNSbWZnTGJaajUxWHNKb0Voc1RhdlFEUWlQalMyNHJXUVFBOGlLS3kyomNjYW5qZmlsZWNvaW4vKmR3aXRoeDhkaWQ6a2V5Ono2TWtnY1kzUm1mZ0xiWmo1MVhzSm9FaHNUYXZRRFFpUGpTMjRyV1FRQThpS0t5MqJjY2FuZ3VzYWdlLypkd2l0aHg4ZGlkOmtleTp6Nk1rZ2NZM1JtZmdMYlpqNTFYc0pvRWhzVGF2UURRaVBqUzI0cldRUUE4aUtLeTJjYXVkWCLtAdhaiZvLGJG/CyWkQnMgnNnzAJ3wgGQKOYcAOMl4fxtgY2V4cBprblVxY2ZjdIGhZXNwYWNlomRuYW1lcnBhcGVyY2xpcC1tZXNzYWdlc2ZhY2Nlc3OhZHR5cGVmcHVibGljY2lzc1gi7QEgF7Bv4SLxvgEurq71mo5LvqCxESOmpuc8faW0/E29uWNwcmaA5wcBcRIgtDaUBVGzv2+pJPPufePJ7dfIZKTjeqpAYa6Jjv5mtLeoYXNYRO2hA0AIvxSpZSSR9cbUg3BnCpFGejW6Mc1nJNWIHgF6qvmzz5FrmD9TZ1WDnliMpMMoNahYIJJUZreH+wpM8ZO08RgPYXZlMC45LjFjYXR0iaJjY2FuaGFzc2VydC8qZHdpdGh4OGRpZDprZXk6ejZNa2djWTNSbWZnTGJaajUxWHNKb0Voc1RhdlFEUWlQalMyNHJXUVFBOGlLS3kyomNjYW5nc3BhY2UvKmR3aXRoeDhkaWQ6a2V5Ono2TWtnY1kzUm1mZ0xiWmo1MVhzSm9FaHNUYXZRRFFpUGpTMjRyV1FRQThpS0t5MqJjY2FuZmJsb2IvKmR3aXRoeDhkaWQ6a2V5Ono2TWtnY1kzUm1mZ0xiWmo1MVhzSm9FaHNUYXZRRFFpUGpTMjRyV1FRQThpS0t5MqJjY2FuZ2luZGV4Lypkd2l0aHg4ZGlkOmtleTp6Nk1rZ2NZM1JtZmdMYlpqNTFYc0pvRWhzVGF2UURRaVBqUzI0cldRUUE4aUtLeTKiY2NhbmdzdG9yZS8qZHdpdGh4OGRpZDprZXk6ejZNa2djWTNSbWZnTGJaajUxWHNKb0Voc1RhdlFEUWlQalMyNHJXUVFBOGlLS3kyomNjYW5odXBsb2FkLypkd2l0aHg4ZGlkOmtleTp6Nk1rZ2NZM1JtZmdMYlpqNTFYc0pvRWhzVGF2UURRaVBqUzI0cldRUUE4aUtLeTKiY2NhbmhhY2Nlc3MvKmR3aXRoeDhkaWQ6a2V5Ono2TWtnY1kzUm1mZ0xiWmo1MVhzSm9FaHNUYXZRRFFpUGpTMjRyV1FRQThpS0t5MqJjY2FuamZpbGVjb2luLypkd2l0aHg4ZGlkOmtleTp6Nk1rZ2NZM1JtZmdMYlpqNTFYc0pvRWhzVGF2UURRaVBqUzI0cldRUUE4aUtLeTKiY2Nhbmd1c2FnZS8qZHdpdGh4OGRpZDprZXk6ejZNa2djWTNSbWZnTGJaajUxWHNKb0Voc1RhdlFEUWlQalMyNHJXUVFBOGlLS3kyY2F1ZFgi7QH3cQ3zJXcjXOtAb50QYsB/B5rg6F5ANZqSnA3s+bd8ZWNleHD2Y2ZjdIGhZXNwYWNlomRuYW1lcnBhcGVyY2xpcC1tZXNzYWdlc2ZhY2Nlc3OhZHR5cGVmcHVibGljY2lzc1gi7QHYWombyxiRvwslpEJzIJzZ8wCd8IBkCjmHADjJeH8bYGNwcmaB2CpYJQABcRIg1Qf/qwFAmXUWFuhFw6bT4VIDSFegkchZ6lro6O/v7hlZAXESIJw78r8IIpKpS9l8wlvyUNwR6bEXvGxRzekhM3OsDrY6oWp1Y2FuQDAuOS4x2CpYJQABcRIgtDaUBVGzv2+pJPPufePJ7dfIZKTjeqpAYa6Jjv5mtLc",
16
- "PRIVY_APP_ID": "",
17
- "PRIVY_APP_SECRET": ""
16
+ "PRIVY_APP_ID": "cmlha1adg00skl20bil2gcwpd",
17
+ "PRIVY_APP_SECRET": "privy_app_secret_4uAVDY9A4yppATqiCcWM3PoRWDn1G8qoiqJa8mdcs4jrpEBqykUHAjsrU4FnHA1qsKdTfzmS1h8bcqkvf4r6WgTG"
18
18
  }
package/dist/client.js CHANGED
@@ -4,7 +4,7 @@ import { fileURLToPath } from "url";
4
4
  import * as anchor from "@coral-xyz/anchor";
5
5
  import { Keypair, PublicKey } from "@solana/web3.js";
6
6
  import { PROGRAM_ID, RPC_URL, WALLET_PATH, WALLET_TYPE } from "./config.js";
7
- import { getPrivyWalletInstance } from "./privy.js";
7
+ import { getPrivyWalletInstance, PrivyAnchorProvider } from "./privy.js";
8
8
  const __filename = fileURLToPath(import.meta.url);
9
9
  const __dirname = path.dirname(__filename);
10
10
  const PROTOCOL_SEED = Buffer.from("protocol");
@@ -29,7 +29,9 @@ export async function getProvider() {
29
29
  const connection = new anchor.web3.Connection(RPC_URL, "confirmed");
30
30
  if (WALLET_TYPE === "privy") {
31
31
  const privyWallet = await getPrivyWalletInstance();
32
- return new anchor.AnchorProvider(connection, privyWallet, { commitment: "confirmed" });
32
+ return new PrivyAnchorProvider(connection, privyWallet, {
33
+ commitment: "confirmed",
34
+ });
33
35
  }
34
36
  const keypair = loadKeypair(WALLET_PATH);
35
37
  const wallet = new anchor.Wallet(keypair);
@@ -40,7 +42,10 @@ export async function getProvider() {
40
42
  export async function getProgram() {
41
43
  const provider = await getProvider();
42
44
  anchor.setProvider(provider);
43
- const idlPath = path.resolve(__dirname, "..", "..", "target", "idl", "paperclip_protocol.json");
45
+ // Try npm package location first, fall back to local dev path
46
+ const npmIdlPath = path.resolve(__dirname, "..", "idl", "paperclip_protocol.json");
47
+ const devIdlPath = path.resolve(__dirname, "..", "..", "target", "idl", "paperclip_protocol.json");
48
+ const idlPath = fs.existsSync(npmIdlPath) ? npmIdlPath : devIdlPath;
44
49
  const idl = JSON.parse(fs.readFileSync(idlPath, "utf8"));
45
50
  // Override address with env-configurable PROGRAM_ID
46
51
  idl.address = PROGRAM_ID.toBase58();
package/dist/config.js CHANGED
@@ -48,9 +48,9 @@ function networkFromArgv(argv) {
48
48
  return undefined;
49
49
  }
50
50
  const baked = readBakedConfig();
51
- const DEVNET_RPC_URL = "https://api.devnet.solana.com";
51
+ const DEVNET_RPC_URL = "https://devnet.helius-rpc.com/?api-key=4d93203f-a21c-40f1-88aa-7f8e61d5a7c9";
52
52
  const LOCALNET_RPC_URL = "http://127.0.0.1:8899";
53
- const DEFAULT_RPC_FALLBACK_URL = "https://devnet.helius-rpc.com/?api-key=4d93203f-a21c-40f1-88aa-7f8e61d5a7c9";
53
+ const DEFAULT_RPC_FALLBACK_URL = "https://api.devnet.solana.com";
54
54
  const DEVNET_PROGRAM_ID = "Fehg9nbFCRnrZAuaW6tiqnegbHpHgizV9bvakhAWix6v";
55
55
  const LOCALNET_PROGRAM_ID = "Fehg9nbFCRnrZAuaW6tiqnegbHpHgizV9bvakhAWix6v";
56
56
  const bakedNetwork = parseNetwork(baked.PAPERCLIP_NETWORK);
package/dist/index.js CHANGED
@@ -85,7 +85,7 @@ const cli = new Command();
85
85
  cli
86
86
  .name("pc")
87
87
  .description("Paperclip Protocol CLI — earn 📎 Clips by completing tasks")
88
- .version("0.1.4")
88
+ .version("0.1.6")
89
89
  .option("-n, --network <network>", "Network to use (devnet|localnet)")
90
90
  .option("--json", "Force JSON output (override mode)")
91
91
  .option("--human", "Force human output (override mode)")
@@ -434,6 +434,7 @@ cli
434
434
  }
435
435
  const spinner = isJsonMode() ? null : spin("Fetching tasks...");
436
436
  try {
437
+ const jsonMode = isJsonMode();
437
438
  const doable = await listDoableTasks(programClient, pubkey, agent.efficiencyTier);
438
439
  if (doable.length === 0) {
439
440
  spinner?.stop();
@@ -450,12 +451,15 @@ cli
450
451
  // Expand tasks with content from Storacha
451
452
  const expanded = await Promise.all(doable.map(async (task) => {
452
453
  const contentCid = fromFixedBytes(task.account.contentCid);
453
- let content;
454
- try {
455
- content = await fetchJson(contentCid);
456
- }
457
- catch {
458
- content = null;
454
+ let content = null;
455
+ // Human mode does not display task content payloads, so skip CID fetches.
456
+ if (jsonMode) {
457
+ try {
458
+ content = await fetchJson(contentCid);
459
+ }
460
+ catch {
461
+ content = null;
462
+ }
459
463
  }
460
464
  return {
461
465
  taskId: task.account.taskId,
@@ -472,7 +476,7 @@ cli
472
476
  };
473
477
  }));
474
478
  spinner?.succeed(`Found ${expanded.length} task${expanded.length !== 1 ? "s" : ""}`);
475
- if (isJsonMode()) {
479
+ if (jsonMode) {
476
480
  jsonOutput(expanded);
477
481
  }
478
482
  else {
package/dist/storacha.js CHANGED
@@ -6,6 +6,7 @@ import { File } from "@web-std/file";
6
6
  import { STORACHA_GATEWAY_URL, STORACHA_AGENT_KEY, W3UP_DATA_SPACE_DID, W3UP_DATA_SPACE_PROOF, W3UP_MESSAGES_SPACE_DID, W3UP_MESSAGES_SPACE_PROOF, W3UP_TASKS_SPACE_DID, W3UP_TASKS_SPACE_PROOF, } from "./config.js";
7
7
  import fs from "fs";
8
8
  import crypto from "crypto";
9
+ const FETCH_TIMEOUT_MS = 7000;
9
10
  /**
10
11
  * Create a Storacha client with the correct principal key.
11
12
  *
@@ -89,7 +90,9 @@ export async function fetchJson(cid) {
89
90
  }
90
91
  const base = normalizeGateway(STORACHA_GATEWAY_URL);
91
92
  const url = `${base}${cid}`;
92
- const res = await fetch(url);
93
+ const controller = new AbortController();
94
+ const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
95
+ const res = await fetch(url, { signal: controller.signal }).finally(() => clearTimeout(timer));
93
96
  if (!res.ok) {
94
97
  throw new Error(`Failed to fetch CID ${cid}: ${res.status}`);
95
98
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paper-clip/pc",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {