@toon-protocol/rig 3.6.0 → 4.1.0

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 (37) hide show
  1. package/README.md +112 -153
  2. package/dist/{chunk-HJDPM37T.js → chunk-7VCVR4GK.js} +12 -8
  3. package/dist/{chunk-HJDPM37T.js.map → chunk-7VCVR4GK.js.map} +1 -1
  4. package/dist/chunk-CDSFVZDV.js +290 -0
  5. package/dist/chunk-CDSFVZDV.js.map +1 -0
  6. package/dist/{chunk-B5ISARMU.js → chunk-GVXBUL3C.js} +15 -2
  7. package/dist/chunk-GVXBUL3C.js.map +1 -0
  8. package/dist/chunk-LTFMPOHU.js +194 -0
  9. package/dist/chunk-LTFMPOHU.js.map +1 -0
  10. package/dist/{chunk-2KJJ7LSO.js → chunk-NCEMQUPA.js} +40 -2
  11. package/dist/chunk-NCEMQUPA.js.map +1 -0
  12. package/dist/{chunk-LCYCKWAT.js → chunk-YZRR2UO6.js} +86 -5
  13. package/dist/chunk-YZRR2UO6.js.map +1 -0
  14. package/dist/cli/rig.js +631 -568
  15. package/dist/cli/rig.js.map +1 -1
  16. package/dist/index.d.ts +10 -115
  17. package/dist/index.js +16 -122
  18. package/dist/index.js.map +1 -1
  19. package/dist/{publisher-BFcnZeJI.d.ts → publisher-DMJ06cuL.d.ts} +98 -2
  20. package/dist/standalone/index.d.ts +241 -438
  21. package/dist/standalone/index.js +19 -11
  22. package/dist/standalone-mode-KTYRTUS6.js +390 -0
  23. package/dist/standalone-mode-KTYRTUS6.js.map +1 -0
  24. package/package.json +7 -5
  25. package/dist/chunk-2KJJ7LSO.js.map +0 -1
  26. package/dist/chunk-B5ISARMU.js.map +0 -1
  27. package/dist/chunk-IZ6KLYQ6.js +0 -724
  28. package/dist/chunk-IZ6KLYQ6.js.map +0 -1
  29. package/dist/chunk-LCYCKWAT.js.map +0 -1
  30. package/dist/chunk-PWNZWQOH.js +0 -404
  31. package/dist/chunk-PWNZWQOH.js.map +0 -1
  32. package/dist/chunk-UZZ2RN7A.js +0 -469
  33. package/dist/chunk-UZZ2RN7A.js.map +0 -1
  34. package/dist/network-bootstrap-27FYE3N7.js +0 -40
  35. package/dist/network-bootstrap-27FYE3N7.js.map +0 -1
  36. package/dist/standalone-mode-5MNNJC2D.js +0 -928
  37. package/dist/standalone-mode-5MNNJC2D.js.map +0 -1
package/dist/cli/rig.js CHANGED
@@ -18,34 +18,17 @@ import {
18
18
  setHeadSymref,
19
19
  updateRef,
20
20
  writeGitObjects
21
- } from "../chunk-HJDPM37T.js";
21
+ } from "../chunk-7VCVR4GK.js";
22
22
  import {
23
23
  DEFAULT_KEYSTORE_PASSWORD,
24
- MinaZkAppStore,
25
24
  MissingIdentityError,
26
- TOPOLOGY_CACHE_FILENAME,
27
25
  clientConfigPath,
28
26
  clientKeystorePath,
29
27
  linkKeystoreInClientConfig,
30
28
  readClientConfigFile,
31
29
  resolveAccountIndex,
32
30
  resolveIdentity
33
- } from "../chunk-PWNZWQOH.js";
34
- import {
35
- ChannelMapStore,
36
- RIG_STANDALONE_ENV,
37
- channelStatus,
38
- defaultDaemonPort,
39
- resolveChannelPaths,
40
- resolveConflictingPath,
41
- standaloneForced
42
- } from "../chunk-LCYCKWAT.js";
43
- import {
44
- MAX_OBJECT_SIZE
45
- } from "../chunk-B5ISARMU.js";
46
- import {
47
- minaPresetForChain
48
- } from "../chunk-UZZ2RN7A.js";
31
+ } from "../chunk-LTFMPOHU.js";
49
32
  import {
50
33
  ARWEAVE_GATEWAYS,
51
34
  COMMENT_KIND,
@@ -56,9 +39,24 @@ import {
56
39
  buildRepoAnnouncement,
57
40
  buildStatus,
58
41
  fetchRemoteState,
42
+ isValidEvmPayoutAddress,
59
43
  parseMaintainers,
60
44
  queryRelay
61
- } from "../chunk-2KJJ7LSO.js";
45
+ } from "../chunk-NCEMQUPA.js";
46
+ import {
47
+ ChannelMapStore,
48
+ RIG_STANDALONE_ENV,
49
+ channelStatus,
50
+ defaultDaemonPort,
51
+ deriveNostrKeyFromMnemonic,
52
+ resolveChannelPaths,
53
+ resolveConflictingPath,
54
+ standaloneForced
55
+ } from "../chunk-YZRR2UO6.js";
56
+ import {
57
+ MAX_OBJECT_SIZE,
58
+ uploadChargeFor
59
+ } from "../chunk-GVXBUL3C.js";
62
60
 
63
61
  // src/cli/rig.ts
64
62
  import { createInterface as createInterface2 } from "readline/promises";
@@ -624,6 +622,21 @@ function generateRigPointerHtml(options) {
624
622
  `;
625
623
  }
626
624
 
625
+ // src/gateway-preference.ts
626
+ var TESTNET_GATEWAY = /(^|\.)ar-io\.dev$/i;
627
+ function isMainnetGateway(url) {
628
+ try {
629
+ return !TESTNET_GATEWAY.test(new URL(url).hostname);
630
+ } catch {
631
+ return false;
632
+ }
633
+ }
634
+ var PREFERRED_GATEWAY = ARWEAVE_GATEWAYS.find(isMainnetGateway) ?? "https://arweave.net";
635
+ function mirrorGatewaysFor(primary) {
636
+ const p = primary.replace(/\/+$/, "");
637
+ return ARWEAVE_GATEWAYS.filter((g) => g.replace(/\/+$/, "") !== p);
638
+ }
639
+
627
640
  // src/cli/rig-pointer-record.ts
628
641
  import { mkdirSync, readFileSync, writeFileSync } from "fs";
629
642
  import { homedir } from "os";
@@ -1098,7 +1111,7 @@ function loadPaidSession(deps, relayUrl) {
1098
1111
  });
1099
1112
  }
1100
1113
  var defaultLoadStandalone = async (options) => {
1101
- const mod = await import("../standalone-mode-5MNNJC2D.js");
1114
+ const mod = await import("../standalone-mode-KTYRTUS6.js");
1102
1115
  return mod.createStandaloneContext(options);
1103
1116
  };
1104
1117
  function identityReport(ctx) {
@@ -1240,11 +1253,10 @@ function parsePushArgs(args) {
1240
1253
  return flags;
1241
1254
  }
1242
1255
  function pointerGateway(env) {
1243
- const preferred = ARWEAVE_GATEWAYS[0] ?? "https://arweave.net";
1244
- return (env["RIG_ARWEAVE_GATEWAY"] ?? preferred).replace(/\/+$/, "");
1256
+ return (env["RIG_ARWEAVE_GATEWAY"] ?? PREFERRED_GATEWAY).replace(/\/+$/, "");
1245
1257
  }
1246
1258
  function mirrorGateways(primary) {
1247
- return ARWEAVE_GATEWAYS.filter((g) => g.replace(/\/+$/, "") !== primary);
1259
+ return mirrorGatewaysFor(primary);
1248
1260
  }
1249
1261
  function planRigPointer(args) {
1250
1262
  const html = generateRigPointerHtml({
@@ -1280,7 +1292,7 @@ function planRigPointer(args) {
1280
1292
  html,
1281
1293
  contentHash,
1282
1294
  bytes,
1283
- fee: args.uploadFee
1295
+ fee: uploadChargeFor(args.feeRates, bytes)
1284
1296
  };
1285
1297
  }
1286
1298
  async function runPush(args, deps) {
@@ -1401,7 +1413,7 @@ async function runPush(args, deps) {
1401
1413
  ownerPubkey: ctx.ownerPubkey,
1402
1414
  repoId,
1403
1415
  relay: relaysUsed[0],
1404
- uploadFee: feeRates.uploadFee
1416
+ feeRates
1405
1417
  });
1406
1418
  }
1407
1419
  } else {
@@ -2041,9 +2053,6 @@ async function runChain(args, deps) {
2041
2053
  }
2042
2054
 
2043
2055
  // src/cli/channel.ts
2044
- import { readFileSync as readFileSync3 } from "fs";
2045
- import { homedir as homedir3 } from "os";
2046
- import { join as join3 } from "path";
2047
2056
  import { parseArgs as parseArgs5 } from "util";
2048
2057
  var CHANNEL_USAGE = `Usage: rig channel <subcommand>
2049
2058
 
@@ -2066,17 +2075,6 @@ Subcommands:
2066
2075
  apex); --deposit adds that much extra collateral after the
2067
2076
  open/resume. On-chain: asks for confirmation (--yes skips).
2068
2077
 
2069
- deploy-zkapp [--graphql-url <url>]
2070
- deploy a dedicated Mina PaymentChannel zkApp for THIS
2071
- identity (the zkApp is single-pair; Mina channels open on
2072
- an identity-owned deployment). Normally automatic \u2014 the
2073
- Mina open path deploys one when needed \u2014 this explicit
2074
- form pre-deploys it (compile \u22481-3 min, inclusion \u22483-6 min).
2075
- On-chain: costs the 1 MINA account-creation fee + tx fee
2076
- from the wallet's MINA. The zkApp key is saved under
2077
- TOON_CLIENT_HOME/keys/. Asks for confirmation (--yes
2078
- skips).
2079
-
2080
2078
  close <channelId>
2081
2079
  close a recorded channel \u2014 an on-chain tx that starts the
