@ghostlytools/cli 1.3.2 → 1.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/SKILL.md +2 -1
- package/dist/assets/{api-SYPJgFRU.js → api-BCwaCbKh.js} +2 -2
- package/dist/assets/{api-aup3Amud.js → api-Bp_hWAiU.js} +100 -20
- package/dist/assets/api-Bp_hWAiU.js.map +1 -0
- package/dist/assets/{apiKit-DYkVEs6r.js → apiKit-3MLUyuhL.js} +21 -6
- package/dist/assets/apiKit-3MLUyuhL.js.map +1 -0
- package/dist/assets/{apps-DcZKu29s.js → apps-B6gqk4GD.js} +1 -1
- package/dist/assets/{apps-Rbk2HHXb.js → apps-BXw3Ui_3.js} +91 -24
- package/dist/assets/apps-BXw3Ui_3.js.map +1 -0
- package/dist/assets/{arkade-CuWlNnCz.js → arkade-CyplNY_v.js} +2 -2
- package/dist/assets/{arkade-CuWlNnCz.js.map → arkade-CyplNY_v.js.map} +1 -1
- package/dist/assets/{backup-CJWLDHuS.js → backup-BGE4BDzf.js} +6 -6
- package/dist/assets/{backup-CJWLDHuS.js.map → backup-BGE4BDzf.js.map} +1 -1
- package/dist/assets/{backup-CSMw2GHv.js → backup-Cm8ptQQA.js} +1 -1
- package/dist/assets/bark_ffi_wasm_bg-EY2uc0Bx.js.map +1 -1
- package/dist/assets/{buttons-QEnAGWoC.js → buttons-52NT4sA_.js} +4 -4
- package/dist/assets/{buttons-QEnAGWoC.js.map → buttons-52NT4sA_.js.map} +1 -1
- package/dist/assets/buttons-PCvHRlkS.js +2 -0
- package/dist/assets/{envelope-K27mzoyu.js → envelope-CsRRCrL3.js} +2 -2
- package/dist/assets/{envelope-K27mzoyu.js.map → envelope-CsRRCrL3.js.map} +1 -1
- package/dist/assets/{errors-DO5z6h_l.js → errors-B-zMxbcd.js} +6 -5
- package/dist/assets/{errors-DO5z6h_l.js.map → errors-B-zMxbcd.js.map} +1 -1
- package/dist/assets/fedimint_client_wasm_bg-BtHHlFpp.js.map +1 -1
- package/dist/assets/{ghostly_iroh_web-Dvzh3Y6E.js → ghostly_iroh_web-BsAEFIjl.js} +30 -36
- package/dist/assets/ghostly_iroh_web-BsAEFIjl.js.map +1 -0
- package/dist/assets/ghostly_iroh_web_bg.wasm +0 -0
- package/dist/assets/{groupFiles-DHTrTA4I.js → groupFiles-BurezP4Z.js} +47 -21
- package/dist/assets/{groupFiles-DHTrTA4I.js.map → groupFiles-BurezP4Z.js.map} +1 -1
- package/dist/assets/{host-C7hA-Za7.js → host-C74Sy5jT.js} +211 -42
- package/dist/assets/host-C74Sy5jT.js.map +1 -0
- package/dist/assets/{hyperdhtRelay-BVoWY2ua.js → hyperdhtRelay-D0MvfMzX.js} +2 -2
- package/dist/assets/{hyperdhtRelay-BVoWY2ua.js.map → hyperdhtRelay-D0MvfMzX.js.map} +1 -1
- package/dist/assets/{irohWeb-BJPk9bMn.js → irohWeb-DynqwRwl.js} +3 -3
- package/dist/assets/{irohWeb-BJPk9bMn.js.map → irohWeb-DynqwRwl.js.map} +1 -1
- package/dist/assets/{main-DC8cUkDx.js → main-DNzQOdNG.js} +168 -57
- package/dist/assets/main-DNzQOdNG.js.map +1 -0
- package/dist/assets/{mainline-CxbxpoU4.js → mainline-COqCHn1R.js} +2 -2
- package/dist/assets/{mainline-CxbxpoU4.js.map → mainline-COqCHn1R.js.map} +1 -1
- package/dist/assets/{networkReason-D42iNuUB.js → networkReason-WikfjkIw.js} +2 -2
- package/dist/assets/{networkReason-D42iNuUB.js.map → networkReason-WikfjkIw.js.map} +1 -1
- package/dist/assets/{nwc-Cm4sYgLv.js → nwc-ClsjYuyP.js} +3 -3
- package/dist/assets/{nwc-Cm4sYgLv.js.map → nwc-ClsjYuyP.js.map} +1 -1
- package/dist/assets/{nwcProvider-0D7vdPDd.js → nwcProvider-ksMYZra9.js} +2 -2
- package/dist/assets/{nwcProvider-0D7vdPDd.js.map → nwcProvider-ksMYZra9.js.map} +1 -1
- package/dist/assets/{profiles-BHc9pEYg.js → profiles-gPCjLOQq.js} +2 -2
- package/dist/assets/{profiles-BHc9pEYg.js.map → profiles-gPCjLOQq.js.map} +1 -1
- package/dist/assets/{registry-CVdSZW5l.js → registry-CJh9L_kF.js} +17 -32
- package/dist/assets/registry-CJh9L_kF.js.map +1 -0
- package/dist/assets/{registry-CLolR0kM.js → registry-S7SFF_JB.js} +1 -1
- package/dist/assets/{resolve-BnlEajYz.js → resolve-BJxBhU5j.js} +18 -3
- package/dist/assets/resolve-BJxBhU5j.js.map +1 -0
- package/dist/assets/{server-CJNEKxzE.js → server-C05N9q4G.js} +298 -100
- package/dist/assets/server-C05N9q4G.js.map +1 -0
- package/dist/assets/{src-yMUQvdyL.js → src-RQ6OJOUO.js} +322 -104
- package/dist/assets/src-RQ6OJOUO.js.map +1 -0
- package/dist/assets/{turnRefs-B2a8to9q.js → turnRefs-u-U75DFx.js} +2 -2
- package/dist/assets/{turnRefs-B2a8to9q.js.map → turnRefs-u-U75DFx.js.map} +1 -1
- package/dist/assets/{usdt-DesFeDiG.js → usdt-A1B3r3Mq.js} +3 -3
- package/dist/assets/{usdt-DesFeDiG.js.map → usdt-A1B3r3Mq.js.map} +1 -1
- package/dist/ghostly.mjs +1 -1
- package/package.json +1 -1
- package/dist/assets/api-aup3Amud.js.map +0 -1
- package/dist/assets/apiKit-DYkVEs6r.js.map +0 -1
- package/dist/assets/apps-Rbk2HHXb.js.map +0 -1
- package/dist/assets/buttons-CGRqZ7z0.js +0 -2
- package/dist/assets/ghostly_iroh_web-Dvzh3Y6E.js.map +0 -1
- package/dist/assets/host-C7hA-Za7.js.map +0 -1
- package/dist/assets/main-DC8cUkDx.js.map +0 -1
- package/dist/assets/registry-CVdSZW5l.js.map +0 -1
- package/dist/assets/resolve-BnlEajYz.js.map +0 -1
- package/dist/assets/server-CJNEKxzE.js.map +0 -1
- package/dist/assets/src-yMUQvdyL.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as SECRET_RESULTS, i as ENGINE_READS, r as ENGINE_METHODS, t as turnRef } from "./turnRefs-
|
|
3
|
-
import { n as EXIT, r as asCliError, t as CliError } from "./errors-
|
|
4
|
-
import { t as CONTACT_KEY } from "./apiKit-
|
|
5
|
-
import { a as createProfile, c as listProfiles, d as profilePaths, f as selectProfile, i as checkProfileName, l as lockOwner, o as currentProfile, s as ghostlyHome, u as profileExists } from "./profiles-
|
|
1
|
+
import { $n as GROUP_FILE_LIMITS, Ja as PR_CHECKS, Xa as RUN_RESULTS, Ya as PR_STATES, Za as STATUS_CARD_LIMITS, qa as ITEM_STATES, to as cardUrl } from "./src-RQ6OJOUO.js";
|
|
2
|
+
import { a as SECRET_RESULTS, i as ENGINE_READS, r as ENGINE_METHODS, t as turnRef } from "./turnRefs-u-U75DFx.js";
|
|
3
|
+
import { i as localFileError, n as EXIT, r as asCliError, t as CliError } from "./errors-B-zMxbcd.js";
|
|
4
|
+
import { t as CONTACT_KEY } from "./apiKit-3MLUyuhL.js";
|
|
5
|
+
import { a as createProfile, c as listProfiles, d as profilePaths, f as selectProfile, i as checkProfileName, l as lockOwner, o as currentProfile, s as ghostlyHome, u as profileExists } from "./profiles-gPCjLOQq.js";
|
|
6
6
|
import { n as ownSocket } from "./privateFolder-Bk9C9oFv.js";
|
|
7
7
|
import { accessSync, constants, existsSync, openSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { dirname, resolve } from "node:path";
|
|
@@ -12,7 +12,7 @@ import { createInterface } from "node:readline";
|
|
|
12
12
|
import { format } from "node:util";
|
|
13
13
|
var package_default = {
|
|
14
14
|
name: "@ghostlytools/cli",
|
|
15
|
-
version: "1.3.
|
|
15
|
+
version: "1.3.4",
|
|
16
16
|
type: "module",
|
|
17
17
|
description: "Ghostly for bots and terminals: the app's own engine, headless on Node, behind a daemon and a `ghostly` command with JSON output.",
|
|
18
18
|
license: "MIT",
|
|
@@ -172,7 +172,7 @@ function parseArgs(argv, spec, idSlot = () => false) {
|
|
|
172
172
|
value = argv[++i];
|
|
173
173
|
}
|
|
174
174
|
if (option.type === "number") {
|
|
175
|
-
const number = Number(value);
|
|
175
|
+
const number = value.trim() === "" ? NaN : Number(value);
|
|
176
176
|
if (!Number.isFinite(number)) throw new CliError("usage", `--${name} takes a number, not ${JSON.stringify(value)}`);
|
|
177
177
|
options[name] = number;
|
|
178
178
|
} else if (option.type === "list") options[name] = [...options[name] ?? [], value];
|
|
@@ -354,10 +354,22 @@ var confirmReal = {
|
|
|
354
354
|
type: "boolean",
|
|
355
355
|
description: "Confirm a Mainnet (real money) spend"
|
|
356
356
|
};
|
|
357
|
-
/**
|
|
357
|
+
/**
|
|
358
|
+
* A path as this command's working directory means it: the daemon runs elsewhere. "" (an unset variable in a script)
|
|
359
|
+
* names no file, never the current folder.
|
|
360
|
+
*/
|
|
358
361
|
function here(path) {
|
|
362
|
+
if (path === "") throw new CliError("usage", "A path given as \"\" names no file");
|
|
359
363
|
return typeof path === "string" ? resolve(path) : void 0;
|
|
360
364
|
}
|
|
365
|
+
/**
|
|
366
|
+
* A list option that names chats (`--to`, `--from`): "" or "group:" alone (an unset variable in a script) names
|
|
367
|
+
* nothing, and is a usage error before the profile opens; it was a prefix of every id and took the only chat or group.
|
|
368
|
+
*/
|
|
369
|
+
function refs(values, flag, what) {
|
|
370
|
+
if (!Array.isArray(values)) return;
|
|
371
|
+
for (const value of values) if (typeof value === "string" && !value.replace(/^group:/, "").trim()) throw new CliError("usage", `--${flag} ${JSON.stringify(value)} names no ${what}`);
|
|
372
|
+
}
|
|
361
373
|
/** A whole number of sats from the command line. */
|
|
362
374
|
function sats(value) {
|
|
363
375
|
const n = Number(value);
|
|
@@ -453,11 +465,12 @@ function cursor(value, flag) {
|
|
|
453
465
|
function cardJson(value) {
|
|
454
466
|
if (value === void 0) return {};
|
|
455
467
|
const text = String(value).trim();
|
|
468
|
+
const file = text.startsWith("{") || text === "-" ? void 0 : here(text);
|
|
456
469
|
let raw;
|
|
457
470
|
try {
|
|
458
|
-
raw = text.startsWith("{") ? text : readFileSync(
|
|
471
|
+
raw = text.startsWith("{") ? text : readFileSync(file ?? 0, "utf8");
|
|
459
472
|
} catch (error) {
|
|
460
|
-
throw new CliError("usage", `--json takes a card as JSON, - for stdin, or a file: ${error instanceof Error ? error.message : String(error)}`);
|
|
473
|
+
throw file !== void 0 && localFileError(error, file) || new CliError("usage", `--json takes a card as JSON, - for stdin, or a file: ${error instanceof Error ? error.message : String(error)}`);
|
|
461
474
|
}
|
|
462
475
|
let parsed;
|
|
463
476
|
try {
|
|
@@ -869,7 +882,7 @@ var commands$8 = {
|
|
|
869
882
|
description: "Replace the three files when they are there"
|
|
870
883
|
}
|
|
871
884
|
},
|
|
872
|
-
run: async ({ options }, a) => (await import("./apps-
|
|
885
|
+
run: async ({ options }, a) => (await import("./apps-B6gqk4GD.js")).initApp(a.dir, {
|
|
873
886
|
name: options.name,
|
|
874
887
|
title: options.title,
|
|
875
888
|
force: options.force === true
|
|
@@ -909,11 +922,11 @@ var commands$8 = {
|
|
|
909
922
|
description: "This version's sequence, higher than the bundle at --out's (default: one more than it)"
|
|
910
923
|
}
|
|
911
924
|
},
|
|
912
|
-
run: async ({ options }, a) => (await import("./apps-
|
|
925
|
+
run: async ({ options }, a) => (await import("./apps-B6gqk4GD.js")).publishApp(a.dir, keyFile(options.key, "app publish <dir> --key <file> [--out <file>] [--sequence n]"), here(options.out), options.sequence)
|
|
913
926
|
},
|
|
914
927
|
"app revoke": {
|
|
915
928
|
method: "app.revoke",
|
|
916
|
-
usage: "app revoke <dir> --key <file> (--digest <digest>... | --up-to <sequence>) [--reason <text>] [--bundle <file>]",
|
|
929
|
+
usage: "app revoke <dir> --key <file> (--digest <digest>... | --up-to <sequence>) [--reason <text>] [--bundle <file>] [--force]",
|
|
917
930
|
summary: "Revoke versions of your app (WISP 1200 · Revocation): a ghostly-revoke/1 statement signed with its publisher key, added to <dir>/ghostly-revoke.json",
|
|
918
931
|
args: ["dir"],
|
|
919
932
|
options: {
|
|
@@ -936,13 +949,18 @@ var commands$8 = {
|
|
|
936
949
|
bundle: {
|
|
937
950
|
type: "string",
|
|
938
951
|
description: "The bundle naming the app (default <dir>/app.ghostlyapp)"
|
|
952
|
+
},
|
|
953
|
+
force: {
|
|
954
|
+
type: "boolean",
|
|
955
|
+
description: "Allow --up-to past the bundle's own sequence (versions published from elsewhere)"
|
|
939
956
|
}
|
|
940
957
|
},
|
|
941
|
-
run: async ({ options }, a) => (await import("./apps-
|
|
958
|
+
run: async ({ options }, a) => (await import("./apps-B6gqk4GD.js")).revokeApp(a.dir, keyFile(options.key, "app revoke <dir> --key <file> (--digest <digest>... | --up-to <sequence>) [--reason <text>]"), {
|
|
942
959
|
digests: options.digest,
|
|
943
960
|
upTo: options["up-to"],
|
|
944
961
|
reason: options.reason,
|
|
945
|
-
bundle: options.bundle
|
|
962
|
+
bundle: here(options.bundle),
|
|
963
|
+
force: options.force === true
|
|
946
964
|
})
|
|
947
965
|
},
|
|
948
966
|
"app send": {
|
|
@@ -996,9 +1014,9 @@ var commands$8 = {
|
|
|
996
1014
|
"app verify": {
|
|
997
1015
|
method: "app.verify",
|
|
998
1016
|
usage: "app verify <bundle|url>",
|
|
999
|
-
summary: "Check a .ghostlyapp as a client would, from a file or an https URL: its manifest and digest, or refused (exit 1) with the reason",
|
|
1017
|
+
summary: "Check a .ghostlyapp as a client would, from a file or an https URL: its manifest and digest, or refused (exit 1) with the reason, revoked when the ghostly-revoke.json beside it revokes this version",
|
|
1000
1018
|
args: ["bundle"],
|
|
1001
|
-
run: async (_, a) => (await import("./apps-
|
|
1019
|
+
run: async (_, a) => (await import("./apps-B6gqk4GD.js")).verifyApp(a.bundle)
|
|
1002
1020
|
},
|
|
1003
1021
|
"store sign": {
|
|
1004
1022
|
method: "store.sign",
|
|
@@ -1008,14 +1026,14 @@ var commands$8 = {
|
|
|
1008
1026
|
options: {
|
|
1009
1027
|
key: {
|
|
1010
1028
|
...key,
|
|
1011
|
-
description: "The store key file (made, owner-only, when it is missing
|
|
1029
|
+
description: "The store key file (made, owner-only, when it is missing, the index names no key and no signed index is in the output folder; keep it offline)"
|
|
1012
1030
|
},
|
|
1013
1031
|
out: {
|
|
1014
1032
|
type: "string",
|
|
1015
1033
|
description: "The folder to write ghostly-store.json and ghostly-store.sig in (default: the index's)"
|
|
1016
1034
|
}
|
|
1017
1035
|
},
|
|
1018
|
-
run: async ({ options }, a) => (await import("./apps-
|
|
1036
|
+
run: async ({ options }, a) => (await import("./apps-B6gqk4GD.js")).signStore(a.index, keyFile(options.key, "store sign <index> --key <file> [--out <dir>]"), here(options.out))
|
|
1019
1037
|
}
|
|
1020
1038
|
};
|
|
1021
1039
|
//#endregion
|
|
@@ -1048,6 +1066,7 @@ var commands$7 = {
|
|
|
1048
1066
|
params: ({ options }, a) => {
|
|
1049
1067
|
if (a.mode !== void 0 && a.mode !== "on" && a.mode !== "off") throw new CliError("usage", "ghostly call auto [on|off] [--from <chat>]... [--rate n]");
|
|
1050
1068
|
if (a.mode !== "on" && (options.from !== void 0 || options.rate !== void 0)) throw new CliError("usage", "--from and --rate go with on: ghostly call auto on --from <chat>");
|
|
1069
|
+
refs(options.from, "from", "chat");
|
|
1051
1070
|
if (a.mode === void 0) return {};
|
|
1052
1071
|
return a.mode === "on" ? {
|
|
1053
1072
|
on: true,
|
|
@@ -1314,6 +1333,7 @@ var commands$6 = {
|
|
|
1314
1333
|
},
|
|
1315
1334
|
params: ({ options }, a) => {
|
|
1316
1335
|
if (!Array.isArray(options.to) || !options.to.length) throw new CliError("usage", "Missing --to: ghostly forward <chat|group> <message>... --to <chat|group>... [--force] [--wait none|sent] [--timeout s]");
|
|
1336
|
+
refs(options.to, "to", "chat or group");
|
|
1317
1337
|
return {
|
|
1318
1338
|
chat: a.chat,
|
|
1319
1339
|
messages: (a.message ?? "").split(" ").filter(Boolean),
|
|
@@ -1336,9 +1356,9 @@ var commands$6 = {
|
|
|
1336
1356
|
},
|
|
1337
1357
|
"invite join": {
|
|
1338
1358
|
method: "invite.join",
|
|
1339
|
-
usage: "invite join <invite-or-link> [--label <name>]",
|
|
1340
|
-
summary: "Join a chat from its invite",
|
|
1341
|
-
args: ["invite"],
|
|
1359
|
+
usage: "invite join [<invite-or-link> | -] [--label <name>]",
|
|
1360
|
+
summary: "Join a chat from its invite; the invite on stdin keeps it out of ps",
|
|
1361
|
+
args: ["invite?"],
|
|
1342
1362
|
options: { label: {
|
|
1343
1363
|
type: "string",
|
|
1344
1364
|
description: "A name for the chat on this side"
|
|
@@ -1771,10 +1791,10 @@ var commands$4 = {
|
|
|
1771
1791
|
description: "Remove the picture"
|
|
1772
1792
|
} },
|
|
1773
1793
|
params: ({ options }, a) => {
|
|
1774
|
-
if (a.path
|
|
1775
|
-
if (a.path
|
|
1794
|
+
if (!a.path && options.clear !== true) throw new CliError("usage", "Missing <jpeg>: ghostly profile picture <jpeg> | --clear");
|
|
1795
|
+
if (a.path && options.clear === true) throw new CliError("usage", "--clear removes the picture: not with a <jpeg>");
|
|
1776
1796
|
return {
|
|
1777
|
-
path: here(a.path),
|
|
1797
|
+
path: here(a.path || void 0),
|
|
1778
1798
|
clear: options.clear === true
|
|
1779
1799
|
};
|
|
1780
1800
|
}
|
|
@@ -1918,9 +1938,9 @@ var commands$3 = {
|
|
|
1918
1938
|
},
|
|
1919
1939
|
"group join": {
|
|
1920
1940
|
method: "group.join",
|
|
1921
|
-
usage: "group join <link>",
|
|
1922
|
-
summary: "Join a group by its link",
|
|
1923
|
-
args: ["link"],
|
|
1941
|
+
usage: "group join [<link> | -]",
|
|
1942
|
+
summary: "Join a group by its link; the link on stdin keeps it out of ps",
|
|
1943
|
+
args: ["link?"],
|
|
1924
1944
|
params: (_, { link }) => ({ link })
|
|
1925
1945
|
},
|
|
1926
1946
|
"group leave": {
|
|
@@ -1978,11 +1998,11 @@ var commands$3 = {
|
|
|
1978
1998
|
description: "Remove the picture"
|
|
1979
1999
|
} },
|
|
1980
2000
|
params: ({ options }, a) => {
|
|
1981
|
-
if (a.path
|
|
1982
|
-
if (a.path
|
|
2001
|
+
if (!a.path && options.clear !== true) throw new CliError("usage", "Missing <jpeg>: ghostly group picture <group> <jpeg> | --clear");
|
|
2002
|
+
if (a.path && options.clear === true) throw new CliError("usage", "--clear removes the picture: not with a <jpeg>");
|
|
1983
2003
|
return {
|
|
1984
2004
|
group: a.group,
|
|
1985
|
-
path: here(a.path),
|
|
2005
|
+
path: here(a.path || void 0),
|
|
1986
2006
|
clear: options.clear === true
|
|
1987
2007
|
};
|
|
1988
2008
|
}
|
|
@@ -2735,28 +2755,45 @@ function idSlot(command) {
|
|
|
2735
2755
|
return (index) => at(index) && (shift === 0 || index + shift >= names.length || at(index + shift));
|
|
2736
2756
|
}
|
|
2737
2757
|
/**
|
|
2738
|
-
* The
|
|
2739
|
-
*
|
|
2740
|
-
|
|
2741
|
-
|
|
2758
|
+
* The placeholders a usage line shows for its positionals, in order: `chat pay <chat> <sats> [--memo t]` →
|
|
2759
|
+
* ["chat", "sats"]. One that is not a plain `<x>` (`[text... | --text <text>]`) is undefined; the flags end them.
|
|
2760
|
+
*/
|
|
2761
|
+
function usageNames(usage) {
|
|
2762
|
+
const words = usage.split(/\s+/);
|
|
2763
|
+
const first = words.findIndex((word) => word.startsWith("<") || word.startsWith("["));
|
|
2764
|
+
const out = [];
|
|
2765
|
+
for (const word of first === -1 ? [] : words.slice(first)) {
|
|
2766
|
+
if (/^\[?-|^\|/.test(word)) break;
|
|
2767
|
+
out.push(/^\[?<([^>]+?)(?:\.\.\.)?>(?:\.\.\.)?\]?$/.exec(word)?.[1]);
|
|
2768
|
+
}
|
|
2769
|
+
return out;
|
|
2770
|
+
}
|
|
2771
|
+
/**
|
|
2772
|
+
* The positionals of a command, by name; missing required ones are a usage error, and so are ones given as "" (an
|
|
2773
|
+
* unset variable in a script), before the profile opens. A leading `name?` is optional: it is given only when every
|
|
2774
|
+
* required one is there too (`file accept [<chat>] <file>`). A trailing `name...` takes the rest and needs at least
|
|
2775
|
+
* one word that is not ""; `name...?` may take none (text from stdin, `chat rename`'s name back to the contact's).
|
|
2742
2776
|
*/
|
|
2743
2777
|
function positionals(command, values) {
|
|
2744
2778
|
let names = command.args ?? [];
|
|
2745
2779
|
const optional = names.filter(leadingOptional);
|
|
2780
|
+
let skip = 0;
|
|
2746
2781
|
if (optional.length) {
|
|
2747
|
-
|
|
2782
|
+
skip = Math.max(0, optional.length - Math.max(0, values.length - (names.length - optional.length)));
|
|
2748
2783
|
names = [...optional.slice(skip).map((name) => name.slice(0, -1)), ...names.filter((name) => !leadingOptional(name))];
|
|
2749
2784
|
}
|
|
2785
|
+
const shown = usageNames(command.usage);
|
|
2786
|
+
const missing = (i, name) => new CliError("usage", `Missing <${shown[i + skip] ?? name}>: ghostly ${command.usage}`);
|
|
2750
2787
|
const out = {};
|
|
2751
2788
|
for (let i = 0; i < names.length; i++) {
|
|
2752
2789
|
const name = names[i].replace(/\?$/, "");
|
|
2753
2790
|
if (name.endsWith("...")) {
|
|
2754
2791
|
const rest = values.slice(i);
|
|
2755
|
-
if (
|
|
2792
|
+
if (rest.every((word) => word === "") && !names[i].endsWith("?")) throw missing(i, name.slice(0, -3));
|
|
2756
2793
|
out[name.slice(0, -3)] = rest.length ? rest.join(" ") : void 0;
|
|
2757
2794
|
return out;
|
|
2758
2795
|
}
|
|
2759
|
-
if (values[i] === void 0) throw
|
|
2796
|
+
if (values[i] === void 0 || values[i] === "") throw missing(i, name);
|
|
2760
2797
|
out[name] = values[i];
|
|
2761
2798
|
}
|
|
2762
2799
|
noMoreThan(names.length, values, command.usage);
|
|
@@ -3131,6 +3168,20 @@ var Feed = class {
|
|
|
3131
3168
|
* a warning. Fills `params` in place; the warning to print on stderr, if any. `tty`: stdin is a terminal, where a
|
|
3132
3169
|
* read would wait on the keyboard without a word.
|
|
3133
3170
|
*/
|
|
3171
|
+
var JOINS = {
|
|
3172
|
+
"group.join": {
|
|
3173
|
+
param: "link",
|
|
3174
|
+
arg: "<link>",
|
|
3175
|
+
command: "group join",
|
|
3176
|
+
what: "A group's link"
|
|
3177
|
+
},
|
|
3178
|
+
"invite.join": {
|
|
3179
|
+
param: "invite",
|
|
3180
|
+
arg: "<invite-or-link>",
|
|
3181
|
+
command: "invite join",
|
|
3182
|
+
what: "An invite"
|
|
3183
|
+
}
|
|
3184
|
+
};
|
|
3134
3185
|
async function secretsFromStdin(method, params, read, tty = false) {
|
|
3135
3186
|
const warning = (what) => `${what} on the command line shows in ps and the shell's history: give it on stdin instead`;
|
|
3136
3187
|
if (method === "wallet.redeem") {
|
|
@@ -3142,6 +3193,16 @@ async function secretsFromStdin(method, params, read, tty = false) {
|
|
|
3142
3193
|
params.token = token;
|
|
3143
3194
|
return;
|
|
3144
3195
|
}
|
|
3196
|
+
const join = JOINS[method];
|
|
3197
|
+
if (join) {
|
|
3198
|
+
if (params[join.param] !== void 0 && params[join.param] !== "-") return warning(join.what);
|
|
3199
|
+
const missing = () => new CliError("usage", `Missing ${join.arg}: give it on stdin (… | ghostly ${join.command}) or as an argument`);
|
|
3200
|
+
if (tty) throw missing();
|
|
3201
|
+
const value = (await read()).trim();
|
|
3202
|
+
if (!value) throw missing();
|
|
3203
|
+
params[join.param] = value;
|
|
3204
|
+
return;
|
|
3205
|
+
}
|
|
3145
3206
|
if (method === "wallet.create") {
|
|
3146
3207
|
const onLine = params.apiKey !== void 0;
|
|
3147
3208
|
if (params.stdin === true) {
|
|
@@ -3160,6 +3221,18 @@ async function secretsFromStdin(method, params, read, tty = false) {
|
|
|
3160
3221
|
return onLine ? warning("An API key") : void 0;
|
|
3161
3222
|
}
|
|
3162
3223
|
}
|
|
3224
|
+
/**
|
|
3225
|
+
* `wallet create --value`: the names given on the command line that the chosen source's form marks `kind: "secret"`
|
|
3226
|
+
* (an NWC URI, a macaroon, a rune, an RPC password), as a warning. `list`: a `wallet.list` answer, where the forms are.
|
|
3227
|
+
*/
|
|
3228
|
+
function secretValuesWarning(list, type, provider, names) {
|
|
3229
|
+
if (!names.length) return void 0;
|
|
3230
|
+
const id = provider ?? (type === "bitcoin" ? "bdk" : void 0);
|
|
3231
|
+
const fields = (list?.offers ?? []).find((o) => o.type === type && o.providers)?.providers?.find((p) => p.id === id)?.fields ?? [];
|
|
3232
|
+
const secret = names.filter((name) => fields.some((f) => f.name === name && f.kind === "secret"));
|
|
3233
|
+
if (!secret.length) return void 0;
|
|
3234
|
+
return `${secret.map((name) => `--value ${name}`).join(", ")} on the command line shows in ps and the shell's history: give ${secret.length > 1 ? "them" : "it"} on stdin instead (--stdin, name=value lines)`;
|
|
3235
|
+
}
|
|
3163
3236
|
//#endregion
|
|
3164
3237
|
//#region src/main.ts
|
|
3165
3238
|
var VERSION = package_default.version;
|
|
@@ -3168,8 +3241,8 @@ var VERSION = package_default.version;
|
|
|
3168
3241
|
* command a running daemon answers loads none of them, and starts about as fast as Node does. Statically imported, they
|
|
3169
3242
|
* cost every command ~100 ms before its socket was opened.
|
|
3170
3243
|
*/
|
|
3171
|
-
var engineApi = () => import("./api-
|
|
3172
|
-
var engineHost = () => import("./host-
|
|
3244
|
+
var engineApi = () => import("./api-BCwaCbKh.js");
|
|
3245
|
+
var engineHost = () => import("./host-C74Sy5jT.js");
|
|
3173
3246
|
var pretty = false;
|
|
3174
3247
|
function print(value) {
|
|
3175
3248
|
process.stdout.write(JSON.stringify(value, null, pretty ? 2 : void 0) + "\n");
|
|
@@ -3384,11 +3457,12 @@ var SPECIAL = [
|
|
|
3384
3457
|
],
|
|
3385
3458
|
["settings set <key> <json-value>", "Change one: relays, irohRelays, hyperdhtRelay, readRelays, iceServers, publicProfiles, online, shareProfile, sendTyping, autoDownloads, serveFiles, nick"],
|
|
3386
3459
|
[
|
|
3387
|
-
"engine <method> [json-params | -] [--confirm-real] [--show-secret]",
|
|
3460
|
+
"engine <method> [json-params | -] [--confirm-real] [--show-secret] [--force]",
|
|
3388
3461
|
"Any call of the app's engine, with its own parameters",
|
|
3389
3462
|
{
|
|
3390
3463
|
"confirm-real": o("boolean", "Confirm a Mainnet (real money) spend"),
|
|
3391
|
-
"show-secret": o("boolean", "Print a call's secrets")
|
|
3464
|
+
"show-secret": o("boolean", "Print a call's secrets"),
|
|
3465
|
+
force: o("boolean", "Send a text that looks like a secret")
|
|
3392
3466
|
}
|
|
3393
3467
|
],
|
|
3394
3468
|
["engine --list", "The engine's calls"],
|
|
@@ -3467,11 +3541,13 @@ function commandHelp(words) {
|
|
|
3467
3541
|
const own = commandWords(row.usage);
|
|
3468
3542
|
return list.every((word, i) => own[i] === word);
|
|
3469
3543
|
});
|
|
3544
|
+
const given = words;
|
|
3470
3545
|
let rows = named(words);
|
|
3471
3546
|
while (!rows.length && words.length > 1) rows = named(words = words.slice(0, -1));
|
|
3472
3547
|
const asked = words.join(" ");
|
|
3473
3548
|
if (!rows.length) throw new CliError("usage", `Unknown command: ${asked} (ghostly help)`);
|
|
3474
3549
|
const exact = rows.filter((row) => commandWords(row.usage).join(" ") === asked);
|
|
3550
|
+
if (!exact.length && words.length < given.length) throw unknownCommand(given[0], given[1]);
|
|
3475
3551
|
const lines = [];
|
|
3476
3552
|
for (const row of exact) {
|
|
3477
3553
|
lines.push(`Usage: ghostly ${row.usage}`, "", ` ${row.summary}`);
|
|
@@ -3611,12 +3687,13 @@ async function profileCommand(sub, argv) {
|
|
|
3611
3687
|
if (parsed.options.passphrase === true) throw new CliError("usage", "The passphrase comes from --passphrase-file or GHOSTLY_BACKUP_PASSPHRASE, never the command line");
|
|
3612
3688
|
if (unsealed && (parsed.options["passphrase-file"] !== void 0 || process.env.GHOSTLY_BACKUP_PASSPHRASE)) throw new CliError("usage", "A passphrase is set (--passphrase-file or GHOSTLY_BACKUP_PASSPHRASE) and --no-passphrase was given: choose one");
|
|
3613
3689
|
const light = parsed.options.light === true ? { light: true } : {};
|
|
3690
|
+
const path = here(out);
|
|
3614
3691
|
const params = unsealed ? {
|
|
3615
|
-
path
|
|
3692
|
+
path,
|
|
3616
3693
|
noPassphrase: true,
|
|
3617
3694
|
...light
|
|
3618
3695
|
} : {
|
|
3619
|
-
path
|
|
3696
|
+
path,
|
|
3620
3697
|
passphrase: backupPassphrase(parsed.options["passphrase-file"]),
|
|
3621
3698
|
...light
|
|
3622
3699
|
};
|
|
@@ -3629,7 +3706,7 @@ async function profileCommand(sub, argv) {
|
|
|
3629
3706
|
const [file, name] = parsed.positionals;
|
|
3630
3707
|
if (!file || !name) throw new CliError("usage", "ghostly profile restore <file> <new profile> [--passphrase-file f] [--use]");
|
|
3631
3708
|
noMoreThan(2, parsed.positionals, "profile restore <file> <new profile> [--passphrase-file f] [--use]");
|
|
3632
|
-
const { backupFileProtection, restoreProfile } = await import("./backup-
|
|
3709
|
+
const { backupFileProtection, restoreProfile } = await import("./backup-Cm8ptQQA.js");
|
|
3633
3710
|
const protection = await backupFileProtection(file);
|
|
3634
3711
|
const paths = await restoreProfile(g.home, checkProfileName(name), file, protection === "none" ? void 0 : backupPassphrase(parsed.options["passphrase-file"], RESTORE_PASSPHRASE));
|
|
3635
3712
|
if (parsed.options.use) selectProfile(g.home, name);
|
|
@@ -3640,7 +3717,7 @@ async function profileCommand(sub, argv) {
|
|
|
3640
3717
|
});
|
|
3641
3718
|
return;
|
|
3642
3719
|
}
|
|
3643
|
-
default: throw
|
|
3720
|
+
default: throw unknownCommand("profile", sub);
|
|
3644
3721
|
}
|
|
3645
3722
|
}
|
|
3646
3723
|
/**
|
|
@@ -3670,10 +3747,24 @@ function onStopSignals(stop) {
|
|
|
3670
3747
|
var BACKUP_PASSPHRASE = "The backup passphrase comes from --passphrase-file or GHOSTLY_BACKUP_PASSPHRASE (at least 12 characters). For a backup that is not encrypted, say so with --no-passphrase";
|
|
3671
3748
|
/** Restore takes no --no-passphrase: a backup made without one says so itself. */
|
|
3672
3749
|
var RESTORE_PASSPHRASE = "This backup is encrypted: its passphrase comes from --passphrase-file or GHOSTLY_BACKUP_PASSPHRASE";
|
|
3673
|
-
/**
|
|
3750
|
+
/**
|
|
3751
|
+
* The backup passphrase: from a file or the environment, never the command line (it would sit in shell history). The
|
|
3752
|
+
* file is said as every other file the CLI reads: "" names none, a folder is named, and an empty one is called that,
|
|
3753
|
+
* not "comes from --passphrase-file" to someone who gave it.
|
|
3754
|
+
*/
|
|
3674
3755
|
function backupPassphrase(file, missing = BACKUP_PASSPHRASE) {
|
|
3675
|
-
|
|
3676
|
-
|
|
3756
|
+
if (typeof file !== "string") {
|
|
3757
|
+
const value = process.env.GHOSTLY_BACKUP_PASSPHRASE;
|
|
3758
|
+
if (!value) throw new CliError("usage", missing);
|
|
3759
|
+
return value;
|
|
3760
|
+
}
|
|
3761
|
+
let value;
|
|
3762
|
+
try {
|
|
3763
|
+
value = readFileSync(here(file), "utf8").replace(/\r?\n$/, "");
|
|
3764
|
+
} catch (error) {
|
|
3765
|
+
throw localFileError(error, file) ?? error;
|
|
3766
|
+
}
|
|
3767
|
+
if (!value) throw new CliError("bad_request", `${file} is empty: it holds the backup's passphrase`);
|
|
3677
3768
|
return value;
|
|
3678
3769
|
}
|
|
3679
3770
|
async function runDaemon(g) {
|
|
@@ -3772,7 +3863,7 @@ async function daemonCommand(argv) {
|
|
|
3772
3863
|
});
|
|
3773
3864
|
return;
|
|
3774
3865
|
}
|
|
3775
|
-
if (sub) throw
|
|
3866
|
+
if (sub) throw unknownCommand("daemon", sub);
|
|
3776
3867
|
const startFor = seconds(parsed, 60);
|
|
3777
3868
|
if (!parsed.options.detach) {
|
|
3778
3869
|
await runDaemon(g);
|
|
@@ -3900,9 +3991,15 @@ async function listenCommand(argv) {
|
|
|
3900
3991
|
}
|
|
3901
3992
|
});
|
|
3902
3993
|
noMoreThan(0, parsed.positionals, SPECIAL.find(([usage]) => usage.startsWith("listen "))[0]);
|
|
3994
|
+
const blank = (value) => typeof value === "string" && value.trim() === "";
|
|
3995
|
+
if (blank(parsed.options.webhook)) throw new CliError("usage", "--webhook needs a URL");
|
|
3996
|
+
if (blank(parsed.options.exec)) throw new CliError("usage", "--exec needs a command");
|
|
3997
|
+
if (parsed.options.cursor === "") throw new CliError("usage", "--cursor needs a file");
|
|
3998
|
+
if (parsed.options.from?.some(blank)) throw new CliError("usage", "--from needs a chat or a contact key");
|
|
3999
|
+
if (parsed.options.group?.some(blank)) throw new CliError("usage", "--group needs a group");
|
|
3903
4000
|
const g = globals(parsed);
|
|
3904
4001
|
pretty = false;
|
|
3905
|
-
if (parsed.options.webhook) checkWebhook(String(parsed.options.webhook));
|
|
4002
|
+
if (parsed.options.webhook !== void 0) checkWebhook(String(parsed.options.webhook));
|
|
3906
4003
|
const types = parsed.options.type ?? [];
|
|
3907
4004
|
const turns = parsed.options.turns === true;
|
|
3908
4005
|
if (turns && types.length) throw new CliError("usage", "--turns takes the place of --type: a turn is a message received, or a group message that mentions you");
|
|
@@ -4042,6 +4139,10 @@ async function engineCommand(argv) {
|
|
|
4042
4139
|
type: "boolean",
|
|
4043
4140
|
description: ""
|
|
4044
4141
|
},
|
|
4142
|
+
force: {
|
|
4143
|
+
type: "boolean",
|
|
4144
|
+
description: ""
|
|
4145
|
+
},
|
|
4045
4146
|
list: {
|
|
4046
4147
|
type: "boolean",
|
|
4047
4148
|
description: ""
|
|
@@ -4071,7 +4172,8 @@ async function engineCommand(argv) {
|
|
|
4071
4172
|
let result = await withSession(g, (s) => s.call("engine.call", {
|
|
4072
4173
|
method,
|
|
4073
4174
|
params,
|
|
4074
|
-
confirmReal
|
|
4175
|
+
confirmReal,
|
|
4176
|
+
force: parsed.options.force === true
|
|
4075
4177
|
}), { offline: ENGINE_READS.includes(method) });
|
|
4076
4178
|
if (method === "getState" && !showSecret && result && typeof result === "object") {
|
|
4077
4179
|
const { hideStateSecrets } = await engineApi();
|
|
@@ -4104,7 +4206,7 @@ async function settingsCommand(sub, argv) {
|
|
|
4104
4206
|
printAnswer(await withSession(g, (s) => s.call("settings.set", { [key]: parsedValue })));
|
|
4105
4207
|
return;
|
|
4106
4208
|
}
|
|
4107
|
-
throw
|
|
4209
|
+
throw unknownCommand("settings", sub);
|
|
4108
4210
|
}
|
|
4109
4211
|
async function textCommand(name, argv) {
|
|
4110
4212
|
const spec = TEXT_COMMANDS[name];
|
|
@@ -4118,7 +4220,7 @@ async function textCommand(name, argv) {
|
|
|
4118
4220
|
if (parsed.options.text !== void 0) {
|
|
4119
4221
|
if (text || parsed.options.stdin) throw new CliError("usage", "Give the text once: as arguments, --text or stdin");
|
|
4120
4222
|
text = String(parsed.options.text);
|
|
4121
|
-
} else if (parsed.options.stdin ||
|
|
4223
|
+
} else if (parsed.options.stdin || words.length === 0 && !process.stdin.isTTY) {
|
|
4122
4224
|
if (text) throw new CliError("usage", "Give the text as arguments or on stdin, not both");
|
|
4123
4225
|
text = (await readStdin()).replace(/\n$/, "");
|
|
4124
4226
|
}
|
|
@@ -4131,7 +4233,7 @@ async function textCommand(name, argv) {
|
|
|
4131
4233
|
};
|
|
4132
4234
|
if (spec.target === "group") params.mentions = parsed.options.mention ?? [];
|
|
4133
4235
|
if (parsed.options.reply !== void 0) params.reply = parsed.options.reply;
|
|
4134
|
-
if (!spec.message) Object.assign(params, (await import("./buttons-
|
|
4236
|
+
if (!spec.message) Object.assign(params, (await import("./buttons-PCvHRlkS.js")).buttonsOf(parsed.options));
|
|
4135
4237
|
print(await withSession(g, (s) => {
|
|
4136
4238
|
if (spec.target === "chat") {
|
|
4137
4239
|
params.wait = parsed.options.wait ?? (s.mode === "one-shot" ? spec.message ? "confirmed" : "sent" : "none");
|
|
@@ -4230,14 +4332,23 @@ async function tableCommand(name, argv) {
|
|
|
4230
4332
|
const params = command.params?.(parsed, args) ?? {};
|
|
4231
4333
|
for (const key of Object.keys(params)) if (params[key] === void 0) delete params[key];
|
|
4232
4334
|
if (command.method === "identity.complete") {
|
|
4233
|
-
if (params.evidenceFile)
|
|
4335
|
+
if (params.evidenceFile) try {
|
|
4336
|
+
params.evidence = readFileSync(params.evidenceFile, "utf8");
|
|
4337
|
+
} catch (error) {
|
|
4338
|
+
throw localFileError(error, params.evidenceFile) ?? error;
|
|
4339
|
+
}
|
|
4234
4340
|
else if (params.stdin) params.evidence = await readStdin();
|
|
4235
4341
|
delete params.evidenceFile;
|
|
4236
4342
|
delete params.stdin;
|
|
4237
4343
|
}
|
|
4344
|
+
const lineValues = command.method === "wallet.create" ? Object.keys(params.values ?? {}) : [];
|
|
4238
4345
|
const secretWarning = await secretsFromStdin(command.method, params, readStdin, process.stdin.isTTY === true);
|
|
4239
4346
|
if (secretWarning) process.stderr.write(`ghostly: ${secretWarning}\n`);
|
|
4240
4347
|
printAnswer(await withSession(g, async (s) => {
|
|
4348
|
+
if (lineValues.length) {
|
|
4349
|
+
const warning = secretValuesWarning(await s.call("wallet.list", { network: params.network ?? "testnet" }).catch(() => void 0), params.type, params.provider, lineValues);
|
|
4350
|
+
if (warning) process.stderr.write(`ghostly: ${warning}\n`);
|
|
4351
|
+
}
|
|
4241
4352
|
const result = await s.call(command.method, params);
|
|
4242
4353
|
if (command.method === "invite.join" && s.mode === "one-shot") await s.call("chat.wait", {
|
|
4243
4354
|
chat: result.chat,
|
|
@@ -4406,4 +4517,4 @@ async function run(argv) {
|
|
|
4406
4517
|
//#endregion
|
|
4407
4518
|
export { run };
|
|
4408
4519
|
|
|
4409
|
-
//# sourceMappingURL=main-
|
|
4520
|
+
//# sourceMappingURL=main-DNzQOdNG.js.map
|