@ghostlytools/cli 1.3.6 → 1.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +15 -6
  2. package/SKILL.md +2 -0
  3. package/dist/assets/{api-Br3msjtR.js → api-CYG-sByy.js} +2 -2
  4. package/dist/assets/{api-z9mlywrn.js → api-Q6QZ0oIy.js} +51 -25
  5. package/dist/assets/api-Q6QZ0oIy.js.map +1 -0
  6. package/dist/assets/{apiKit-lX3Xx_hY.js → apiKit-C9lLXQwt.js} +2 -2
  7. package/dist/assets/{apiKit-lX3Xx_hY.js.map → apiKit-C9lLXQwt.js.map} +1 -1
  8. package/dist/assets/{apps-Dn7rCafG.js → apps-Bs8xpLGv.js} +1 -1
  9. package/dist/assets/{apps-BUK6bEKW.js → apps-tcynvhlC.js} +2 -2
  10. package/dist/assets/{apps-BUK6bEKW.js.map → apps-tcynvhlC.js.map} +1 -1
  11. package/dist/assets/{arkade-uqAXA5BT.js → arkade-BgmB0fUk.js} +2 -2
  12. package/dist/assets/{arkade-uqAXA5BT.js.map → arkade-BgmB0fUk.js.map} +1 -1
  13. package/dist/assets/{backup-_JnqeFZb.js → backup-OlEHNkqQ.js} +1 -1
  14. package/dist/assets/{backup-CJQK5Miu.js → backup-XtMACsQ0.js} +4 -4
  15. package/dist/assets/{backup-CJQK5Miu.js.map → backup-XtMACsQ0.js.map} +1 -1
  16. package/dist/assets/buttons-BoGOYSHb.js +2 -0
  17. package/dist/assets/{buttons-DDvTuViY.js → buttons-mdl2Gol2.js} +3 -3
  18. package/dist/assets/{buttons-DDvTuViY.js.map → buttons-mdl2Gol2.js.map} +1 -1
  19. package/dist/assets/daemonProof-B53VnZ1Z.js +100 -0
  20. package/dist/assets/daemonProof-B53VnZ1Z.js.map +1 -0
  21. package/dist/assets/{envelope-BqvBb6hC.js → envelope-DHf3mrHv.js} +2 -2
  22. package/dist/assets/{envelope-BqvBb6hC.js.map → envelope-DHf3mrHv.js.map} +1 -1
  23. package/dist/assets/{groupFiles-B1lL6wch.js → groupFiles-CqrujNN1.js} +7 -4
  24. package/dist/assets/groupFiles-CqrujNN1.js.map +1 -0
  25. package/dist/assets/{host-DFzlKU3P.js → host-Mz74S9cM.js} +50 -12
  26. package/dist/assets/host-Mz74S9cM.js.map +1 -0
  27. package/dist/assets/{hyperdhtRelay-DwlOY6aD.js → hyperdhtRelay-BnqdXvmv.js} +2 -2
  28. package/dist/assets/{hyperdhtRelay-DwlOY6aD.js.map → hyperdhtRelay-BnqdXvmv.js.map} +1 -1
  29. package/dist/assets/{irohWeb-DZDCyL9b.js → irohWeb-C8iKFl8d.js} +2 -2
  30. package/dist/assets/{irohWeb-DZDCyL9b.js.map → irohWeb-C8iKFl8d.js.map} +1 -1
  31. package/dist/assets/{main-DgbdDDif.js → main-BXwlJall.js} +149 -52
  32. package/dist/assets/main-BXwlJall.js.map +1 -0
  33. package/dist/assets/{mainline-BHE5-R3A.js → mainline-CbrPHUrZ.js} +2 -2
  34. package/dist/assets/{mainline-BHE5-R3A.js.map → mainline-CbrPHUrZ.js.map} +1 -1
  35. package/dist/assets/{networkReason-Btv4b2C4.js → networkReason-CrrB031Y.js} +2 -2
  36. package/dist/assets/{networkReason-Btv4b2C4.js.map → networkReason-CrrB031Y.js.map} +1 -1
  37. package/dist/assets/{nwc-z0pa5zql.js → nwc-Cu5zwrE8.js} +3 -3
  38. package/dist/assets/{nwc-z0pa5zql.js.map → nwc-Cu5zwrE8.js.map} +1 -1
  39. package/dist/assets/{nwcProvider-C1AhPoFW.js → nwcProvider-BVVFfJM5.js} +2 -2
  40. package/dist/assets/{nwcProvider-C1AhPoFW.js.map → nwcProvider-BVVFfJM5.js.map} +1 -1
  41. package/dist/assets/{registry-JrnwUWmh.js → registry-BKm8N_TI.js} +3 -3
  42. package/dist/assets/{registry-JrnwUWmh.js.map → registry-BKm8N_TI.js.map} +1 -1
  43. package/dist/assets/{registry-C09xXyx-.js → registry-CshC1EyV.js} +1 -1
  44. package/dist/assets/{resolve-Ssq1nDy4.js → resolve-DKGaPeMz.js} +2 -2
  45. package/dist/assets/{resolve-Ssq1nDy4.js.map → resolve-DKGaPeMz.js.map} +1 -1
  46. package/dist/assets/{server-D6FSznK5.js → server-CaDVxJKy.js} +53 -21
  47. package/dist/assets/{server-D6FSznK5.js.map → server-CaDVxJKy.js.map} +1 -1
  48. package/dist/assets/{src-XhjSWnp-.js → src-CVYir6mP.js} +152 -18
  49. package/dist/assets/src-CVYir6mP.js.map +1 -0
  50. package/dist/assets/{turnRefs-Cle33IoV.js → turnRefs-BeWsTX0o.js} +2 -2
  51. package/dist/assets/{turnRefs-Cle33IoV.js.map → turnRefs-BeWsTX0o.js.map} +1 -1
  52. package/dist/assets/{usdt-DO86yko2.js → usdt-C5Q0BWHr.js} +3 -3
  53. package/dist/assets/{usdt-DO86yko2.js.map → usdt-C5Q0BWHr.js.map} +1 -1
  54. package/dist/ghostly.mjs +1 -1
  55. package/package.json +1 -1
  56. package/dist/assets/api-z9mlywrn.js.map +0 -1
  57. package/dist/assets/buttons-DDB9Xdha.js +0 -2
  58. package/dist/assets/groupFiles-B1lL6wch.js.map +0 -1
  59. package/dist/assets/host-DFzlKU3P.js.map +0 -1
  60. package/dist/assets/main-DgbdDDif.js.map +0 -1
  61. package/dist/assets/privateFolder-Bk9C9oFv.js +0 -38
  62. package/dist/assets/privateFolder-Bk9C9oFv.js.map +0 -1
  63. package/dist/assets/src-XhjSWnp-.js.map +0 -1
