@ghostlytools/cli 1.3.1 → 1.3.3
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 +6 -6
- package/dist/assets/{api-Dq2QXQDk.js → api-B1fADtqd.js} +65 -20
- package/dist/assets/api-B1fADtqd.js.map +1 -0
- package/dist/assets/{api-TiB5QSG9.js → api-Bg7bh_6E.js} +2 -2
- package/dist/assets/{apiKit-CA2x5GJF.js → apiKit-BsUZPHLn.js} +44 -33
- package/dist/assets/apiKit-BsUZPHLn.js.map +1 -0
- package/dist/assets/{apps-BxV9i_Y4.js → apps-BaUNt-5c.js} +91 -24
- package/dist/assets/apps-BaUNt-5c.js.map +1 -0
- package/dist/assets/{apps-C-T8jeji.js → apps-DepomCiX.js} +1 -1
- package/dist/assets/{arkade-C_qDmHnm.js → arkade-CNnMaoEQ.js} +4 -4
- package/dist/assets/arkade-CNnMaoEQ.js.map +1 -0
- package/dist/assets/{backup-Bugnn3Ue.js → backup-B5Q1W-Ey.js} +1 -1
- package/dist/assets/{backup-CnMN2oUK.js → backup-Bbz3lyC5.js} +6 -6
- package/dist/assets/{backup-CnMN2oUK.js.map → backup-Bbz3lyC5.js.map} +1 -1
- package/dist/assets/buttons-CUd7j0ZA.js +2 -0
- package/dist/assets/{buttons-CvNBzh2S.js → buttons-DS1iURp8.js} +4 -4
- package/dist/assets/{buttons-CvNBzh2S.js.map → buttons-DS1iURp8.js.map} +1 -1
- package/dist/assets/{envelope-WjingYSc.js → envelope-fNC941z4.js} +2 -2
- package/dist/assets/{envelope-WjingYSc.js.map → envelope-fNC941z4.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/{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-IEUW3DQz.js → groupFiles-6qnLCnOm.js} +82 -29
- package/dist/assets/groupFiles-6qnLCnOm.js.map +1 -0
- package/dist/assets/{host-CWt-jtqz.js → host-Dj3C1fn3.js} +305 -68
- package/dist/assets/host-Dj3C1fn3.js.map +1 -0
- package/dist/assets/{hyperdhtRelay-CPYwSLLS.js → hyperdhtRelay-CrgYQN-g.js} +2 -2
- package/dist/assets/{hyperdhtRelay-CPYwSLLS.js.map → hyperdhtRelay-CrgYQN-g.js.map} +1 -1
- package/dist/assets/{irohWeb-4oChIOIp.js → irohWeb-DSyLp5Re.js} +3 -3
- package/dist/assets/{irohWeb-4oChIOIp.js.map → irohWeb-DSyLp5Re.js.map} +1 -1
- package/dist/assets/{main-DbqcdShN.js → main-BDd11Yd1.js} +159 -54
- package/dist/assets/main-BDd11Yd1.js.map +1 -0
- package/dist/assets/{mainline-ZkiOnevs.js → mainline-CLTo0nGa.js} +2 -2
- package/dist/assets/{mainline-ZkiOnevs.js.map → mainline-CLTo0nGa.js.map} +1 -1
- package/dist/assets/{networkReason-CetKkTPo.js → networkReason-DwcadZz5.js} +2 -2
- package/dist/assets/{networkReason-CetKkTPo.js.map → networkReason-DwcadZz5.js.map} +1 -1
- package/dist/assets/{nwc-CgkqyvsE.js → nwc-BNFyyECH.js} +3 -3
- package/dist/assets/{nwc-CgkqyvsE.js.map → nwc-BNFyyECH.js.map} +1 -1
- package/dist/assets/{nwcProvider-4eVz_8HP.js → nwcProvider-BbjJOfLD.js} +2 -2
- package/dist/assets/{nwcProvider-4eVz_8HP.js.map → nwcProvider-BbjJOfLD.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-DlAKO0Fy.js → registry-CoLHpxkb.js} +17 -32
- package/dist/assets/registry-CoLHpxkb.js.map +1 -0
- package/dist/assets/{registry-OBJBnHVQ.js → registry-DoTb6uEr.js} +1 -1
- package/dist/assets/{resolve-CouHhCpJ.js → resolve-BIDJ-UUl.js} +18 -3
- package/dist/assets/resolve-BIDJ-UUl.js.map +1 -0
- package/dist/assets/{server-Ss0gJ-nw.js → server-Bx3RGkBC.js} +380 -116
- package/dist/assets/server-Bx3RGkBC.js.map +1 -0
- package/dist/assets/{src-DDzSERyc.js → src-D7gcJElT.js} +470 -242
- package/dist/assets/src-D7gcJElT.js.map +1 -0
- package/dist/assets/{turnRefs-B87dtrHM.js → turnRefs-PN644M9o.js} +2 -2
- package/dist/assets/{turnRefs-B87dtrHM.js.map → turnRefs-PN644M9o.js.map} +1 -1
- package/dist/assets/{usdt-Fj8CQO2z.js → usdt-B2TLRlYa.js} +3 -3
- package/dist/assets/{usdt-Fj8CQO2z.js.map → usdt-B2TLRlYa.js.map} +1 -1
- package/dist/ghostly.mjs +1 -1
- package/package.json +1 -1
- package/dist/assets/api-Dq2QXQDk.js.map +0 -1
- package/dist/assets/apiKit-CA2x5GJF.js.map +0 -1
- package/dist/assets/apps-BxV9i_Y4.js.map +0 -1
- package/dist/assets/arkade-C_qDmHnm.js.map +0 -1
- package/dist/assets/buttons-Bj7Vf7Pn.js +0 -2
- package/dist/assets/ghostly_iroh_web-Dvzh3Y6E.js.map +0 -1
- package/dist/assets/groupFiles-IEUW3DQz.js.map +0 -1
- package/dist/assets/host-CWt-jtqz.js.map +0 -1
- package/dist/assets/main-DbqcdShN.js.map +0 -1
- package/dist/assets/registry-DlAKO0Fy.js.map +0 -1
- package/dist/assets/resolve-CouHhCpJ.js.map +0 -1
- package/dist/assets/server-Ss0gJ-nw.js.map +0 -1
- package/dist/assets/src-DDzSERyc.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-D7gcJElT.js";
|
|
2
|
+
import { a as SECRET_RESULTS, i as ENGINE_READS, r as ENGINE_METHODS, t as turnRef } from "./turnRefs-PN644M9o.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-BsUZPHLn.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.3",
|
|
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-DepomCiX.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-DepomCiX.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-DepomCiX.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-DepomCiX.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-DepomCiX.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-Bg7bh_6E.js");
|
|
3245
|
+
var engineHost = () => import("./host-Dj3C1fn3.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");
|
|
@@ -3467,11 +3540,13 @@ function commandHelp(words) {
|
|
|
3467
3540
|
const own = commandWords(row.usage);
|
|
3468
3541
|
return list.every((word, i) => own[i] === word);
|
|
3469
3542
|
});
|
|
3543
|
+
const given = words;
|
|
3470
3544
|
let rows = named(words);
|
|
3471
3545
|
while (!rows.length && words.length > 1) rows = named(words = words.slice(0, -1));
|
|
3472
3546
|
const asked = words.join(" ");
|
|
3473
3547
|
if (!rows.length) throw new CliError("usage", `Unknown command: ${asked} (ghostly help)`);
|
|
3474
3548
|
const exact = rows.filter((row) => commandWords(row.usage).join(" ") === asked);
|
|
3549
|
+
if (!exact.length && words.length < given.length) throw unknownCommand(given[0], given[1]);
|
|
3475
3550
|
const lines = [];
|
|
3476
3551
|
for (const row of exact) {
|
|
3477
3552
|
lines.push(`Usage: ghostly ${row.usage}`, "", ` ${row.summary}`);
|
|
@@ -3611,12 +3686,13 @@ async function profileCommand(sub, argv) {
|
|
|
3611
3686
|
if (parsed.options.passphrase === true) throw new CliError("usage", "The passphrase comes from --passphrase-file or GHOSTLY_BACKUP_PASSPHRASE, never the command line");
|
|
3612
3687
|
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
3688
|
const light = parsed.options.light === true ? { light: true } : {};
|
|
3689
|
+
const path = here(out);
|
|
3614
3690
|
const params = unsealed ? {
|
|
3615
|
-
path
|
|
3691
|
+
path,
|
|
3616
3692
|
noPassphrase: true,
|
|
3617
3693
|
...light
|
|
3618
3694
|
} : {
|
|
3619
|
-
path
|
|
3695
|
+
path,
|
|
3620
3696
|
passphrase: backupPassphrase(parsed.options["passphrase-file"]),
|
|
3621
3697
|
...light
|
|
3622
3698
|
};
|
|
@@ -3629,7 +3705,7 @@ async function profileCommand(sub, argv) {
|
|
|
3629
3705
|
const [file, name] = parsed.positionals;
|
|
3630
3706
|
if (!file || !name) throw new CliError("usage", "ghostly profile restore <file> <new profile> [--passphrase-file f] [--use]");
|
|
3631
3707
|
noMoreThan(2, parsed.positionals, "profile restore <file> <new profile> [--passphrase-file f] [--use]");
|
|
3632
|
-
const { backupFileProtection, restoreProfile } = await import("./backup-
|
|
3708
|
+
const { backupFileProtection, restoreProfile } = await import("./backup-B5Q1W-Ey.js");
|
|
3633
3709
|
const protection = await backupFileProtection(file);
|
|
3634
3710
|
const paths = await restoreProfile(g.home, checkProfileName(name), file, protection === "none" ? void 0 : backupPassphrase(parsed.options["passphrase-file"], RESTORE_PASSPHRASE));
|
|
3635
3711
|
if (parsed.options.use) selectProfile(g.home, name);
|
|
@@ -3640,7 +3716,7 @@ async function profileCommand(sub, argv) {
|
|
|
3640
3716
|
});
|
|
3641
3717
|
return;
|
|
3642
3718
|
}
|
|
3643
|
-
default: throw
|
|
3719
|
+
default: throw unknownCommand("profile", sub);
|
|
3644
3720
|
}
|
|
3645
3721
|
}
|
|
3646
3722
|
/**
|
|
@@ -3670,10 +3746,24 @@ function onStopSignals(stop) {
|
|
|
3670
3746
|
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
3747
|
/** Restore takes no --no-passphrase: a backup made without one says so itself. */
|
|
3672
3748
|
var RESTORE_PASSPHRASE = "This backup is encrypted: its passphrase comes from --passphrase-file or GHOSTLY_BACKUP_PASSPHRASE";
|
|
3673
|
-
/**
|
|
3749
|
+
/**
|
|
3750
|
+
* The backup passphrase: from a file or the environment, never the command line (it would sit in shell history). The
|
|
3751
|
+
* file is said as every other file the CLI reads: "" names none, a folder is named, and an empty one is called that,
|
|
3752
|
+
* not "comes from --passphrase-file" to someone who gave it.
|
|
3753
|
+
*/
|
|
3674
3754
|
function backupPassphrase(file, missing = BACKUP_PASSPHRASE) {
|
|
3675
|
-
|
|
3676
|
-
|
|
3755
|
+
if (typeof file !== "string") {
|
|
3756
|
+
const value = process.env.GHOSTLY_BACKUP_PASSPHRASE;
|
|
3757
|
+
if (!value) throw new CliError("usage", missing);
|
|
3758
|
+
return value;
|
|
3759
|
+
}
|
|
3760
|
+
let value;
|
|
3761
|
+
try {
|
|
3762
|
+
value = readFileSync(here(file), "utf8").replace(/\r?\n$/, "");
|
|
3763
|
+
} catch (error) {
|
|
3764
|
+
throw localFileError(error, file) ?? error;
|
|
3765
|
+
}
|
|
3766
|
+
if (!value) throw new CliError("bad_request", `${file} is empty: it holds the backup's passphrase`);
|
|
3677
3767
|
return value;
|
|
3678
3768
|
}
|
|
3679
3769
|
async function runDaemon(g) {
|
|
@@ -3772,7 +3862,7 @@ async function daemonCommand(argv) {
|
|
|
3772
3862
|
});
|
|
3773
3863
|
return;
|
|
3774
3864
|
}
|
|
3775
|
-
if (sub) throw
|
|
3865
|
+
if (sub) throw unknownCommand("daemon", sub);
|
|
3776
3866
|
const startFor = seconds(parsed, 60);
|
|
3777
3867
|
if (!parsed.options.detach) {
|
|
3778
3868
|
await runDaemon(g);
|
|
@@ -3900,9 +3990,15 @@ async function listenCommand(argv) {
|
|
|
3900
3990
|
}
|
|
3901
3991
|
});
|
|
3902
3992
|
noMoreThan(0, parsed.positionals, SPECIAL.find(([usage]) => usage.startsWith("listen "))[0]);
|
|
3993
|
+
const blank = (value) => typeof value === "string" && value.trim() === "";
|
|
3994
|
+
if (blank(parsed.options.webhook)) throw new CliError("usage", "--webhook needs a URL");
|
|
3995
|
+
if (blank(parsed.options.exec)) throw new CliError("usage", "--exec needs a command");
|
|
3996
|
+
if (parsed.options.cursor === "") throw new CliError("usage", "--cursor needs a file");
|
|
3997
|
+
if (parsed.options.from?.some(blank)) throw new CliError("usage", "--from needs a chat or a contact key");
|
|
3998
|
+
if (parsed.options.group?.some(blank)) throw new CliError("usage", "--group needs a group");
|
|
3903
3999
|
const g = globals(parsed);
|
|
3904
4000
|
pretty = false;
|
|
3905
|
-
if (parsed.options.webhook) checkWebhook(String(parsed.options.webhook));
|
|
4001
|
+
if (parsed.options.webhook !== void 0) checkWebhook(String(parsed.options.webhook));
|
|
3906
4002
|
const types = parsed.options.type ?? [];
|
|
3907
4003
|
const turns = parsed.options.turns === true;
|
|
3908
4004
|
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");
|
|
@@ -4104,7 +4200,7 @@ async function settingsCommand(sub, argv) {
|
|
|
4104
4200
|
printAnswer(await withSession(g, (s) => s.call("settings.set", { [key]: parsedValue })));
|
|
4105
4201
|
return;
|
|
4106
4202
|
}
|
|
4107
|
-
throw
|
|
4203
|
+
throw unknownCommand("settings", sub);
|
|
4108
4204
|
}
|
|
4109
4205
|
async function textCommand(name, argv) {
|
|
4110
4206
|
const spec = TEXT_COMMANDS[name];
|
|
@@ -4118,7 +4214,7 @@ async function textCommand(name, argv) {
|
|
|
4118
4214
|
if (parsed.options.text !== void 0) {
|
|
4119
4215
|
if (text || parsed.options.stdin) throw new CliError("usage", "Give the text once: as arguments, --text or stdin");
|
|
4120
4216
|
text = String(parsed.options.text);
|
|
4121
|
-
} else if (parsed.options.stdin ||
|
|
4217
|
+
} else if (parsed.options.stdin || words.length === 0 && !process.stdin.isTTY) {
|
|
4122
4218
|
if (text) throw new CliError("usage", "Give the text as arguments or on stdin, not both");
|
|
4123
4219
|
text = (await readStdin()).replace(/\n$/, "");
|
|
4124
4220
|
}
|
|
@@ -4131,7 +4227,7 @@ async function textCommand(name, argv) {
|
|
|
4131
4227
|
};
|
|
4132
4228
|
if (spec.target === "group") params.mentions = parsed.options.mention ?? [];
|
|
4133
4229
|
if (parsed.options.reply !== void 0) params.reply = parsed.options.reply;
|
|
4134
|
-
if (!spec.message) Object.assign(params, (await import("./buttons-
|
|
4230
|
+
if (!spec.message) Object.assign(params, (await import("./buttons-CUd7j0ZA.js")).buttonsOf(parsed.options));
|
|
4135
4231
|
print(await withSession(g, (s) => {
|
|
4136
4232
|
if (spec.target === "chat") {
|
|
4137
4233
|
params.wait = parsed.options.wait ?? (s.mode === "one-shot" ? spec.message ? "confirmed" : "sent" : "none");
|
|
@@ -4230,14 +4326,23 @@ async function tableCommand(name, argv) {
|
|
|
4230
4326
|
const params = command.params?.(parsed, args) ?? {};
|
|
4231
4327
|
for (const key of Object.keys(params)) if (params[key] === void 0) delete params[key];
|
|
4232
4328
|
if (command.method === "identity.complete") {
|
|
4233
|
-
if (params.evidenceFile)
|
|
4329
|
+
if (params.evidenceFile) try {
|
|
4330
|
+
params.evidence = readFileSync(params.evidenceFile, "utf8");
|
|
4331
|
+
} catch (error) {
|
|
4332
|
+
throw localFileError(error, params.evidenceFile) ?? error;
|
|
4333
|
+
}
|
|
4234
4334
|
else if (params.stdin) params.evidence = await readStdin();
|
|
4235
4335
|
delete params.evidenceFile;
|
|
4236
4336
|
delete params.stdin;
|
|
4237
4337
|
}
|
|
4338
|
+
const lineValues = command.method === "wallet.create" ? Object.keys(params.values ?? {}) : [];
|
|
4238
4339
|
const secretWarning = await secretsFromStdin(command.method, params, readStdin, process.stdin.isTTY === true);
|
|
4239
4340
|
if (secretWarning) process.stderr.write(`ghostly: ${secretWarning}\n`);
|
|
4240
4341
|
printAnswer(await withSession(g, async (s) => {
|
|
4342
|
+
if (lineValues.length) {
|
|
4343
|
+
const warning = secretValuesWarning(await s.call("wallet.list", { network: params.network ?? "testnet" }).catch(() => void 0), params.type, params.provider, lineValues);
|
|
4344
|
+
if (warning) process.stderr.write(`ghostly: ${warning}\n`);
|
|
4345
|
+
}
|
|
4241
4346
|
const result = await s.call(command.method, params);
|
|
4242
4347
|
if (command.method === "invite.join" && s.mode === "one-shot") await s.call("chat.wait", {
|
|
4243
4348
|
chat: result.chat,
|
|
@@ -4406,4 +4511,4 @@ async function run(argv) {
|
|
|
4406
4511
|
//#endregion
|
|
4407
4512
|
export { run };
|
|
4408
4513
|
|
|
4409
|
-
//# sourceMappingURL=main-
|
|
4514
|
+
//# sourceMappingURL=main-BDd11Yd1.js.map
|