2082
2080
  settlement challenge window (the peer's settlementTimeout).
@@ -2100,8 +2098,6 @@ async function runChannel(args, deps) {
2100
2098
  return runChannelList(rest, deps);
2101
2099
  case "open":
2102
2100
  return runChannelOpen(rest, deps);
2103
- case "deploy-zkapp":
2104
- return runChannelDeployZkApp(rest, deps);
2105
2101
  case "close":
2106
2102
  return runChannelWithdrawStep(rest, deps, "close");
2107
2103
  case "settle":
@@ -2294,9 +2290,6 @@ async function runChannelOpen(args, deps) {
2294
2290
  io2.out(
2295
2291
  "If a live channel is already recorded for this peer it is RESUMED (no on-chain spend); otherwise an on-chain channel open locks the peer-negotiated initial deposit from your wallet (plus gas)."
2296
2292
  );
2297
- io2.out(
2298
- "On a Mina chain a first open may also deploy a dedicated PaymentChannel zkApp for this identity (~1.1 MINA + fees; compile \u22481-3 min, inclusion \u22483-6 min \u2014 progress is printed)."
2299
- );
2300
2293
  }
2301
2294
  const gate = await confirmOnChain(
2302
2295
  io2,
@@ -2355,160 +2348,6 @@ async function runChannelOpen(args, deps) {
2355
2348
  await stopQuietly(ctx);
2356
2349
  }
2357
2350
  }
2358
- async function runChannelDeployZkApp(args, deps) {
2359
- const { io: io2, env } = deps;
2360
- let graphqlFlag;
2361
- let yes = false;
2362
- let json = false;
2363
- try {
2364
- const { values } = parseArgs5({
2365
- args,
2366
- options: {
2367
- "graphql-url": { type: "string" },
2368
- yes: { type: "boolean", default: false },
2369
- json: { type: "boolean", default: false },
2370
- help: { type: "boolean", short: "h", default: false }
2371
- }
2372
- });
2373
- if (values.help) {
2374
- io2.out(CHANNEL_USAGE);
2375
- return 0;
2376
- }
2377
- graphqlFlag = values["graphql-url"];
2378
- yes = values.yes ?? false;
2379
- json = values.json ?? false;
2380
- } catch (err) {
2381
- io2.err(err instanceof Error ? err.message : String(err));
2382
- io2.err(CHANNEL_USAGE);
2383
- return 2;
2384
- }
2385
- try {
2386
- const dir = env["TOON_CLIENT_HOME"] ?? join3(homedir3(), ".toon-client");
2387
- let file = {};
2388
- try {
2389
- file = JSON.parse(readFileSync3(join3(dir, "config.json"), "utf8"));
2390
- } catch {
2391
- }
2392
- const pinned = env["TOON_CLIENT_CHAIN"] ?? file.chain;
2393
- const chain = pinned !== void 0 && pinned.startsWith("mina:") ? pinned : "mina:devnet";
2394
- const graphqlUrl = graphqlFlag ?? file.minaChannel?.graphqlUrl ?? minaPresetForChain(chain)?.graphqlUrl;
2395
- if (!graphqlUrl) {
2396
- throw new Error(
2397
- `no Mina GraphQL endpoint for ${chain} \u2014 pass --graphql-url or set minaChannel.graphqlUrl in the client config`
2398
- );
2399
- }
2400
- const resolved = await resolveIdentity({
2401
- env,
2402
- cwd: deps.cwd,
2403
- warn: (line) => io2.err(line)
2404
- });
2405
- const identity = {
2406
- pubkey: resolved.pubkey,
2407
- source: resolved.source,
2408
- sourceLabel: resolved.sourceLabel
2409
- };
2410
- const store = MinaZkAppStore.forHome(dir);
2411
- const existing = store.lookup(resolved.pubkey, chain);
2412
- if (existing) {
2413
- if (json) {
2414
- io2.emitJson({
2415
- command: "channel deploy-zkapp",
2416
- identity,
2417
- executed: false,
2418
- plan: { chain, graphqlUrl },
2419
- result: {
2420
- zkAppAddress: existing.zkAppAddress,
2421
- deployTxHash: existing.deployTxHash ?? null,
2422
- vkHash: existing.vkHash ?? null,
2423
- storePath: store.filePath
2424
- },
2425
- hint: "a zkApp is already recorded for this identity/chain \u2014 the open path reuses it"
2426
- });
2427
- return 0;
2428
- }
2429
- io2.out(
2430
- `A Mina zkApp is already recorded for this identity on ${chain}: ` + existing.zkAppAddress
2431
- );
2432
- io2.out(`The open path reuses it (record: ${store.filePath}).`);
2433
- return 0;
2434
- }
2435
- const plan = { chain, graphqlUrl };
2436
- if (!json) {
2437
- io2.out("Mina zkApp deploy plan:");
2438
- io2.out(` chain ${chain}`);
2439
- io2.out(` graphql ${graphqlUrl}`);
2440
- io2.out(renderIdentityLine(identity));
2441
- io2.out(
2442
- "Deploys a dedicated PaymentChannel zkApp for this identity \u2014 on-chain: the 1 MINA account-creation fee + a 0.1 MINA tx fee from your wallet. Compile \u22481-3 min; inclusion \u22483-6 min."
2443
- );
2444
- }
2445
- const gate = await confirmOnChain(
2446
- io2,
2447
- { yes, json },
2448
- "Proceed with the on-chain zkApp deploy? [y/N] "
2449
- );
2450
- if (gate === "json-plan") {
2451
- io2.emitJson({
2452
- command: "channel deploy-zkapp",
2453
- identity,
2454
- executed: false,
2455
- plan,
2456
- hint: "plan only \u2014 re-run with --yes to deploy (on-chain: ~1.1 MINA + fees)"
2457
- });
2458
- return 0;
2459
- }
2460
- if (gate !== "proceed") return gate;
2461
- const client = await import("@toon-protocol/client");
2462
- const derived = await client.deriveFullIdentity(
2463
- resolved.mnemonic,
2464
- resolved.accountIndex
2465
- );
2466
- if (!derived.mina.privateKey) {
2467
- throw new Error(
2468
- "no Mina key derived \u2014 the optional `mina-signer` dependency is not installed"
2469
- );
2470
- }
2471
- const record = await client.deployMinaChannelZkApp({
2472
- graphqlUrl,
2473
- payerPrivateKey: derived.mina.privateKey,
2474
- onProgress: (line) => io2.err(`rig: mina: ${line}`)
2475
- });
2476
- store.save({
2477
- identity: resolved.pubkey,
2478
- chain,
2479
- zkAppAddress: record.zkAppAddress,
2480
- zkAppPrivateKey: record.zkAppPrivateKey,
2481
- feePayer: record.feePayer,
2482
- ...record.deployTxHash !== void 0 ? { deployTxHash: record.deployTxHash } : {},
2483
- ...record.vkHash !== void 0 ? { vkHash: record.vkHash } : {},
2484
- deployedAt: (/* @__PURE__ */ new Date()).toISOString(),
2485
- source: "rig channel deploy-zkapp (npm @toon-protocol/mina-zkapp build)"
2486
- });
2487
- if (json) {
2488
- io2.emitJson({
2489
- command: "channel deploy-zkapp",
2490
- identity,
2491
- executed: true,
2492
- plan,
2493
- result: {
2494
- zkAppAddress: record.zkAppAddress,
2495
- deployTxHash: record.deployTxHash ?? null,
2496
- vkHash: record.vkHash ?? null,
2497
- storePath: store.filePath
2498
- }
2499
- });
2500
- return 0;
2501
- }
2502
- io2.out(`Deployed Mina PaymentChannel zkApp ${record.zkAppAddress}.`);
2503
- if (record.deployTxHash) io2.out(` deploy tx ${record.deployTxHash}`);
2504
- if (record.vkHash) io2.out(` vk hash ${record.vkHash}`);
2505
- io2.out(` record ${store.filePath} (holds the zkApp key \u2014 back it up)`);
2506
- io2.out("The next Mina channel open/push finds and reuses this zkApp.");
2507
- return 0;
2508
- } catch (err) {
2509
- return emitCliError(io2, json, "channel deploy-zkapp", err);
2510
- }
2511
- }
2512
2351
  function withdrawPrecheck(step, channelId, watermark, nowSec) {
2513
2352
  const status = channelStatus(watermark, nowSec);
2514
2353
  if (status === "settled") {
@@ -2678,7 +2517,7 @@ async function stopQuietly(ctx) {
2678
2517
 
2679
2518
  // src/cli/clone.ts
2680
2519
  import { mkdtemp, mkdir, readdir, rename, rm, rmdir } from "fs/promises";
2681
- import { basename, dirname as dirname2, join as join4, resolve } from "path";
2520
+ import { basename, dirname as dirname2, join as join3, resolve } from "path";
2682
2521
  import { parseArgs as parseArgs6 } from "util";
2683
2522
  var CLONE_USAGE = `Usage: rig clone <relay-url> <owner>/<repo-id> [dir] [options]
2684
2523
 
@@ -2849,7 +2688,7 @@ async function runClone(args, deps) {
2849
2688
  }
2850
2689
  const parent = dirname2(dest);
2851
2690
  await mkdir(parent, { recursive: true });
2852
- tempDir = await mkdtemp(join4(parent, `.${basename(dest)}.rig-clone-`));
2691
+ tempDir = await mkdtemp(join3(parent, `.${basename(dest)}.rig-clone-`));
2853
2692
  await runGit(tempDir, [
2854
2693
  "init",
2855
2694
  "--quiet",
@@ -4102,48 +3941,39 @@ async function warnIfNotMaintainer(deps, args) {
4102
3941
  }
4103
3942
 
4104
3943
  // src/cli/entry.ts
4105
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
4106
- import { homedir as homedir4 } from "os";
4107
- import { dirname as dirname3, join as join5 } from "path";
3944
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
3945
+ import { homedir as homedir3 } from "os";
3946
+ import { dirname as dirname3, join as join4 } from "path";
4108
3947
  import { parseArgs as parseArgs9 } from "util";
4109
- var SANDBOX_BTP_URL = "wss://proxy.sandbox.devnet.toonprotocol.dev:443";
4110
- var SANDBOX_RELAY_URL = "wss://relay-ws.sandbox.devnet.toonprotocol.dev";
4111
- var ENTRY_USAGE = `Usage: rig entry [apex | sandbox | <wss-url>] [options]
4112
-
4113
- Choose which network entry node (payment ingress + relay) rig talks to. Free:
4114
- only the local config file is touched.
4115
-
4116
- rig entry show the effective entry endpoints and their source
4117
- rig entry apex revert to the default apex: clears the btpUrl/
4118
- relayUrl override so core's genesis seed (or a live
4119
- announce) applies \u2014 settles evm | sol | mina
4120
- rig entry sandbox the devnet sandbox entry (Mina USDC ONLY \u2014 the
4121
- cross-currency multihop demo path):
4122
- btp ${SANDBOX_BTP_URL}
4123
- relay ${SANDBOX_RELAY_URL}
4124
- rig entry <wss-url> an explicit BTP endpoint (ws:// or wss://); pass
4125
- --relay <url> to also set the relay
4126
-
4127
- Switching entries deletes the cached topology (the next paid command
4128
- re-discovers the new entry's announce) and leaves existing payment channels
4129
- behind \u2014 channels are per-entry-peer; the next paid command opens or resumes
4130
- one with the new peer.
3948
+ var ENTRY_USAGE = `Usage: rig entry [<connector-url> | clear] [options]
3949
+
3950
+ Name the TOON connector rig pays. A connector describes itself on GET /ilp
3951
+ (its addresses, routes and prices, settlement chains), so its URL is the
3952
+ whole configuration. Free: only the local config file is touched.
3953
+
3954
+ rig entry show the effective connector + relay and their source
3955
+ rig entry <connector-url> record the node's client-edge URL (https://\u2026);
3956
+ pass --relay <wss-url> to also record its free-read relay
3957
+ rig entry clear forget the recorded connector (and relay)
3958
+
3959
+ Existing payment channels are per node: after switching, the next paid
3960
+ command opens or adopts a channel with the new node.
4131
3961
 
4132
3962
  NOTE for repos: the relay a repo publishes to is its git \`origin\` remote,
4133
- which OVERRIDES the config relayUrl. After switching entries, point the repo
4134
- too: \`rig remote add origin <relay-url>\` (or use a fresh repo).
3963
+ which OVERRIDES the config relayUrl. After switching, point the repo too:
3964
+ \`rig remote add origin <relay-url>\` (or use a fresh repo).
4135
3965
 
4136
3966
  Options:
4137
- --relay <url> with <wss-url>: also set the relay URL
3967
+ --relay <url> with <connector-url>: also record the relay URL (ws:// or wss://)
4138
3968
  --json machine-readable envelope
4139
3969
  -h, --help show this help`;
4140
3970
  function configPathFor2(env) {
4141
- const dir = env["TOON_CLIENT_HOME"] ?? join5(homedir4(), ".toon-client");
4142
- return join5(dir, "config.json");
3971
+ const dir = env["TOON_CLIENT_HOME"] ?? join4(homedir3(), ".toon-client");
3972
+ return join4(dir, "config.json");
4143
3973
  }
4144
3974
  function readEntryConfig(configPath) {
4145
3975
  try {
4146
- return JSON.parse(readFileSync4(configPath, "utf8"));
3976
+ return JSON.parse(readFileSync3(configPath, "utf8"));
4147
3977
  } catch (err) {
4148
3978
  if (err.code === "ENOENT") return {};
4149
3979
  throw new Error(
@@ -4156,20 +3986,28 @@ function writeEntryConfig(configPath, file) {
4156
3986
  writeFileSync3(configPath, `${JSON.stringify(file, null, 2)}
4157
3987
  `, "utf8");
4158
3988
  }
4159
- async function loadGenesisSeedDefault() {
4160
- try {
4161
- const { loadGenesisSeed } = await import("../network-bootstrap-27FYE3N7.js");
4162
- return loadGenesisSeed();
4163
- } catch {
4164
- return void 0;
4165
- }
4166
- }
4167
- function classifyEntry(btpUrl, seedBtp, btpSource) {
4168
- if (btpUrl === null) return null;
4169
- if (btpUrl === SANDBOX_BTP_URL) return "sandbox";
4170
- if (btpSource === "genesis-seed" || btpUrl === seedBtp) return "apex";
4171
- return "custom";
4172
- }
3989
+ function effectiveEntry(env, file) {
3990
+ let connectorUrl = null;
3991
+ let connectorSource = null;
3992
+ if (env["TOON_CONNECTOR"]) {
3993
+ connectorUrl = env["TOON_CONNECTOR"];
3994
+ connectorSource = "env";
3995
+ } else if (env["TOON_CLIENT_PROXY_URL"]) {
3996
+ connectorUrl = env["TOON_CLIENT_PROXY_URL"];
3997
+ connectorSource = "legacy-env";
3998
+ } else if (file.connectorUrl) {
3999
+ connectorUrl = file.connectorUrl;
4000
+ connectorSource = "config";
4001
+ } else if (file.proxyUrl) {
4002
+ connectorUrl = file.proxyUrl;
4003
+ connectorSource = "legacy-config";
4004
+ }
4005
+ const relayUrl = env["TOON_CLIENT_RELAY_URL"] ?? file.relayUrl ?? null;
4006
+ const relaySource = env["TOON_CLIENT_RELAY_URL"] ? "env" : file.relayUrl ? "config" : null;
4007
+ return { connectorUrl, connectorSource, relayUrl, relaySource };
4008
+ }
4009
+ var HTTP_URL = /^https?:\/\/.+/i;
4010
+ var WS_URL = /^wss?:\/\/.+/i;
4173
4011
  async function runEntry(args, deps) {
4174
4012
  const { io: io2, env } = deps;
4175
4013
  let positionals;
@@ -4199,7 +4037,7 @@ async function runEntry(args, deps) {
4199
4037
  }
4200
4038
  if (positionals.length > 1) {
4201
4039
  io2.err(
4202
- `rig entry takes at most one argument, got ${positionals.length}: ${positionals.join(" ")}`
4040
+ `rig entry takes at most one argument, got ${String(positionals.length)}: ${positionals.join(" ")}`
4203
4041
  );
4204
4042
  io2.err(ENTRY_USAGE);
4205
4043
  return 2;
@@ -4207,36 +4045,33 @@ async function runEntry(args, deps) {
4207
4045
  const target = positionals[0];
4208
4046
  let mutation;
4209
4047
  if (target !== void 0) {
4210
- if (target === "apex") {
4211
- if (relayFlag !== void 0) {
4212
- io2.err("--relay only applies to an explicit <wss-url> entry");
4213
- io2.err(ENTRY_USAGE);
4214
- return 2;
4215
- }
4216
- mutation = { kind: "apex" };
4217
- } else if (target === "sandbox") {
4048
+ if (target === "clear") {
4218
4049
  if (relayFlag !== void 0) {
4219
- io2.err("--relay only applies to an explicit <wss-url> entry");
4050
+ io2.err("--relay only applies to a <connector-url> entry");
4220
4051
  io2.err(ENTRY_USAGE);
4221
4052
  return 2;
4222
4053
  }
4223
- mutation = { kind: "sandbox" };
4224
- } else if (/^wss?:\/\/.+/.test(target)) {
4225
- if (relayFlag !== void 0 && !/^wss?:\/\/.+/.test(relayFlag)) {
4226
- io2.err(
4227
- `--relay must be a ws(s) URL, got ${JSON.stringify(relayFlag)}`
4228
- );
4054
+ mutation = { kind: "clear" };
4055
+ } else if (HTTP_URL.test(target)) {
4056
+ if (relayFlag !== void 0 && !WS_URL.test(relayFlag)) {
4057
+ io2.err(`--relay must be a ws(s) URL, got ${JSON.stringify(relayFlag)}`);
4229
4058
  io2.err(ENTRY_USAGE);
4230
4059
  return 2;
4231
4060
  }
4232
4061
  mutation = {
4233
4062
  kind: "url",
4234
- btpUrl: target,
4063
+ connectorUrl: target.replace(/\/+$/, ""),
4235
4064
  ...relayFlag !== void 0 ? { relayUrl: relayFlag } : {}
4236
4065
  };
4066
+ } else if (WS_URL.test(target)) {
4067
+ io2.err(
4068
+ `${JSON.stringify(target)} is a WebSocket URL \u2014 the entry is the connector's http(s) URL (the one whose GET /ilp describes it); pass the relay with --relay`
4069
+ );
4070
+ io2.err(ENTRY_USAGE);
4071
+ return 2;
4237
4072
  } else {
4238
4073
  io2.err(
4239
- `unknown entry ${JSON.stringify(target)} \u2014 expected \`apex\`, \`sandbox\`, or a ws(s):// URL`
4074
+ `unknown entry ${JSON.stringify(target)} \u2014 expected a connector http(s):// URL or \`clear\``
4240
4075
  );
4241
4076
  io2.err(ENTRY_USAGE);
4242
4077
  return 2;
@@ -4245,136 +4080,78 @@ async function runEntry(args, deps) {
4245
4080
  try {
4246
4081
  const configPath = configPathFor2(env);
4247
4082
  const file = readEntryConfig(configPath);
4248
- const seed = await (deps.loadGenesisSeed ?? loadGenesisSeedDefault)();
4249
- const effective = () => {
4250
- const btpExplicit = env["TOON_CLIENT_PROXY_URL"] ?? env["TOON_CLIENT_BTP_URL"] ?? file.proxyUrl ?? file.btpUrl;
4251
- const btpFromEnv = env["TOON_CLIENT_PROXY_URL"] ?? env["TOON_CLIENT_BTP_URL"];
4252
- const relayExplicit = env["TOON_CLIENT_RELAY_URL"] ?? file.relayUrl;
4253
- return {
4254
- btpUrl: btpExplicit ?? seed?.btpEndpoint ?? null,
4255
- btpSource: btpExplicit ? btpFromEnv ? "env" : "config" : seed?.btpEndpoint ? "genesis-seed" : null,
4256
- relayUrl: relayExplicit ?? seed?.relayUrl ?? null,
4257
- relaySource: env["TOON_CLIENT_RELAY_URL"] ? "env" : file.relayUrl ? "config" : seed?.relayUrl ? "genesis-seed" : null
4258
- };
4259
- };
4083
+ const label = (source) => source === "env" ? "env" : source === "legacy-env" ? "env (TOON_CLIENT_PROXY_URL, pre-4.0 spelling \u2014 set TOON_CONNECTOR)" : source === "config" ? `config (${configPath})` : source === "legacy-config" ? `config proxyUrl (${configPath}, pre-4.0 spelling)` : "not configured";
4260
4084
  if (mutation === void 0) {
4261
- const state2 = effective();
4262
- const entry = classifyEntry(state2.btpUrl, seed?.btpEndpoint, state2.btpSource);
4085
+ const state2 = effectiveEntry(env, file);
4263
4086
  if (json) {
4264
4087
  io2.emitJson({
4265
4088
  command: "entry",
4266
- entry,
4267
- btpUrl: state2.btpUrl,
4268
- btpSource: state2.btpSource,
4269
- relayUrl: state2.relayUrl,
4270
- relaySource: state2.relaySource,
4089
+ ...state2,
4271
4090
  configPath
4272
4091
  });
4273
4092
  return 0;
4274
4093
  }
4275
- const label = (source) => source === "env" ? "env" : source === "config" ? `config (${configPath})` : source === "genesis-seed" ? "default apex \u2014 genesis seed" : "not configured";
4276
4094
  io2.out(
4277
- `Entry: ${entry ?? "(none)"}${entry === "sandbox" ? " (Mina USDC only \u2014 multihop demo path)" : ""}`
4095
+ `Connector ${state2.connectorUrl ?? "(none)"} [${label(state2.connectorSource)}]`
4278
4096
  );
4279
- io2.out(` btp ${state2.btpUrl ?? "(none)"} [${label(state2.btpSource)}]`);
4280
- io2.out(` relay ${state2.relayUrl ?? "(none)"} [${label(state2.relaySource)}]`);
4281
4097
  io2.out(
4282
- "Switch with `rig entry apex`, `rig entry sandbox`, or `rig entry <wss-url>`."
4098
+ `Relay ${state2.relayUrl ?? "(none)"} [${label(state2.relaySource)}]`
4283
4099
  );
4284
- return 0;
4285
- }
4286
- const warnings = [];
4287
- for (const envVar of [
4288
- "TOON_CLIENT_BTP_URL",
4289
- "TOON_CLIENT_PROXY_URL",
4290
- "TOON_CLIENT_RELAY_URL"
4291
- ]) {
4292
- if (env[envVar] !== void 0) {
4293
- warnings.push(
4294
- `${envVar}=${env[envVar]} is set in the environment and overrides the config value you just wrote \u2014 unset it for this entry to take effect.`
4100
+ if (!state2.connectorUrl) {
4101
+ io2.out(
4102
+ "Set one with `rig entry <connector-url>` (or export TOON_CONNECTOR)."
4295
4103
  );
4296
4104
  }
4105
+ return 0;
4297
4106
  }
4298
- warnings.push(
4299
- "Payment channels are per-entry-peer: existing channels (see `rig channels`) are not used with the new entry \u2014 the next paid command opens or resumes a channel with the new peer."
4300
- );
4301
- const hadProxyUrl = file.proxyUrl !== void 0;
4302
- let wroteBtp;
4303
- let wroteRelay;
4304
- if (mutation.kind === "apex") {
4305
- wroteBtp = null;
4306
- wroteRelay = null;
4307
- delete file.btpUrl;
4308
- delete file.relayUrl;
4309
- delete file.proxyUrl;
4310
- } else if (mutation.kind === "sandbox") {
4311
- wroteBtp = SANDBOX_BTP_URL;
4312
- wroteRelay = SANDBOX_RELAY_URL;
4313
- file.btpUrl = SANDBOX_BTP_URL;
4314
- file.relayUrl = SANDBOX_RELAY_URL;
4315
- delete file.proxyUrl;
4316
- } else {
4317
- wroteBtp = mutation.btpUrl;
4318
- wroteRelay = mutation.relayUrl ?? null;
4319
- file.btpUrl = mutation.btpUrl;
4320
- if (mutation.relayUrl !== void 0) file.relayUrl = mutation.relayUrl;
4321
- delete file.proxyUrl;
4322
- }
4323
- if (hadProxyUrl) {
4107
+ const warnings = [];
4108
+ const next = { ...file };
4109
+ if (file.proxyUrl !== void 0 || file.btpUrl !== void 0) {
4110
+ delete next.proxyUrl;
4111
+ delete next.btpUrl;
4324
4112
  warnings.push(
4325
- "Removed the legacy `proxyUrl` config field \u2014 it outranks `btpUrl` and would have silently kept the old entry."
4113
+ "removed the pre-4.0 proxyUrl/btpUrl fields (the connector URL replaces both)"
4326
4114
  );
4327
4115
  }
4328
- writeEntryConfig(configPath, file);
4329
- let clearedTopologyCache = false;
4330
- try {
4331
- unlinkSync(join5(dirname3(configPath), TOPOLOGY_CACHE_FILENAME));
4332
- clearedTopologyCache = true;
4333
- } catch {
4116
+ if (mutation.kind === "clear") {
4117
+ delete next.connectorUrl;
4118
+ delete next.relayUrl;
4119
+ } else {
4120
+ next.connectorUrl = mutation.connectorUrl;
4121
+ if (mutation.relayUrl !== void 0) next.relayUrl = mutation.relayUrl;
4334
4122
  }
4335
- if (mutation.kind === "sandbox") {
4336
- const effectiveChain = env["TOON_CLIENT_CHAIN"] ?? file.chain ?? file.supportedChains?.[0];
4337
- if (effectiveChain === void 0 || !effectiveChain.startsWith("mina")) {
4338
- warnings.push(
4339
- "The sandbox entry settles ONLY mina:devnet \u2014 run `rig chain set mina` " + (effectiveChain !== void 0 ? `(currently ${effectiveChain}).` : "to pin it.")
4340
- );
4341
- }
4123
+ if (env["TOON_CONNECTOR"] || env["TOON_CLIENT_PROXY_URL"]) {
4342
4124
  warnings.push(
4343
- `Repos publish to their git \`origin\` relay, which OVERRIDES the config relayUrl \u2014 for the sandbox demo run \`rig remote add origin ${SANDBOX_RELAY_URL}\` in the repo (or use a fresh repo).`
4125
+ "TOON_CONNECTOR / TOON_CLIENT_PROXY_URL is set in this shell and outranks the config file"
4344
4126
  );
4345
4127
  }
4346
- const state = effective();
4128
+ writeEntryConfig(configPath, next);
4129
+ const wrote = {
4130
+ connectorUrl: next.connectorUrl ?? null,
4131
+ relayUrl: next.relayUrl ?? null
4132
+ };
4133
+ const state = effectiveEntry(env, next);
4347
4134
  if (json) {
4348
4135
  io2.emitJson({
4349
4136
  command: "entry",
4350
- entry: mutation.kind === "url" ? "custom" : mutation.kind,
4351
- btpUrl: state.btpUrl,
4352
- btpSource: state.btpSource,
4353
- relayUrl: state.relayUrl,
4354
- relaySource: state.relaySource,
4355
- wrote: { btpUrl: wroteBtp, relayUrl: wroteRelay },
4356
- clearedTopologyCache,
4137
+ ...state,
4138
+ wrote,
4357
4139
  configPath,
4358
- ...warnings.length ? { warnings } : {}
4140
+ ...warnings.length > 0 ? { warnings } : {}
4359
4141
  });
4360
4142
  return 0;
4361
4143
  }
4362
- if (mutation.kind === "apex") {
4363
- io2.out("Entry reverted to the default apex (cleared btpUrl/relayUrl/proxyUrl).");
4364
- io2.out(` btp ${state.btpUrl ?? "(genesis seed unavailable)"}`);
4365
- io2.out(` relay ${state.relayUrl ?? "(genesis seed unavailable)"}`);
4366
- } else if (mutation.kind === "sandbox") {
4367
- io2.out("Entry set to the devnet sandbox (Mina USDC only \u2014 multihop demo path).");
4368
- io2.out(` btp ${SANDBOX_BTP_URL}`);
4369
- io2.out(` relay ${SANDBOX_RELAY_URL}`);
4144
+ if (mutation.kind === "clear") {
4145
+ io2.out(`Cleared the recorded connector in ${configPath}.`);
4370
4146
  } else {
4371
- io2.out(`Entry set to ${mutation.btpUrl}.`);
4372
- if (mutation.relayUrl !== void 0) io2.out(` relay ${mutation.relayUrl}`);
4147
+ io2.out(`Recorded connector ${mutation.connectorUrl} in ${configPath}.`);
4148
+ if (mutation.relayUrl !== void 0)
4149
+ io2.out(`Recorded relay ${mutation.relayUrl}.`);
4373
4150
  }
4151
+ for (const w of warnings) io2.out(`warning: ${w}`);
4374
4152
  io2.out(
4375
- `Saved to ${configPath}.` + (clearedTopologyCache ? " Cleared the cached topology." : "")
4153
+ `Effective connector: ${state.connectorUrl ?? "(none)"} [${label(state.connectorSource)}]`
4376
4154
  );
4377
- for (const w of warnings) io2.err(`warning: ${w}`);
4378
4155
  return 0;
4379
4156
  } catch (err) {
4380
4157
  return emitCliError(io2, json, "entry", err);
@@ -4601,21 +4378,20 @@ async function runFetch(args, deps) {
4601
4378
  }
4602
4379
 
4603
4380
  // src/cli/fund.ts
4604
- import { readFileSync as readFileSync5 } from "fs";
4605
- import { homedir as homedir5 } from "os";
4606
- import { join as join6 } from "path";
4381
+ import { readFileSync as readFileSync4 } from "fs";
4382
+ import { homedir as homedir4 } from "os";
4383
+ import { join as join5 } from "path";
4607
4384
  import { parseArgs as parseArgs11 } from "util";
4608
4385
  var DEVNET_FAUCET_URL = "https://faucet.devnet.toonprotocol.dev";
4609
- var CHAINS = ["evm", "solana", "mina"];
4386
+ var CHAINS = ["evm", "solana"];
4610
4387
  var CHAIN_ALIASES = {
4611
4388
  evm: "evm",
4612
4389
  eth: "evm",
4613
4390
  sol: "solana",
4614
4391
  solana: "solana",
4615
- mina: "mina",
4616
4392
  all: "all"
4617
4393
  };
4618
- var CHAIN_ARG_LIST2 = "evm | sol | solana | mina | all";
4394
+ var CHAIN_ARG_LIST2 = "evm | sol | solana | all";
4619
4395
  function resolveChainArg(raw) {
4620
4396
  if (raw === void 0) return void 0;
4621
4397
  const canonical = CHAIN_ALIASES[raw.toLowerCase()];
@@ -4681,13 +4457,8 @@ function sharedDevnetOrigin(env, file, originRelays = []) {
4681
4457
  }
4682
4458
  return void 0;
4683
4459
  }
4684
- async function loadGenesisSeedDefault2() {
4685
- try {
4686
- const { loadGenesisSeed } = await import("../network-bootstrap-27FYE3N7.js");
4687
- return loadGenesisSeed();
4688
- } catch {
4689
- return void 0;
4690
- }
4460
+ async function loadGenesisSeedDefault() {
4461
+ return void 0;
4691
4462
  }
4692
4463
  async function resolveEffectiveNetwork(opts) {
4693
4464
  const { env, cwd } = opts;
@@ -4705,7 +4476,7 @@ async function resolveEffectiveNetwork(opts) {
4705
4476
  ...originRelays
4706
4477
  ].some((url) => url !== void 0);
4707
4478
  if (!anyConfiguredOrigin) {
4708
- const seed = await (opts.loadGenesisSeed ?? loadGenesisSeedDefault2)();
4479
+ const seed = await (opts.loadGenesisSeed ?? loadGenesisSeedDefault)();
4709
4480
  devnetOrigin = devnetHost(seed?.relayUrl) ?? devnetHost(seed?.btpEndpoint);
4710
4481
  seededOrigin = devnetOrigin !== void 0;
4711
4482
  }
@@ -4748,11 +4519,11 @@ function noFaucetGuidance(network, devnetOrigin) {
4748
4519
  return `${head}. If you meant the shared devnet, set TOON_CLIENT_NETWORK=devnet and re-run \`rig fund\` \u2014 no faucet URL is needed there. If this is a self-hosted network with its own faucet, set TOON_CLIENT_FAUCET_URL (or the faucetUrl config field). ${external}`;
4749
4520
  }
4750
4521
  function readFundConfig(env) {
4751
- const dir = env["TOON_CLIENT_HOME"] ?? join6(homedir5(), ".toon-client");
4752
- const configPath = join6(dir, "config.json");
4522
+ const dir = env["TOON_CLIENT_HOME"] ?? join5(homedir4(), ".toon-client");
4523
+ const configPath = join5(dir, "config.json");
4753
4524
  try {
4754
4525
  return {
4755
- file: JSON.parse(readFileSync5(configPath, "utf8")),
4526
+ file: JSON.parse(readFileSync4(configPath, "utf8")),
4756
4527
  configPath
4757
4528
  };
4758
4529
  } catch (err) {
@@ -4830,14 +4601,13 @@ async function runFund(args, deps) {
4830
4601
  sourceLabel: resolved.sourceLabel
4831
4602
  };
4832
4603
  const client = await import("@toon-protocol/client");
4833
- const derived = await client.deriveFullIdentity(
4834
- resolved.mnemonic,
4835
- resolved.accountIndex
4836
- );
4604
+ const derived = client.deriveFullIdentity(resolved.mnemonic, {
4605
+ accountIndex: resolved.accountIndex,
4606
+ scheme: "legacy"
4607
+ });
4837
4608
  const addresses = {
4838
4609
  evm: derived.evm.address || null,
4839
- solana: derived.solana.publicKey || null,
4840
- mina: derived.mina.publicKey || null
4610
+ solana: derived.solana.publicKey || null
4841
4611
  };
4842
4612
  if (!faucetUrl) {
4843
4613
  const guidance = noFaucetGuidance(network, devnetOrigin);
@@ -4857,14 +4627,13 @@ async function runFund(args, deps) {
4857
4627
  io2.out("Wallet addresses:");
4858
4628
  io2.out(` evm ${addresses.evm ?? "(no key derived)"}`);
4859
4629
  io2.out(` solana ${addresses.solana ?? "(no key derived)"}`);
4860
- io2.out(` mina ${addresses.mina ?? "(no key derived \u2014 optional mina-signer dependency missing)"}`);
4861
4630
  return 0;
4862
4631
  }
4863
4632
  const timeoutEnv = env["TOON_CLIENT_FAUCET_TIMEOUT_MS"];
4864
- const timeoutFor = (chain) => {
4633
+ const timeoutFor = () => {
4865
4634
  if (timeoutEnv && Number.isFinite(Number(timeoutEnv))) return Number(timeoutEnv);
4866
4635
  if (file.faucetTimeoutMs !== void 0) return file.faucetTimeoutMs;
4867
- return chain === "mina" ? 13e4 : 9e4;
4636
+ return 9e4;
4868
4637
  };
4869
4638
  if (!json && inferredDevnet) {
4870
4639
  io2.out(
@@ -4874,7 +4643,7 @@ async function runFund(args, deps) {
4874
4643
  if (!json) {
4875
4644
  const list = targetChains.join(", ");
4876
4645
  io2.out(
4877
- `Requesting ${targetChains.length === 1 ? "" : "parallel "}USDC drip${targetChains.length === 1 ? "" : "s"} from ${faucetUrl} for ${list} \u2026` + (targetChains.includes("mina") ? " (mina settles slowly; this can take ~2 minutes)" : "")
4646
+ `Requesting ${targetChains.length === 1 ? "" : "parallel "}USDC drip${targetChains.length === 1 ? "" : "s"} from ${faucetUrl} for ${list} \u2026`
4878
4647
  );
4879
4648
  }
4880
4649
  const singleChain = targetChains.length === 1;
@@ -4885,13 +4654,13 @@ async function runFund(args, deps) {
4885
4654
  chain,
4886
4655
  funded: false,
4887
4656
  address: null,
4888
- error: `no ${chain} address could be derived for this identity` + (chain === "mina" ? " (install the optional mina-signer dependency)" : "")
4657
+ error: `no ${chain} address could be derived for this identity`
4889
4658
  };
4890
4659
  }
4891
4660
  const started = Date.now();
4892
4661
  try {
4893
4662
  const { response } = await client.fundWallet(faucetUrl, address, chain, {
4894
- timeout: timeoutFor(chain),
4663
+ timeout: timeoutFor(),
4895
4664
  ...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
4896
4665
  });
4897
4666
  return {
@@ -5000,7 +4769,8 @@ import { dirname as dirname4 } from "path";
5000
4769
  import { createInterface } from "readline/promises";
5001
4770
  import { parseArgs as parseArgs12 } from "util";
5002
4771
  async function loadKeyOps() {
5003
- return await import("@toon-protocol/client");
4772
+ const { generateKeystore, importKeystore } = await import("@toon-protocol/client");
4773
+ return { generateKeystore, importKeystore, deriveNostrKeyFromMnemonic };
5004
4774
  }
5005
4775
  var IdentityExistsError = class extends Error {
5006
4776
  constructor(pubkey, sourceLabel) {
@@ -5544,12 +5314,6 @@ async function resolveProfileRelay(flagRelay, originRelay, toonRelays) {
5544
5314
  ...toonRelays
5545
5315
  ].filter((u) => u !== void 0 && isWebSocketRelay2(u));
5546
5316
  if (candidates[0] !== void 0) return candidates[0];
5547
- try {
5548
- const { loadGenesisSeed } = await import("../network-bootstrap-27FYE3N7.js");
5549
- const seed = loadGenesisSeed();
5550
- if (seed?.relayUrl && isWebSocketRelay2(seed.relayUrl)) return seed.relayUrl;
5551
- } catch {
5552
- }
5553
5317
  return void 0;
5554
5318
  }
5555
5319
  async function resolveOrInitGitRepo(deps, flags) {
@@ -5735,33 +5499,10 @@ async function runInit(args, deps) {
5735
5499
 
5736
5500
  // src/cli/maintainers.ts
5737
5501
  import { parseArgs as parseArgs14 } from "util";
5738
- var HEX64_RE3 = /^[0-9a-f]{64}$/;
5739
- var WS_URL_RE4 = /^wss?:\/\//i;
5740
- var MAINTAINERS_USAGE = `Usage: rig maintainers <list|add|remove> [<pubkey>] [options]
5741
5502
 
5742
- Manage a repo's declared maintainers (#287). Status authority is consumer-side:
5743
- rig and rig-web honor an issue/PR status (kind:1630-1633) ONLY when its author
5744
- is the repo owner or a declared maintainer. The owner is always an implicit
5745
- maintainer; this command edits the explicit set on the kind:30617 announcement.
5746
-
5747
- Subcommands:
5748
- list show the owner + declared maintainers \u2014 FREE (relay read)
5749
- add <pubkey> add a maintainer (npub or 64-char hex) \u2014 PAID: republishes
5750
- the kind:30617 (permanent, non-refundable)
5751
- remove <pubkey> remove a maintainer \u2014 PAID: republishes the kind:30617
5752
-
5753
- add/remove must run under the repo OWNER's identity (only the owner's
5754
- announcement is authoritative).
5755
-
5756
- Options:
5757
- --repo-id <id> repository id / NIP-34 d-tag (default: git config)
5758
- --owner <pubkey> repository owner (npub or hex; default: git config)
5759
- --remote <name> publish/read via this configured git remote (default: origin)
5760
- --relay <url> ad-hoc relay override (exactly one for add/remove)
5761
- --yes skip the fee confirmation (required when not a TTY)
5762
- --json machine-readable envelope
5763
- -h, --help show this help`;
5764
- var MAINT_OPTIONS = {
5503
+ // src/cli/repo-command.ts
5504
+ var WS_URL_RE4 = /^wss?:\/\//i;
5505
+ var REPO_COMMAND_OPTIONS = {
5765
5506
  json: { type: "boolean", default: false },
5766
5507
  yes: { type: "boolean", default: false },
5767
5508
  relay: { type: "string", multiple: true },
@@ -5770,7 +5511,7 @@ var MAINT_OPTIONS = {
5770
5511
  owner: { type: "string" },
5771
5512
  help: { type: "boolean", short: "h", default: false }
5772
5513
  };
5773
- function pickFlags(values) {
5514
+ function pickRepoCommandFlags(values) {
5774
5515
  const flags = {
5775
5516
  json: values["json"] === true,
5776
5517
  yes: values["yes"] === true,
@@ -5783,7 +5524,7 @@ function pickFlags(values) {
5783
5524
  flags.owner = ownerToHex(values["owner"]);
5784
5525
  return flags;
5785
5526
  }
5786
- async function resolveContext(flags, deps) {
5527
+ async function resolveRepoContext(flags, deps) {
5787
5528
  let repoRoot;
5788
5529
  let toonConfig = {
5789
5530
  relays: []
@@ -5812,6 +5553,33 @@ async function resolveContext(flags, deps) {
5812
5553
  ...repoRoot !== void 0 ? { repoRoot } : {}
5813
5554
  };
5814
5555
  }
5556
+
5557
+ // src/cli/maintainers.ts
5558
+ var HEX64_RE3 = /^[0-9a-f]{64}$/;
5559
+ var MAINTAINERS_USAGE = `Usage: rig maintainers <list|add|remove> [<pubkey>] [options]
5560
+
5561
+ Manage a repo's declared maintainers (#287). Status authority is consumer-side:
5562
+ rig and rig-web honor an issue/PR status (kind:1630-1633) ONLY when its author
5563
+ is the repo owner or a declared maintainer. The owner is always an implicit
5564
+ maintainer; this command edits the explicit set on the kind:30617 announcement.
5565
+
5566
+ Subcommands:
5567
+ list show the owner + declared maintainers \u2014 FREE (relay read)
5568
+ add <pubkey> add a maintainer (npub or 64-char hex) \u2014 PAID: republishes
5569
+ the kind:30617 (permanent, non-refundable)
5570
+ remove <pubkey> remove a maintainer \u2014 PAID: republishes the kind:30617
5571
+
5572
+ add/remove must run under the repo OWNER's identity (only the owner's
5573
+ announcement is authoritative).
5574
+
5575
+ Options:
5576
+ --repo-id <id> repository id / NIP-34 d-tag (default: git config)
5577
+ --owner <pubkey> repository owner (npub or hex; default: git config)
5578
+ --remote <name> publish/read via this configured git remote (default: origin)
5579
+ --relay <url> ad-hoc relay override (exactly one for add/remove)
5580
+ --yes skip the fee confirmation (required when not a TTY)
5581
+ --json machine-readable envelope
5582
+ -h, --help show this help`;
5815
5583
  async function runMaintainers(args, deps) {
5816
5584
  const { io: io2 } = deps;
5817
5585
  const [sub, ...rest] = args;
@@ -5841,13 +5609,13 @@ async function runList2(args, deps) {
5841
5609
  try {
5842
5610
  const { values, positionals } = parseArgs14({
5843
5611
  args,
5844
- options: MAINT_OPTIONS,
5612
+ options: REPO_COMMAND_OPTIONS,
5845
5613
  allowPositionals: true
5846
5614
  });
5847
5615
  if (positionals.length > 0) {
5848
5616
  throw new Error("rig maintainers list takes no positional arguments");
5849
5617
  }
5850
- flags = pickFlags(values);
5618
+ flags = pickRepoCommandFlags(values);
5851
5619
  } catch (err) {
5852
5620
  io2.err(err instanceof Error ? err.message : String(err));
5853
5621
  io2.err(MAINTAINERS_USAGE);
@@ -5858,7 +5626,7 @@ async function runList2(args, deps) {
5858
5626
  return 0;
5859
5627
  }
5860
5628
  try {
5861
- const ctx = await resolveContext(flags, deps);
5629
+ const ctx = await resolveRepoContext(flags, deps);
5862
5630
  const wsRelays = ctx.relays.filter((url) => WS_URL_RE4.test(url));
5863
5631
  if (wsRelays.length === 0) {
5864
5632
  throw new InvalidRelayUrlError(
@@ -5908,10 +5676,10 @@ async function runMutate(op, args, deps) {
5908
5676
  try {
5909
5677
  const { values, positionals } = parseArgs14({
5910
5678
  args,
5911
- options: MAINT_OPTIONS,
5679
+ options: REPO_COMMAND_OPTIONS,
5912
5680
  allowPositionals: true
5913
5681
  });
5914
- flags = pickFlags(values);
5682
+ flags = pickRepoCommandFlags(values);
5915
5683
  if (flags.help) {
5916
5684
  io2.out(MAINTAINERS_USAGE);
5917
5685
  return 0;
@@ -5930,7 +5698,7 @@ async function runMutate(op, args, deps) {
5930
5698
  }
5931
5699
  let standaloneCtx;
5932
5700
  try {
5933
- const ctx = await resolveContext(flags, deps);
5701
+ const ctx = await resolveRepoContext(flags, deps);
5934
5702
  if (ctx.relays.length > 1) {
5935
5703
  io2.err(singleRelayRefusal(ctx.resolved, "Nothing was published or paid."));
5936
5704
  return 1;
@@ -5985,7 +5753,13 @@ async function runMutate(op, args, deps) {
5985
5753
  const next = op === "add" ? [...current, pubkey] : current.filter((m) => m !== pubkey);
5986
5754
  const name = remote.name ?? ctx.repoId;
5987
5755
  const description = remote.description ?? "";
5988
- const event = buildRepoAnnouncement(ctx.repoId, name, description, next);
5756
+ const event = buildRepoAnnouncement(
5757
+ ctx.repoId,
5758
+ name,
5759
+ description,
5760
+ next,
5761
+ remote.payout
5762
+ );
5989
5763
  const fee = (await standaloneCtx.publisher.getFeeRates()).eventFee.toString();
5990
5764
  const action = `kind:30617 maintainers ${op} ${pubkey.slice(0, 8)}\u2026`;
5991
5765
  if (!flags.json) {
@@ -6314,57 +6088,39 @@ var DvmBuyJobError = class extends Error {
6314
6088
  }
6315
6089
  };
6316
6090
  var defaultSubmitDvmBuyJob = async (request) => {
6317
- const { finalizeEvent } = await import("nostr-tools/pure");
6318
- const event = finalizeEvent(
6319
- {
6091
+ let answer;
6092
+ try {
6093
+ answer = await request.submit({
6320
6094
  kind: ARNS_BUY_JOB_KIND,
6321
- created_at: Math.floor(Date.now() / 1e3),
6322
- content: "",
6323
- tags: [
6324
- ["param", "name", request.name],
6325
- ["param", "type", request.type],
6326
- ...request.years !== null ? [["param", "years", String(request.years)]] : [],
6327
- ["param", "processId", request.processId]
6095
+ params: [
6096
+ ["name", request.name],
6097
+ ["type", request.type],
6098
+ ...request.years !== null ? [["years", String(request.years)]] : [],
6099
+ ["processId", request.processId]
6328
6100
  ]
6329
- },
6330
- request.nostrSecretKey
6331
- );
6332
- const url = `${request.viaUrl.replace(/\/+$/, "")}/store`;
6333
- let response;
6334
- try {
6335
- response = await fetch(url, {
6336
- method: "POST",
6337
- headers: { "content-type": "application/json" },
6338
- body: JSON.stringify({ event })
6339
6101
  });
6340
6102
  } catch (err) {
6341
6103
  throw new DvmBuyJobError(
6342
- `could not reach the DVM at ${url}: ${err instanceof Error ? err.message : String(err)}`
6104
+ `could not reach the DVM at ${request.destination}: ${err instanceof Error ? err.message : String(err)}`
6343
6105
  );
6344
6106
  }
6345
- let body;
6346
- try {
6347
- body = await response.json();
6348
- } catch {
6107
+ if (!answer.accept) {
6349
6108
  throw new DvmBuyJobError(
6350
- `the DVM at ${url} returned a non-JSON response (HTTP ${response.status})`
6109
+ `the DVM at ${request.destination} rejected the kind:${ARNS_BUY_JOB_KIND} buy job (HTTP ${answer.status}${answer.code ? `, ${answer.code}` : ""})${answer.message ? `: ${answer.message}` : ""}`
6351
6110
  );
6352
6111
  }
6353
- if (!response.ok || body.accept !== true) {
6354
- throw new DvmBuyJobError(
6355
- `the DVM rejected the kind:${ARNS_BUY_JOB_KIND} buy job (HTTP ${response.status}${body.code ? `, ${body.code}` : ""})${body.message ? `: ${body.message}` : ""}`
6356
- );
6357
- }
6358
- const registryTxId = body.result?.registryTxId;
6112
+ const registryTxId = answer.result?.["registryTxId"];
6359
6113
  if (typeof registryTxId !== "string" || registryTxId.length === 0) {
6360
6114
  throw new DvmBuyJobError(
6361
- `the DVM accepted the job but returned no registryTxId \u2014 response: ` + JSON.stringify(body.result ?? null)
6115
+ `the DVM accepted the job but returned no registryTxId \u2014 response: ` + JSON.stringify(answer.result ?? null)
6362
6116
  );
6363
6117
  }
6118
+ const quotedMario = answer.result?.["quotedMario"];
6119
+ const syncAttributesTxId = answer.result?.["syncAttributesTxId"];
6364
6120
  return {
6365
6121
  registryTxId,
6366
- quotedMario: typeof body.result?.quotedMario === "string" ? body.result.quotedMario : null,
6367
- syncAttributesTxId: typeof body.result?.syncAttributesTxId === "string" ? body.result.syncAttributesTxId : null
6122
+ quotedMario: typeof quotedMario === "string" ? quotedMario : null,
6123
+ syncAttributesTxId: typeof syncAttributesTxId === "string" ? syncAttributesTxId : null
6368
6124
  };
6369
6125
  };
6370
6126
  var GAS_STATION_JOB_KIND = 5096;
@@ -6376,47 +6132,23 @@ var GasStationJobError = class extends Error {
6376
6132
  }
6377
6133
  reason;
6378
6134
  };
6379
- async function postGasStationJob(viaUrl, params, nostrSecretKey) {
6380
- const { finalizeEvent } = await import("nostr-tools/pure");
6381
- const event = finalizeEvent(
6382
- {
6383
- kind: GAS_STATION_JOB_KIND,
6384
- created_at: Math.floor(Date.now() / 1e3),
6385
- content: "",
6386
- tags: params.map(([k, v]) => ["param", k, v])
6387
- },
6388
- nostrSecretKey
6389
- );
6390
- const url = `${viaUrl.replace(/\/+$/, "")}/store`;
6391
- let response;
6135
+ async function submitGasStationJob(destination, params, submit) {
6136
+ let answer;
6392
6137
  try {
6393
- response = await fetch(url, {
6394
- method: "POST",
6395
- headers: { "content-type": "application/json" },
6396
- body: JSON.stringify({ event })
6397
- });
6138
+ answer = await submit({ kind: GAS_STATION_JOB_KIND, params });
6398
6139
  } catch (err) {
6399
6140
  throw new GasStationJobError(
6400
6141
  "unreachable",
6401
- `could not reach the DVM at ${url}: ${err instanceof Error ? err.message : String(err)}`
6142
+ `could not reach the DVM at ${destination}: ${err instanceof Error ? err.message : String(err)}`
6402
6143
  );
6403
6144
  }
6404
- let body;
6405
- try {
6406
- body = await response.json();
6407
- } catch {
6408
- throw new GasStationJobError(
6409
- "bad_response",
6410
- `the DVM at ${url} returned a non-JSON response (HTTP ${response.status})`
6411
- );
6412
- }
6413
- if (!response.ok || body.accept !== true || !body.result) {
6145
+ if (!answer.accept || !answer.result) {
6414
6146
  throw new GasStationJobError(
6415
- String(body.code ?? "rejected"),
6416
- `the DVM rejected the kind:${GAS_STATION_JOB_KIND} job (HTTP ${response.status}${body.code ? `, ${body.code}` : ""})${body.message ? `: ${body.message}` : ""}`
6147
+ String(answer.code ?? "rejected"),
6148
+ `the DVM at ${destination} rejected the kind:${GAS_STATION_JOB_KIND} job (HTTP ${answer.status}${answer.code ? `, ${answer.code}` : ""})${answer.message ? `: ${answer.message}` : ""}`
6417
6149
  );
6418
6150
  }
6419
- const result = body.result;
6151
+ const result = answer.result;
6420
6152
  if (result["status"] === "failed") {
6421
6153
  throw new GasStationJobError(
6422
6154
  String(result["reason"] ?? "failed"),
@@ -6427,13 +6159,13 @@ async function postGasStationJob(viaUrl, params, nostrSecretKey) {
6427
6159
  }
6428
6160
  var defaultGasStationClient = {
6429
6161
  quote: async (args) => {
6430
- const result = await postGasStationJob(
6431
- args.viaUrl,
6162
+ const result = await submitGasStationJob(
6163
+ args.destination,
6432
6164
  [
6433
6165
  ["phase", "quote"],
6434
6166
  ...args.transaction !== void 0 ? [["transaction", args.transaction]] : []
6435
6167
  ],
6436
- args.nostrSecretKey
6168
+ args.submit
6437
6169
  );
6438
6170
  const { quoteId, feePayer, maxLamports, recentBlockhash, expiresAt } = result;
6439
6171
  if (typeof quoteId !== "string" || typeof feePayer !== "string" || typeof maxLamports !== "string" || typeof recentBlockhash !== "string" || typeof expiresAt !== "number") {
@@ -6445,15 +6177,15 @@ var defaultGasStationClient = {
6445
6177
  return { quoteId, feePayer, maxLamports, recentBlockhash, expiresAt };
6446
6178
  },
6447
6179
  execute: async (args) => {
6448
- const result = await postGasStationJob(
6449
- args.viaUrl,
6180
+ const result = await submitGasStationJob(
6181
+ args.destination,
6450
6182
  [
6451
6183
  ["phase", "execute"],
6452
6184
  ["transaction", args.transaction],
6453
6185
  ["quoteId", args.quoteId],
6454
6186
  ["idempotencyKey", args.idempotencyKey]
6455
6187
  ],
6456
- args.nostrSecretKey
6188
+ args.submit
6457
6189
  );
6458
6190
  const signature = result["signature"];
6459
6191
  if (typeof signature !== "string" || signature.length === 0) {
@@ -6470,7 +6202,7 @@ var defaultGasStationClient = {
6470
6202
  };
6471
6203
  }
6472
6204
  };
6473
- var DEVNET_DVM_URL = "https://dvm.devnet.toonprotocol.dev";
6205
+ var DEVNET_DVM_DESTINATION = "g.toon.ario";
6474
6206
  var NAME_USAGE = `Usage: rig name <buy|set|status> <name> [options]
6475
6207
 
6476
6208
  ArNS naming for Arweave-served artifacts. A name registered on the ar.io
@@ -6483,7 +6215,7 @@ mnemonic that pays for pushes (derived at m/44'/501'/0'/0'). Fund it with
6483
6215
  registry program \u2014 NOT through TOON ILP payment channels.
6484
6216
 
6485
6217
  Commands:
6486
- name buy <name> [--years n | --permabuy] [--via <dvm-url>]
6218
+ name buy <name> [--years n | --permabuy] [--via <destination>]
6487
6219
  quote (mARIO) \u2192 confirm \u2192 register. The spawned ANT is
6488
6220
  owned by this identity's Solana key. Default: 1-year
6489
6221
  lease. PAID \u2014 spends mARIO from the Solana wallet.
@@ -6491,7 +6223,7 @@ Commands:
6491
6223
  the ANT (dust SOL only), then a store DVM executes the
6492
6224
  kind:5095 buy job and pays the mARIO from ITS wallet;
6493
6225
  the name is owned by your ANT from inception.
6494
- name set <name> <txId> [--undername <sub>] [--ttl <seconds>] [--via <dvm-url>]
6226
+ name set <name> <txId> [--undername <sub>] [--ttl <seconds>] [--via <destination>]
6495
6227
  point the name (or an undername) at an Arweave txId
6496
6228
  (typically a deployed path manifest). Signs an ANT
6497
6229
  record update with the Solana key. With --via: the
@@ -6523,17 +6255,24 @@ Options:
6523
6255
  unverified.
6524
6256
  --process-id <id> explicit ArNS registry program id \u2014 a Solana program
6525
6257
  address (overrides --network; or RIG_ARIO_PROCESS_ID)
6526
- --via <dvm-url> broker buy/set through a store DVM's job endpoint (or
6527
- RIG_ARNS_DVM_URL). The DVM pays the mARIO; you own the
6528
- name via your spawned ANT. Direct backend targeting
6529
- stubs the job payment (dev/e2e) \u2014 the paid path runs
6530
- the same job through the connector payment proxy.
6258
+ --via <destination> broker buy/set through a store node's DVM, naming its
6259
+ ILP DESTINATION (or RIG_ARNS_DVM_DESTINATION), e.g.
6260
+ --via ${DEVNET_DVM_DESTINATION}. The DVM pays the mARIO;
6261
+ you own the name via your spawned ANT.
6262
+ The job travels as a PAID packet, exactly as a git-object
6263
+ store write does \u2014 the store sits behind the connector's
6264
+ payment termination, so there is no unpaid endpoint to
6265
+ aim at. That node's ingress, route price and payment
6266
+ channel are all discovered from its own announce, so the
6267
+ destination is the only thing you supply.
6268
+ Costs one store-route packet per job (two for set:
6269
+ quote + execute).
6531
6270
  DEVNET DEFAULT: with --network devnet and the TOON
6532
6271
  network on devnet too, buy/set default to the deployed
6533
- devnet store DVM (${DEVNET_DVM_URL})
6272
+ devnet store DVM (${DEVNET_DVM_DESTINATION})
6534
6273
  unless --direct opts out.
6535
6274
  --direct force the direct wallet-paid path: never default (or
6536
- read RIG_ARNS_DVM_URL as) a --via DVM. Mutually
6275
+ read RIG_ARNS_DVM_DESTINATION as) a --via DVM. Mutually
6537
6276
  exclusive with an explicit --via.
6538
6277
  --yes skip the confirmation (required when not a TTY) for
6539
6278
  buy/set
@@ -6543,6 +6282,25 @@ Options:
6543
6282
  var DEFAULT_TTL_SECONDS = 3600;
6544
6283
  var DEFAULT_LEASE_YEARS = 1;
6545
6284
  var NETWORKS = ["mainnet", "devnet"];
6285
+ async function openStoreJobSession(deps, destination) {
6286
+ const ctx = await (deps.loadStandalone ?? defaultLoadStandalone)({
6287
+ env: deps.env,
6288
+ cwd: deps.cwd,
6289
+ warn: (line) => deps.io.err(line),
6290
+ storeDestination: destination
6291
+ });
6292
+ const submitStoreJob = ctx.publisher.submitStoreJob;
6293
+ if (!submitStoreJob) {
6294
+ await ctx.stop();
6295
+ throw new Error(
6296
+ "the active publisher cannot submit a store job (submitStoreJob unavailable) \u2014 `rig name --via` requires the standalone publisher"
6297
+ );
6298
+ }
6299
+ return {
6300
+ submit: (request) => submitStoreJob.call(ctx.publisher, request),
6301
+ stop: () => ctx.stop()
6302
+ };
6303
+ }
6546
6304
  async function resolveNameContext(deps, network, processId, mode) {
6547
6305
  const { io: io2, env } = deps;
6548
6306
  const resolved = await resolveIdentity({
@@ -6556,10 +6314,10 @@ async function resolveNameContext(deps, network, processId, mode) {
6556
6314
  sourceLabel: resolved.sourceLabel
6557
6315
  };
6558
6316
  const client = await import("@toon-protocol/client");
6559
- const derived = await client.deriveFullIdentity(
6560
- resolved.mnemonic,
6561
- resolved.accountIndex
6562
- );
6317
+ const derived = client.deriveFullIdentity(resolved.mnemonic, {
6318
+ accountIndex: resolved.accountIndex,
6319
+ scheme: "legacy"
6320
+ });
6563
6321
  const solanaAddress = derived.solana.publicKey;
6564
6322
  if (!solanaAddress) {
6565
6323
  throw new Error(
@@ -6573,7 +6331,7 @@ async function resolveNameContext(deps, network, processId, mode) {
6573
6331
  network,
6574
6332
  ...processId !== void 0 ? { processId } : {}
6575
6333
  });
6576
- const nostrSecretKey = client.deriveNostrKeyFromMnemonic(
6334
+ const nostrSecretKey = deriveNostrKeyFromMnemonic(
6577
6335
  resolved.mnemonic,
6578
6336
  resolved.accountIndex
6579
6337
  ).secretKey;
@@ -6644,12 +6402,17 @@ function parseNameFlags(args, env) {
6644
6402
  "--direct and --via are mutually exclusive: --direct forces the wallet-paid path, --via brokers through a DVM"
6645
6403
  );
6646
6404
  }
6647
- const viaRaw = values.direct ? void 0 : values.via ?? env["RIG_ARNS_DVM_URL"];
6405
+ const viaRaw = values.direct ? void 0 : values.via ?? env["RIG_ARNS_DVM_DESTINATION"] ?? env["RIG_ARNS_DVM_URL"];
6648
6406
  let via;
6649
6407
  if (viaRaw !== void 0) {
6650
- if (!/^https?:\/\/.+/.test(viaRaw)) {
6408
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(viaRaw)) {
6651
6409
  throw new Error(
6652
- `--via must be an http(s) DVM endpoint URL, got ${JSON.stringify(viaRaw)}`
6410
+ `--via names an ILP destination, not a URL (got ${JSON.stringify(viaRaw)}). Brokered ArNS jobs travel as paid packets to a store node, e.g. --via ${DEVNET_DVM_DESTINATION}. The node's endpoint, price and payment channel are discovered from its own announce.`
6411
+ );
6412
+ }
6413
+ if (!/^g\.[a-z0-9]+(\.[a-z0-9-]+)*$/i.test(viaRaw)) {
6414
+ throw new Error(
6415
+ `--via must be an ILP destination like ${DEVNET_DVM_DESTINATION}, got ${JSON.stringify(viaRaw)}`
6653
6416
  );
6654
6417
  }
6655
6418
  via = viaRaw;
@@ -6755,14 +6518,20 @@ async function runBuy(name, flags, deps) {
6755
6518
  io2.out(`Spawned ANT ${spawn3.processId} (tx ${spawn3.signature})`);
6756
6519
  }
6757
6520
  const submit = deps.submitDvmBuyJob ?? defaultSubmitDvmBuyJob;
6758
- const receipt2 = await submit({
6759
- viaUrl: via,
6760
- name,
6761
- type,
6762
- years,
6763
- processId: spawn3.processId,
6764
- nostrSecretKey: signed.nostrSecretKey
6765
- });
6521
+ const session = await openStoreJobSession(deps, via);
6522
+ let receipt2;
6523
+ try {
6524
+ receipt2 = await submit({
6525
+ destination: via,
6526
+ name,
6527
+ type,
6528
+ years,
6529
+ processId: spawn3.processId,
6530
+ submit: session.submit
6531
+ });
6532
+ } finally {
6533
+ await session.stop();
6534
+ }
6766
6535
  const result2 = {
6767
6536
  registryTxId: receipt2.registryTxId,
6768
6537
  antProcessId: spawn3.processId,
@@ -6910,25 +6679,32 @@ async function runSet(name, txId, flags, deps) {
6910
6679
  );
6911
6680
  if (via !== null) {
6912
6681
  const gas = deps.gasStation ?? defaultGasStationClient;
6913
- const quote = await gas.quote({
6914
- viaUrl: via,
6915
- nostrSecretKey: signed.nostrSecretKey
6916
- });
6917
- const wireTx = await signed.sdk.buildSetRecordTransaction({
6918
- antProcessId,
6919
- undername: undername ?? "@",
6920
- transactionId: txId,
6921
- ttlSeconds: flags.ttl,
6922
- feePayer: quote.feePayer,
6923
- recentBlockhash: quote.recentBlockhash
6924
- });
6925
- const executed = await gas.execute({
6926
- viaUrl: via,
6927
- transaction: wireTx,
6928
- quoteId: quote.quoteId,
6929
- idempotencyKey: randomUUID(),
6930
- nostrSecretKey: signed.nostrSecretKey
6931
- });
6682
+ const session = await openStoreJobSession(deps, via);
6683
+ let quote;
6684
+ let executed;
6685
+ try {
6686
+ quote = await gas.quote({
6687
+ destination: via,
6688
+ submit: session.submit
6689
+ });
6690
+ const wireTx = await signed.sdk.buildSetRecordTransaction({
6691
+ antProcessId,
6692
+ undername: undername ?? "@",
6693
+ transactionId: txId,
6694
+ ttlSeconds: flags.ttl,
6695
+ feePayer: quote.feePayer,
6696
+ recentBlockhash: quote.recentBlockhash
6697
+ });
6698
+ executed = await gas.execute({
6699
+ destination: via,
6700
+ transaction: wireTx,
6701
+ quoteId: quote.quoteId,
6702
+ idempotencyKey: randomUUID(),
6703
+ submit: session.submit
6704
+ });
6705
+ } finally {
6706
+ await session.stop();
6707
+ }
6932
6708
  const gasStation = {
6933
6709
  quoteId: quote.quoteId,
6934
6710
  feePayer: quote.feePayer,
@@ -7092,9 +6868,9 @@ async function runName(args, deps) {
7092
6868
  } catch {
7093
6869
  }
7094
6870
  if (toonDevnet) {
7095
- flags = { ...flags, via: DEVNET_DVM_URL };
6871
+ flags = { ...flags, via: DEVNET_DVM_DESTINATION };
7096
6872
  io2.err(
7097
- `Brokering via the devnet store DVM ${DEVNET_DVM_URL} (no --via given; pass --direct for a wallet-paid ${sub}).`
6873
+ `Brokering via the devnet store DVM ${DEVNET_DVM_DESTINATION} (no --via given; pass --direct for a wallet-paid ${sub}).`
7098
6874
  );
7099
6875
  }
7100
6876
  }
@@ -7120,8 +6896,285 @@ async function runName(args, deps) {
7120
6896
  }
7121
6897
  }
7122
6898
 
7123
- // src/cli/site.ts
6899
+ // src/cli/payout.ts
7124
6900
  import { parseArgs as parseArgs16 } from "util";
6901
+ import { getAddress } from "viem";
6902
+ var PAYOUT_USAGE = `Usage: rig payout <set|clear|show> [<address>] [options]
6903
+
6904
+ Manage a repo's declared payout pointer (rig#92, part of the payout epic
6905
+ toon-protocol/toon-meta#391). No pointer on the kind:30617 announcement means
6906
+ a serving node keeps 100% of repo-scoped write fees; a pointer opts the repo
6907
+ into that node's declared ownerFeeShare split.
6908
+
6909
+ Subcommands:
6910
+ show show the declared payout pointer \u2014 FREE (relay read)
6911
+ set <address> declare the payout pointer (EVM address, EIP-55
6912
+ checksummed or all-lowercase) \u2014 PAID: republishes the
6913
+ kind:30617 (permanent, non-refundable). v1 accepts evm
6914
+ addresses only.
6915
+ clear remove the payout pointer \u2014 PAID: republishes the
6916
+ kind:30617
6917
+
6918
+ set/clear must run under the repo OWNER's identity (only the owner's
6919
+ announcement is authoritative).
6920
+
6921
+ Options:
6922
+ --repo-id <id> repository id / NIP-34 d-tag (default: git config)
6923
+ --owner <pubkey> repository owner (npub or hex; default: git config)
6924
+ --remote <name> publish/read via this configured git remote (default: origin)
6925
+ --relay <url> ad-hoc relay override (exactly one for set/clear)
6926
+ --yes skip the fee confirmation (required when not a TTY)
6927
+ --json machine-readable envelope
6928
+ -h, --help show this help`;
6929
+ async function runPayout(args, deps) {
6930
+ const { io: io2 } = deps;
6931
+ const [sub, ...rest] = args;
6932
+ switch (sub) {
6933
+ case "show":
6934
+ return runShow2(rest, deps);
6935
+ case "set":
6936
+ return runMutate2("set", rest, deps);
6937
+ case "clear":
6938
+ return runMutate2("clear", rest, deps);
6939
+ case "--help":
6940
+ case "-h":
6941
+ case "help":
6942
+ io2.out(PAYOUT_USAGE);
6943
+ return 0;
6944
+ default:
6945
+ io2.err(
6946
+ sub === void 0 ? "missing subcommand: rig payout <set|clear|show>" : `unknown rig payout subcommand: ${sub}`
6947
+ );
6948
+ io2.err(PAYOUT_USAGE);
6949
+ return 2;
6950
+ }
6951
+ }
6952
+ async function runShow2(args, deps) {
6953
+ const { io: io2 } = deps;
6954
+ let flags;
6955
+ try {
6956
+ const { values, positionals } = parseArgs16({
6957
+ args,
6958
+ options: REPO_COMMAND_OPTIONS,
6959
+ allowPositionals: true
6960
+ });
6961
+ if (positionals.length > 0) {
6962
+ throw new Error("rig payout show takes no positional arguments");
6963
+ }
6964
+ flags = pickRepoCommandFlags(values);
6965
+ } catch (err) {
6966
+ io2.err(err instanceof Error ? err.message : String(err));
6967
+ io2.err(PAYOUT_USAGE);
6968
+ return 2;
6969
+ }
6970
+ if (flags.help) {
6971
+ io2.out(PAYOUT_USAGE);
6972
+ return 0;
6973
+ }
6974
+ try {
6975
+ const ctx = await resolveRepoContext(flags, deps);
6976
+ const wsRelays = ctx.relays.filter((url) => WS_URL_RE4.test(url));
6977
+ if (wsRelays.length === 0) {
6978
+ throw new InvalidRelayUrlError(
6979
+ ctx.relays[0] ?? "",
6980
+ "reads need a ws:// or wss:// relay"
6981
+ );
6982
+ }
6983
+ const remote = await fetchRemoteState({
6984
+ relayUrls: wsRelays,
6985
+ ownerPubkey: ctx.owner,
6986
+ repoId: ctx.repoId,
6987
+ ...deps.webSocketFactory ? { webSocketFactory: deps.webSocketFactory } : {}
6988
+ });
6989
+ const payout = remote.payout;
6990
+ if (flags.json) {
6991
+ io2.emitJson({
6992
+ command: "payout show",
6993
+ repoAddr: { ownerPubkey: ctx.owner, repoId: ctx.repoId },
6994
+ announced: remote.announced,
6995
+ payout
6996
+ });
6997
+ return 0;
6998
+ }
6999
+ io2.out(`Repo: 30617:${ctx.owner}:${ctx.repoId}`);
7000
+ if (!remote.announced) {
7001
+ io2.out("No kind:30617 announcement found \u2014 no payout pointer.");
7002
+ return 0;
7003
+ }
7004
+ io2.out(
7005
+ payout === null ? "Payout: none (the serving node keeps 100% of repo-scoped write fees)" : `Payout: ${payout.chain} ${payout.address}`
7006
+ );
7007
+ return 0;
7008
+ } catch (err) {
7009
+ return emitCliError(io2, flags.json, "payout show", err);
7010
+ }
7011
+ }
7012
+ async function runMutate2(op, args, deps) {
7013
+ const { io: io2 } = deps;
7014
+ const command = `payout ${op}`;
7015
+ let flags;
7016
+ let nextPayout = null;
7017
+ try {
7018
+ const { values, positionals } = parseArgs16({
7019
+ args,
7020
+ options: REPO_COMMAND_OPTIONS,
7021
+ allowPositionals: true
7022
+ });
7023
+ flags = pickRepoCommandFlags(values);
7024
+ if (flags.help) {
7025
+ io2.out(PAYOUT_USAGE);
7026
+ return 0;
7027
+ }
7028
+ if (op === "set") {
7029
+ if (positionals.length !== 1) {
7030
+ throw new Error("expected exactly one <address> to set");
7031
+ }
7032
+ const raw = positionals[0];
7033
+ if (!isValidEvmPayoutAddress(raw)) {
7034
+ throw new Error(
7035
+ `<address> must be a valid EVM address (0x + 40 hex chars, correctly EIP-55 checksummed if mixed-case) \u2014 got ${JSON.stringify(raw)}. See toon-meta#391 (payout epic): v1 accepts evm addresses only.`
7036
+ );
7037
+ }
7038
+ nextPayout = { chain: "evm", address: getAddress(raw) };
7039
+ } else if (positionals.length !== 0) {
7040
+ throw new Error("rig payout clear takes no positional arguments");
7041
+ }
7042
+ } catch (err) {
7043
+ io2.err(err instanceof Error ? err.message : String(err));
7044
+ io2.err(PAYOUT_USAGE);
7045
+ return 2;
7046
+ }
7047
+ let standaloneCtx;
7048
+ try {
7049
+ const ctx = await resolveRepoContext(flags, deps);
7050
+ if (ctx.relays.length > 1) {
7051
+ io2.err(singleRelayRefusal(ctx.resolved, "Nothing was published or paid."));
7052
+ return 1;
7053
+ }
7054
+ const relayUrl = ctx.relays[0];
7055
+ if (relayUrl === void 0 || !WS_URL_RE4.test(relayUrl)) {
7056
+ throw new InvalidRelayUrlError(
7057
+ relayUrl ?? "",
7058
+ "a paid publish needs a ws:// or wss:// relay"
7059
+ );
7060
+ }
7061
+ const load = deps.loadStandalone ?? defaultLoadStandalone;
7062
+ standaloneCtx = await load({
7063
+ env: deps.env,
7064
+ cwd: deps.cwd,
7065
+ warn: (line) => io2.err(line),
7066
+ relayUrl
7067
+ });
7068
+ const identity = identityReport(standaloneCtx);
7069
+ if (identity.pubkey.toLowerCase() !== ctx.owner.toLowerCase()) {
7070
+ io2.err(
7071
+ `rig: only the repo owner (${ctx.owner.slice(0, 8)}\u2026) can change the payout pointer \u2014 the active identity is ${identity.pubkey.slice(0, 8)}\u2026. A non-owner republish would write your own (ignored) announcement. Nothing was published or paid.`
7072
+ );
7073
+ return 1;
7074
+ }
7075
+ const remote = await fetchRemoteState({
7076
+ relayUrls: [relayUrl],
7077
+ ownerPubkey: ctx.owner,
7078
+ repoId: ctx.repoId,
7079
+ ...deps.webSocketFactory ? { webSocketFactory: deps.webSocketFactory } : {}
7080
+ });
7081
+ if (!remote.announced) {
7082
+ io2.err(
7083
+ `rig: 30617:${ctx.owner.slice(0, 8)}\u2026:${ctx.repoId} has no announcement yet \u2014 run \`rig push\` to publish the repo (with its real name/description) before managing the payout pointer. Nothing was published or paid.`
7084
+ );
7085
+ return 1;
7086
+ }
7087
+ const current = remote.payout;
7088
+ if (nextPayout !== null && current !== null && current.chain === nextPayout.chain && current.address === nextPayout.address) {
7089
+ io2.err(
7090
+ `rig: payout is already set to ${current.chain} ${current.address} \u2014 nothing to do (not published).`
7091
+ );
7092
+ return 0;
7093
+ }
7094
+ if (op === "clear" && current === null) {
7095
+ io2.err("rig: no payout pointer is set \u2014 nothing to do (not published).");
7096
+ return 0;
7097
+ }
7098
+ const name = remote.name ?? ctx.repoId;
7099
+ const description = remote.description ?? "";
7100
+ const event = buildRepoAnnouncement(
7101
+ ctx.repoId,
7102
+ name,
7103
+ description,
7104
+ remote.maintainers,
7105
+ nextPayout
7106
+ );
7107
+ const fee = (await standaloneCtx.publisher.getFeeRates()).eventFee.toString();
7108
+ const action = nextPayout ? `kind:30617 payout set ${nextPayout.chain} ${nextPayout.address}` : "kind:30617 payout clear";
7109
+ if (!flags.json) {
7110
+ io2.out(`Republish ${action}`);
7111
+ io2.out(`Repo: 30617:${ctx.owner}:${ctx.repoId}`);
7112
+ io2.out(
7113
+ `Payout after: ${nextPayout ? `${nextPayout.chain} ${nextPayout.address}` : "(none)"}`
7114
+ );
7115
+ io2.out(`Fee: ${feeLabel(fee)}. Writes are permanent and non-refundable.`);
7116
+ }
7117
+ if (!flags.yes) {
7118
+ if (flags.json) {
7119
+ io2.emitJson({
7120
+ command,
7121
+ repoAddr: { ownerPubkey: ctx.owner, repoId: ctx.repoId },
7122
+ identity,
7123
+ executed: false,
7124
+ feeEstimate: fee,
7125
+ payout: nextPayout,
7126
+ hint: "estimate only \u2014 re-run with --yes to publish (permanent, non-refundable)"
7127
+ });
7128
+ return 0;
7129
+ }
7130
+ if (!io2.isInteractive) {
7131
+ io2.err(
7132
+ "refusing to spend channel funds without confirmation in a non-interactive session \u2014 re-run with --yes (or use --json for an estimate)"
7133
+ );
7134
+ return 1;
7135
+ }
7136
+ const proceed = await io2.confirm(
7137
+ `Proceed with paid republish (${feeLabel(fee)})? [y/N] `
7138
+ );
7139
+ if (!proceed) {
7140
+ io2.err("aborted \u2014 nothing was published.");
7141
+ return 1;
7142
+ }
7143
+ }
7144
+ const receipt = await standaloneCtx.publisher.publishEvent(event, [
7145
+ relayUrl
7146
+ ]);
7147
+ const result = serializeEventReceipt(event.kind, receipt);
7148
+ if (flags.json) {
7149
+ io2.emitJson({
7150
+ command,
7151
+ repoAddr: { ownerPubkey: ctx.owner, repoId: ctx.repoId },
7152
+ identity,
7153
+ executed: true,
7154
+ feeEstimate: fee,
7155
+ payout: nextPayout,
7156
+ result
7157
+ });
7158
+ } else {
7159
+ io2.out(
7160
+ `Published ${action}: ${result.eventId} paid ${result.feePaid} base units`
7161
+ );
7162
+ }
7163
+ return 0;
7164
+ } catch (err) {
7165
+ return emitCliError(io2, flags.json, command, err);
7166
+ } finally {
7167
+ if (standaloneCtx) {
7168
+ try {
7169
+ await standaloneCtx.stop();
7170
+ } catch {
7171
+ }
7172
+ }
7173
+ }
7174
+ }
7175
+
7176
+ // src/cli/site.ts
7177
+ import { parseArgs as parseArgs17 } from "util";
7125
7178
 
7126
7179
  // src/arweave-manifest.ts
7127
7180
  var DEFAULT_INDEX = "index.html";
@@ -7140,22 +7193,22 @@ function buildArweaveManifest(entries, indexPath = DEFAULT_INDEX, fallbackTxId)
7140
7193
  }
7141
7194
 
7142
7195
  // src/cli/site-record.ts
7143
- import { mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
7144
- import { homedir as homedir6 } from "os";
7145
- import { join as join7 } from "path";
7196
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
7197
+ import { homedir as homedir5 } from "os";
7198
+ import { join as join6 } from "path";
7146
7199
  var SITE_MANIFEST_FILENAME = "site-manifests.json";
7147
7200
  function storeDir2(env) {
7148
- return env["TOON_CLIENT_HOME"] ?? join7(homedir6(), ".toon-client");
7201
+ return env["TOON_CLIENT_HOME"] ?? join6(homedir5(), ".toon-client");
7149
7202
  }
7150
7203
  function storePath2(env) {
7151
- return join7(storeDir2(env), SITE_MANIFEST_FILENAME);
7204
+ return join6(storeDir2(env), SITE_MANIFEST_FILENAME);
7152
7205
  }
7153
7206
  function keyOf(repoId, ref) {
7154
7207
  return `${repoId}\0${ref}`;
7155
7208
  }
7156
7209
  function readFile2(env) {
7157
7210
  try {
7158
- const parsed = JSON.parse(readFileSync6(storePath2(env), "utf8"));
7211
+ const parsed = JSON.parse(readFileSync5(storePath2(env), "utf8"));
7159
7212
  if (parsed && typeof parsed === "object") return parsed;
7160
7213
  return {};
7161
7214
  } catch {
@@ -7176,7 +7229,7 @@ function writeSiteRecord(env, record) {
7176
7229
 
7177
7230
  // src/cli/site.ts
7178
7231
  var MANIFEST_CONTENT_TYPE = "application/x.arweave-manifest+json";
7179
- var DEFAULT_GATEWAY = ARWEAVE_GATEWAYS[0] ?? "https://arweave.net";
7232
+ var DEFAULT_GATEWAY = PREFERRED_GATEWAY;
7180
7233
  var SITE_USAGE = `Usage: rig site <publish|url> [ref] [options]
7181
7234
 
7182
7235
  Serve a pushed repo as a permanent, path-routed website. \`rig push\` already
@@ -7212,7 +7265,7 @@ Options (publish):
7212
7265
  estimate (nothing uploaded)
7213
7266
  -h, --help show this help`;
7214
7267
  function parseSiteArgs(args) {
7215
- const { values, positionals } = parseArgs16({
7268
+ const { values, positionals } = parseArgs17({
7216
7269
  args,
7217
7270
  options: {
7218
7271
  index: { type: "string" },
@@ -7364,7 +7417,6 @@ async function runSitePublish(args, deps) {
7364
7417
  }
7365
7418
  }
7366
7419
  const feeRates = await ctx.publisher.getFeeRates();
7367
- const { uploadFee } = feeRates;
7368
7420
  const previewEntries = blobs.map((b) => ({
7369
7421
  path: b.path,
7370
7422
  txId: known.get(b.sha) ?? PLACEHOLDER_TX
@@ -7380,9 +7432,12 @@ async function runSitePublish(args, deps) {
7380
7432
  if (flags.forceReupload) {
7381
7433
  const { objects } = await reader.statObjects(uniqueShas);
7382
7434
  reuploadBytes = objects.reduce((sum, o) => sum + o.size, 0);
7383
- reuploadFee = BigInt(objects.length) * uploadFee;
7435
+ reuploadFee = objects.reduce(
7436
+ (sum, o) => sum + uploadChargeFor(feeRates, o.size),
7437
+ 0n
7438
+ );
7384
7439
  }
7385
- const manifestFee = uploadFee;
7440
+ const manifestFee = uploadChargeFor(feeRates, manifestBytes);
7386
7441
  const totalFee = manifestFee + reuploadFee;
7387
7442
  const gateway = flags.gateway ?? deps.env["RIG_ARWEAVE_GATEWAY"] ?? DEFAULT_GATEWAY;
7388
7443
  const baseJson = () => ({
@@ -7622,6 +7677,10 @@ Commands rig owns:
7622
7677
  maintainers add <pubkey> remove republish the kind:30617 to change who may
7623
7678
  maintainers remove <pubkey> author authoritative issue/PR status (owner is
7624
7679
  always an implicit maintainer)
7680
+ payout show show the repo's declared payout pointer (free);
7681
+ payout set <address> set/clear republish the kind:30617 \u2014 no pointer
7682
+ payout clear means the serving node keeps 100% of
7683
+ repo-scoped write fees (v1: evm addresses only)
7625
7684
  fund drip devnet faucet funds to this identity's
7626
7685
  wallet (free); on other networks prints the
7627
7686
  address(es) to fund externally
@@ -7629,10 +7688,11 @@ Commands rig owns:
7629
7688
  (free \u2014 chain reads and local state only)
7630
7689
  chain [set <c>|unset] choose which chain/USDC settles paid writes
7631
7690
  (evm|sol|mina); free \u2014 reads/writes local config
7632
- entry [apex|sandbox|url] choose the network entry node (payment ingress +
7633
- relay): apex settles evm|sol|mina, sandbox is the
7634
- Mina-only multihop demo path; free \u2014 writes local
7635
- config and clears the cached topology
7691
+ entry <url> | entry clear choose the connector that prices and settles paid
7692
+ writes \u2014 one URL is the whole configuration, since
7693
+ its GET /ilp describes it; --relay <wss-url>
7694
+ records the free-read relay beside it; free \u2014
7695
+ writes local config
7636
7696
  name buy <name> ArNS naming (#367): buy points a human name at an
7637
7697
  name set <name> <txId> Arweave txId, owned + paid by this identity's
7638
7698
  name status <name> Solana key. buy spends mARIO on Solana via the
@@ -7715,6 +7775,8 @@ async function dispatch(argv2, deps) {
7715
7775
  return runPr(rest, deps);
7716
7776
  case "maintainers":
7717
7777
  return runMaintainers(rest, deps);
7778
+ case "payout":
7779
+ return runPayout(rest, deps);
7718
7780
  case "channel":
7719
7781
  return runChannel(rest, deps);
7720
7782
  case "channels":
@@ -7806,6 +7868,7 @@ var RIG_OWNED_VERBS = /* @__PURE__ */ new Set([
7806
7868
  "comment",
7807
7869
  "pr",
7808
7870
  "maintainers",
7871
+ "payout",
7809
7872
  "channel",
7810
7873
  "channels",
7811
7874
  "fund",