@1sat/cli 0.0.120 → 0.0.121
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 +689 -61
- package/package.json +5 -4
- package/src/commands/permissions.ts +269 -0
- package/src/commands/serve.ts +25 -62
- package/src/config.ts +5 -4
- package/src/context.ts +21 -1
- package/src/help.ts +82 -1
- package/src/main.ts +5 -0
- package/src/wallet-api/admin.ts +107 -0
- package/src/wallet-api/deny.ts +194 -0
- package/src/wallet-api/endpoint.ts +108 -0
- package/src/wallet-api/grants.ts +238 -0
- package/src/wallet-api/permission-store.ts +106 -0
package/dist/cli.js
CHANGED
|
@@ -386,6 +386,120 @@ var init_monitor_once = __esm(() => {
|
|
|
386
386
|
init_monitor_lock();
|
|
387
387
|
});
|
|
388
388
|
|
|
389
|
+
// src/wallet-api/admin.ts
|
|
390
|
+
import { PERMISSION_SCHEME_IDS } from "@1sat/types";
|
|
391
|
+
import {
|
|
392
|
+
LocalWalletPermissionsManager
|
|
393
|
+
} from "@1sat/wallet";
|
|
394
|
+
function adminWallet(wallet, store) {
|
|
395
|
+
const manager = new LocalWalletPermissionsManager(wallet, CLI_ADMIN_ORIGINATOR, { permissionModules: adminPermissionModules }, { store });
|
|
396
|
+
return withAdminOriginator(manager);
|
|
397
|
+
}
|
|
398
|
+
function withAdminOriginator(manager) {
|
|
399
|
+
return new Proxy(manager, {
|
|
400
|
+
get(target, property, receiver) {
|
|
401
|
+
const value = Reflect.get(target, property, receiver);
|
|
402
|
+
if (typeof value !== "function")
|
|
403
|
+
return value;
|
|
404
|
+
return (args) => value.call(target, args, CLI_ADMIN_ORIGINATOR);
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
var CLI_ADMIN_ORIGINATOR = "1sat-cli.internal", adminPermissionModules;
|
|
409
|
+
var init_admin = __esm(() => {
|
|
410
|
+
adminPermissionModules = Object.fromEntries(PERMISSION_SCHEME_IDS.map((scheme) => [
|
|
411
|
+
scheme,
|
|
412
|
+
{
|
|
413
|
+
onRequest: async (req) => ({ args: req.args }),
|
|
414
|
+
onResponse: async (res) => res
|
|
415
|
+
}
|
|
416
|
+
]));
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
// src/wallet-api/permission-store.ts
|
|
420
|
+
import {
|
|
421
|
+
existsSync as existsSync3,
|
|
422
|
+
mkdirSync as mkdirSync2,
|
|
423
|
+
readFileSync as readFileSync4,
|
|
424
|
+
renameSync,
|
|
425
|
+
writeFileSync as writeFileSync4
|
|
426
|
+
} from "node:fs";
|
|
427
|
+
import { dirname, join as join5 } from "node:path";
|
|
428
|
+
import {
|
|
429
|
+
permissionKeyToString
|
|
430
|
+
} from "@1sat/wallet";
|
|
431
|
+
function permissionStorePath(dataDir, chain) {
|
|
432
|
+
return join5(dataDir, `permissions-${chain}.json`);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
class FilePermissionStore {
|
|
436
|
+
path;
|
|
437
|
+
constructor(path) {
|
|
438
|
+
this.path = path;
|
|
439
|
+
}
|
|
440
|
+
async findGrant(key) {
|
|
441
|
+
return this.load().grants[permissionKeyToString(key)] ?? null;
|
|
442
|
+
}
|
|
443
|
+
async putGrant(grant) {
|
|
444
|
+
const file = this.load();
|
|
445
|
+
file.grants[permissionKeyToString(grant.key)] = grant;
|
|
446
|
+
this.save(file);
|
|
447
|
+
}
|
|
448
|
+
async deleteGrant(key) {
|
|
449
|
+
const file = this.load();
|
|
450
|
+
const id = permissionKeyToString(key);
|
|
451
|
+
if (!(id in file.grants))
|
|
452
|
+
return;
|
|
453
|
+
delete file.grants[id];
|
|
454
|
+
this.save(file);
|
|
455
|
+
}
|
|
456
|
+
async deleteAllForOriginator(originator) {
|
|
457
|
+
const file = this.load();
|
|
458
|
+
let removed = 0;
|
|
459
|
+
for (const [id, grant] of Object.entries(file.grants)) {
|
|
460
|
+
if (grant.key.originator === originator) {
|
|
461
|
+
delete file.grants[id];
|
|
462
|
+
removed++;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
if (removed > 0)
|
|
466
|
+
this.save(file);
|
|
467
|
+
return removed;
|
|
468
|
+
}
|
|
469
|
+
async listGrants(filter) {
|
|
470
|
+
const out = [];
|
|
471
|
+
for (const grant of Object.values(this.load().grants)) {
|
|
472
|
+
if (filter?.originator && grant.key.originator !== filter.originator)
|
|
473
|
+
continue;
|
|
474
|
+
if (filter?.type && grant.key.type !== filter.type)
|
|
475
|
+
continue;
|
|
476
|
+
out.push(grant);
|
|
477
|
+
}
|
|
478
|
+
return out;
|
|
479
|
+
}
|
|
480
|
+
load() {
|
|
481
|
+
if (!existsSync3(this.path))
|
|
482
|
+
return { version: FILE_VERSION, grants: {} };
|
|
483
|
+
const raw = readFileSync4(this.path, "utf8");
|
|
484
|
+
const parsed = JSON.parse(raw);
|
|
485
|
+
if (parsed.version !== FILE_VERSION) {
|
|
486
|
+
throw new Error(`Unsupported permission store version ${String(parsed.version)} in ${this.path}`);
|
|
487
|
+
}
|
|
488
|
+
return { version: FILE_VERSION, grants: parsed.grants ?? {} };
|
|
489
|
+
}
|
|
490
|
+
save(file) {
|
|
491
|
+
const dir = dirname(this.path);
|
|
492
|
+
if (!existsSync3(dir))
|
|
493
|
+
mkdirSync2(dir, { recursive: true, mode: 448 });
|
|
494
|
+
const tmp = `${this.path}.${process.pid}.tmp`;
|
|
495
|
+
writeFileSync4(tmp, `${JSON.stringify(file, null, 2)}
|
|
496
|
+
`, { mode: 384 });
|
|
497
|
+
renameSync(tmp, this.path);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
var FILE_VERSION = 1;
|
|
501
|
+
var init_permission_store = () => {};
|
|
502
|
+
|
|
389
503
|
// src/context.ts
|
|
390
504
|
import { createContext } from "@1sat/actions";
|
|
391
505
|
import { createNodeWallet as createNodeWallet2 } from "@1sat/wallet-node";
|
|
@@ -405,7 +519,8 @@ async function loadContext(privateKey, opts) {
|
|
|
405
519
|
backups: config.backups,
|
|
406
520
|
skipInitialMonitor: true
|
|
407
521
|
});
|
|
408
|
-
const
|
|
522
|
+
const wallet = adminWallet(walletResult.wallet, new FilePermissionStore(permissionStorePath(dataDir, opts.chain)));
|
|
523
|
+
const ctx = createContext(wallet, {
|
|
409
524
|
services: walletResult.services,
|
|
410
525
|
chain: opts.chain,
|
|
411
526
|
dataDir,
|
|
@@ -428,6 +543,8 @@ async function loadContext(privateKey, opts) {
|
|
|
428
543
|
var init_context = __esm(() => {
|
|
429
544
|
init_config();
|
|
430
545
|
init_monitor_once();
|
|
546
|
+
init_admin();
|
|
547
|
+
init_permission_store();
|
|
431
548
|
});
|
|
432
549
|
|
|
433
550
|
// src/output.ts
|
|
@@ -563,7 +680,7 @@ var init_action = __esm(() => {
|
|
|
563
680
|
});
|
|
564
681
|
|
|
565
682
|
// src/help.ts
|
|
566
|
-
import { readFileSync as
|
|
683
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
567
684
|
import chalk2 from "chalk";
|
|
568
685
|
function getVersion() {
|
|
569
686
|
return VERSION;
|
|
@@ -717,7 +834,7 @@ var VERSION, GLOBAL_OPTIONS, ENV_VARS, COMMANDS;
|
|
|
717
834
|
var init_help = __esm(() => {
|
|
718
835
|
VERSION = (() => {
|
|
719
836
|
try {
|
|
720
|
-
const pkg = JSON.parse(
|
|
837
|
+
const pkg = JSON.parse(readFileSync5(new URL("../package.json", import.meta.url), "utf8"));
|
|
721
838
|
return pkg.version || "0.0.11";
|
|
722
839
|
} catch {
|
|
723
840
|
return "0.0.11";
|
|
@@ -1390,7 +1507,82 @@ var init_help = __esm(() => {
|
|
|
1390
1507
|
{ name: "monitor", description: "Monitor daemon only" },
|
|
1391
1508
|
{
|
|
1392
1509
|
name: "wallet-api",
|
|
1393
|
-
description: "App-facing BRC-100 wallet endpoint only (headless, default port 3321)"
|
|
1510
|
+
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"
|
|
1511
|
+
}
|
|
1512
|
+
]
|
|
1513
|
+
},
|
|
1514
|
+
{
|
|
1515
|
+
group: "Server",
|
|
1516
|
+
name: "permissions",
|
|
1517
|
+
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.",
|
|
1518
|
+
subcommands: [
|
|
1519
|
+
{
|
|
1520
|
+
name: "list",
|
|
1521
|
+
description: "Show grants, grouped by app origin",
|
|
1522
|
+
positional: "[<origin>]"
|
|
1523
|
+
},
|
|
1524
|
+
{
|
|
1525
|
+
name: "grant",
|
|
1526
|
+
description: "Allow an app one or more permissions. Selectors may be combined in one call.",
|
|
1527
|
+
positional: "<origin>",
|
|
1528
|
+
args: [
|
|
1529
|
+
{
|
|
1530
|
+
flag: "--protocol",
|
|
1531
|
+
values: "<name>",
|
|
1532
|
+
description: "Protocol name; requires --level"
|
|
1533
|
+
},
|
|
1534
|
+
{
|
|
1535
|
+
flag: "--level",
|
|
1536
|
+
values: "<0|1|2>",
|
|
1537
|
+
description: "Protocol security level"
|
|
1538
|
+
},
|
|
1539
|
+
{
|
|
1540
|
+
flag: "--counterparty",
|
|
1541
|
+
values: "<hex|self|anyone>",
|
|
1542
|
+
description: "Counterparty for a level 0/2 protocol (default self; ignored at level 1), or a certificate's verifier (also --verifier)"
|
|
1543
|
+
},
|
|
1544
|
+
{
|
|
1545
|
+
flag: "--privileged",
|
|
1546
|
+
description: "Grant the privileged variant of a protocol/certificate"
|
|
1547
|
+
},
|
|
1548
|
+
{ flag: "--basket", values: "<name>", description: "Basket access" },
|
|
1549
|
+
{
|
|
1550
|
+
flag: "--label",
|
|
1551
|
+
values: "<name>",
|
|
1552
|
+
description: "Action label (a level-1 `action label <name>` protocol)"
|
|
1553
|
+
},
|
|
1554
|
+
{
|
|
1555
|
+
flag: "--certificate",
|
|
1556
|
+
values: "<type>",
|
|
1557
|
+
description: "Certificate type; requires --fields"
|
|
1558
|
+
},
|
|
1559
|
+
{
|
|
1560
|
+
flag: "--fields",
|
|
1561
|
+
values: "<a,b>",
|
|
1562
|
+
description: "Certificate fields that may be disclosed"
|
|
1563
|
+
},
|
|
1564
|
+
{
|
|
1565
|
+
flag: "--spending",
|
|
1566
|
+
values: "<satoshis>",
|
|
1567
|
+
description: "Monthly spending cap; month-to-date spend is read from action history on every check"
|
|
1568
|
+
}
|
|
1569
|
+
]
|
|
1570
|
+
},
|
|
1571
|
+
{
|
|
1572
|
+
name: "revoke",
|
|
1573
|
+
description: "Remove matching grants, or every grant for an origin",
|
|
1574
|
+
positional: "<origin>",
|
|
1575
|
+
args: [
|
|
1576
|
+
{
|
|
1577
|
+
flag: "--all",
|
|
1578
|
+
description: "Remove every grant for the origin"
|
|
1579
|
+
},
|
|
1580
|
+
{
|
|
1581
|
+
flag: "--protocol",
|
|
1582
|
+
values: "<name>",
|
|
1583
|
+
description: "Same selectors as `grant`"
|
|
1584
|
+
}
|
|
1585
|
+
]
|
|
1394
1586
|
}
|
|
1395
1587
|
]
|
|
1396
1588
|
},
|
|
@@ -1573,7 +1765,7 @@ var init_authfetch_request = __esm(() => {
|
|
|
1573
1765
|
});
|
|
1574
1766
|
|
|
1575
1767
|
// src/commands/authfetch.ts
|
|
1576
|
-
import { readFileSync as
|
|
1768
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
1577
1769
|
import { confirm, isCancel as isCancel2 } from "@clack/prompts";
|
|
1578
1770
|
async function handleAuthfetchCommand(args, opts) {
|
|
1579
1771
|
if (!args[0] || args[0] === "help" || args.includes("--help") || args.includes("-h")) {
|
|
@@ -1632,7 +1824,7 @@ Unknown or missing method: ${method ?? ""}`);
|
|
|
1632
1824
|
}
|
|
1633
1825
|
let body;
|
|
1634
1826
|
if (bodyRaw !== undefined) {
|
|
1635
|
-
body = bodyRaw.startsWith("@") ?
|
|
1827
|
+
body = bodyRaw.startsWith("@") ? readFileSync6(bodyRaw.slice(1), "utf8") : bodyRaw;
|
|
1636
1828
|
if (!headers["Content-Type"] && !headers["content-type"]) {
|
|
1637
1829
|
headers["Content-Type"] = "application/json";
|
|
1638
1830
|
}
|
|
@@ -2077,7 +2269,7 @@ var init_init = __esm(() => {
|
|
|
2077
2269
|
});
|
|
2078
2270
|
|
|
2079
2271
|
// src/beef.ts
|
|
2080
|
-
import { readFileSync as
|
|
2272
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
2081
2273
|
import { Utils } from "@bsv/sdk";
|
|
2082
2274
|
function parseBeefFlag(value) {
|
|
2083
2275
|
if (!value)
|
|
@@ -2086,7 +2278,7 @@ function parseBeefFlag(value) {
|
|
|
2086
2278
|
if (!value.includes(`
|
|
2087
2279
|
`) && !value.includes(" ") && value.length < 4096) {
|
|
2088
2280
|
try {
|
|
2089
|
-
const buf =
|
|
2281
|
+
const buf = readFileSync7(value);
|
|
2090
2282
|
return Array.from(buf);
|
|
2091
2283
|
} catch {}
|
|
2092
2284
|
}
|
|
@@ -2227,7 +2419,7 @@ var init_locks = __esm(() => {
|
|
|
2227
2419
|
});
|
|
2228
2420
|
|
|
2229
2421
|
// src/commands/mcp-proxy.ts
|
|
2230
|
-
import { existsSync as
|
|
2422
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
2231
2423
|
import { Hash, KeyDeriver, PrivateKey as PrivateKey3, Signature, Utils as Utils2 } from "@bsv/sdk";
|
|
2232
2424
|
import { createLogger, initLogger } from "evlog";
|
|
2233
2425
|
function generateNonce() {
|
|
@@ -2236,12 +2428,12 @@ function generateNonce() {
|
|
|
2236
2428
|
return toBase64(Array.from(bytes));
|
|
2237
2429
|
}
|
|
2238
2430
|
function getClientKey() {
|
|
2239
|
-
|
|
2240
|
-
if (
|
|
2241
|
-
return PrivateKey3.fromWif(
|
|
2431
|
+
mkdirSync3(KEY_DIR, { recursive: true });
|
|
2432
|
+
if (existsSync4(CLIENT_KEY_PATH)) {
|
|
2433
|
+
return PrivateKey3.fromWif(readFileSync8(CLIENT_KEY_PATH, "utf-8").trim());
|
|
2242
2434
|
}
|
|
2243
2435
|
const key = PrivateKey3.fromRandom();
|
|
2244
|
-
|
|
2436
|
+
writeFileSync5(CLIENT_KEY_PATH, key.toWif(), { mode: 384 });
|
|
2245
2437
|
const log = createLogger({ context: "startup" });
|
|
2246
2438
|
log.set({ event: "key_generated", path: CLIENT_KEY_PATH });
|
|
2247
2439
|
log.emit();
|
|
@@ -2739,7 +2931,7 @@ var init_opns = __esm(() => {
|
|
|
2739
2931
|
});
|
|
2740
2932
|
|
|
2741
2933
|
// src/commands/ordinals.ts
|
|
2742
|
-
import { readFileSync as
|
|
2934
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
2743
2935
|
import { basename, extname } from "node:path";
|
|
2744
2936
|
import {
|
|
2745
2937
|
burnOrdinals,
|
|
@@ -2857,7 +3049,7 @@ async function ordinalsInscribe(args, opts) {
|
|
|
2857
3049
|
}
|
|
2858
3050
|
let fileBytes;
|
|
2859
3051
|
try {
|
|
2860
|
-
fileBytes =
|
|
3052
|
+
fileBytes = readFileSync9(file);
|
|
2861
3053
|
} catch (err) {
|
|
2862
3054
|
fatal(`Failed to read file: ${err.message}`);
|
|
2863
3055
|
}
|
|
@@ -3045,6 +3237,335 @@ var init_ordinals = __esm(() => {
|
|
|
3045
3237
|
};
|
|
3046
3238
|
});
|
|
3047
3239
|
|
|
3240
|
+
// src/wallet-api/grants.ts
|
|
3241
|
+
import {
|
|
3242
|
+
normalizeOriginator,
|
|
3243
|
+
permissionKeyToString as permissionKeyToString2
|
|
3244
|
+
} from "@1sat/wallet";
|
|
3245
|
+
function shellQuote(value) {
|
|
3246
|
+
if (value !== "" && /^[A-Za-z0-9_@%+=:,./-]+$/.test(value))
|
|
3247
|
+
return value;
|
|
3248
|
+
return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
3249
|
+
}
|
|
3250
|
+
function grantCommand(spec, prefix = GRANT_COMMAND) {
|
|
3251
|
+
const key = spec.key;
|
|
3252
|
+
const parts = [prefix, shellQuote(key.originator)];
|
|
3253
|
+
switch (key.type) {
|
|
3254
|
+
case "protocol": {
|
|
3255
|
+
if (key.protocolLevel === 1 && key.protocolName.startsWith(LABEL_PROTOCOL_PREFIX)) {
|
|
3256
|
+
parts.push("--label", shellQuote(key.protocolName.slice(LABEL_PROTOCOL_PREFIX.length)));
|
|
3257
|
+
break;
|
|
3258
|
+
}
|
|
3259
|
+
parts.push("--protocol", shellQuote(key.protocolName));
|
|
3260
|
+
parts.push("--level", String(key.protocolLevel));
|
|
3261
|
+
if (key.protocolLevel !== 1 && key.counterparty) {
|
|
3262
|
+
parts.push("--counterparty", shellQuote(key.counterparty));
|
|
3263
|
+
}
|
|
3264
|
+
if (key.privileged)
|
|
3265
|
+
parts.push("--privileged");
|
|
3266
|
+
break;
|
|
3267
|
+
}
|
|
3268
|
+
case "basket":
|
|
3269
|
+
parts.push("--basket", shellQuote(key.basket));
|
|
3270
|
+
break;
|
|
3271
|
+
case "certificate":
|
|
3272
|
+
parts.push("--certificate", shellQuote(key.certType));
|
|
3273
|
+
parts.push("--fields", shellQuote(key.fields.join(",")));
|
|
3274
|
+
if (key.verifier) {
|
|
3275
|
+
parts.push("--counterparty", shellQuote(key.verifier));
|
|
3276
|
+
}
|
|
3277
|
+
if (key.privileged)
|
|
3278
|
+
parts.push("--privileged");
|
|
3279
|
+
break;
|
|
3280
|
+
case "spending":
|
|
3281
|
+
parts.push("--spending", String(spec.authorizedAmount ?? 0));
|
|
3282
|
+
break;
|
|
3283
|
+
}
|
|
3284
|
+
return parts.join(" ");
|
|
3285
|
+
}
|
|
3286
|
+
function describeKey(spec) {
|
|
3287
|
+
const key = spec.key;
|
|
3288
|
+
switch (key.type) {
|
|
3289
|
+
case "protocol": {
|
|
3290
|
+
if (key.protocolLevel === 1 && key.protocolName.startsWith(LABEL_PROTOCOL_PREFIX)) {
|
|
3291
|
+
return `label ${key.protocolName.slice(LABEL_PROTOCOL_PREFIX.length)}`;
|
|
3292
|
+
}
|
|
3293
|
+
const bits = [`"${key.protocolName}" (level ${key.protocolLevel}`];
|
|
3294
|
+
if (key.protocolLevel !== 1 && key.counterparty) {
|
|
3295
|
+
bits.push(`, counterparty ${key.counterparty}`);
|
|
3296
|
+
}
|
|
3297
|
+
if (key.privileged)
|
|
3298
|
+
bits.push(", privileged");
|
|
3299
|
+
return `protocol ${bits.join("")})`;
|
|
3300
|
+
}
|
|
3301
|
+
case "basket":
|
|
3302
|
+
return `basket "${key.basket}"`;
|
|
3303
|
+
case "certificate":
|
|
3304
|
+
return `cert "${key.certType}" fields [${key.fields.join(", ")}]${key.verifier ? ` to ${key.verifier}` : ""}${key.privileged ? " (privileged)" : ""}`;
|
|
3305
|
+
case "spending":
|
|
3306
|
+
return `spending up to ${spec.authorizedAmount ?? 0} sat per month`;
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
function specsFromFlags(origin, flags) {
|
|
3310
|
+
const originator = normalizeOriginator(origin);
|
|
3311
|
+
if (!originator)
|
|
3312
|
+
throw new Error("An app origin is required");
|
|
3313
|
+
const specs = [];
|
|
3314
|
+
if (flags.protocol !== undefined || flags.label !== undefined) {
|
|
3315
|
+
if (flags.protocol !== undefined && flags.label !== undefined) {
|
|
3316
|
+
throw new Error("Use either --protocol or --label, not both");
|
|
3317
|
+
}
|
|
3318
|
+
const isLabel = flags.label !== undefined;
|
|
3319
|
+
const name = isLabel ? `${LABEL_PROTOCOL_PREFIX}${flags.label}` : flags.protocol;
|
|
3320
|
+
if (!name.trim() || name === LABEL_PROTOCOL_PREFIX) {
|
|
3321
|
+
throw new Error(isLabel ? "--label requires a name" : "--protocol requires a name");
|
|
3322
|
+
}
|
|
3323
|
+
const level = isLabel ? 1 : parseLevel(flags.level);
|
|
3324
|
+
specs.push({
|
|
3325
|
+
key: {
|
|
3326
|
+
type: "protocol",
|
|
3327
|
+
originator,
|
|
3328
|
+
privileged: flags.privileged,
|
|
3329
|
+
protocolLevel: level,
|
|
3330
|
+
protocolName: name,
|
|
3331
|
+
counterparty: level === 1 ? "" : flags.counterparty ?? "self"
|
|
3332
|
+
}
|
|
3333
|
+
});
|
|
3334
|
+
}
|
|
3335
|
+
if (flags.basket !== undefined) {
|
|
3336
|
+
if (!flags.basket.trim())
|
|
3337
|
+
throw new Error("--basket requires a name");
|
|
3338
|
+
specs.push({
|
|
3339
|
+
key: { type: "basket", originator, basket: flags.basket }
|
|
3340
|
+
});
|
|
3341
|
+
}
|
|
3342
|
+
if (flags.certificate !== undefined) {
|
|
3343
|
+
if (!flags.certificate.trim()) {
|
|
3344
|
+
throw new Error("--certificate requires a certificate type");
|
|
3345
|
+
}
|
|
3346
|
+
const fields = (flags.fields ?? "").split(",").map((f) => f.trim()).filter((f) => f.length > 0);
|
|
3347
|
+
if (fields.length === 0) {
|
|
3348
|
+
throw new Error("--certificate requires --fields <a,b>");
|
|
3349
|
+
}
|
|
3350
|
+
specs.push({
|
|
3351
|
+
key: {
|
|
3352
|
+
type: "certificate",
|
|
3353
|
+
originator,
|
|
3354
|
+
privileged: flags.privileged,
|
|
3355
|
+
verifier: flags.counterparty ?? "",
|
|
3356
|
+
certType: flags.certificate,
|
|
3357
|
+
fields: fields.sort()
|
|
3358
|
+
}
|
|
3359
|
+
});
|
|
3360
|
+
}
|
|
3361
|
+
if (flags.spending !== undefined) {
|
|
3362
|
+
const satoshis = Number(flags.spending);
|
|
3363
|
+
if (!Number.isInteger(satoshis) || satoshis <= 0) {
|
|
3364
|
+
throw new Error("--spending requires a positive whole number of satoshis");
|
|
3365
|
+
}
|
|
3366
|
+
specs.push({
|
|
3367
|
+
key: { type: "spending", originator },
|
|
3368
|
+
authorizedAmount: satoshis
|
|
3369
|
+
});
|
|
3370
|
+
}
|
|
3371
|
+
if (specs.length === 0) {
|
|
3372
|
+
throw new Error("Nothing selected. Pass --protocol/--label, --basket, --certificate or --spending");
|
|
3373
|
+
}
|
|
3374
|
+
return specs;
|
|
3375
|
+
}
|
|
3376
|
+
function parseLevel(value) {
|
|
3377
|
+
if (value === undefined) {
|
|
3378
|
+
throw new Error("--protocol requires --level <0|1|2>");
|
|
3379
|
+
}
|
|
3380
|
+
if (value !== "0" && value !== "1" && value !== "2") {
|
|
3381
|
+
throw new Error(`--level must be 0, 1 or 2 (got ${value})`);
|
|
3382
|
+
}
|
|
3383
|
+
return Number(value);
|
|
3384
|
+
}
|
|
3385
|
+
function specId(spec) {
|
|
3386
|
+
return permissionKeyToString2(spec.key);
|
|
3387
|
+
}
|
|
3388
|
+
var LABEL_PROTOCOL_PREFIX = "action label ", GRANT_COMMAND = "1sat permissions grant";
|
|
3389
|
+
var init_grants = () => {};
|
|
3390
|
+
|
|
3391
|
+
// src/commands/permissions.ts
|
|
3392
|
+
import { normalizeOriginator as normalizeOriginator2 } from "@1sat/wallet";
|
|
3393
|
+
async function handlePermissionsCommand(args, opts) {
|
|
3394
|
+
const [subcommand, ...rest] = args;
|
|
3395
|
+
switch (subcommand) {
|
|
3396
|
+
case "list":
|
|
3397
|
+
case "ls":
|
|
3398
|
+
return permissionsList(rest, opts);
|
|
3399
|
+
case "grant":
|
|
3400
|
+
return permissionsGrant(rest, opts);
|
|
3401
|
+
case "revoke":
|
|
3402
|
+
return permissionsRevoke(rest, opts);
|
|
3403
|
+
default:
|
|
3404
|
+
printCommandHelp("permissions", opts.json);
|
|
3405
|
+
if (subcommand && subcommand !== "help") {
|
|
3406
|
+
process.exit(1);
|
|
3407
|
+
}
|
|
3408
|
+
}
|
|
3409
|
+
}
|
|
3410
|
+
function openStore(opts) {
|
|
3411
|
+
const path = permissionStorePath(ensureDataDir(), opts.chain);
|
|
3412
|
+
return { store: new FilePermissionStore(path), path };
|
|
3413
|
+
}
|
|
3414
|
+
function commandPrefix(opts) {
|
|
3415
|
+
return opts.chain === "test" ? "1sat --chain test permissions grant" : "1sat permissions grant";
|
|
3416
|
+
}
|
|
3417
|
+
function grantFlags(args) {
|
|
3418
|
+
return {
|
|
3419
|
+
protocol: extractFlag(args, "--protocol"),
|
|
3420
|
+
level: extractFlag(args, "--level"),
|
|
3421
|
+
counterparty: extractFlag(args, "--counterparty") ?? extractFlag(args, "--verifier"),
|
|
3422
|
+
privileged: hasFlag(args, "--privileged"),
|
|
3423
|
+
basket: extractFlag(args, "--basket"),
|
|
3424
|
+
label: extractFlag(args, "--label"),
|
|
3425
|
+
certificate: extractFlag(args, "--certificate"),
|
|
3426
|
+
fields: extractFlag(args, "--fields"),
|
|
3427
|
+
spending: extractFlag(args, "--spending")
|
|
3428
|
+
};
|
|
3429
|
+
}
|
|
3430
|
+
function firstPositional(args) {
|
|
3431
|
+
for (let i = 0;i < args.length; i++) {
|
|
3432
|
+
const arg = args[i];
|
|
3433
|
+
if (arg.startsWith("--")) {
|
|
3434
|
+
if (!BOOLEAN_FLAGS.has(arg))
|
|
3435
|
+
i++;
|
|
3436
|
+
continue;
|
|
3437
|
+
}
|
|
3438
|
+
return arg;
|
|
3439
|
+
}
|
|
3440
|
+
return;
|
|
3441
|
+
}
|
|
3442
|
+
function toSpec(grant) {
|
|
3443
|
+
return grant.authorizedAmount != null ? { key: grant.key, authorizedAmount: grant.authorizedAmount } : { key: grant.key };
|
|
3444
|
+
}
|
|
3445
|
+
async function permissionsList(args, opts) {
|
|
3446
|
+
const { store, path } = openStore(opts);
|
|
3447
|
+
const origin = firstPositional(args);
|
|
3448
|
+
const filter = origin ? { originator: normalizeOriginator2(origin) } : {};
|
|
3449
|
+
const grants = await store.listGrants(filter);
|
|
3450
|
+
if (opts.json) {
|
|
3451
|
+
output(grants, opts);
|
|
3452
|
+
return;
|
|
3453
|
+
}
|
|
3454
|
+
if (grants.length === 0) {
|
|
3455
|
+
output(origin ? `No grants for ${normalizeOriginator2(origin)} in ${path}` : `No grants in ${path}`, opts);
|
|
3456
|
+
return;
|
|
3457
|
+
}
|
|
3458
|
+
const byOrigin = new Map;
|
|
3459
|
+
for (const grant of grants) {
|
|
3460
|
+
const list = byOrigin.get(grant.key.originator) ?? [];
|
|
3461
|
+
list.push(grant);
|
|
3462
|
+
byOrigin.set(grant.key.originator, list);
|
|
3463
|
+
}
|
|
3464
|
+
for (const name of [...byOrigin.keys()].sort()) {
|
|
3465
|
+
console.log(`
|
|
3466
|
+
${formatValue(name)}`);
|
|
3467
|
+
for (const grant of byOrigin.get(name) ?? []) {
|
|
3468
|
+
const expiry = grant.expiry > 0 ? ` expires ${new Date(grant.expiry * 1000).toISOString()}` : "";
|
|
3469
|
+
console.log(` ${describeKey(toSpec(grant))}${formatLabel(expiry)}${formatLabel(grant.reason ? ` — ${grant.reason}` : "")}`);
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
3472
|
+
console.log(`
|
|
3473
|
+
${grants.length} grant(s) in ${path}`);
|
|
3474
|
+
}
|
|
3475
|
+
async function permissionsGrant(args, opts) {
|
|
3476
|
+
const origin = firstPositional(args);
|
|
3477
|
+
if (!origin)
|
|
3478
|
+
fatal("Missing <origin>. See `1sat permissions --help`");
|
|
3479
|
+
let specs;
|
|
3480
|
+
try {
|
|
3481
|
+
specs = specsFromFlags(origin, grantFlags(args));
|
|
3482
|
+
} catch (err) {
|
|
3483
|
+
return fatal(err.message);
|
|
3484
|
+
}
|
|
3485
|
+
const { store, path } = openStore(opts);
|
|
3486
|
+
const now = Date.now();
|
|
3487
|
+
for (const spec of specs) {
|
|
3488
|
+
await store.putGrant({
|
|
3489
|
+
key: spec.key,
|
|
3490
|
+
expiry: 0,
|
|
3491
|
+
grantedAt: now,
|
|
3492
|
+
...spec.authorizedAmount != null ? { authorizedAmount: spec.authorizedAmount } : {}
|
|
3493
|
+
});
|
|
3494
|
+
}
|
|
3495
|
+
if (opts.json) {
|
|
3496
|
+
output({
|
|
3497
|
+
granted: specs.map((spec) => ({
|
|
3498
|
+
id: specId(spec),
|
|
3499
|
+
key: spec.key,
|
|
3500
|
+
authorizedAmount: spec.authorizedAmount
|
|
3501
|
+
})),
|
|
3502
|
+
store: path
|
|
3503
|
+
}, opts);
|
|
3504
|
+
return;
|
|
3505
|
+
}
|
|
3506
|
+
for (const spec of specs) {
|
|
3507
|
+
console.log(`granted ${describeKey(spec)}`);
|
|
3508
|
+
}
|
|
3509
|
+
console.log(`
|
|
3510
|
+
${specs.length} grant(s) written to ${path}. A running \`1sat serve wallet-api\` picks them up on the app's next call.`);
|
|
3511
|
+
}
|
|
3512
|
+
async function permissionsRevoke(args, opts) {
|
|
3513
|
+
const origin = firstPositional(args);
|
|
3514
|
+
if (!origin)
|
|
3515
|
+
fatal("Missing <origin>. See `1sat permissions --help`");
|
|
3516
|
+
const originator = normalizeOriginator2(origin);
|
|
3517
|
+
const { store, path } = openStore(opts);
|
|
3518
|
+
if (hasFlag(args, "--all")) {
|
|
3519
|
+
const removed = await store.deleteAllForOriginator(originator);
|
|
3520
|
+
if (opts.json) {
|
|
3521
|
+
output({ originator, removed, store: path }, opts);
|
|
3522
|
+
return;
|
|
3523
|
+
}
|
|
3524
|
+
output(`revoked ${removed} grant(s) for ${originator}`, opts);
|
|
3525
|
+
return;
|
|
3526
|
+
}
|
|
3527
|
+
let specs;
|
|
3528
|
+
try {
|
|
3529
|
+
specs = specsFromFlags(origin, grantFlags(args));
|
|
3530
|
+
} catch (err) {
|
|
3531
|
+
return fatal(`${err.message}. Pass --all to revoke everything for an origin.`);
|
|
3532
|
+
}
|
|
3533
|
+
const removed = [];
|
|
3534
|
+
const missing = [];
|
|
3535
|
+
for (const spec of specs) {
|
|
3536
|
+
if (await store.findGrant(spec.key)) {
|
|
3537
|
+
await store.deleteGrant(spec.key);
|
|
3538
|
+
removed.push(spec);
|
|
3539
|
+
} else {
|
|
3540
|
+
missing.push(spec);
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
if (opts.json) {
|
|
3544
|
+
output({
|
|
3545
|
+
revoked: removed.map((spec) => ({ id: specId(spec), key: spec.key })),
|
|
3546
|
+
notFound: missing.map((spec) => ({ id: specId(spec), key: spec.key })),
|
|
3547
|
+
store: path
|
|
3548
|
+
}, opts);
|
|
3549
|
+
return;
|
|
3550
|
+
}
|
|
3551
|
+
for (const spec of removed)
|
|
3552
|
+
console.log(`revoked ${describeKey(spec)}`);
|
|
3553
|
+
for (const spec of missing) {
|
|
3554
|
+
console.log(`no such grant: ${describeKey(spec)} (grant it with \`${grantCommand(spec, commandPrefix(opts))}\`)`);
|
|
3555
|
+
}
|
|
3556
|
+
console.log(`
|
|
3557
|
+
${removed.length} grant(s) removed from ${path}`);
|
|
3558
|
+
}
|
|
3559
|
+
var BOOLEAN_FLAGS;
|
|
3560
|
+
var init_permissions = __esm(() => {
|
|
3561
|
+
init_config();
|
|
3562
|
+
init_help();
|
|
3563
|
+
init_output();
|
|
3564
|
+
init_grants();
|
|
3565
|
+
init_permission_store();
|
|
3566
|
+
BOOLEAN_FLAGS = new Set(["--privileged", "--all"]);
|
|
3567
|
+
});
|
|
3568
|
+
|
|
3048
3569
|
// src/commands/remote.ts
|
|
3049
3570
|
import { StorageClient } from "@1sat/wallet-node";
|
|
3050
3571
|
import { WalletServerClient, topUpStorage } from "@1sat/wallet-server";
|
|
@@ -3533,8 +4054,135 @@ var init_repricer = __esm(() => {
|
|
|
3533
4054
|
init_providers();
|
|
3534
4055
|
});
|
|
3535
4056
|
|
|
4057
|
+
// src/wallet-api/deny.ts
|
|
4058
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4059
|
+
import {
|
|
4060
|
+
normalizeOriginator as normalizeOriginator3,
|
|
4061
|
+
permissionKeyFromRequest,
|
|
4062
|
+
permissionKeysFromGroup
|
|
4063
|
+
} from "@1sat/wallet";
|
|
4064
|
+
function bindPermissionDenials(manager, options = {}) {
|
|
4065
|
+
const prefix = options.grantCommandPrefix ?? GRANT_COMMAND;
|
|
4066
|
+
const log = options.log ?? ((line) => console.log(line));
|
|
4067
|
+
const record = (origin, commands) => {
|
|
4068
|
+
const message = denialMessage(origin, commands);
|
|
4069
|
+
inFlight.getStore()?.push(message);
|
|
4070
|
+
log(`[wallet-api] ${message}`);
|
|
4071
|
+
};
|
|
4072
|
+
const single = async (request) => {
|
|
4073
|
+
const origin = displayOrigin(request);
|
|
4074
|
+
try {
|
|
4075
|
+
record(origin, [grantCommand(specFromRequest(request), prefix)]);
|
|
4076
|
+
} catch (err) {
|
|
4077
|
+
log(`[wallet-api] could not describe request: ${err.message}`);
|
|
4078
|
+
}
|
|
4079
|
+
await safely(log, "deny", () => manager.denyPermission(request.requestID));
|
|
4080
|
+
};
|
|
4081
|
+
const grouped = async (request) => {
|
|
4082
|
+
const origin = normalizeOriginator3(request.originator);
|
|
4083
|
+
try {
|
|
4084
|
+
record(origin, specsFromGroupedRequest(request).map((spec) => grantCommand(spec, prefix)));
|
|
4085
|
+
} catch (err) {
|
|
4086
|
+
log(`[wallet-api] could not describe request: ${err.message}`);
|
|
4087
|
+
}
|
|
4088
|
+
await safely(log, "deny", () => manager.denyGroupedPermission(request.requestID));
|
|
4089
|
+
};
|
|
4090
|
+
manager.bindCallback("onProtocolPermissionRequested", single);
|
|
4091
|
+
manager.bindCallback("onBasketAccessRequested", single);
|
|
4092
|
+
manager.bindCallback("onCertificateAccessRequested", single);
|
|
4093
|
+
manager.bindCallback("onSpendingAuthorizationRequested", single);
|
|
4094
|
+
manager.bindCallback("onGroupedPermissionRequested", grouped);
|
|
4095
|
+
}
|
|
4096
|
+
async function withDenialMessage(fn) {
|
|
4097
|
+
const messages = [];
|
|
4098
|
+
try {
|
|
4099
|
+
return await inFlight.run(messages, fn);
|
|
4100
|
+
} catch (err) {
|
|
4101
|
+
const code = err?.code;
|
|
4102
|
+
if (code !== DENIED_CODE || messages.length === 0)
|
|
4103
|
+
throw err;
|
|
4104
|
+
const denied = new Error(messages.join(" "));
|
|
4105
|
+
denied.code = DENIED_CODE;
|
|
4106
|
+
throw denied;
|
|
4107
|
+
}
|
|
4108
|
+
}
|
|
4109
|
+
function denialMessage(origin, commands) {
|
|
4110
|
+
const quoted = commands.map((c) => `\`${c}\``);
|
|
4111
|
+
const list = quoted.length > 1 ? `${quoted.slice(0, -1).join(", ")} and ${quoted[quoted.length - 1]}` : quoted[0];
|
|
4112
|
+
return `permission denied for ${origin}: run ${list} and retry`;
|
|
4113
|
+
}
|
|
4114
|
+
function specFromRequest(request) {
|
|
4115
|
+
const key = permissionKeyFromRequest(request);
|
|
4116
|
+
return key.type === "spending" ? { key, authorizedAmount: request.spending?.satoshis ?? 0 } : { key };
|
|
4117
|
+
}
|
|
4118
|
+
function specsFromGroupedRequest(request) {
|
|
4119
|
+
const amount = request.permissions.spendingAuthorization?.amount ?? 0;
|
|
4120
|
+
return permissionKeysFromGroup(request.originator, request.permissions).map((key) => key.type === "spending" ? { key, authorizedAmount: amount } : { key });
|
|
4121
|
+
}
|
|
4122
|
+
function displayOrigin(request) {
|
|
4123
|
+
return normalizeOriginator3(request.originator);
|
|
4124
|
+
}
|
|
4125
|
+
async function safely(log, what, fn) {
|
|
4126
|
+
try {
|
|
4127
|
+
await fn();
|
|
4128
|
+
} catch (err) {
|
|
4129
|
+
log(`[wallet-api] ${what} failed: ${err.message}`);
|
|
4130
|
+
}
|
|
4131
|
+
}
|
|
4132
|
+
var inFlight, DENIED_CODE = "ERR_PERMISSION_DENIED";
|
|
4133
|
+
var init_deny = __esm(() => {
|
|
4134
|
+
init_grants();
|
|
4135
|
+
inFlight = new AsyncLocalStorage;
|
|
4136
|
+
});
|
|
4137
|
+
|
|
4138
|
+
// src/wallet-api/endpoint.ts
|
|
4139
|
+
import { LocalWalletPermissionsManager as LocalWalletPermissionsManager2 } from "@1sat/wallet";
|
|
4140
|
+
import {
|
|
4141
|
+
startBRC100Server
|
|
4142
|
+
} from "@1sat/wallet-server";
|
|
4143
|
+
function walletApiHandle(wallet) {
|
|
4144
|
+
return {
|
|
4145
|
+
async call(method, args, origin) {
|
|
4146
|
+
const fn = wallet[method];
|
|
4147
|
+
if (typeof fn !== "function") {
|
|
4148
|
+
throw new Error(`Method not available: ${method}`);
|
|
4149
|
+
}
|
|
4150
|
+
return withDenialMessage(() => fn.call(wallet, args, origin));
|
|
4151
|
+
}
|
|
4152
|
+
};
|
|
4153
|
+
}
|
|
4154
|
+
async function startWalletApi(options) {
|
|
4155
|
+
const log = options.log ?? ((line) => console.log(line));
|
|
4156
|
+
const manager = new LocalWalletPermissionsManager2(options.wallet, CLI_ADMIN_ORIGINATOR, { ...options.managerConfig }, { store: new FilePermissionStore(options.storePath) });
|
|
4157
|
+
bindPermissionDenials(manager, {
|
|
4158
|
+
grantCommandPrefix: options.grantCommandPrefix,
|
|
4159
|
+
log
|
|
4160
|
+
});
|
|
4161
|
+
const server = await startBRC100Server({
|
|
4162
|
+
host: options.host,
|
|
4163
|
+
port: options.port,
|
|
4164
|
+
adminOriginator: CLI_ADMIN_ORIGINATOR,
|
|
4165
|
+
wallet: walletApiHandle(manager),
|
|
4166
|
+
onEvent: (event) => {
|
|
4167
|
+
if (event.event !== "brc100_call")
|
|
4168
|
+
return;
|
|
4169
|
+
const line = `[wallet-api] ${event.method} from ${event.origin || "(no origin)"} -> ${event.status}`;
|
|
4170
|
+
log(event.error ? `${line}: ${event.error}` : line);
|
|
4171
|
+
}
|
|
4172
|
+
});
|
|
4173
|
+
return {
|
|
4174
|
+
server,
|
|
4175
|
+
close: () => server.close()
|
|
4176
|
+
};
|
|
4177
|
+
}
|
|
4178
|
+
var init_endpoint = __esm(() => {
|
|
4179
|
+
init_admin();
|
|
4180
|
+
init_deny();
|
|
4181
|
+
init_permission_store();
|
|
4182
|
+
});
|
|
4183
|
+
|
|
3536
4184
|
// src/commands/serve.ts
|
|
3537
|
-
import { join as
|
|
4185
|
+
import { join as join6 } from "node:path";
|
|
3538
4186
|
import {
|
|
3539
4187
|
createNodeWallet as createNodeWallet3
|
|
3540
4188
|
} from "@1sat/wallet-node";
|
|
@@ -3543,8 +4191,7 @@ import {
|
|
|
3543
4191
|
KnexHandleCertStore,
|
|
3544
4192
|
KnexPendingStore,
|
|
3545
4193
|
createHostServer,
|
|
3546
|
-
createWalletServer
|
|
3547
|
-
startBRC100Server
|
|
4194
|
+
createWalletServer
|
|
3548
4195
|
} from "@1sat/wallet-server";
|
|
3549
4196
|
import { initLogger as initLogger2 } from "evlog";
|
|
3550
4197
|
import knexLib from "knex";
|
|
@@ -3620,7 +4267,7 @@ async function resolveServe(opts) {
|
|
|
3620
4267
|
};
|
|
3621
4268
|
}
|
|
3622
4269
|
function deriveSqliteFilename(dataDir, chain) {
|
|
3623
|
-
return
|
|
4270
|
+
return join6(dataDir, `wallet-${chain}.db`);
|
|
3624
4271
|
}
|
|
3625
4272
|
function resolveMonitorEnabled(configured) {
|
|
3626
4273
|
const fromEnv = process.env.ONESAT_MONITOR;
|
|
@@ -3674,7 +4321,7 @@ async function runWithStorage(resolved, mode) {
|
|
|
3674
4321
|
skipInitialMonitor: true
|
|
3675
4322
|
});
|
|
3676
4323
|
if (mode === "wallet-api") {
|
|
3677
|
-
const dapp = await startDappEndpoint(walletResult);
|
|
4324
|
+
const dapp = await startDappEndpoint(walletResult, resolved);
|
|
3678
4325
|
return {
|
|
3679
4326
|
stop: async () => {
|
|
3680
4327
|
await dapp.close();
|
|
@@ -3812,7 +4459,7 @@ async function startWalletServer(resolved, walletResult, accounts, mode) {
|
|
|
3812
4459
|
}
|
|
3813
4460
|
function createMessageboxKnex(storage, messagebox, resolved) {
|
|
3814
4461
|
if (storage.provider === "bun-sqlite") {
|
|
3815
|
-
const filename = messagebox?.dbPath ??
|
|
4462
|
+
const filename = messagebox?.dbPath ?? join6(resolved.dataDir, `messagebox-${resolved.chain}.db`);
|
|
3816
4463
|
return knexLib({
|
|
3817
4464
|
client: "sqlite3",
|
|
3818
4465
|
connection: { filename },
|
|
@@ -3870,50 +4517,24 @@ function waitForShutdown() {
|
|
|
3870
4517
|
process.once("SIGTERM", () => handler("SIGTERM"));
|
|
3871
4518
|
});
|
|
3872
4519
|
}
|
|
3873
|
-
function
|
|
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) {
|
|
4520
|
+
async function startDappEndpoint(walletResult, resolved) {
|
|
3886
4521
|
const config = loadConfig();
|
|
3887
4522
|
const dapp = config.server?.dapp ?? {};
|
|
3888
4523
|
const host = dapp.host ?? DEFAULT_DAPP_HOST;
|
|
3889
4524
|
const port = dapp.port ?? (process.env.ONESAT_DAPP_PORT ? Number(process.env.ONESAT_DAPP_PORT) : DEFAULT_DAPP_PORT);
|
|
3890
|
-
const
|
|
3891
|
-
const
|
|
4525
|
+
const storePath = permissionStorePath(resolved.dataDir, resolved.chain);
|
|
4526
|
+
const grantCommandPrefix = resolved.chain === "test" ? "1sat --chain test permissions grant" : GRANT_COMMAND;
|
|
4527
|
+
const api = await startWalletApi({
|
|
4528
|
+
wallet: walletResult.wallet,
|
|
4529
|
+
storePath,
|
|
3892
4530
|
host,
|
|
3893
4531
|
port,
|
|
3894
|
-
|
|
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
|
-
}
|
|
4532
|
+
grantCommandPrefix
|
|
3913
4533
|
});
|
|
3914
4534
|
console.log(`[wallet-api] BRC-100 app endpoint on http://${host}:${port}`);
|
|
3915
|
-
console.log(
|
|
3916
|
-
|
|
4535
|
+
console.log(`[wallet-api] permission grants: ${storePath}`);
|
|
4536
|
+
console.log("[wallet-api] ungranted requests are denied; the error names the `1sat permissions grant` command that allows them");
|
|
4537
|
+
return { close: () => api.close() };
|
|
3917
4538
|
}
|
|
3918
4539
|
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
4540
|
var init_serve = __esm(() => {
|
|
@@ -3924,6 +4545,9 @@ var init_serve = __esm(() => {
|
|
|
3924
4545
|
init_monitor_lock();
|
|
3925
4546
|
init_output();
|
|
3926
4547
|
init_repricer();
|
|
4548
|
+
init_endpoint();
|
|
4549
|
+
init_grants();
|
|
4550
|
+
init_permission_store();
|
|
3927
4551
|
DEFAULT_BASELINE_BYTES = 1024 * 1024 * 1024;
|
|
3928
4552
|
});
|
|
3929
4553
|
|
|
@@ -3978,7 +4602,7 @@ var init_social = __esm(() => {
|
|
|
3978
4602
|
});
|
|
3979
4603
|
|
|
3980
4604
|
// src/commands/storage.ts
|
|
3981
|
-
import { join as
|
|
4605
|
+
import { join as join7 } from "node:path";
|
|
3982
4606
|
import { StorageBunSqlite, nominateInvalidReqs } from "@1sat/wallet-node";
|
|
3983
4607
|
import { StorageProvider } from "@bsv/wallet-toolbox";
|
|
3984
4608
|
async function handleStorageCommand(args, opts) {
|
|
@@ -4048,7 +4672,7 @@ async function openServeStorage(opts) {
|
|
|
4048
4672
|
} else {
|
|
4049
4673
|
storage = new StorageBunSqlite({
|
|
4050
4674
|
...baseOptions,
|
|
4051
|
-
filename:
|
|
4675
|
+
filename: join7(ensureDataDir(), `wallet-${chain}.db`)
|
|
4052
4676
|
});
|
|
4053
4677
|
}
|
|
4054
4678
|
await storage.makeAvailable();
|
|
@@ -5447,13 +6071,13 @@ var init_wallet = __esm(() => {
|
|
|
5447
6071
|
});
|
|
5448
6072
|
|
|
5449
6073
|
// src/main.ts
|
|
5450
|
-
import { existsSync as
|
|
6074
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
5451
6075
|
import { resolve } from "node:path";
|
|
5452
6076
|
import { config as loadEnv } from "dotenv";
|
|
5453
6077
|
function applyEnvFiles(paths) {
|
|
5454
6078
|
for (const p of paths) {
|
|
5455
6079
|
const abs = resolve(p);
|
|
5456
|
-
if (!
|
|
6080
|
+
if (!existsSync5(abs)) {
|
|
5457
6081
|
throw new Error(`Env file not found: ${p}`);
|
|
5458
6082
|
}
|
|
5459
6083
|
const result = loadEnv({ path: abs, quiet: true, override: true });
|
|
@@ -5537,6 +6161,9 @@ async function main() {
|
|
|
5537
6161
|
case "serve":
|
|
5538
6162
|
await handleServeCommand(rest, flags);
|
|
5539
6163
|
break;
|
|
6164
|
+
case "permissions":
|
|
6165
|
+
await handlePermissionsCommand(rest, flags);
|
|
6166
|
+
break;
|
|
5540
6167
|
case "storage":
|
|
5541
6168
|
await handleStorageCommand(rest, flags);
|
|
5542
6169
|
break;
|
|
@@ -5564,6 +6191,7 @@ var init_main = __esm(() => {
|
|
|
5564
6191
|
init_messagebox();
|
|
5565
6192
|
init_opns();
|
|
5566
6193
|
init_ordinals();
|
|
6194
|
+
init_permissions();
|
|
5567
6195
|
init_remote();
|
|
5568
6196
|
init_serve();
|
|
5569
6197
|
init_social();
|