@1sat/cli 0.0.120 → 0.0.122

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/dist/cli.js CHANGED
@@ -1,15 +1,7 @@
1
1
  #!/usr/bin/env node
2
- var __esm = (fn, res, err) => () => {
3
- if (fn)
4
- try {
5
- res = fn(fn = 0);
6
- } catch (e) {
7
- err = [e];
8
- }
9
- if (err)
10
- throw err[0];
11
- return res;
12
- };
2
+ import { createRequire } from "node:module";
3
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
4
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
13
5
 
14
6
  // src/args.ts
15
7
  function parseGlobalFlags(args) {
@@ -386,6 +378,120 @@ var init_monitor_once = __esm(() => {
386
378
  init_monitor_lock();
387
379
  });
388
380
 
381
+ // src/wallet-api/admin.ts
382
+ import { PERMISSION_SCHEME_IDS } from "@1sat/types";
383
+ import {
384
+ LocalWalletPermissionsManager
385
+ } from "@1sat/wallet";
386
+ function adminWallet(wallet, store) {
387
+ const manager = new LocalWalletPermissionsManager(wallet, CLI_ADMIN_ORIGINATOR, { permissionModules: adminPermissionModules }, { store });
388
+ return withAdminOriginator(manager);
389
+ }
390
+ function withAdminOriginator(manager) {
391
+ return new Proxy(manager, {
392
+ get(target, property, receiver) {
393
+ const value = Reflect.get(target, property, receiver);
394
+ if (typeof value !== "function")
395
+ return value;
396
+ return (args) => value.call(target, args, CLI_ADMIN_ORIGINATOR);
397
+ }
398
+ });
399
+ }
400
+ var CLI_ADMIN_ORIGINATOR = "1sat-cli.internal", adminPermissionModules;
401
+ var init_admin = __esm(() => {
402
+ adminPermissionModules = Object.fromEntries(PERMISSION_SCHEME_IDS.map((scheme) => [
403
+ scheme,
404
+ {
405
+ onRequest: async (req) => ({ args: req.args }),
406
+ onResponse: async (res) => res
407
+ }
408
+ ]));
409
+ });
410
+
411
+ // src/wallet-api/permission-store.ts
412
+ import {
413
+ existsSync as existsSync3,
414
+ mkdirSync as mkdirSync2,
415
+ readFileSync as readFileSync4,
416
+ renameSync,
417
+ writeFileSync as writeFileSync4
418
+ } from "node:fs";
419
+ import { dirname, join as join5 } from "node:path";
420
+ import {
421
+ permissionKeyToString
422
+ } from "@1sat/wallet";
423
+ function permissionStorePath(dataDir, chain) {
424
+ return join5(dataDir, `permissions-${chain}.json`);
425
+ }
426
+
427
+ class FilePermissionStore {
428
+ path;
429
+ constructor(path) {
430
+ this.path = path;
431
+ }
432
+ async findGrant(key) {
433
+ return this.load().grants[permissionKeyToString(key)] ?? null;
434
+ }
435
+ async putGrant(grant) {
436
+ const file = this.load();
437
+ file.grants[permissionKeyToString(grant.key)] = grant;
438
+ this.save(file);
439
+ }
440
+ async deleteGrant(key) {
441
+ const file = this.load();
442
+ const id = permissionKeyToString(key);
443
+ if (!(id in file.grants))
444
+ return;
445
+ delete file.grants[id];
446
+ this.save(file);
447
+ }
448
+ async deleteAllForOriginator(originator) {
449
+ const file = this.load();
450
+ let removed = 0;
451
+ for (const [id, grant] of Object.entries(file.grants)) {
452
+ if (grant.key.originator === originator) {
453
+ delete file.grants[id];
454
+ removed++;
455
+ }
456
+ }
457
+ if (removed > 0)
458
+ this.save(file);
459
+ return removed;
460
+ }
461
+ async listGrants(filter) {
462
+ const out = [];
463
+ for (const grant of Object.values(this.load().grants)) {
464
+ if (filter?.originator && grant.key.originator !== filter.originator)
465
+ continue;
466
+ if (filter?.type && grant.key.type !== filter.type)
467
+ continue;
468
+ out.push(grant);
469
+ }
470
+ return out;
471
+ }
472
+ load() {
473
+ if (!existsSync3(this.path))
474
+ return { version: FILE_VERSION, grants: {} };
475
+ const raw = readFileSync4(this.path, "utf8");
476
+ const parsed = JSON.parse(raw);
477
+ if (parsed.version !== FILE_VERSION) {
478
+ throw new Error(`Unsupported permission store version ${String(parsed.version)} in ${this.path}`);
479
+ }
480
+ return { version: FILE_VERSION, grants: parsed.grants ?? {} };
481
+ }
482
+ save(file) {
483
+ const dir = dirname(this.path);
484
+ if (!existsSync3(dir))
485
+ mkdirSync2(dir, { recursive: true, mode: 448 });
486
+ const tmp = `${this.path}.${process.pid}.tmp`;
487
+ writeFileSync4(tmp, `${JSON.stringify(file, null, 2)}
488
+ `, { mode: 384 });
489
+ renameSync(tmp, this.path);
490
+ }
491
+ }
492
+ var FILE_VERSION = 1;
493
+ var init_permission_store = () => {};
494
+
389
495
  // src/context.ts
390
496
  import { createContext } from "@1sat/actions";
391
497
  import { createNodeWallet as createNodeWallet2 } from "@1sat/wallet-node";
@@ -405,7 +511,8 @@ async function loadContext(privateKey, opts) {
405
511
  backups: config.backups,
406
512
  skipInitialMonitor: true
407
513
  });