@@ -1,9 +1,9 @@
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-XhjSWnp-.js";
2
- import { a as SECRET_RESULTS, i as ENGINE_READS, r as ENGINE_METHODS, t as turnRef } from "./turnRefs-Cle33IoV.js";
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-CVYir6mP.js";
2
+ import { a as SECRET_RESULTS, i as ENGINE_READS, r as ENGINE_METHODS, t as turnRef } from "./turnRefs-BeWsTX0o.js";
3
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-lX3Xx_hY.js";
4
+ import { t as CONTACT_KEY } from "./apiKit-C9lLXQwt.js";
5
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
- import { n as ownSocket } from "./privateFolder-Bk9C9oFv.js";
6
+ import { a as provesDaemon, c as ownSocket, o as readDaemonSecret, r as newNonce, t as HELLO } from "./daemonProof-B53VnZ1Z.js";
7
7
  import { accessSync, constants, existsSync, openSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
8
8
  import { dirname, resolve } from "node:path";
9
9
  import { spawn } from "node:child_process";
@@ -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.6",
15
+ version: "1.3.8",
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",
@@ -230,18 +230,45 @@ function liftGlobals(argv) {
230
230
  }
231
231
  //#endregion
232
232
  //#region src/client.ts
233
+ /** The id of the hello on every connection: a string, so it is never one of the calls' numbers. */
234
+ var HELLO_ID = "hello";
235
+ /** The refusal of a listener that did not prove it is the profile's daemon (`isNotTheDaemon` tells it apart). */
236
+ function notTheDaemon(socket) {
237
+ return new CliError("refused", `What answers on ${socket} did not prove it is this profile's daemon, so nothing was sent to it. A daemon of an older release cannot: \`ghostly daemon restart\` starts this one's`, { reason: "not-the-daemon" });
238
+ }
239
+ function isNotTheDaemon(error) {
240
+ return error instanceof CliError && error.code === "refused" && error.details?.reason === "not-the-daemon";
241
+ }
233
242
  /**
234
243
  * A connection to a profile's daemon over its socket (WISP 1100 § Local control API). `null` from `connectDaemon`
235
- * means no daemon answers there: a one-shot command runs the profile itself.
244
+ * means no daemon answers there: a one-shot command runs the profile itself. *
245
+ * The first line on it is a `daemon.hello`, and no call is written until its answer proves the other end read the
246
+ * profile's daemon secret (`daemonProof.ts`): until then every call waits, and a listener that does not prove it gets
247
+ * every call refused (`refused`, `isNotTheDaemon`) having read nothing but the hello. Nothing it writes before that
248
+ * is read either.
236
249
  */
237
250
  var DaemonClient = class {
238
251
  socket;
252
+ address;
239
253
  nextId = 1;
240
254
  pending = /* @__PURE__ */ new Map();
241
255
  eventListener = null;
242
256
  closedListener = null;
243
- constructor(socket) {
257
+ verified = false;
258
+ refusal = null;
259
+ ready;
260
+ settle;
261
+ constructor(socket, address) {
244
262
  this.socket = socket;
263
+ this.address = address;
264
+ this.ready = new Promise((resolve, reject) => {
265
+ this.settle = {
266
+ resolve,
267
+ reject
268
+ };
269
+ });
270
+ this.ready.catch(() => {});
271
+ const nonce = newNonce();
245
272
  createInterface({
246
273
  input: socket,
247
274
  crlfDelay: Infinity
@@ -252,6 +279,17 @@ var DaemonClient = class {
252
279
  } catch {
253
280
  return;
254
281
  }
282
+ if (!this.verified) {
283
+ if (this.refusal || message?.id !== HELLO_ID) return;
284
+ const secret = readDaemonSecret(this.address.dir);
285
+ if (!message.error && secret && provesDaemon(secret, nonce, message.result)) {
286
+ this.verified = true;
287
+ this.settle.resolve();
288
+ return;
289
+ }
290
+ this.refuse();
291
+ return;
292
+ }
255
293
  if (message.event) {
256
294
  this.eventListener?.(message.event);
257
295
  return;
@@ -263,24 +301,41 @@ var DaemonClient = class {
263
301
  else waiting.resolve(message.result);
264
302
  });
265
303
  socket.on("close", () => {
266
- for (const waiting of this.pending.values()) waiting.reject(new CliError("unavailable", "The daemon went away"));
304
+ const why = this.refusal ?? new CliError("unavailable", "The daemon went away");
305
+ if (!this.verified) this.settle.reject(why);
306
+ for (const waiting of this.pending.values()) waiting.reject(why);
267
307
  this.pending.clear();
268
308
  this.closedListener?.();
269
309
  });
270
310
  socket.on("error", () => {});
311
+ socket.write(JSON.stringify({
312
+ id: HELLO_ID,
313
+ method: HELLO,
314
+ params: { nonce }
315
+ }) + "\n");
316
+ }
317
+ refuse() {
318
+ this.refusal = notTheDaemon(this.address.socket);
319
+ this.settle.reject(this.refusal);
320
+ this.socket.destroy();
271
321
  }
272
322
  call(method, params = {}) {
323
+ if (this.socket.destroyed && this.refusal) return Promise.reject(this.refusal);
273
324
  const id = this.nextId++;
274
325
  return new Promise((resolve, reject) => {
275
326
  this.pending.set(id, {
276
327
  resolve,
277
328
  reject
278
329
  });
279
- this.socket.write(JSON.stringify({
280
- id,
281
- method,
282
- params
283
- }) + "\n");
330
+ this.ready.then(() => {
331
+ if (this.pending.has(id) && !this.socket.destroyed) this.socket.write(JSON.stringify({
332
+ id,
333
+ method,
334
+ params
335
+ }) + "\n");
336
+ }, (error) => {
337
+ if (this.pending.delete(id)) reject(error);
338
+ });
284
339
  });
285
340
  }
286
341
  /** Events after `since` (replayed from the journal), then every new one, until the connection closes. */
@@ -297,7 +352,12 @@ var DaemonClient = class {
297
352
  this.socket.destroy();
298
353
  }
299
354
  };
300
- function connectDaemon(path, timeoutMs = 2e3) {
355
+ /**
356
+ * `timeoutMs` bounds the connect only. The client it gives has sent its hello; whether the listener is the daemon is
357
+ * known at the first call (above).
358
+ */
359
+ function connectDaemon(address, timeoutMs = 2e3) {
360
+ const path = address.socket;
301
361
  if (!ownSocket(path)) return Promise.resolve(null);
302
362
  return new Promise((resolve) => {
303
363
  const socket = connect(path);
@@ -307,7 +367,7 @@ function connectDaemon(path, timeoutMs = 2e3) {
307
367
  }, timeoutMs);
308
368
  socket.once("connect", () => {
309
369
  clearTimeout(timer);
310
- resolve(new DaemonClient(socket));
370
+ resolve(new DaemonClient(socket, address));
311
371
  });
312
372
  socket.once("error", () => {
313
373
  clearTimeout(timer);
@@ -882,7 +942,7 @@ var commands$8 = {
882
942
  description: "Replace the three files when they are there"
883
943
  }
884
944
  },
885
- run: async ({ options }, a) => (await import("./apps-Dn7rCafG.js")).initApp(a.dir, {
945
+ run: async ({ options }, a) => (await import("./apps-Bs8xpLGv.js")).initApp(a.dir, {
886
946
  name: options.name,
887
947
  title: options.title,
888
948
  force: options.force === true
@@ -922,7 +982,7 @@ var commands$8 = {
922
982
  description: "This version's sequence, higher than the bundle at --out's (default: one more than it)"
923
983
  }
924
984
  },
925
- run: async ({ options }, a) => (await import("./apps-Dn7rCafG.js")).publishApp(a.dir, keyFile(options.key, "app publish <dir> --key <file> [--out <file>] [--sequence n]"), here(options.out), options.sequence)
985
+ run: async ({ options }, a) => (await import("./apps-Bs8xpLGv.js")).publishApp(a.dir, keyFile(options.key, "app publish <dir> --key <file> [--out <file>] [--sequence n]"), here(options.out), options.sequence)
926
986
  },
927
987
  "app revoke": {
928
988
  method: "app.revoke",
@@ -955,7 +1015,7 @@ var commands$8 = {
955
1015
  description: "Allow --up-to past the bundle's own sequence (versions published from elsewhere)"
956
1016
  }
957
1017
  },
958
- run: async ({ options }, a) => (await import("./apps-Dn7rCafG.js")).revokeApp(a.dir, keyFile(options.key, "app revoke <dir> --key <file> (--digest <digest>... | --up-to <sequence>) [--reason <text>]"), {
1018
+ run: async ({ options }, a) => (await import("./apps-Bs8xpLGv.js")).revokeApp(a.dir, keyFile(options.key, "app revoke <dir> --key <file> (--digest <digest>... | --up-to <sequence>) [--reason <text>]"), {
959
1019
  digests: options.digest,
960
1020
  upTo: options["up-to"],
961
1021
  reason: options.reason,
@@ -1016,7 +1076,7 @@ var commands$8 = {
1016
1076
  usage: "app verify <bundle|url>",
1017
1077
  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",
1018
1078
  args: ["bundle"],
1019
- run: async (_, a) => (await import("./apps-Dn7rCafG.js")).verifyApp(a.bundle)
1079
+ run: async (_, a) => (await import("./apps-Bs8xpLGv.js")).verifyApp(a.bundle)
1020
1080
  },
1021
1081
  "store sign": {
1022
1082
  method: "store.sign",
@@ -1033,7 +1093,7 @@ var commands$8 = {
1033
1093
  description: "The folder to write ghostly-store.json and ghostly-store.sig in (default: the index's)"
1034
1094
  }
1035
1095
  },
1036
- run: async ({ options }, a) => (await import("./apps-Dn7rCafG.js")).signStore(a.index, keyFile(options.key, "store sign <index> --key <file> [--out <dir>]"), here(options.out))
1096
+ run: async ({ options }, a) => (await import("./apps-Bs8xpLGv.js")).signStore(a.index, keyFile(options.key, "store sign <index> --key <file> [--out <dir>]"), here(options.out))
1037
1097
  }
1038
1098
  };
1039
1099
  //#endregion
@@ -2223,20 +2283,22 @@ var commands$1 = {
2223
2283
  },
2224
2284
  "chat pay": {
2225
2285
  method: "chat.pay",
2226
- usage: "chat pay <chat> <sats> [--memo t] [--network testnet] [--confirm-real]",
2286
+ usage: "chat pay <chat> <sats> [--memo t] [--network testnet] [--confirm-real] [--force]",
2227
2287
  summary: "Send ecash to a contact (Mainnet needs --confirm-real)",
2228
2288
  args: ["chat", "amount"],
2229
2289
  options: {
2230
2290
  memo,
2231
2291
  network: net,
2232
- "confirm-real": confirmReal
2292
+ "confirm-real": confirmReal,
2293
+ force
2233
2294
  },
2234
2295
  params: ({ options }, a) => ({
2235
2296
  chat: a.chat,
2236
2297
  amount: sats(a.amount),
2237
2298
  memo: options.memo,
2238
2299
  network: options.network,
2239
- confirmReal: options["confirm-real"] === true
2300
+ confirmReal: options["confirm-real"] === true,
2301
+ force: options.force === true
2240
2302
  })
2241
2303
  },
2242
2304
  "chat pay-request": {
@@ -2267,7 +2329,7 @@ var commands$1 = {
2267
2329
  },
2268
2330
  "chat request": {
2269
2331
  method: "chat.request",
2270
- usage: "chat request <chat> <sats> [--memo t] [--method m] [--rail cashu|lightning] [--network testnet] [--card <id>]",
2332
+ usage: "chat request <chat> <sats> [--memo t] [--method m] [--rail cashu|lightning] [--network testnet] [--card <id>] [--force]",
2271
2333
  summary: "Ask a contact to pay",
2272
2334
  args: ["chat", "amount"],
2273
2335
  options: {
@@ -2281,7 +2343,8 @@ var commands$1 = {
2281
2343
  description: "cashu or lightning"
2282
2344
  },
2283
2345
  network: net,
2284
- card
2346
+ card,
2347
+ force
2285
2348
  },
2286
2349
  params: ({ options }, a) => ({
2287
2350
  chat: a.chat,
@@ -2290,7 +2353,8 @@ var commands$1 = {
2290
2353
  method: options.method,
2291
2354
  rail: options.rail,
2292
2355
  network: options.network,
2293
- card: options.card
2356
+ card: options.card,
2357
+ force: options.force === true
2294
2358
  })
2295
2359
  },
2296
2360
  "lightning default": {
@@ -3061,10 +3125,20 @@ function runExec(command, event) {
3061
3125
  child.stdin.end(JSON.stringify(event) + "\n");
3062
3126
  });
3063
3127
  }
3064
- async function post(url, event) {
3065
- for (let attempt = 0; attempt < 3; attempt++) {
3128
+ /** The longest wait between two tries of a webhook that is not answering. */
3129
+ var RETRY_MAX_MS = 6e4;
3130
+ /**
3131
+ * Posts one event; resolves true once the bridge took it, false when it refused it (a 4xx or a redirect, three times).
3132
+ * A bridge that is not there (refused connection, timeout, 5xx, 408, 429) has not handled the event: it is tried again,
3133
+ * waiting up to a minute between tries, for as long as it takes, so the cursor never moves past it. Before, three tries
3134
+ * in 3.5 s and the cursor moved on: a bridge restarting for longer lost every event of the time it was down, for good.
3135
+ */
3136
+ async function post(url, event, retryMs) {
3137
+ let wait = retryMs, holding = false;
3138
+ for (let attempt = 0;; attempt++) {
3139
+ let down = true;
3066
3140
  try {
3067
- if ((await fetch(url, {
3141
+ const response = await fetch(url, {
3068
3142
  method: "POST",
3069
3143
  redirect: "manual",
3070
3144
  headers: {
@@ -3074,11 +3148,22 @@ async function post(url, event) {
3074
3148
  },
3075
3149
  body: JSON.stringify(event),
3076
3150
  signal: AbortSignal.timeout(1e4)
3077
- })).ok) return true;
3151
+ });
3152
+ response.body?.cancel().catch(() => {});
3153
+ if (response.ok) {
3154
+ if (holding) process.stderr.write(`ghostly: the webhook at ${url.href} answers again: event ${event.seq} taken\n`);
3155
+ return true;
3156
+ }
3157
+ down = response.status >= 500 || response.status === 408 || response.status === 429;
3078
3158
  } catch {}
3079
- await new Promise((resolve) => setTimeout(resolve, 500 * 2 ** attempt));
3159
+ if (!down && attempt >= 2) return false;
3160
+ if (down && attempt >= 2 && !holding) {
3161
+ holding = true;
3162
+ process.stderr.write(`ghostly: the webhook at ${url.href} is not answering: holding event ${event.seq} (${event.type}) until it does\n`);
3163
+ }
3164
+ await new Promise((resolve) => setTimeout(resolve, wait));
3165
+ wait = Math.min(wait * 2, RETRY_MAX_MS);
3080
3166
  }
3081
- return false;
3082
3167
  }
3083
3168
  /**
3084
3169
  * A handler that takes events in order, one at a time. A step that throws (the output closed, the allowlist's lookup,
@@ -3107,7 +3192,7 @@ function eventHandler(options) {
3107
3192
  const code = await runExec(options.exec, event);
3108
3193
  if (code !== 0) process.stderr.write(`ghostly: --exec exited ${code} on event ${event.seq} (${event.type})\n`);
3109
3194
  }
3110
- if (webhook && !await post(webhook, event)) process.stderr.write(`ghostly: the webhook did not take event ${event.seq} (${event.type})\n`);
3195
+ if (webhook && !await post(webhook, event, options.retryMs ?? 500)) process.stderr.write(`ghostly: the webhook did not take event ${event.seq} (${event.type})\n`);
3111
3196
  if (full) await options.drained?.();
3112
3197
  if (options.cursor) writeCursor(options.cursor, event.seq);
3113
3198
  }).catch((error) => {
@@ -3241,8 +3326,8 @@ var VERSION = package_default.version;
3241
3326
  * command a running daemon answers loads none of them, and starts about as fast as Node does. Statically imported, they
3242
3327
  * cost every command ~100 ms before its socket was opened.
3243
3328
  */
3244
- var engineApi = () => import("./api-Br3msjtR.js");
3245
- var engineHost = () => import("./host-DFzlKU3P.js");
3329
+ var engineApi = () => import("./api-CYG-sByy.js");
3330
+ var engineHost = () => import("./host-Mz74S9cM.js");
3246
3331
  var pretty = false;
3247
3332
  function print(value) {
3248
3333
  process.stdout.write(JSON.stringify(value, null, pretty ? 2 : void 0) + "\n");
@@ -3338,7 +3423,7 @@ function readOnly(method, params) {
3338
3423
  }
3339
3424
  async function session(g, options) {
3340
3425
  requireProfile(g);
3341
- const client = await connectDaemon(g.paths.socket);
3426
+ const client = await connectDaemon(g.paths);
3342
3427
  if (client) await warnVersion(client);
3343
3428
  if (client) return {
3344
3429
  mode: "daemon",
@@ -3706,7 +3791,7 @@ async function profileCommand(sub, argv) {
3706
3791
  const [file, name] = parsed.positionals;
3707
3792
  if (!file || !name) throw new CliError("usage", "ghostly profile restore <file> <new profile> [--passphrase-file f] [--use]");
3708
3793
  noMoreThan(2, parsed.positionals, "profile restore <file> <new profile> [--passphrase-file f] [--use]");
3709
- const { backupFileProtection, restoreProfile } = await import("./backup-_JnqeFZb.js");
3794
+ const { backupFileProtection, restoreProfile } = await import("./backup-OlEHNkqQ.js");
3710
3795
  const protection = await backupFileProtection(file);
3711
3796
  const paths = await restoreProfile(g.home, checkProfileName(name), file, protection === "none" ? void 0 : backupPassphrase(parsed.options["passphrase-file"], RESTORE_PASSPHRASE));
3712
3797
  if (parsed.options.use) selectProfile(g.home, name);
@@ -3769,7 +3854,7 @@ function backupPassphrase(file, missing = BACKUP_PASSPHRASE) {
3769
3854
  }
3770
3855
  async function runDaemon(g) {
3771
3856
  requireProfile(g);
3772
- if (await connectDaemon(g.paths.socket)) throw new CliError("busy", `A daemon already runs profile ${g.profile}`);
3857
+ if (await connectDaemon(g.paths)) throw new CliError("busy", `A daemon already runs profile ${g.profile}`);
3773
3858
  const { keepServing, openHost, serve } = await engineHost();
3774
3859
  const host = await openHost(g.paths, "daemon", VERSION);
3775
3860
  let served;
@@ -3819,7 +3904,7 @@ async function daemonCommand(argv) {
3819
3904
  ].includes(sub)) noMoreThan(0, parsed.positionals, sub === "status" ? "daemon status" : sub ? `daemon ${sub} [--timeout s]` : "daemon [--detach] [--timeout s]");
3820
3905
  if (sub === "status") {
3821
3906
  knownProfile(g);
3822
- const client = await connectDaemon(g.paths.socket);
3907
+ const client = await connectDaemon(g.paths);
3823
3908
  if (!client) {
3824
3909
  print({
3825
3910
  running: false,
@@ -3842,7 +3927,7 @@ async function daemonCommand(argv) {
3842
3927
  if (sub === "stop") {
3843
3928
  knownProfile(g);
3844
3929
  const stopFor = seconds(parsed, 20);
3845
- const client = await connectDaemon(g.paths.socket);
3930
+ const client = await connectDaemon(g.paths);
3846
3931
  if (!client) throw new CliError("not_found", `No daemon runs profile ${g.profile}`);
3847
3932
  print({
3848
3933
  stopped: true,
@@ -3852,7 +3937,7 @@ async function daemonCommand(argv) {
3852
3937
  }
3853
3938
  if (sub === "restart") {
3854
3939
  const stopFor = seconds(parsed, 20), startFor = seconds(parsed, 60);
3855
- const client = await connectDaemon(g.paths.socket);
3940
+ const client = await connectDaemon(g.paths);
3856
3941
  const before = client ? (await client.call("status").catch(() => null))?.version ?? null : null;
3857
3942
  print({
3858
3943
  restarted: true,
@@ -3883,7 +3968,19 @@ function seconds(parsed, fallback) {
3883
3968
  * the lock a moment before it exits); its pid.
3884
3969
  */
3885
3970
  async function stopDaemon(g, client, seconds) {
3886
- const { pid } = await client.call("daemon.stop");
3971
+ let pid;
3972
+ try {
3973
+ ({pid} = await client.call("daemon.stop"));
3974
+ } catch (error) {
3975
+ if (!isNotTheDaemon(error)) throw error;
3976
+ const holder = lockOwner(g.paths);
3977
+ if (holder === null) throw error;
3978
+ process.stderr.write(`ghostly: what answers on the socket did not prove it is this profile's daemon (an older release's cannot): stopping the process that holds the profile (${holder}) with SIGTERM\n`);
3979
+ pid = holder;
3980
+ try {
3981
+ process.kill(pid, "SIGTERM");
3982
+ } catch {}
3983
+ }
3887
3984
  client.close();
3888
3985
  const until = Date.now() + seconds * 1e3;
3889
3986
  const running = () => lockOwner(g.paths) === pid || processAlive(pid);
@@ -3907,7 +4004,7 @@ function processAlive(pid) {
3907
4004
  /** A daemon in the background, running this command's code, once its socket answers. */
3908
4005
  async function startDetached(g, seconds) {
3909
4006
  requireProfile(g);
3910
- if (await connectDaemon(g.paths.socket)) throw new CliError("busy", `A daemon already runs profile ${g.profile}`);
4007
+ if (await connectDaemon(g.paths)) throw new CliError("busy", `A daemon already runs profile ${g.profile}`);
3911
4008
  heldBy(g);
3912
4009
  const log = openSync(g.paths.log, "a", 384);
3913
4010
  const child = spawn(process.execPath, [
@@ -3932,7 +4029,7 @@ async function startDetached(g, seconds) {
3932
4029
  while (!client && Date.now() < until) {
3933
4030
  if (child.exitCode !== null) break;
3934
4031
  await new Promise((resolve) => setTimeout(resolve, 200));
3935
- client = await connectDaemon(g.paths.socket, 500);
4032
+ client = await connectDaemon(g.paths, 500);
3936
4033
  }
3937
4034
  if (!client) {
3938
4035
  heldBy(g, child.pid);
@@ -4030,11 +4127,11 @@ async function listenCommand(argv) {
4030
4127
  });
4031
4128
  };
4032
4129
  requireProfile(g);
4033
- const client = await connectDaemon(g.paths.socket);
4130
+ const client = await connectDaemon(g.paths);
4034
4131
  if (client) await warnVersion(client);
4035
4132
  if (client) {
4036
4133
  const handle = await start((m, p) => client.call(m, p), async (chat) => {
4037
- const c = await connectDaemon(g.paths.socket, 1e3);
4134
+ const c = await connectDaemon(g.paths, 1e3);
4038
4135
  if (!c) return null;
4039
4136
  try {
4040
4137
  return (await c.call("chat.get", { chat })).peer || null;
@@ -4051,7 +4148,7 @@ async function listenCommand(argv) {
4051
4148
  await new Promise((resolve) => setTimeout(resolve, wait));
4052
4149
  wait = 1e3;
4053
4150
  if (current || feed.paused) return;
4054
- const next = await connectDaemon(g.paths.socket, 1e3);
4151
+ const next = await connectDaemon(g.paths, 1e3);
4055
4152
  if (next) {
4056
4153
  current = next;
4057
4154
  await follow(next).catch(() => {
@@ -4233,7 +4330,7 @@ async function textCommand(name, argv) {
4233
4330
  };
4234
4331
  if (spec.target === "group") params.mentions = parsed.options.mention ?? [];
4235
4332
  if (parsed.options.reply !== void 0) params.reply = parsed.options.reply;
4236
- if (!spec.message) Object.assign(params, (await import("./buttons-DDB9Xdha.js")).buttonsOf(parsed.options));
4333
+ if (!spec.message) Object.assign(params, (await import("./buttons-BoGOYSHb.js")).buttonsOf(parsed.options));
4237
4334
  print(await withSession(g, (s) => {
4238
4335
  if (spec.target === "chat") {
4239
4336
  params.wait = parsed.options.wait ?? (s.mode === "one-shot" ? spec.message ? "confirmed" : "sent" : "none");
@@ -4291,10 +4388,10 @@ async function identityAddCommand(argv) {
4291
4388
  if (event.type === "identity.approval" || event.type === "identity.progress") process.stderr.write(JSON.stringify(event) + "\n");
4292
4389
  };
4293
4390
  requireProfile(g);
4294
- const client = await connectDaemon(g.paths.socket);
4391
+ const client = await connectDaemon(g.paths);
4295
4392
  if (client) {
4296
4393
  await warnVersion(client);
4297
- const watcher = await connectDaemon(g.paths.socket);
4394
+ const watcher = await connectDaemon(g.paths);
4298
4395
  await watcher?.subscribe(void 0, tell);
4299
4396
  try {
4300
4397
  print(await client.call("identity.add", params));
@@ -4369,7 +4466,7 @@ async function callPipeCommand(argv) {
4369
4466
  const parsed = parseArgs(argv, {}, idSlot({ args: ["call...?"] }));
4370
4467
  const g = globals(parsed);
4371
4468
  const ref = parsed.positionals.join(" ") || void 0;
4372
- const client = await connectDaemon(g.paths.socket);
4469
+ const client = await connectDaemon(g.paths);
4373
4470
  if (!client) throw new CliError("unavailable", `No daemon runs profile ${g.profile}: calls need one (ghostly daemon --detach)`);
4374
4471
  let call;
4375
4472
  try {
@@ -4517,4 +4614,4 @@ async function run(argv) {
4517
4614
  //#endregion
4518
4615
  export { run };
4519
4616
 
4520
- //# sourceMappingURL=main-DgbdDDif.js.map
4617
+ //# sourceMappingURL=main-BXwlJall.js.map