408
- const ctx = createContext(walletResult.wallet, {
514
+ const wallet = adminWallet(walletResult.wallet, new FilePermissionStore(permissionStorePath(dataDir, opts.chain)));
515
+ const ctx = createContext(wallet, {
409
516
  services: walletResult.services,
410
517
  chain: opts.chain,
411
518
  dataDir,
@@ -428,6 +535,8 @@ async function loadContext(privateKey, opts) {
428
535
  var init_context = __esm(() => {
429
536
  init_config();
430
537
  init_monitor_once();
538
+ init_admin();
539
+ init_permission_store();
431
540
  });
432
541
 
433
542
  // src/output.ts
@@ -513,11 +622,11 @@ async function handleActionCommand(args, opts) {
513
622
  Registered actions:
514
623
  `);
515
624
  const grouped = new Map;
516
- for (const action of actions) {
517
- const cat = action.meta.category;
625
+ for (const action2 of actions) {
626
+ const cat = action2.meta.category;
518
627
  if (!grouped.has(cat))
519
628
  grouped.set(cat, []);
520
- grouped.get(cat).push(action);
629
+ grouped.get(cat).push(action2);
521
630
  }
522
631
  for (const [category, categoryActions] of grouped) {
523
632
  console.log(` ${category}:`);
@@ -563,7 +672,7 @@ var init_action = __esm(() => {
563
672
  });
564
673
 
565
674
  // src/help.ts
566
- import { readFileSync as readFileSync4 } from "node:fs";
675
+ import { readFileSync as readFileSync5 } from "node:fs";
567
676
  import chalk2 from "chalk";
568
677
  function getVersion() {
569
678
  return VERSION;
@@ -717,7 +826,7 @@ var VERSION, GLOBAL_OPTIONS, ENV_VARS, COMMANDS;
717
826
  var init_help = __esm(() => {
718
827
  VERSION = (() => {
719
828
  try {
720
- const pkg = JSON.parse(readFileSync4(new URL("../package.json", import.meta.url), "utf8"));
829
+ const pkg = JSON.parse(readFileSync5(new URL("../package.json", import.meta.url), "utf8"));
721
830
  return pkg.version || "0.0.11";
722
831
  } catch {
723
832
  return "0.0.11";
@@ -1390,7 +1499,82 @@ var init_help = __esm(() => {
1390
1499
  { name: "monitor", description: "Monitor daemon only" },
1391
1500
  {
1392
1501
  name: "wallet-api",
1393
- description: "App-facing BRC-100 wallet endpoint only (headless, default port 3321)"
1502
+ description: "App-facing BRC-100 wallet endpoint only (headless, default port 3321). Ungranted requests are denied with the `1sat permissions grant` command that would allow them"
1503
+ }
1504
+ ]
1505
+ },
1506
+ {
1507
+ group: "Server",
1508
+ name: "permissions",
1509
+ description: "What apps on `1sat serve wallet-api` may do. Grants live in <dataDir>/permissions-<chain>.json; the endpoint reads the file on every check, so a grant applies to the app's next call without a restart. Works with the server stopped or running and needs no wallet key.",
1510
+ subcommands: [
1511
+ {
1512
+ name: "list",
1513
+ description: "Show grants, grouped by app origin",
1514
+ positional: "[<origin>]"
1515
+ },
1516
+ {
1517
+ name: "grant",
1518
+ description: "Allow an app one or more permissions. Selectors may be combined in one call.",
1519
+ positional: "<origin>",
1520
+ args: [
1521
+ {
1522
+ flag: "--protocol",
1523
+ values: "<name>",
1524
+ description: "Protocol name; requires --level"
1525
+ },
1526
+ {
1527
+ flag: "--level",
1528
+ values: "<0|1|2>",
1529
+ description: "Protocol security level"
1530
+ },
1531
+ {
1532
+ flag: "--counterparty",
1533
+ values: "<hex|self|anyone>",
1534
+ description: "Counterparty for a level 0/2 protocol (default self; ignored at level 1), or a certificate's verifier (also --verifier)"
1535
+ },
1536
+ {
1537
+ flag: "--privileged",
1538
+ description: "Grant the privileged variant of a protocol/certificate"
1539
+ },
1540
+ { flag: "--basket", values: "<name>", description: "Basket access" },
1541
+ {
1542
+ flag: "--label",
1543
+ values: "<name>",
1544
+ description: "Action label (a level-1 `action label <name>` protocol)"
1545
+ },
1546
+ {
1547
+ flag: "--certificate",
1548
+ values: "<type>",
1549
+ description: "Certificate type; requires --fields"
1550
+ },
1551
+ {
1552
+ flag: "--fields",
1553
+ values: "<a,b>",
1554
+ description: "Certificate fields that may be disclosed"
1555
+ },
1556
+ {
1557
+ flag: "--spending",
1558
+ values: "<satoshis>",
1559
+ description: "Monthly spending cap; month-to-date spend is read from action history on every check"
1560
+ }
1561
+ ]
1562
+ },
1563
+ {
1564
+ name: "revoke",
1565
+ description: "Remove matching grants, or every grant for an origin",
1566
+ positional: "<origin>",
1567
+ args: [
1568
+ {
1569
+ flag: "--all",
1570
+ description: "Remove every grant for the origin"
1571
+ },
1572
+ {
1573
+ flag: "--protocol",
1574
+ values: "<name>",
1575
+ description: "Same selectors as `grant`"
1576
+ }
1577
+ ]
1394
1578
  }
1395
1579
  ]
1396
1580
  },
@@ -1476,7 +1660,7 @@ function createApprovalAuth(wallet) {
1476
1660
  get(target, property) {
1477
1661
  if (property === "createAction")
1478
1662
  return async (...args) => {
1479
- const satoshis = args[0].outputs?.reduce((sum, output) => sum + output.satoshis, 0) ?? 0;
1663
+ const satoshis = args[0].outputs?.reduce((sum, output2) => sum + output2.satoshis, 0) ?? 0;
1480
1664
  if (!Number.isSafeInteger(satoshis) || satoshis <= 0)
1481
1665
  throw new Error("Invalid HTTP payment amount");
1482
1666
  if (approved === false)
@@ -1573,7 +1757,7 @@ var init_authfetch_request = __esm(() => {
1573
1757
  });
1574
1758
 
1575
1759
  // src/commands/authfetch.ts
1576
- import { readFileSync as readFileSync5 } from "node:fs";
1760
+ import { readFileSync as readFileSync6 } from "node:fs";
1577
1761
  import { confirm, isCancel as isCancel2 } from "@clack/prompts";
1578
1762
  async function handleAuthfetchCommand(args, opts) {
1579
1763
  if (!args[0] || args[0] === "help" || args.includes("--help") || args.includes("-h")) {
@@ -1632,7 +1816,7 @@ Unknown or missing method: ${method ?? ""}`);
1632
1816
  }
1633
1817
  let body;
1634
1818
  if (bodyRaw !== undefined) {
1635
- body = bodyRaw.startsWith("@") ? readFileSync5(bodyRaw.slice(1), "utf8") : bodyRaw;
1819
+ body = bodyRaw.startsWith("@") ? readFileSync6(bodyRaw.slice(1), "utf8") : bodyRaw;
1636
1820
  if (!headers["Content-Type"] && !headers["content-type"]) {
1637
1821
  headers["Content-Type"] = "application/json";
1638
1822
  }
@@ -2000,15 +2184,15 @@ async function handleInitCommand(_args, opts) {
2000
2184
  }
2001
2185
  let wif;
2002
2186
  if (keySource === "generate") {
2003
- const pk = PrivateKey2.fromRandom();
2004
- wif = pk.toWif();
2005
- const address = pk.toPublicKey().toAddress();
2187
+ const pk2 = PrivateKey2.fromRandom();
2188
+ wif = pk2.toWif();
2189
+ const address2 = pk2.toPublicKey().toAddress();
2006
2190
  console.log();
2007
2191
  console.log(formatWarning(" IMPORTANT: Back up your private key!"));
2008
2192
  console.log(formatWarning(" If you lose it, your funds are gone forever."));
2009
2193
  console.log();
2010
2194
  console.log(` ${formatValue("WIF:")} ${wif}`);
2011
- console.log(` ${formatValue("Address:")} ${address}`);
2195
+ console.log(` ${formatValue("Address:")} ${address2}`);
2012
2196
  console.log();
2013
2197
  const confirmed = await confirm2({
2014
2198
  message: "Have you saved your private key somewhere safe?"
@@ -2077,7 +2261,7 @@ var init_init = __esm(() => {
2077
2261
  });
2078
2262
 
2079
2263
  // src/beef.ts
2080
- import { readFileSync as readFileSync6 } from "node:fs";
2264
+ import { readFileSync as readFileSync7 } from "node:fs";
2081
2265
  import { Utils } from "@bsv/sdk";
2082
2266
  function parseBeefFlag(value) {
2083
2267
  if (!value)
@@ -2086,7 +2270,7 @@ function parseBeefFlag(value) {
2086
2270
  if (!value.includes(`
2087
2271
  `) && !value.includes(" ") && value.length < 4096) {
2088
2272
  try {
2089
- const buf = readFileSync6(value);
2273
+ const buf = readFileSync7(value);
2090
2274
  return Array.from(buf);
2091
2275
  } catch {}
2092
2276
  }
@@ -2227,7 +2411,7 @@ var init_locks = __esm(() => {
2227
2411
  });
2228
2412
 
2229
2413
  // src/commands/mcp-proxy.ts
2230
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
2414
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
2231
2415
  import { Hash, KeyDeriver, PrivateKey as PrivateKey3, Signature, Utils as Utils2 } from "@bsv/sdk";
2232
2416
  import { createLogger, initLogger } from "evlog";
2233
2417
  function generateNonce() {
@@ -2236,12 +2420,12 @@ function generateNonce() {
2236
2420
  return toBase64(Array.from(bytes));
2237
2421
  }
2238
2422
  function getClientKey() {
2239
- mkdirSync2(KEY_DIR, { recursive: true });
2240
- if (existsSync3(CLIENT_KEY_PATH)) {
2241
- return PrivateKey3.fromWif(readFileSync7(CLIENT_KEY_PATH, "utf-8").trim());
2423
+ mkdirSync3(KEY_DIR, { recursive: true });
2424
+ if (existsSync4(CLIENT_KEY_PATH)) {
2425
+ return PrivateKey3.fromWif(readFileSync8(CLIENT_KEY_PATH, "utf-8").trim());
2242
2426
  }
2243
2427
  const key = PrivateKey3.fromRandom();
2244
- writeFileSync4(CLIENT_KEY_PATH, key.toWif(), { mode: 384 });
2428
+ writeFileSync5(CLIENT_KEY_PATH, key.toWif(), { mode: 384 });
2245
2429
  const log = createLogger({ context: "startup" });
2246
2430
  log.set({ event: "key_generated", path: CLIENT_KEY_PATH });
2247
2431
  log.emit();
@@ -2739,7 +2923,7 @@ var init_opns = __esm(() => {
2739
2923
  });
2740
2924
 
2741
2925
  // src/commands/ordinals.ts
2742
- import { readFileSync as readFileSync8 } from "node:fs";
2926
+ import { readFileSync as readFileSync9 } from "node:fs";
2743
2927
  import { basename, extname } from "node:path";
2744
2928
  import {
2745
2929
  burnOrdinals,
@@ -2857,7 +3041,7 @@ async function ordinalsInscribe(args, opts) {
2857
3041
  }
2858
3042
  let fileBytes;
2859
3043
  try {
2860
- fileBytes = readFileSync8(file);
3044
+ fileBytes = readFileSync9(file);
2861
3045
  } catch (err) {
2862
3046
  fatal(`Failed to read file: ${err.message}`);
2863
3047
  }
@@ -3045,6 +3229,335 @@ var init_ordinals = __esm(() => {
3045
3229
  };
3046
3230
  });
3047
3231
 
3232
+ // src/wallet-api/grants.ts
3233
+ import {
3234
+ normalizeOriginator,
3235
+ permissionKeyToString as permissionKeyToString2
3236
+ } from "@1sat/wallet";
3237
+ function shellQuote(value) {
3238
+ if (value !== "" && /^[A-Za-z0-9_@%+=:,./-]+$/.test(value))
3239
+ return value;
3240
+ return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
3241
+ }
3242
+ function grantCommand(spec, prefix = GRANT_COMMAND) {
3243
+ const key = spec.key;
3244
+ const parts = [prefix, shellQuote(key.originator)];
3245
+ switch (key.type) {
3246
+ case "protocol": {
3247
+ if (key.protocolLevel === 1 && key.protocolName.startsWith(LABEL_PROTOCOL_PREFIX)) {
3248
+ parts.push("--label", shellQuote(key.protocolName.slice(LABEL_PROTOCOL_PREFIX.length)));
3249
+ break;
3250
+ }
3251
+ parts.push("--protocol", shellQuote(key.protocolName));
3252
+ parts.push("--level", String(key.protocolLevel));
3253
+ if (key.protocolLevel !== 1 && key.counterparty) {
3254
+ parts.push("--counterparty", shellQuote(key.counterparty));
3255
+ }
3256
+ if (key.privileged)
3257
+ parts.push("--privileged");
3258
+ break;
3259
+ }
3260
+ case "basket":
3261
+ parts.push("--basket", shellQuote(key.basket));
3262
+ break;
3263
+ case "certificate":
3264
+ parts.push("--certificate", shellQuote(key.certType));
3265
+ parts.push("--fields", shellQuote(key.fields.join(",")));
3266
+ if (key.verifier) {
3267
+ parts.push("--counterparty", shellQuote(key.verifier));
3268
+ }
3269
+ if (key.privileged)
3270
+ parts.push("--privileged");
3271
+ break;
3272
+ case "spending":
3273
+ parts.push("--spending", String(spec.authorizedAmount ?? 0));
3274
+ break;
3275
+ }
3276
+ return parts.join(" ");
3277
+ }
3278
+ function describeKey(spec) {
3279
+ const key = spec.key;
3280
+ switch (key.type) {
3281
+ case "protocol": {
3282
+ if (key.protocolLevel === 1 && key.protocolName.startsWith(LABEL_PROTOCOL_PREFIX)) {
3283
+ return `label ${key.protocolName.slice(LABEL_PROTOCOL_PREFIX.length)}`;
3284
+ }
3285
+ const bits = [`"${key.protocolName}" (level ${key.protocolLevel}`];
3286
+ if (key.protocolLevel !== 1 && key.counterparty) {
3287
+ bits.push(`, counterparty ${key.counterparty}`);
3288
+ }
3289
+ if (key.privileged)
3290
+ bits.push(", privileged");
3291
+ return `protocol ${bits.join("")})`;
3292
+ }
3293
+ case "basket":
3294
+ return `basket "${key.basket}"`;
3295
+ case "certificate":
3296
+ return `cert "${key.certType}" fields [${key.fields.join(", ")}]${key.verifier ? ` to ${key.verifier}` : ""}${key.privileged ? " (privileged)" : ""}`;
3297
+ case "spending":
3298
+ return `spending up to ${spec.authorizedAmount ?? 0} sat per month`;
3299
+ }
3300
+ }
3301
+ function specsFromFlags(origin, flags) {
3302
+ const originator = normalizeOriginator(origin);
3303
+ if (!originator)
3304
+ throw new Error("An app origin is required");
3305
+ const specs = [];
3306
+ if (flags.protocol !== undefined || flags.label !== undefined) {
3307
+ if (flags.protocol !== undefined && flags.label !== undefined) {
3308
+ throw new Error("Use either --protocol or --label, not both");
3309
+ }
3310
+ const isLabel = flags.label !== undefined;
3311
+ const name = isLabel ? `${LABEL_PROTOCOL_PREFIX}${flags.label}` : flags.protocol;
3312
+ if (!name.trim() || name === LABEL_PROTOCOL_PREFIX) {
3313
+ throw new Error(isLabel ? "--label requires a name" : "--protocol requires a name");
3314
+ }
3315
+ const level = isLabel ? 1 : parseLevel(flags.level);
3316
+ specs.push({
3317
+ key: {
3318
+ type: "protocol",
3319
+ originator,
3320
+ privileged: flags.privileged,
3321
+ protocolLevel: level,
3322
+ protocolName: name,
3323
+ counterparty: level === 1 ? "" : flags.counterparty ?? "self"
3324
+ }
3325
+ });
3326
+ }
3327
+ if (flags.basket !== undefined) {
3328
+ if (!flags.basket.trim())
3329
+ throw new Error("--basket requires a name");
3330
+ specs.push({
3331
+ key: { type: "basket", originator, basket: flags.basket }
3332
+ });
3333
+ }
3334
+ if (flags.certificate !== undefined) {
3335
+ if (!flags.certificate.trim()) {
3336
+ throw new Error("--certificate requires a certificate type");
3337
+ }
3338
+ const fields = (flags.fields ?? "").split(",").map((f) => f.trim()).filter((f) => f.length > 0);
3339
+ if (fields.length === 0) {
3340
+ throw new Error("--certificate requires --fields <a,b>");
3341
+ }
3342
+ specs.push({
3343
+ key: {
3344
+ type: "certificate",
3345
+ originator,
3346
+ privileged: flags.privileged,
3347
+ verifier: flags.counterparty ?? "",
3348
+ certType: flags.certificate,
3349
+ fields: fields.sort()
3350
+ }
3351
+ });
3352
+ }
3353
+ if (flags.spending !== undefined) {
3354
+ const satoshis = Number(flags.spending);
3355
+ if (!Number.isInteger(satoshis) || satoshis <= 0) {
3356
+ throw new Error("--spending requires a positive whole number of satoshis");
3357
+ }
3358
+ specs.push({
3359
+ key: { type: "spending", originator },
3360
+ authorizedAmount: satoshis
3361
+ });
3362
+ }
3363
+ if (specs.length === 0) {
3364
+ throw new Error("Nothing selected. Pass --protocol/--label, --basket, --certificate or --spending");
3365
+ }
3366
+ return specs;
3367
+ }
3368
+ function parseLevel(value) {
3369
+ if (value === undefined) {
3370
+ throw new Error("--protocol requires --level <0|1|2>");
3371
+ }
3372
+ if (value !== "0" && value !== "1" && value !== "2") {
3373
+ throw new Error(`--level must be 0, 1 or 2 (got ${value})`);
3374
+ }
3375
+ return Number(value);
3376
+ }
3377
+ function specId(spec) {
3378
+ return permissionKeyToString2(spec.key);
3379
+ }
3380
+ var LABEL_PROTOCOL_PREFIX = "action label ", GRANT_COMMAND = "1sat permissions grant";
3381
+ var init_grants = () => {};
3382
+
3383
+ // src/commands/permissions.ts
3384
+ import { normalizeOriginator as normalizeOriginator2 } from "@1sat/wallet";
3385
+ async function handlePermissionsCommand(args, opts) {
3386
+ const [subcommand, ...rest] = args;
3387
+ switch (subcommand) {
3388
+ case "list":
3389
+ case "ls":
3390
+ return permissionsList(rest, opts);
3391
+ case "grant":
3392
+ return permissionsGrant(rest, opts);
3393
+ case "revoke":
3394
+ return permissionsRevoke(rest, opts);
3395
+ default:
3396
+ printCommandHelp("permissions", opts.json);
3397
+ if (subcommand && subcommand !== "help") {
3398
+ process.exit(1);
3399
+ }
3400
+ }
3401
+ }
3402
+ function openStore(opts) {
3403
+ const path = permissionStorePath(ensureDataDir(), opts.chain);
3404
+ return { store: new FilePermissionStore(path), path };
3405
+ }
3406
+ function commandPrefix(opts) {
3407
+ return opts.chain === "test" ? "1sat --chain test permissions grant" : "1sat permissions grant";
3408
+ }
3409
+ function grantFlags(args) {
3410
+ return {
3411
+ protocol: extractFlag(args, "--protocol"),
3412
+ level: extractFlag(args, "--level"),
3413
+ counterparty: extractFlag(args, "--counterparty") ?? extractFlag(args, "--verifier"),
3414
+ privileged: hasFlag(args, "--privileged"),
3415
+ basket: extractFlag(args, "--basket"),
3416
+ label: extractFlag(args, "--label"),
3417
+ certificate: extractFlag(args, "--certificate"),
3418
+ fields: extractFlag(args, "--fields"),
3419
+ spending: extractFlag(args, "--spending")
3420
+ };
3421
+ }
3422
+ function firstPositional(args) {
3423
+ for (let i = 0;i < args.length; i++) {
3424
+ const arg = args[i];
3425
+ if (arg.startsWith("--")) {
3426
+ if (!BOOLEAN_FLAGS.has(arg))
3427
+ i++;
3428
+ continue;
3429
+ }
3430
+ return arg;
3431
+ }
3432
+ return;
3433
+ }
3434
+ function toSpec(grant) {
3435
+ return grant.authorizedAmount != null ? { key: grant.key, authorizedAmount: grant.authorizedAmount } : { key: grant.key };
3436
+ }
3437
+ async function permissionsList(args, opts) {
3438
+ const { store, path } = openStore(opts);
3439
+ const origin = firstPositional(args);
3440
+ const filter = origin ? { originator: normalizeOriginator2(origin) } : {};
3441
+ const grants = await store.listGrants(filter);
3442
+ if (opts.json) {
3443
+ output(grants, opts);
3444
+ return;
3445
+ }
3446
+ if (grants.length === 0) {
3447
+ output(origin ? `No grants for ${normalizeOriginator2(origin)} in ${path}` : `No grants in ${path}`, opts);
3448
+ return;
3449
+ }
3450
+ const byOrigin = new Map;
3451
+ for (const grant of grants) {
3452
+ const list = byOrigin.get(grant.key.originator) ?? [];
3453
+ list.push(grant);
3454
+ byOrigin.set(grant.key.originator, list);
3455
+ }
3456
+ for (const name of [...byOrigin.keys()].sort()) {
3457
+ console.log(`
3458
+ ${formatValue(name)}`);
3459
+ for (const grant of byOrigin.get(name) ?? []) {
3460
+ const expiry = grant.expiry > 0 ? ` expires ${new Date(grant.expiry * 1000).toISOString()}` : "";
3461
+ console.log(` ${describeKey(toSpec(grant))}${formatLabel(expiry)}${formatLabel(grant.reason ? ` — ${grant.reason}` : "")}`);
3462
+ }
3463
+ }
3464
+ console.log(`
3465
+ ${grants.length} grant(s) in ${path}`);
3466
+ }
3467
+ async function permissionsGrant(args, opts) {
3468
+ const origin = firstPositional(args);
3469
+ if (!origin)
3470
+ fatal("Missing <origin>. See `1sat permissions --help`");
3471
+ let specs;
3472
+ try {
3473
+ specs = specsFromFlags(origin, grantFlags(args));
3474
+ } catch (err) {
3475
+ return fatal(err.message);
3476
+ }
3477
+ const { store, path } = openStore(opts);
3478
+ const now = Date.now();
3479
+ for (const spec of specs) {
3480
+ await store.putGrant({
3481
+ key: spec.key,
3482
+ expiry: 0,
3483
+ grantedAt: now,
3484
+ ...spec.authorizedAmount != null ? { authorizedAmount: spec.authorizedAmount } : {}
3485
+ });
3486
+ }
3487
+ if (opts.json) {
3488
+ output({
3489
+ granted: specs.map((spec) => ({
3490
+ id: specId(spec),
3491
+ key: spec.key,
3492
+ authorizedAmount: spec.authorizedAmount
3493
+ })),
3494
+ store: path
3495
+ }, opts);
3496
+ return;
3497
+ }
3498
+ for (const spec of specs) {
3499
+ console.log(`granted ${describeKey(spec)}`);
3500
+ }
3501
+ console.log(`
3502
+ ${specs.length} grant(s) written to ${path}. A running \`1sat serve wallet-api\` picks them up on the app's next call.`);
3503
+ }
3504
+ async function permissionsRevoke(args, opts) {
3505
+ const origin = firstPositional(args);
3506
+ if (!origin)
3507
+ fatal("Missing <origin>. See `1sat permissions --help`");
3508
+ const originator = normalizeOriginator2(origin);
3509
+ const { store, path } = openStore(opts);
3510
+ if (hasFlag(args, "--all")) {
3511
+ const removed2 = await store.deleteAllForOriginator(originator);
3512
+ if (opts.json) {
3513
+ output({ originator, removed: removed2, store: path }, opts);
3514
+ return;
3515
+ }
3516
+ output(`revoked ${removed2} grant(s) for ${originator}`, opts);
3517
+ return;
3518
+ }
3519
+ let specs;
3520
+ try {
3521
+ specs = specsFromFlags(origin, grantFlags(args));
3522
+ } catch (err) {
3523
+ return fatal(`${err.message}. Pass --all to revoke everything for an origin.`);
3524
+ }
3525
+ const removed = [];
3526
+ const missing = [];
3527
+ for (const spec of specs) {
3528
+ if (await store.findGrant(spec.key)) {
3529
+ await store.deleteGrant(spec.key);
3530
+ removed.push(spec);
3531
+ } else {
3532
+ missing.push(spec);
3533
+ }
3534
+ }
3535
+ if (opts.json) {
3536
+ output({
3537
+ revoked: removed.map((spec) => ({ id: specId(spec), key: spec.key })),
3538
+ notFound: missing.map((spec) => ({ id: specId(spec), key: spec.key })),
3539
+ store: path
3540
+ }, opts);
3541
+ return;
3542
+ }
3543
+ for (const spec of removed)
3544
+ console.log(`revoked ${describeKey(spec)}`);
3545
+ for (const spec of missing) {
3546
+ console.log(`no such grant: ${describeKey(spec)} (grant it with \`${grantCommand(spec, commandPrefix(opts))}\`)`);
3547
+ }
3548
+ console.log(`
3549
+ ${removed.length} grant(s) removed from ${path}`);
3550
+ }
3551
+ var BOOLEAN_FLAGS;
3552
+ var init_permissions = __esm(() => {
3553
+ init_config();
3554
+ init_help();
3555
+ init_output();
3556
+ init_grants();
3557
+ init_permission_store();
3558
+ BOOLEAN_FLAGS = new Set(["--privileged", "--all"]);
3559
+ });
3560
+
3048
3561
  // src/commands/remote.ts
3049
3562
  import { StorageClient } from "@1sat/wallet-node";
3050
3563
  import { WalletServerClient, topUpStorage } from "@1sat/wallet-server";
@@ -3533,8 +4046,135 @@ var init_repricer = __esm(() => {
3533
4046
  init_providers();
3534
4047
  });
3535
4048
 
4049
+ // src/wallet-api/deny.ts
4050
+ import { AsyncLocalStorage } from "node:async_hooks";
4051
+ import {
4052
+ normalizeOriginator as normalizeOriginator3,
4053
+ permissionKeyFromRequest,
4054
+ permissionKeysFromGroup
4055
+ } from "@1sat/wallet";
4056
+ function bindPermissionDenials(manager, options = {}) {
4057
+ const prefix = options.grantCommandPrefix ?? GRANT_COMMAND;
4058
+ const log = options.log ?? ((line) => console.log(line));
4059
+ const record = (origin, commands) => {
4060
+ const message = denialMessage(origin, commands);
4061
+ inFlight.getStore()?.push(message);
4062
+ log(`[wallet-api] ${message}`);
4063
+ };
4064
+ const single = async (request) => {
4065
+ const origin = displayOrigin(request);
4066
+ try {
4067
+ record(origin, [grantCommand(specFromRequest(request), prefix)]);
4068
+ } catch (err) {
4069
+ log(`[wallet-api] could not describe request: ${err.message}`);
4070
+ }
4071
+ await safely(log, "deny", () => manager.denyPermission(request.requestID));
4072
+ };
4073
+ const grouped = async (request) => {
4074
+ const origin = normalizeOriginator3(request.originator);
4075
+ try {
4076
+ record(origin, specsFromGroupedRequest(request).map((spec) => grantCommand(spec, prefix)));
4077
+ } catch (err) {
4078
+ log(`[wallet-api] could not describe request: ${err.message}`);
4079
+ }
4080
+ await safely(log, "deny", () => manager.denyGroupedPermission(request.requestID));
4081
+ };
4082
+ manager.bindCallback("onProtocolPermissionRequested", single);
4083
+ manager.bindCallback("onBasketAccessRequested", single);
4084
+ manager.bindCallback("onCertificateAccessRequested", single);
4085
+ manager.bindCallback("onSpendingAuthorizationRequested", single);
4086
+ manager.bindCallback("onGroupedPermissionRequested", grouped);
4087
+ }
4088
+ async function withDenialMessage(fn) {
4089
+ const messages = [];
4090
+ try {
4091
+ return await inFlight.run(messages, fn);
4092
+ } catch (err) {
4093
+ const code = err?.code;
4094
+ if (code !== DENIED_CODE || messages.length === 0)
4095
+ throw err;
4096
+ const denied = new Error(messages.join(" "));
4097
+ denied.code = DENIED_CODE;
4098
+ throw denied;
4099
+ }
4100
+ }
4101
+ function denialMessage(origin, commands) {
4102
+ const quoted = commands.map((c) => `\`${c}\``);
4103
+ const list = quoted.length > 1 ? `${quoted.slice(0, -1).join(", ")} and ${quoted[quoted.length - 1]}` : quoted[0];
4104
+ return `permission denied for ${origin}: run ${list} and retry`;
4105
+ }
4106
+ function specFromRequest(request) {
4107
+ const key = permissionKeyFromRequest(request);
4108
+ return key.type === "spending" ? { key, authorizedAmount: request.spending?.satoshis ?? 0 } : { key };
4109
+ }
4110
+ function specsFromGroupedRequest(request) {
4111
+ const amount = request.permissions.spendingAuthorization?.amount ?? 0;
4112
+ return permissionKeysFromGroup(request.originator, request.permissions).map((key) => key.type === "spending" ? { key, authorizedAmount: amount } : { key });
4113
+ }
4114
+ function displayOrigin(request) {
4115
+ return normalizeOriginator3(request.originator);
4116
+ }
4117
+ async function safely(log, what, fn) {
4118
+ try {
4119
+ await fn();
4120
+ } catch (err) {
4121
+ log(`[wallet-api] ${what} failed: ${err.message}`);
4122
+ }
4123
+ }
4124
+ var inFlight, DENIED_CODE = "ERR_PERMISSION_DENIED";
4125
+ var init_deny = __esm(() => {
4126
+ init_grants();
4127
+ inFlight = new AsyncLocalStorage;
4128
+ });
4129
+
4130
+ // src/wallet-api/endpoint.ts
4131
+ import { LocalWalletPermissionsManager as LocalWalletPermissionsManager2 } from "@1sat/wallet";
4132
+ import {
4133
+ startBRC100Server
4134
+ } from "@1sat/wallet-server";
4135
+ function walletApiHandle(wallet) {
4136
+ return {
4137
+ async call(method, args, origin) {
4138
+ const fn = wallet[method];
4139
+ if (typeof fn !== "function") {
4140
+ throw new Error(`Method not available: ${method}`);
4141
+ }
4142
+ return withDenialMessage(() => fn.call(wallet, args, origin));
4143
+ }
4144
+ };
4145
+ }
4146
+ async function startWalletApi(options) {
4147
+ const log = options.log ?? ((line) => console.log(line));
4148
+ const manager = new LocalWalletPermissionsManager2(options.wallet, CLI_ADMIN_ORIGINATOR, { ...options.managerConfig }, { store: new FilePermissionStore(options.storePath) });
4149
+ bindPermissionDenials(manager, {
4150
+ grantCommandPrefix: options.grantCommandPrefix,
4151
+ log
4152
+ });
4153
+ const server = await startBRC100Server({
4154
+ host: options.host,
4155
+ port: options.port,
4156
+ adminOriginator: CLI_ADMIN_ORIGINATOR,
4157
+ wallet: walletApiHandle(manager),
4158
+ onEvent: (event) => {
4159
+ if (event.event !== "brc100_call")
4160
+ return;
4161
+ const line = `[wallet-api] ${event.method} from ${event.origin || "(no origin)"} -> ${event.status}`;
4162
+ log(event.error ? `${line}: ${event.error}` : line);
4163
+ }
4164
+ });
4165
+ return {
4166
+ server,
4167
+ close: () => server.close()
4168
+ };
4169
+ }
4170
+ var init_endpoint = __esm(() => {
4171
+ init_admin();
4172
+ init_deny();
4173
+ init_permission_store();
4174
+ });
4175
+
3536
4176
  // src/commands/serve.ts
3537
- import { join as join5 } from "node:path";
4177
+ import { join as join6 } from "node:path";
3538
4178
  import {
3539
4179
  createNodeWallet as createNodeWallet3
3540
4180
  } from "@1sat/wallet-node";
@@ -3543,8 +4183,7 @@ import {
3543
4183
  KnexHandleCertStore,
3544
4184
  KnexPendingStore,
3545
4185
  createHostServer,
3546
- createWalletServer,
3547
- startBRC100Server
4186
+ createWalletServer
3548
4187
  } from "@1sat/wallet-server";
3549
4188
  import { initLogger as initLogger2 } from "evlog";
3550
4189
  import knexLib from "knex";
@@ -3620,7 +4259,7 @@ async function resolveServe(opts) {
3620
4259
  };
3621
4260
  }
3622
4261
  function deriveSqliteFilename(dataDir, chain) {
3623
- return join5(dataDir, `wallet-${chain}.db`);
4262
+ return join6(dataDir, `wallet-${chain}.db`);
3624
4263
  }
3625
4264
  function resolveMonitorEnabled(configured) {
3626
4265
  const fromEnv = process.env.ONESAT_MONITOR;
@@ -3674,7 +4313,7 @@ async function runWithStorage(resolved, mode) {
3674
4313
  skipInitialMonitor: true
3675
4314
  });
3676
4315
  if (mode === "wallet-api") {
3677
- const dapp = await startDappEndpoint(walletResult);
4316
+ const dapp = await startDappEndpoint(walletResult, resolved);
3678
4317
  return {
3679
4318
  stop: async () => {
3680
4319
  await dapp.close();
@@ -3745,7 +4384,7 @@ async function startWalletServer(resolved, walletResult, accounts, mode) {
3745
4384
  ttlSeconds: sessionStoreCfg.ttlSeconds
3746
4385
  } : undefined;
3747
4386
  if (mode === "wallet") {
3748
- const handle = createWalletServer({
4387
+ const handle2 = createWalletServer({
3749
4388
  wallet: walletResult.wallet,
3750
4389
  storage: walletResult.getActiveStorage(),
3751
4390
  serverIdentityKey: walletResult.wallet.identityKey,
@@ -3755,10 +4394,10 @@ async function startWalletServer(resolved, walletResult, accounts, mode) {
3755
4394
  accounts: accounts?.walletServerAccounts,
3756
4395
  sessionStore
3757
4396
  });
3758
- const port = await handle.start();
3759
- const notes = [resolved.accounts.enabled ? "accounts: on" : ""].filter(Boolean).join(", ");
3760
- console.log(`[wallet] listening on ${resolved.host}:${port}${notes ? ` (${notes})` : ""}`);
3761
- return { stop: () => handle.stop() };
4397
+ const port2 = await handle2.start();
4398
+ const notes2 = [resolved.accounts.enabled ? "accounts: on" : ""].filter(Boolean).join(", ");
4399
+ console.log(`[wallet] listening on ${resolved.host}:${port2}${notes2 ? ` (${notes2})` : ""}`);
4400
+ return { stop: () => handle2.stop() };
3762
4401
  }
3763
4402
  const serverCfg = config.server ?? {};
3764
4403
  const paymailCfg = serverCfg.paymail;
@@ -3812,7 +4451,7 @@ async function startWalletServer(resolved, walletResult, accounts, mode) {
3812
4451
  }
3813
4452
  function createMessageboxKnex(storage, messagebox, resolved) {
3814
4453
  if (storage.provider === "bun-sqlite") {
3815
- const filename = messagebox?.dbPath ?? join5(resolved.dataDir, `messagebox-${resolved.chain}.db`);
4454
+ const filename = messagebox?.dbPath ?? join6(resolved.dataDir, `messagebox-${resolved.chain}.db`);
3816
4455
  return knexLib({
3817
4456
  client: "sqlite3",
3818
4457
  connection: { filename },
@@ -3870,50 +4509,24 @@ function waitForShutdown() {
3870
4509
  process.once("SIGTERM", () => handler("SIGTERM"));
3871
4510
  });
3872
4511
  }
3873
- function walletApiHandle(wallet) {
3874
- return {
3875
- async call(method, args, origin) {
3876
- const iface = wallet;
3877
- const fn = iface[method];
3878
- if (typeof fn !== "function") {
3879
- throw new Error(`Method not available: ${method}`);
3880
- }
3881
- return await fn.call(iface, args, origin);
3882
- }
3883
- };
3884
- }
3885
- async function startDappEndpoint(walletResult) {
4512
+ async function startDappEndpoint(walletResult, resolved) {
3886
4513
  const config = loadConfig();
3887
4514
  const dapp = config.server?.dapp ?? {};
3888
4515
  const host = dapp.host ?? DEFAULT_DAPP_HOST;
3889
4516
  const port = dapp.port ?? (process.env.ONESAT_DAPP_PORT ? Number(process.env.ONESAT_DAPP_PORT) : DEFAULT_DAPP_PORT);
3890
- const approve = dapp.approve === true;
3891
- const handle = await startBRC100Server({
4517
+ const storePath = permissionStorePath(resolved.dataDir, resolved.chain);
4518
+ const grantCommandPrefix = resolved.chain === "test" ? "1sat --chain test permissions grant" : GRANT_COMMAND;
4519
+ const api = await startWalletApi({
4520
+ wallet: walletResult.wallet,
4521
+ storePath,
3892
4522
  host,
3893
4523
  port,
3894
- wallet: walletApiHandle(walletResult.wallet),
3895
- approvalPolicy: approve ? async ({ method, origin, args }) => {
3896
- const { confirm, isCancel } = await import("@clack/prompts");
3897
- console.log(`
3898
- [brc-100] ${method} requested by ${origin}`);
3899
- console.log(` ${JSON.stringify(args).slice(0, 400)}`);
3900
- const ok = await confirm({
3901
- message: `Approve ${method}?`
3902
- });
3903
- if (isCancel(ok) || !ok) {
3904
- throw new Error("User denied permission");
3905
- }
3906
- } : undefined,
3907
- onEvent: (event) => {
3908
- if (event.event === "brc100_sensitive") {
3909
- const trustNote = event.trusted ? " (trusted)" : approve ? "" : " (auto-approved)";
3910
- console.log(`[brc-100] sensitive ${event.method} from ${event.origin}${trustNote}`);
3911
- }
3912
- }
4524
+ grantCommandPrefix
3913
4525
  });
3914
4526
  console.log(`[wallet-api] BRC-100 app endpoint on http://${host}:${port}`);
3915
- console.log(approve ? "[wallet-api] sensitive methods require TTY confirmation" : "[wallet-api] sensitive methods are AUTO-APPROVED (headless). " + "Set server.dapp.approve=true to require confirmation.");
3916
- return { close: async () => await handle.close() };
4527
+ console.log(`[wallet-api] permission grants: ${storePath}`);
4528
+ console.log("[wallet-api] ungranted requests are denied; the error names the `1sat permissions grant` command that allows them");
4529
+ return { close: () => api.close() };
3917
4530
  }
3918
4531
  var DEFAULT_HOST = "127.0.0.1", DEFAULT_PORT = 8100, DEFAULT_STACK_URL = "https://api.1sat.app", DEFAULT_BASELINE_BYTES, DEFAULT_PURCHASE_UNIT_BYTES = 1073741824, DEFAULT_SATS_PER_UNIT = 1e6, DEFAULT_DURATION_BLOCKS = 4383, DEFAULT_STORAGE_IDENTITY_KEY = "1sat-cli-default", DEFAULT_PG_SCHEMA = "messagebox", DEFAULT_DAPP_HOST = "127.0.0.1", DEFAULT_DAPP_PORT = 3321;
3919
4532
  var init_serve = __esm(() => {
@@ -3924,6 +4537,9 @@ var init_serve = __esm(() => {
3924
4537
  init_monitor_lock();
3925
4538
  init_output();
3926
4539
  init_repricer();
4540
+ init_endpoint();
4541
+ init_grants();
4542
+ init_permission_store();
3927
4543
  DEFAULT_BASELINE_BYTES = 1024 * 1024 * 1024;
3928
4544
  });
3929
4545
 
@@ -3978,7 +4594,7 @@ var init_social = __esm(() => {
3978
4594
  });
3979
4595
 
3980
4596
  // src/commands/storage.ts
3981
- import { join as join6 } from "node:path";
4597
+ import { join as join7 } from "node:path";
3982
4598
  import { StorageBunSqlite, nominateInvalidReqs } from "@1sat/wallet-node";
3983
4599
  import { StorageProvider } from "@bsv/wallet-toolbox";
3984
4600
  async function handleStorageCommand(args, opts) {
@@ -4048,7 +4664,7 @@ async function openServeStorage(opts) {
4048
4664
  } else {
4049
4665
  storage = new StorageBunSqlite({
4050
4666
  ...baseOptions,
4051
- filename: join6(ensureDataDir(), `wallet-${chain}.db`)
4667
+ filename: join7(ensureDataDir(), `wallet-${chain}.db`)
4052
4668
  });
4053
4669
  }
4054
4670
  await storage.makeAvailable();
@@ -4081,11 +4697,11 @@ function selectedSweepClasses(only, skip) {
4081
4697
  throw new Error("--only and --skip are mutually exclusive");
4082
4698
  }
4083
4699
  if (only) {
4084
- const selected = parseSweepClasses(only, "--only");
4085
- if (selected.size === 0) {
4700
+ const selected2 = parseSweepClasses(only, "--only");
4701
+ if (selected2.size === 0) {
4086
4702
  throw new Error("--only requires at least one class");
4087
4703
  }
4088
- return selected;
4704
+ return selected2;
4089
4705
  }
4090
4706
  const selected = new Set(SWEEP_CLASSES);
4091
4707
  if (skip) {
@@ -5188,8 +5804,8 @@ async function walletListOutputs(args, opts) {
5188
5804
  ${result.totalOutputs} total outputs in basket '${basket}':
5189
5805
  `);
5190
5806
  for (const out of result.outputs) {
5191
- const tags = out.tags?.join(", ") || "none";
5192
- console.log(` ${out.outpoint} | ${out.satoshis} sats | tags: ${tags}`);
5807
+ const tags2 = out.tags?.join(", ") || "none";
5808
+ console.log(` ${out.outpoint} | ${out.satoshis} sats | tags: ${tags2}`);
5193
5809
  }
5194
5810
  console.log();
5195
5811
  }
@@ -5447,13 +6063,14 @@ var init_wallet = __esm(() => {
5447
6063
  });
5448
6064
 
5449
6065
  // src/main.ts
5450
- import { existsSync as existsSync4 } from "node:fs";
6066
+ var exports_main = {};
6067
+ import { existsSync as existsSync5 } from "node:fs";
5451
6068
  import { resolve } from "node:path";
5452
6069
  import { config as loadEnv } from "dotenv";
5453
6070
  function applyEnvFiles(paths) {
5454
6071
  for (const p of paths) {
5455
6072
  const abs = resolve(p);
5456
- if (!existsSync4(abs)) {
6073
+ if (!existsSync5(abs)) {
5457
6074
  throw new Error(`Env file not found: ${p}`);
5458
6075
  }
5459
6076
  const result = loadEnv({ path: abs, quiet: true, override: true });
@@ -5537,6 +6154,9 @@ async function main() {
5537
6154
  case "serve":
5538
6155
  await handleServeCommand(rest, flags);
5539
6156
  break;
6157
+ case "permissions":
6158
+ await handlePermissionsCommand(rest, flags);
6159
+ break;
5540
6160
  case "storage":
5541
6161
  await handleStorageCommand(rest, flags);
5542
6162
  break;
@@ -5564,6 +6184,7 @@ var init_main = __esm(() => {
5564
6184
  init_messagebox();
5565
6185
  init_opns();
5566
6186
  init_ordinals();
6187
+ init_permissions();
5567
6188
  init_remote();
5568
6189
  init_serve();
5569
6190
  init_social();
@@ -5586,4 +6207,4 @@ var init_main = __esm(() => {
5586
6207
 
5587
6208
  // src/cli.ts
5588
6209
  process.env.DOTENV_CONFIG_QUIET = "true";
5589
- await Promise.resolve().then(() => (init_main(), {}));
6210
+ await Promise.resolve().then(() => (init_main(), exports_main));