@zeph-to/cli 2.7.0 → 2.8.0

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 CHANGED
@@ -244,7 +244,8 @@ block here.
244
244
  `zeph listener` by hand — every `zeph cc` checks the PID file and
245
245
  skips the spawn when one is already alive, so opening a dozen
246
246
  terminals doesn't create a dozen daemons. The daemon survives
247
- between `zeph cc` invocations.
247
+ between `zeph cc` invocations — but not a reboot, which is what the
248
+ login-time service below is for.
248
249
 
249
250
  Project name resolves from `CLAUDE_PROJECT_DIR` /
250
251
  `CURSOR_PROJECT_DIR` / `WINDSURF_PROJECT_DIR` if set, else the git
@@ -271,6 +272,43 @@ block here.
271
272
  listener can't target an unnamed session that way, but you keep your
272
273
  existing multiplexer setup.
273
274
 
275
+ ### Starting the listener at login (macOS)
276
+
277
+ A reboot ends the daemon, and nothing starts it again until you open a
278
+ terminal and run `zeph cc`. Until you do, the phone shows **no agents** —
279
+ the machine is on, the past sessions are on disk, and the app has no one
280
+ to hear from.
281
+
282
+ ```bash
283
+ zeph listener --install-service
284
+ ```
285
+
286
+ That registers a launchd LaunchAgent (`to.zeph.listener`) that starts the
287
+ listener at every login. `--uninstall-service` removes it and
288
+ `--service-status` shows what the installed one points at; `zeph install`
289
+ offers it during setup and `zeph uninstall` takes it away again.
290
+
291
+ What you get back after a reboot is your **past sessions**, ready to
292
+ resume from the phone — not live agents. tmux does not survive a reboot
293
+ either, and this doesn't try to bring those sessions back to life.
294
+
295
+ Two details worth knowing:
296
+
297
+ - It fires at **user login**, not at boot. Without automatic login, the
298
+ machine stays silent while it sits on the login screen.
299
+ - The plist bakes in absolute paths for node and the CLI, a `PATH` that
300
+ can reach tmux, and a UTF-8 `LANG`. launchd gives a job
301
+ `/usr/bin:/bin:/usr/sbin:/sbin` and no locale at all: without the PATH
302
+ the daemon exits 127 at every login, and without the locale tmux
303
+ escapes the separator the session list is split on, so every session is
304
+ silently dropped while everything else looks healthy. If any of that
305
+ drifts — a version-manager node upgrade, say — `zeph verify` names it,
306
+ and re-running `--install-service` repoints the plist.
307
+
308
+ With the service installed, launchd owns the process: `zeph cc` and
309
+ `zeph listener --stop|--restart` ask launchd rather than signalling the
310
+ PID, so there is never a second daemon racing the first.
311
+
274
312
  ### Diagnostics
275
313
 
276
314
  The auto-spawned listener writes to three files under `~/.zeph/`:
package/dist/ask.js CHANGED
@@ -141,7 +141,7 @@ const handleAsk = async (args) => {
141
141
  // hook maps that to DENY — a command blocked for a reason that has nothing
142
142
  // to do with the user. Every other command in cli.ts resolves env this way.
143
143
  const apiKey = args['api-key'] || (0, config_js_1.resolvedEnv)('ZEPH_API_KEY') || config.apiKey;
144
- const hookId = args.hook || (0, config_js_1.resolvedEnv)('ZEPH_HOOK_ID') || config.hookId;
144
+ const hookId = args.hook || (0, config_js_1.resolveHookId)();
145
145
  const title = args.title || '';
146
146
  if (!apiKey || !hookId || !title) {
147
147
  const missing = [!title && '--title', !apiKey && 'an API key', !hookId && 'a hook id']
package/dist/cli.js CHANGED
@@ -90,6 +90,15 @@ ${usageAgentLines()}
90
90
  --restart stop it and relaunch in the background
91
91
  (needed after an upgrade: 'npm i -g' swaps
92
92
  the package but never the live process)
93
+ --install-service
94
+ start the listener at every login (macOS
95
+ launchd). Without it the daemon only exists
96
+ after a 'zeph cc', so a reboot leaves the
97
+ phone picker empty until you open a terminal
98
+ --uninstall-service
99
+ remove the login-time service
100
+ --service-status
101
+ show what the installed service points at
93
102
 
94
103
  Notify options:
95
104
  --title <text> Push title
@@ -126,6 +135,10 @@ Install options:
126
135
  to manual key entry)
127
136
  --key <api-key> API key (non-interactive; skips browser login)
128
137
  --hook <hook-id> Hook ID (non-interactive)
138
+ --service Install the login-time listener service without
139
+ asking (macOS). Interactive runs offer it anyway;
140
+ scripted runs need this flag
141
+ --no-service Never install it
129
142
  --base-url <url> Base URL (non-interactive)
130
143
  --relogin Force a fresh browser sign-in even if a login is
131
144
  already saved (switch account)
package/dist/config.d.ts CHANGED
@@ -7,7 +7,13 @@ export interface ZephConfig {
7
7
  wsUrl?: string;
8
8
  deviceId?: string;
9
9
  }
10
- export declare const resolvedEnv: (key: string) => string | undefined;
10
+ export declare const resolvedEnv: (key: string, env?: NodeJS.ProcessEnv) => string | undefined;
11
+ /**
12
+ * The two-way hook id: `ZEPH_HOOK_ID` when it carries a real value, else
13
+ * `hookId` from ~/.zeph/config.json — what `zeph setup` writes. The plugin's
14
+ * gate.sh `zeph_hook_id` and the MCP server resolve it in the same order.
15
+ */
16
+ export declare const resolveHookId: (env?: NodeJS.ProcessEnv) => string | undefined;
11
17
  export declare const PROJECT_DIR_ENV_VARS: readonly ["CLAUDE_PROJECT_DIR", "CURSOR_PROJECT_DIR", "WINDSURF_PROJECT_DIR"];
12
18
  /** First set project-dir env (unresolved `${VAR}` placeholders ignored), else cwd. */
13
19
  export declare const detectProjectDir: () => string;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU,QAA2B,CAAC;AACnD,eAAO,MAAM,WAAW,QAAkC,CAAC;AAE3D,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,SAGlD,CAAC;AAMF,eAAO,MAAM,oBAAoB,+EAAgF,CAAC;AAElH,sFAAsF;AACtF,eAAO,MAAM,gBAAgB,QAAO,MAMnC,CAAC;AAEF,eAAO,MAAM,UAAU,QAAO,UAM7B,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,QAAQ,UAAU,KAAG,IAM/C,CAAC;AAEF,eAAO,MAAM,OAAO,QAOhB,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU,QAA2B,CAAC;AACnD,eAAO,MAAM,WAAW,QAAkC,CAAC;AAE3D,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,EAAE,MAAK,MAAM,CAAC,UAAwB,KAAG,MAAM,GAAG,SAGxF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,MAAK,MAAM,CAAC,UAAwB,KAAG,MAAM,GAAG,SACrB,CAAC;AAM1D,eAAO,MAAM,oBAAoB,+EAAgF,CAAC;AAElH,sFAAsF;AACtF,eAAO,MAAM,gBAAgB,QAAO,MAMnC,CAAC;AAEF,eAAO,MAAM,UAAU,QAAO,UAM7B,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,QAAQ,UAAU,KAAG,IAM/C,CAAC;AAEF,eAAO,MAAM,OAAO,QAOhB,CAAC"}
package/dist/config.js CHANGED
@@ -1,16 +1,23 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VERSION = exports.saveConfig = exports.loadConfig = exports.detectProjectDir = exports.PROJECT_DIR_ENV_VARS = exports.resolvedEnv = exports.CONFIG_FILE = exports.CONFIG_DIR = void 0;
3
+ exports.VERSION = exports.saveConfig = exports.loadConfig = exports.detectProjectDir = exports.PROJECT_DIR_ENV_VARS = exports.resolveHookId = exports.resolvedEnv = exports.CONFIG_FILE = exports.CONFIG_DIR = void 0;
4
4
  const fs_1 = require("fs");
5
5
  const os_1 = require("os");
6
6
  const path_1 = require("path");
7
7
  exports.CONFIG_DIR = (0, path_1.join)((0, os_1.homedir)(), '.zeph');
8
8
  exports.CONFIG_FILE = (0, path_1.join)(exports.CONFIG_DIR, 'config.json');
9
- const resolvedEnv = (key) => {
10
- const val = process.env[key];
9
+ const resolvedEnv = (key, env = process.env) => {
10
+ const val = env[key];
11
11
  return val && !val.startsWith('${') ? val : undefined;
12
12
  };
13
13
  exports.resolvedEnv = resolvedEnv;
14
+ /**
15
+ * The two-way hook id: `ZEPH_HOOK_ID` when it carries a real value, else
16
+ * `hookId` from ~/.zeph/config.json — what `zeph setup` writes. The plugin's
17
+ * gate.sh `zeph_hook_id` and the MCP server resolve it in the same order.
18
+ */
19
+ const resolveHookId = (env = process.env) => (0, exports.resolvedEnv)('ZEPH_HOOK_ID', env) || (0, exports.loadConfig)().hookId;
20
+ exports.resolveHookId = resolveHookId;
14
21
  // Per-agent project-dir env vars, in precedence order. Deliberately NOT part
15
22
  // of the remote-agent registry: Cursor/Windsurf carry project-dir envs but
16
23
  // are not remote-controllable via tmux — the two tables have different
@@ -18,5 +18,16 @@ export declare const mergeJsonFile: (filePath: string, patch: Record<string, unk
18
18
  * unit testing.
19
19
  */
20
20
  export declare const filterAgentsByIds: (detected: Agent[], only: string) => Agent[];
21
+ /**
22
+ * Whether this install should register the LaunchAgent that starts the
23
+ * listener at login.
24
+ *
25
+ * Interactive installs offer it and default to yes: an empty phone picker
26
+ * after every reboot is precisely the kind of thing a default should fix.
27
+ * Scripted installs (`--key`, CI, provisioning) get it only when asked —
28
+ * planting a background job on a machine whose operator only wanted an API
29
+ * key written is a surprise, not a convenience.
30
+ */
31
+ export declare const serviceInstallChoice: (args: Record<string, string | boolean>, nonInteractive: boolean) => "yes" | "no" | "ask";
21
32
  export declare const handleInstall: (args: Record<string, string | boolean>) => Promise<number>;
22
33
  //# sourceMappingURL=installer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../src/installer.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AA4BzC;;;GAGG;AACH;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,YAAY,MAAM,GAAG,SAAS,EAAE,SAAS,OAAO,KAAG,OACxD,CAAC;AAoBzB;6EAC6E;AAC7E,eAAO,MAAM,aAAa,GAAI,UAAU,MAAM,EAAE,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAqBhF,CAAC;AAgQF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAU,KAAK,EAAE,EAAE,MAAM,MAAM,KAAG,KAAK,EAKxE,CAAC;AA8EF,eAAO,MAAM,aAAa,GAAU,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAG,OAAO,CAAC,MAAM,CAgH1F,CAAC"}
1
+ {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../src/installer.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AA6BzC;;;GAGG;AACH;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,YAAY,MAAM,GAAG,SAAS,EAAE,SAAS,OAAO,KAAG,OACxD,CAAC;AAoBzB;6EAC6E;AAC7E,eAAO,MAAM,aAAa,GAAI,UAAU,MAAM,EAAE,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAqBhF,CAAC;AAgQF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAU,KAAK,EAAE,EAAE,MAAM,MAAM,KAAG,KAAK,EAKxE,CAAC;AA8EF;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,GAC/B,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,EACtC,gBAAgB,OAAO,KACtB,KAAK,GAAG,IAAI,GAAG,KAKjB,CAAC;AAgCF,eAAO,MAAM,aAAa,GAAU,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAG,OAAO,CAAC,MAAM,CAuH1F,CAAC"}
package/dist/installer.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleInstall = exports.filterAgentsByIds = exports.mergeJsonFile = exports.shouldReauth = void 0;
3
+ exports.handleInstall = exports.serviceInstallChoice = exports.filterAgentsByIds = exports.mergeJsonFile = exports.shouldReauth = void 0;
4
4
  const child_process_1 = require("child_process");
5
5
  const fs_1 = require("fs");
6
6
  const os_1 = require("os");
@@ -10,11 +10,13 @@ const zeph_hook_js_1 = require("./zeph-hook.js");
10
10
  const config_js_1 = require("./config.js");
11
11
  const login_js_1 = require("./login.js");
12
12
  const agents_js_1 = require("./agents.js");
13
+ const listener_service_js_1 = require("./listener-service.js");
13
14
  const templates_js_1 = require("./templates.js");
14
15
  const HOME = (0, os_1.homedir)();
15
16
  // ── Helpers ──────────────────────────────────────────────────────
16
17
  const ok = (msg) => console.log(` + ${msg}`);
17
18
  const fail = (msg) => console.log(` - ${msg}`);
19
+ const skipNote = (msg) => console.log(` · ${msg}`);
18
20
  /**
19
21
  * True when install should auto-open browser login (ADR 0002): interactive
20
22
  * context with no existing credential (--key/env/config all absent).
@@ -392,6 +394,57 @@ const collectCredentials = async (args, installArgs, nonInteractive, existing) =
392
394
  const hookInput = await promptInput(' Hook ID (optional, for prompt/input): ');
393
395
  return { apiKey, hookId: hookInput || undefined, baseUrl: existing.baseUrl };
394
396
  };
397
+ // ── Login-time service ───────────────────────────────────────────
398
+ /**
399
+ * Whether this install should register the LaunchAgent that starts the
400
+ * listener at login.
401
+ *
402
+ * Interactive installs offer it and default to yes: an empty phone picker
403
+ * after every reboot is precisely the kind of thing a default should fix.
404
+ * Scripted installs (`--key`, CI, provisioning) get it only when asked —
405
+ * planting a background job on a machine whose operator only wanted an API
406
+ * key written is a surprise, not a convenience.
407
+ */
408
+ const serviceInstallChoice = (args, nonInteractive) => {
409
+ if (args['no-service'] === true)
410
+ return 'no';
411
+ if (!(0, listener_service_js_1.serviceSupported)())
412
+ return 'no';
413
+ if (args.service === true)
414
+ return 'yes';
415
+ return nonInteractive ? 'no' : 'ask';
416
+ };
417
+ exports.serviceInstallChoice = serviceInstallChoice;
418
+ /** Run the choice: ask when it should be asked, then install if wanted. */
419
+ const applyServiceChoice = async (choice) => {
420
+ if (choice === 'no')
421
+ return;
422
+ let wanted = choice === 'yes';
423
+ if (choice === 'ask') {
424
+ try {
425
+ const { confirm } = await import('@inquirer/prompts');
426
+ wanted = await confirm({
427
+ message: 'Start the listener at every login? (so the phone sees this machine after a reboot)',
428
+ default: true,
429
+ });
430
+ }
431
+ catch {
432
+ // Ctrl-C or no TTY — an unanswered question is not a yes.
433
+ wanted = false;
434
+ }
435
+ }
436
+ if (!wanted) {
437
+ skipNote('login-time service not installed (add it later: zeph listener --install-service)');
438
+ return;
439
+ }
440
+ const result = await (0, listener_service_js_1.installService)();
441
+ for (const note of result.notes)
442
+ ok(note);
443
+ if (result.ok)
444
+ ok('listener starts at every login');
445
+ else
446
+ fail(`login-time service: ${result.reason}`);
447
+ };
395
448
  // ── Main Install Flow ────────────────────────────────────────────
396
449
  const handleInstall = async (args) => {
397
450
  const installArgs = {
@@ -458,6 +511,9 @@ const handleInstall = async (args) => {
458
511
  if (selected.length === 0) {
459
512
  console.log(' (no agents selected — only the config file will be saved)');
460
513
  }
514
+ if ((0, exports.serviceInstallChoice)(args, nonInteractive) !== 'no') {
515
+ console.log(' - Offer to start the listener at every login');
516
+ }
461
517
  console.log(' - Test connection');
462
518
  }
463
519
  // 5. Save config — merge over the latest on-disk config (re-read, since a
@@ -481,7 +537,10 @@ const handleInstall = async (args) => {
481
537
  if (installer)
482
538
  installer();
483
539
  }
484
- // 7. Test connection
540
+ // 7. Login-time service — the only thing that makes the phone see this
541
+ // machine after a reboot without a terminal being opened first.
542
+ await applyServiceChoice((0, exports.serviceInstallChoice)(args, nonInteractive));
543
+ // 8. Test connection
485
544
  console.log('\n Testing connection...');
486
545
  await testConnection(apiKey, baseUrl);
487
546
  console.log('\n Done! Restart your agents.\n');
@@ -2,6 +2,7 @@ export declare const LISTENER_PID_FILE: string;
2
2
  /** CLI version the running daemon booted from. Absent ⇒ pre-stamp build. */
3
3
  export declare const LISTENER_VERSION_FILE: string;
4
4
  export declare const LISTENER_LOG_FILE: string;
5
+ export declare const sleep: (ms: number) => Promise<void>;
5
6
  /**
6
7
  * PID of the listener on record, or null when the file is missing or stale.
7
8
  * Stale PID files (process gone) read as "no listener" so a crashed daemon
@@ -1 +1 @@
1
- {"version":3,"file":"listener-process.d.ts","sourceRoot":"","sources":["../src/listener-process.ts"],"names":[],"mappings":"AAwBA,eAAO,MAAM,iBAAiB,QAAiC,CAAC;AAChE,4EAA4E;AAC5E,eAAO,MAAM,qBAAqB,QAAqC,CAAC;AACxE,eAAO,MAAM,iBAAiB,QAAiC,CAAC;AAIhE;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,QAAO,MAAM,GAAG,IAS9C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,QAAO,MAAM,GAAG,IAMlD,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,KAAG,IAItD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,MAAM,KAAG,IASlD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,yBAAyB,QAAO,IAQ5C,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAU,KAAK,MAAM,EAAE,kBAAiB,KAAG,OAAO,CAAC,OAAO,CAqBlF,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,cAAc,QAAO,MAAM,GAAG,IAM1C,CAAC;AASF,eAAO,MAAM,wBAAwB,QAAO,IAM3C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAAO,OAiBxC,CAAC"}
1
+ {"version":3,"file":"listener-process.d.ts","sourceRoot":"","sources":["../src/listener-process.ts"],"names":[],"mappings":"AAwBA,eAAO,MAAM,iBAAiB,QAAiC,CAAC;AAChE,4EAA4E;AAC5E,eAAO,MAAM,qBAAqB,QAAqC,CAAC;AACxE,eAAO,MAAM,iBAAiB,QAAiC,CAAC;AAEhE,eAAO,MAAM,KAAK,GAAI,IAAI,MAAM,KAAG,OAAO,CAAC,IAAI,CAAsD,CAAC;AAEtG;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,QAAO,MAAM,GAAG,IAS9C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,QAAO,MAAM,GAAG,IAMlD,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,KAAG,IAItD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,MAAM,KAAG,IASlD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,yBAAyB,QAAO,IAQ5C,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAU,KAAK,MAAM,EAAE,kBAAiB,KAAG,OAAO,CAAC,OAAO,CAqBlF,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,cAAc,QAAO,MAAM,GAAG,IAM1C,CAAC;AAgBF,eAAO,MAAM,wBAAwB,QAAO,IAO3C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAAO,OAgBxC,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.spawnListenerDetached = exports.rotateListenerLogIfLarge = exports.resolveCliPath = exports.stopListener = exports.clearStaleListenerRuntime = exports.clearListenerRuntime = exports.writeListenerRuntime = exports.runningListenerVersion = exports.runningListenerPid = exports.LISTENER_LOG_FILE = exports.LISTENER_VERSION_FILE = exports.LISTENER_PID_FILE = void 0;
3
+ exports.spawnListenerDetached = exports.rotateListenerLogIfLarge = exports.resolveCliPath = exports.stopListener = exports.clearStaleListenerRuntime = exports.clearListenerRuntime = exports.writeListenerRuntime = exports.runningListenerVersion = exports.runningListenerPid = exports.sleep = exports.LISTENER_LOG_FILE = exports.LISTENER_VERSION_FILE = exports.LISTENER_PID_FILE = void 0;
4
4
  const node_child_process_1 = require("node:child_process");
5
5
  const node_fs_1 = require("node:fs");
6
6
  const node_os_1 = require("node:os");
@@ -28,6 +28,7 @@ exports.LISTENER_PID_FILE = (0, node_path_1.join)(ZEPH_DIR, 'listener.pid');
28
28
  exports.LISTENER_VERSION_FILE = (0, node_path_1.join)(ZEPH_DIR, 'listener.version');
29
29
  exports.LISTENER_LOG_FILE = (0, node_path_1.join)(ZEPH_DIR, 'listener.log');
30
30
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
31
+ exports.sleep = sleep;
31
32
  /**
32
33
  * PID of the listener on record, or null when the file is missing or stale.
33
34
  * Stale PID files (process gone) read as "no listener" so a crashed daemon
@@ -121,7 +122,7 @@ const stopListener = async (pid, timeoutMs = 3_000) => {
121
122
  }
122
123
  const deadline = Date.now() + timeoutMs;
123
124
  while (Date.now() < deadline) {
124
- await sleep(50);
125
+ await (0, exports.sleep)(50);
125
126
  try {
126
127
  process.kill(pid, 0);
127
128
  }
@@ -166,7 +167,14 @@ exports.resolveCliPath = resolveCliPath;
166
167
  /**
167
168
  * Rotate the listener log once it grows past 5 MB. The daemon runs for days
168
169
  * and writes 2-3 lines per 5-s cycle, so without rotation the file climbs
169
- * into the tens of megabytes. Keep the previous run's tail under `.old`.
170
+ * into the tens of megabytes. Keep the previous window under `.old`.
171
+ *
172
+ * Copy-truncate rather than rename, because the writer is not always ours.
173
+ * Under the login-time LaunchAgent, launchd opens `StandardOutPath` itself
174
+ * and holds that fd for the life of the job: a rename moves the directory
175
+ * entry and leaves launchd appending to the very same inode, now called
176
+ * `.old`. The log the user tails would never shrink. Emptying the file in
177
+ * place keeps the inode — and the fd — valid.
170
178
  */
171
179
  const LISTENER_LOG_MAX_BYTES = 5 * 1024 * 1024;
172
180
  const rotateListenerLogIfLarge = () => {
@@ -175,7 +183,8 @@ const rotateListenerLogIfLarge = () => {
175
183
  return;
176
184
  if ((0, node_fs_1.statSync)(exports.LISTENER_LOG_FILE).size <= LISTENER_LOG_MAX_BYTES)
177
185
  return;
178
- (0, node_fs_1.renameSync)(exports.LISTENER_LOG_FILE, exports.LISTENER_LOG_FILE + '.old');
186
+ (0, node_fs_1.copyFileSync)(exports.LISTENER_LOG_FILE, exports.LISTENER_LOG_FILE + '.old');
187
+ (0, node_fs_1.truncateSync)(exports.LISTENER_LOG_FILE, 0);
179
188
  }
180
189
  catch { /* best-effort */ }
181
190
  };
@@ -192,7 +201,6 @@ const spawnListenerDetached = () => {
192
201
  return false;
193
202
  try {
194
203
  (0, node_fs_1.mkdirSync)(ZEPH_DIR, { recursive: true });
195
- (0, exports.rotateListenerLogIfLarge)();
196
204
  const out = (0, node_fs_1.openSync)(exports.LISTENER_LOG_FILE, 'a');
197
205
  const child = (0, node_child_process_1.spawn)(process.execPath, [cliPath, 'listener'], {
198
206
  detached: true,
@@ -0,0 +1,165 @@
1
+ /**
2
+ * The listener as an OS service: a launchd LaunchAgent that starts it at user
3
+ * login so the phone's picker is populated without anyone opening a terminal.
4
+ *
5
+ * Until this existed, the only thing that ever started the daemon was
6
+ * `zeph cc` (wrapper.ts → spawnListenerDetached). After a reboot that means an
7
+ * empty picker until the user happens to launch an agent — the machine is
8
+ * online, the past sessions are on disk, and the app says "no agents".
9
+ *
10
+ * Two constraints shape this file:
11
+ *
12
+ * 1. **Node builtins only.** wrapper.ts reads `serviceInstalled()` on the
13
+ * `zeph cc` hot path, and the same rule that keeps listener.ts out of
14
+ * wrapper.ts (ws + the crypto stack on every agent launch) applies here.
15
+ * listener-process.ts is the one import, and it is builtins-only too.
16
+ *
17
+ * 2. **Everything the job needs is resolved at install time and baked into
18
+ * the plist.** launchd hands a job `/usr/bin:/bin:/usr/sbin:/sbin` and
19
+ * nothing else — no shell profile, no homebrew prefix. `verifyTmux()`
20
+ * (listener.ts) exits 127 when tmux is not on PATH, so a plist without a
21
+ * baked PATH produces a service that dies at every login and is restarted
22
+ * forever by KeepAlive. The interpreter is baked for the same reason.
23
+ */
24
+ /** Reverse-DNS launchd label. Lives on the user's machine once installed —
25
+ * renaming it orphans every plist already out there. */
26
+ export declare const SERVICE_LABEL = "to.zeph.listener";
27
+ export interface ServiceSpec {
28
+ readonly nodePath: string;
29
+ readonly cliPath: string;
30
+ readonly tmuxPath: string;
31
+ readonly logPath: string;
32
+ /** PATH baked into the job: tmux dir, node dir, then launchd's own. */
33
+ readonly pathEnv: string;
34
+ /** UTF-8 locale baked into the job. Always UTF-8 — see resolveServiceSpec. */
35
+ readonly lang: string;
36
+ }
37
+ export type SpecResolution = {
38
+ readonly ok: true;
39
+ readonly value: ServiceSpec;
40
+ readonly warning?: string;
41
+ } | {
42
+ readonly ok: false;
43
+ readonly reason: string;
44
+ };
45
+ /** The machine facts spec resolution needs, injectable so tests describe a
46
+ * machine instead of inheriting the developer's own. */
47
+ export interface ServiceEnv {
48
+ readonly exists: (path: string) => boolean;
49
+ /** Major version of the node at `path`, or null when it won't run. */
50
+ readonly nodeMajor: (path: string) => number | null;
51
+ /** Absolute tmux path from the invoking shell's PATH, or null. */
52
+ readonly whichTmux: () => string | null;
53
+ readonly cliPath: () => string | null;
54
+ /** The invoking shell's LANG, or undefined when it has none. */
55
+ readonly lang: () => string | undefined;
56
+ }
57
+ export interface ServiceStatus {
58
+ readonly supported: boolean;
59
+ readonly installed: boolean;
60
+ readonly label: string;
61
+ readonly plistPath: string;
62
+ /** Read back from the installed plist — null when nothing is installed. */
63
+ readonly nodePath: string | null;
64
+ readonly cliPath: string | null;
65
+ readonly pathEnv: string | null;
66
+ readonly langEnv: string | null;
67
+ /** Programs the plist names that are no longer on disk. The shape a node
68
+ * upgrade leaves behind: registered, listed by launchctl, unable to run. */
69
+ readonly missing: readonly string[];
70
+ }
71
+ /** launchd only exists on macOS. Every other platform is an explicit refusal,
72
+ * never a quiet success. */
73
+ export declare const serviceSupported: () => boolean;
74
+ export declare const servicePlistPath: () => string;
75
+ export declare const serviceInstalled: () => boolean;
76
+ export declare const defaultServiceEnv: ServiceEnv;
77
+ /**
78
+ * Decide what to bake into the plist, or say why we can't. Nothing is written
79
+ * and nothing is registered — a caller that gets `ok: false` has touched
80
+ * nothing on the machine.
81
+ */
82
+ export declare const resolveServiceSpec: (env?: ServiceEnv) => SpecResolution;
83
+ export declare const renderLaunchAgentPlist: (spec: ServiceSpec) => string;
84
+ /**
85
+ * What is registered on this machine and whether it can still run. The single
86
+ * source `verify`, `--service-status` and the `zeph cc` branch all read, so
87
+ * they can never disagree about whether a service is installed.
88
+ */
89
+ export declare const serviceStatus: () => ServiceStatus;
90
+ /**
91
+ * Side effects the service operations need, injectable so tests can drive the
92
+ * launchd sequence without a launchd.
93
+ */
94
+ export interface ServiceOpDeps {
95
+ readonly supported: () => boolean;
96
+ readonly resolveSpec: () => SpecResolution;
97
+ /** PID of a listener running right now, or null. */
98
+ readonly runningPid: () => number | null;
99
+ readonly stopListener: (pid: number) => Promise<boolean>;
100
+ readonly launchctl: (args: string[]) => {
101
+ ok: boolean;
102
+ stderr: string;
103
+ };
104
+ readonly settle: (ms: number) => Promise<void>;
105
+ }
106
+ export type ServiceOpResult = {
107
+ readonly ok: true;
108
+ readonly notes: readonly string[];
109
+ } | {
110
+ readonly ok: false;
111
+ readonly reason: string;
112
+ readonly notes: readonly string[];
113
+ };
114
+ export declare const defaultServiceOpDeps: ServiceOpDeps;
115
+ /**
116
+ * Register the LaunchAgent and get the daemon running under it.
117
+ *
118
+ * The order is load-bearing, not incidental:
119
+ *
120
+ * - **Any listener already running is stopped first.** `handleListener` exits
121
+ * **0** when it finds another listener alive, and it only does so quietly
122
+ * under ZEPH_LISTENER_AUTOSTART, which a launchd job does not have. So a
123
+ * kickstart into a live `zeph cc` daemon produces a clean exit, and
124
+ * `KeepAlive:{SuccessfulExit:false}` reads a clean exit as "stay down" —
125
+ * for the whole login session. `launchctl list` shows the job; nothing runs.
126
+ * - **Nothing is written until the spec resolves.** A refusal leaves the
127
+ * machine exactly as it was.
128
+ * - **The daemon is confirmed alive afterwards.** Without that check the
129
+ * failure above ships silently.
130
+ */
131
+ export declare const installService: (deps?: ServiceOpDeps) => Promise<ServiceOpResult>;
132
+ /** Unregister the job and remove the plist. Doing only one of the two leaves
133
+ * either a running service with no plist or a plist launchd ignores. */
134
+ export declare const uninstallService: (deps?: ServiceOpDeps) => Promise<ServiceOpResult>;
135
+ /**
136
+ * Stop the service until the next login.
137
+ *
138
+ * `bootout` and not `disable`: disable writes to launchd's persistent disabled
139
+ * database, survives logins, and leaves no obvious way back — a `--stop` that
140
+ * quietly becomes permanent. Session-scoped is what stopping should mean.
141
+ */
142
+ export declare const stopService: (deps?: ServiceOpDeps) => ServiceOpResult;
143
+ /** Restart the service in place. `-k` kills the running instance first, so this
144
+ * is also how a version-drifted daemon is replaced. */
145
+ export declare const restartService: (deps?: ServiceOpDeps) => ServiceOpResult;
146
+ export interface ServiceHealthRow {
147
+ readonly label: string;
148
+ readonly state: 'pass' | 'warn' | 'fail';
149
+ }
150
+ /** The two live facts a health check can't read off the plist. */
151
+ export interface ServiceProbe {
152
+ /** Can a process started with this PATH find tmux? */
153
+ readonly tmuxOnPath: (pathEnv: string) => boolean;
154
+ /** Is launchd actually running the job right now? */
155
+ readonly loaded: () => boolean;
156
+ }
157
+ export declare const defaultServiceProbe: ServiceProbe;
158
+ /**
159
+ * Why this exists: every way the service breaks leaves it looking installed.
160
+ * `launchctl list` shows the job whether or not its interpreter still exists,
161
+ * and a PATH without tmux makes the daemon exit 127 at every login while the
162
+ * registration sits there unchanged. None of that is visible without asking.
163
+ */
164
+ export declare const serviceHealthChecks: (status: ServiceStatus, probe?: ServiceProbe) => ServiceHealthRow[];
165
+ //# sourceMappingURL=listener-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listener-service.d.ts","sourceRoot":"","sources":["../src/listener-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,424 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.serviceHealthChecks = exports.defaultServiceProbe = exports.restartService = exports.stopService = exports.uninstallService = exports.installService = exports.defaultServiceOpDeps = exports.serviceStatus = exports.renderLaunchAgentPlist = exports.resolveServiceSpec = exports.defaultServiceEnv = exports.serviceInstalled = exports.servicePlistPath = exports.serviceSupported = exports.SERVICE_LABEL = void 0;
4
+ const node_child_process_1 = require("node:child_process");
5
+ const node_fs_1 = require("node:fs");
6
+ const node_os_1 = require("node:os");
7
+ const node_path_1 = require("node:path");
8
+ const listener_process_js_1 = require("./listener-process.js");
9
+ /**
10
+ * The listener as an OS service: a launchd LaunchAgent that starts it at user
11
+ * login so the phone's picker is populated without anyone opening a terminal.
12
+ *
13
+ * Until this existed, the only thing that ever started the daemon was
14
+ * `zeph cc` (wrapper.ts → spawnListenerDetached). After a reboot that means an
15
+ * empty picker until the user happens to launch an agent — the machine is
16
+ * online, the past sessions are on disk, and the app says "no agents".
17
+ *
18
+ * Two constraints shape this file:
19
+ *
20
+ * 1. **Node builtins only.** wrapper.ts reads `serviceInstalled()` on the
21
+ * `zeph cc` hot path, and the same rule that keeps listener.ts out of
22
+ * wrapper.ts (ws + the crypto stack on every agent launch) applies here.
23
+ * listener-process.ts is the one import, and it is builtins-only too.
24
+ *
25
+ * 2. **Everything the job needs is resolved at install time and baked into
26
+ * the plist.** launchd hands a job `/usr/bin:/bin:/usr/sbin:/sbin` and
27
+ * nothing else — no shell profile, no homebrew prefix. `verifyTmux()`
28
+ * (listener.ts) exits 127 when tmux is not on PATH, so a plist without a
29
+ * baked PATH produces a service that dies at every login and is restarted
30
+ * forever by KeepAlive. The interpreter is baked for the same reason.
31
+ */
32
+ /** Reverse-DNS launchd label. Lives on the user's machine once installed —
33
+ * renaming it orphans every plist already out there. */
34
+ exports.SERVICE_LABEL = 'to.zeph.listener';
35
+ /** `package.json` engines floor. A node below this starts and dies. */
36
+ const MIN_NODE_MAJOR = 18;
37
+ /** What launchd itself puts on PATH. Ours is prepended, never replaces this. */
38
+ const LAUNCHD_BASE_PATH = ['/usr/bin', '/bin', '/usr/sbin', '/sbin'];
39
+ /** Interpreters to try when the CLI's own install prefix has none. */
40
+ const NODE_FALLBACKS = ['/opt/homebrew/bin/node', '/usr/local/bin/node'];
41
+ /**
42
+ * Locale to fall back on when the shell has none, or has a non-UTF-8 one.
43
+ *
44
+ * launchd hands a job no LANG at all. tmux then runs in the C locale and
45
+ * escapes every non-ASCII byte of its format output to `_` — including the
46
+ * U+241F separator the session inventory is split on. The daemon starts
47
+ * clean, tmux answers, and every session is discarded as unparseable: the
48
+ * phone shows nothing while `launchctl list` and the log both look healthy.
49
+ */
50
+ const FALLBACK_LANG = 'en_US.UTF-8';
51
+ /** tmux locations to try when the shell can't answer `command -v`. */
52
+ const TMUX_FALLBACKS = ['/opt/homebrew/bin/tmux', '/usr/local/bin/tmux', '/usr/bin/tmux'];
53
+ /** Version-manager roots. A node under one of these is gone after the next
54
+ * upgrade, which silently breaks the service months later. */
55
+ const VERSION_MANAGER_DIRS = ['/.nvm/', '/.fnm/', '/.volta/', '/.nodenv/', '/.asdf/', '/.n/'];
56
+ /** launchd only exists on macOS. Every other platform is an explicit refusal,
57
+ * never a quiet success. */
58
+ const serviceSupported = () => process.platform === 'darwin';
59
+ exports.serviceSupported = serviceSupported;
60
+ const servicePlistPath = () => (0, node_path_1.join)((0, node_os_1.homedir)(), 'Library', 'LaunchAgents', `${exports.SERVICE_LABEL}.plist`);
61
+ exports.servicePlistPath = servicePlistPath;
62
+ const serviceInstalled = () => (0, node_fs_1.existsSync)((0, exports.servicePlistPath)());
63
+ exports.serviceInstalled = serviceInstalled;
64
+ /** This user's launchd GUI domain, and our job within it. Not exported: every
65
+ * launchctl call lives in this file, and the label is an implementation
66
+ * detail of that. */
67
+ const serviceDomain = () => `gui/${process.getuid?.() ?? 0}`;
68
+ const serviceTarget = () => `${serviceDomain()}/${exports.SERVICE_LABEL}`;
69
+ const nodeMajorOf = (path) => {
70
+ try {
71
+ const out = (0, node_child_process_1.execFileSync)(path, ['-v'], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] });
72
+ const major = Number(out.trim().replace(/^v/, '').split('.')[0]);
73
+ return Number.isFinite(major) ? major : null;
74
+ }
75
+ catch {
76
+ return null;
77
+ }
78
+ };
79
+ const whichTmuxFromShell = () => {
80
+ try {
81
+ // `/bin/sh -c` on purpose: `which` in an interactive zsh resolves the
82
+ // user's tmux *alias*, which is not a path we can hand to launchd.
83
+ const out = (0, node_child_process_1.execFileSync)('/bin/sh', ['-c', 'command -v tmux'], {
84
+ encoding: 'utf-8',
85
+ stdio: ['ignore', 'pipe', 'ignore'],
86
+ }).trim();
87
+ return out.startsWith('/') ? out : null;
88
+ }
89
+ catch {
90
+ return null;
91
+ }
92
+ };
93
+ exports.defaultServiceEnv = {
94
+ exists: node_fs_1.existsSync,
95
+ nodeMajor: nodeMajorOf,
96
+ whichTmux: whichTmuxFromShell,
97
+ cliPath: listener_process_js_1.resolveCliPath,
98
+ lang: () => process.env.LANG,
99
+ };
100
+ /**
101
+ * The interpreter that owns the CLI's install prefix.
102
+ *
103
+ * A global install lives at `<prefix>/lib/node_modules/@zeph-to/cli/dist/cli.js`
104
+ * and its node is `<prefix>/bin/node`. Preferring it keeps the plist internally
105
+ * consistent; picking whichever node happens to exist can pair a homebrew
106
+ * interpreter with a `~/.local` install — a combination that works on the
107
+ * machine that wrote it and breaks on the next one.
108
+ */
109
+ const prefixNodeFor = (cliPath) => {
110
+ const marker = '/lib/node_modules/';
111
+ const at = cliPath.indexOf(marker);
112
+ if (at < 0)
113
+ return null;
114
+ return (0, node_path_1.join)(cliPath.slice(0, at), 'bin', 'node');
115
+ };
116
+ const isVersionManaged = (nodePath) => VERSION_MANAGER_DIRS.some((dir) => nodePath.includes(dir));
117
+ const dedupe = (values) => [...new Set(values)];
118
+ /**
119
+ * Decide what to bake into the plist, or say why we can't. Nothing is written
120
+ * and nothing is registered — a caller that gets `ok: false` has touched
121
+ * nothing on the machine.
122
+ */
123
+ const resolveServiceSpec = (env = exports.defaultServiceEnv) => {
124
+ const cliPath = env.cliPath();
125
+ if (!cliPath) {
126
+ return { ok: false, reason: 'could not resolve the zeph CLI entry (dist/cli.js) to run' };
127
+ }
128
+ const tmuxPath = env.whichTmux() ?? TMUX_FALLBACKS.find(env.exists) ?? null;
129
+ if (!tmuxPath) {
130
+ return { ok: false, reason: 'tmux not found — install tmux first (the listener drives tmux panes)' };
131
+ }
132
+ const prefixNode = prefixNodeFor(cliPath);
133
+ const candidates = dedupe([...(prefixNode ? [prefixNode] : []), ...NODE_FALLBACKS, process.execPath]);
134
+ const nodePath = candidates.find((c) => env.exists(c) && (env.nodeMajor(c) ?? 0) >= MIN_NODE_MAJOR);
135
+ if (!nodePath) {
136
+ return { ok: false, reason: `no node >= ${MIN_NODE_MAJOR} found to run the listener with` };
137
+ }
138
+ const pathEnv = dedupe([(0, node_path_1.dirname)(tmuxPath), (0, node_path_1.dirname)(nodePath), ...LAUNCHD_BASE_PATH]).join(':');
139
+ const shellLang = env.lang();
140
+ const lang = shellLang && /utf-?8$/i.test(shellLang) ? shellLang : FALLBACK_LANG;
141
+ const spec = { nodePath, cliPath, tmuxPath, logPath: listener_process_js_1.LISTENER_LOG_FILE, pathEnv, lang };
142
+ if (isVersionManaged(nodePath)) {
143
+ return {
144
+ ok: true,
145
+ value: spec,
146
+ warning: `${nodePath} belongs to a node version manager — the service will stop working ` +
147
+ `after a node upgrade. Re-run \`zeph listener --install-service\` if that happens ` +
148
+ `(\`zeph verify\` reports it).`,
149
+ };
150
+ }
151
+ return { ok: true, value: spec };
152
+ };
153
+ exports.resolveServiceSpec = resolveServiceSpec;
154
+ const escapeXml = (value) => value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
155
+ const renderLaunchAgentPlist = (spec) => {
156
+ const s = (value) => `<string>${escapeXml(value)}</string>`;
157
+ return `<?xml version="1.0" encoding="UTF-8"?>
158
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
159
+ <plist version="1.0">
160
+ <dict>
161
+ <key>Label</key>
162
+ ${s(exports.SERVICE_LABEL)}
163
+ <key>ProgramArguments</key>
164
+ <array>
165
+ ${s(spec.nodePath)}
166
+ ${s(spec.cliPath)}
167
+ <string>listener</string>
168
+ </array>
169
+ <key>RunAtLoad</key>
170
+ <true/>
171
+ <key>KeepAlive</key>
172
+ <dict>
173
+ <key>SuccessfulExit</key>
174
+ <false/>
175
+ </dict>
176
+ <key>ThrottleInterval</key>
177
+ <integer>30</integer>
178
+ <key>StandardOutPath</key>
179
+ ${s(spec.logPath)}
180
+ <key>StandardErrorPath</key>
181
+ ${s(spec.logPath)}
182
+ <key>EnvironmentVariables</key>
183
+ <dict>
184
+ <key>PATH</key>
185
+ ${s(spec.pathEnv)}
186
+ <key>LANG</key>
187
+ ${s(spec.lang)}
188
+ <key>ZEPH_LISTENER_SERVICE</key>
189
+ <string>1</string>
190
+ </dict>
191
+ </dict>
192
+ </plist>
193
+ `;
194
+ };
195
+ exports.renderLaunchAgentPlist = renderLaunchAgentPlist;
196
+ const unescapeXml = (value) => value.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');
197
+ /** ProgramArguments strings, in order, from an installed plist. */
198
+ const readProgramArguments = (xml) => {
199
+ const block = /<key>ProgramArguments<\/key>\s*<array>([\s\S]*?)<\/array>/.exec(xml);
200
+ if (!block)
201
+ return [];
202
+ return [...block[1].matchAll(/<string>([\s\S]*?)<\/string>/g)].map((m) => unescapeXml(m[1]));
203
+ };
204
+ const readEnvValue = (xml, key) => {
205
+ const found = new RegExp(`<key>${key}</key>\\s*<string>([\\s\\S]*?)</string>`).exec(xml);
206
+ return found ? unescapeXml(found[1]) : null;
207
+ };
208
+ const NOT_INSTALLED = {
209
+ installed: false,
210
+ nodePath: null,
211
+ cliPath: null,
212
+ pathEnv: null,
213
+ langEnv: null,
214
+ missing: [],
215
+ };
216
+ /**
217
+ * What is registered on this machine and whether it can still run. The single
218
+ * source `verify`, `--service-status` and the `zeph cc` branch all read, so
219
+ * they can never disagree about whether a service is installed.
220
+ */
221
+ const serviceStatus = () => {
222
+ const plistPath = (0, exports.servicePlistPath)();
223
+ const base = { supported: (0, exports.serviceSupported)(), label: exports.SERVICE_LABEL, plistPath };
224
+ if (!(0, node_fs_1.existsSync)(plistPath))
225
+ return { ...base, ...NOT_INSTALLED };
226
+ let xml = '';
227
+ try {
228
+ xml = (0, node_fs_1.readFileSync)(plistPath, 'utf-8');
229
+ }
230
+ catch {
231
+ // The plist is there but unreadable — installed, nothing knowable.
232
+ return { ...base, ...NOT_INSTALLED, installed: true };
233
+ }
234
+ const [nodePath = null, cliPath = null] = readProgramArguments(xml);
235
+ const missing = [nodePath, cliPath].filter((p) => !!p && !(0, node_fs_1.existsSync)(p));
236
+ return {
237
+ ...base,
238
+ installed: true,
239
+ nodePath,
240
+ cliPath,
241
+ pathEnv: readEnvValue(xml, 'PATH'),
242
+ langEnv: readEnvValue(xml, 'LANG'),
243
+ missing,
244
+ };
245
+ };
246
+ exports.serviceStatus = serviceStatus;
247
+ /** How long to give launchd to get the daemon up before calling the install
248
+ * a failure. Generous: the daemon writes its pid file as its first act. */
249
+ const POST_INSTALL_SETTLE_MS = 3_000;
250
+ const runLaunchctl = (args) => {
251
+ try {
252
+ (0, node_child_process_1.execFileSync)('launchctl', args, { stdio: ['ignore', 'ignore', 'pipe'] });
253
+ return { ok: true, stderr: '' };
254
+ }
255
+ catch (err) {
256
+ const stderr = err.stderr?.toString().trim() ?? '';
257
+ return { ok: false, stderr };
258
+ }
259
+ };
260
+ exports.defaultServiceOpDeps = {
261
+ supported: exports.serviceSupported,
262
+ resolveSpec: () => (0, exports.resolveServiceSpec)(),
263
+ runningPid: listener_process_js_1.runningListenerPid,
264
+ stopListener: listener_process_js_1.stopListener,
265
+ launchctl: runLaunchctl,
266
+ settle: listener_process_js_1.sleep,
267
+ };
268
+ const UNSUPPORTED = 'the login-time service needs launchd — only macOS is supported today ' +
269
+ '(run `zeph listener` yourself, or keep using `zeph cc` to autostart it)';
270
+ /**
271
+ * Register the LaunchAgent and get the daemon running under it.
272
+ *
273
+ * The order is load-bearing, not incidental:
274
+ *
275
+ * - **Any listener already running is stopped first.** `handleListener` exits
276
+ * **0** when it finds another listener alive, and it only does so quietly
277
+ * under ZEPH_LISTENER_AUTOSTART, which a launchd job does not have. So a
278
+ * kickstart into a live `zeph cc` daemon produces a clean exit, and
279
+ * `KeepAlive:{SuccessfulExit:false}` reads a clean exit as "stay down" —
280
+ * for the whole login session. `launchctl list` shows the job; nothing runs.
281
+ * - **Nothing is written until the spec resolves.** A refusal leaves the
282
+ * machine exactly as it was.
283
+ * - **The daemon is confirmed alive afterwards.** Without that check the
284
+ * failure above ships silently.
285
+ */
286
+ const installService = async (deps = exports.defaultServiceOpDeps) => {
287
+ const notes = [];
288
+ if (!deps.supported())
289
+ return { ok: false, reason: UNSUPPORTED, notes };
290
+ const spec = deps.resolveSpec();
291
+ if (!spec.ok)
292
+ return { ok: false, reason: spec.reason, notes };
293
+ if (spec.warning)
294
+ notes.push(spec.warning);
295
+ const running = deps.runningPid();
296
+ if (running !== null) {
297
+ await deps.stopListener(running);
298
+ notes.push(`stopped the listener already running (pid ${running}) so launchd can own it`);
299
+ }
300
+ // Nothing to unload before a first install — that failure is the normal path.
301
+ deps.launchctl(['bootout', serviceTarget()]);
302
+ const plistPath = (0, exports.servicePlistPath)();
303
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(plistPath), { recursive: true });
304
+ (0, node_fs_1.writeFileSync)(plistPath, (0, exports.renderLaunchAgentPlist)(spec.value));
305
+ notes.push(`wrote ${plistPath}`);
306
+ const bootstrap = deps.launchctl(['bootstrap', serviceDomain(), plistPath]);
307
+ if (!bootstrap.ok) {
308
+ return { ok: false, reason: `launchctl bootstrap failed: ${bootstrap.stderr || 'no detail'}`, notes };
309
+ }
310
+ // Best-effort: a job disabled by an earlier `launchctl disable` (ours never
311
+ // calls it, but a user's hand might have) would otherwise refuse to start.
312
+ deps.launchctl(['enable', serviceTarget()]);
313
+ const kickstart = deps.launchctl(['kickstart', '-k', serviceTarget()]);
314
+ if (!kickstart.ok) {
315
+ return { ok: false, reason: `launchctl kickstart failed: ${kickstart.stderr || 'no detail'}`, notes };
316
+ }
317
+ await deps.settle(POST_INSTALL_SETTLE_MS);
318
+ if (deps.runningPid() === null) {
319
+ return {
320
+ ok: false,
321
+ reason: `the service was registered but the listener did not stay up — see ${listener_process_js_1.LISTENER_LOG_FILE}`,
322
+ notes,
323
+ };
324
+ }
325
+ return { ok: true, notes };
326
+ };
327
+ exports.installService = installService;
328
+ /** Unregister the job and remove the plist. Doing only one of the two leaves
329
+ * either a running service with no plist or a plist launchd ignores. */
330
+ const uninstallService = async (deps = exports.defaultServiceOpDeps) => {
331
+ const notes = [];
332
+ const plistPath = (0, exports.servicePlistPath)();
333
+ if (!(0, node_fs_1.existsSync)(plistPath))
334
+ return { ok: true, notes: ['service not installed — nothing to remove'] };
335
+ deps.launchctl(['bootout', serviceTarget()]);
336
+ notes.push(`unloaded ${exports.SERVICE_LABEL}`);
337
+ (0, node_fs_1.rmSync)(plistPath, { force: true });
338
+ notes.push(`removed ${plistPath}`);
339
+ return { ok: true, notes };
340
+ };
341
+ exports.uninstallService = uninstallService;
342
+ /** One launchctl call reported as a ServiceOpResult, naming the subcommand in
343
+ * the failure so the user sees which step launchd refused. */
344
+ const askLaunchd = (deps, args, note) => {
345
+ const result = deps.launchctl(args);
346
+ if (result.ok)
347
+ return { ok: true, notes: [note] };
348
+ return { ok: false, reason: `launchctl ${args[0]} failed: ${result.stderr || 'no detail'}`, notes: [] };
349
+ };
350
+ /**
351
+ * Stop the service until the next login.
352
+ *
353
+ * `bootout` and not `disable`: disable writes to launchd's persistent disabled
354
+ * database, survives logins, and leaves no obvious way back — a `--stop` that
355
+ * quietly becomes permanent. Session-scoped is what stopping should mean.
356
+ */
357
+ const stopService = (deps = exports.defaultServiceOpDeps) => askLaunchd(deps, ['bootout', serviceTarget()], `unloaded ${exports.SERVICE_LABEL} — it comes back at the next login`);
358
+ exports.stopService = stopService;
359
+ /** Restart the service in place. `-k` kills the running instance first, so this
360
+ * is also how a version-drifted daemon is replaced. */
361
+ const restartService = (deps = exports.defaultServiceOpDeps) => askLaunchd(deps, ['kickstart', '-k', serviceTarget()], `restarted ${exports.SERVICE_LABEL}`);
362
+ exports.restartService = restartService;
363
+ const pathHasTmux = (pathEnv) => pathEnv.split(':').filter(Boolean).some((dir) => (0, node_fs_1.existsSync)((0, node_path_1.join)(dir, 'tmux')));
364
+ const jobIsLoaded = () => runLaunchctl(['print', serviceTarget()]).ok;
365
+ exports.defaultServiceProbe = { tmuxOnPath: pathHasTmux, loaded: jobIsLoaded };
366
+ /**
367
+ * Why this exists: every way the service breaks leaves it looking installed.
368
+ * `launchctl list` shows the job whether or not its interpreter still exists,
369
+ * and a PATH without tmux makes the daemon exit 127 at every login while the
370
+ * registration sits there unchanged. None of that is visible without asking.
371
+ */
372
+ const serviceHealthChecks = (status, probe = exports.defaultServiceProbe) => {
373
+ if (!status.supported)
374
+ return [];
375
+ if (!status.installed) {
376
+ return [
377
+ {
378
+ label: 'no login-time service — after a reboot the phone sees nothing until you run `zeph cc` '
379
+ + '(add it: zeph listener --install-service)',
380
+ state: 'warn',
381
+ },
382
+ ];
383
+ }
384
+ const rows = [{ label: `${status.label} registered`, state: 'pass' }];
385
+ if (status.missing.length > 0) {
386
+ rows.push({
387
+ label: `service points at ${status.missing.join(', ')} — gone. Re-run: zeph listener --install-service`,
388
+ state: 'fail',
389
+ });
390
+ }
391
+ else {
392
+ rows.push({ label: 'service programs still exist', state: 'pass' });
393
+ }
394
+ // launchd hands the job its own bare PATH, so tmux has to come from what
395
+ // the plist baked in. Without it verifyTmux() exits 127 at every login.
396
+ if (status.pathEnv && probe.tmuxOnPath(status.pathEnv)) {
397
+ rows.push({ label: 'tmux reachable from the service PATH', state: 'pass' });
398
+ }
399
+ else {
400
+ rows.push({
401
+ label: 'tmux NOT on the service PATH — the daemon exits 127 at login. '
402
+ + 'Re-run: zeph listener --install-service',
403
+ state: 'fail',
404
+ });
405
+ }
406
+ // Without a UTF-8 LANG, tmux escapes the U+241F field separator to `_` and
407
+ // every session is dropped as unparseable — with nothing in the log that
408
+ // says so except a raw line the user has no reason to read as fatal.
409
+ if (status.langEnv && /utf-?8$/i.test(status.langEnv)) {
410
+ rows.push({ label: 'service locale is UTF-8', state: 'pass' });
411
+ }
412
+ else {
413
+ rows.push({
414
+ label: 'service LANG is not UTF-8 — tmux mangles the session separator and every session is dropped. '
415
+ + 'Re-run: zeph listener --install-service',
416
+ state: 'fail',
417
+ });
418
+ }
419
+ rows.push(probe.loaded()
420
+ ? { label: 'launchd is running the job', state: 'pass' }
421
+ : { label: 'registered but launchd is not running it — it returns at the next login', state: 'warn' });
422
+ return rows;
423
+ };
424
+ exports.serviceHealthChecks = serviceHealthChecks;
@@ -1 +1 @@
1
- {"version":3,"file":"listener.d.ts","sourceRoot":"","sources":["../src/listener.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"listener.d.ts","sourceRoot":"","sources":["../src/listener.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AA4BH,OAAO,EAA0C,KAAK,SAAS,EAAE,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAUxH,OAAO,EAAiD,KAAK,UAAU,EAA4C,MAAM,kBAAkB,CAAC;AAE5I,OAAO,EAA4E,KAAK,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACvI,OAAO,EAA6C,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAmCtG,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAElD,UAAU,YAAY;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB;;;;;;;;;;;;OAYG;IACH,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C,MAAM,WAAW,oBAAoB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,qBAAqB,GAC9B,MAAM,SAAS,YAAY,EAAE,EAC7B,MAAK,MAAmB,KACzB,oBAAoB,EAYtB,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAI,OAAO,oBAAoB,EAAE,KAAG,MACP,CAAC;AAEnE,eAAO,MAAM,mBAAmB,GAAI,UAAU,YAAY,EAAE,KAAG,MAS7C,CAAC;AAEnB;6BAC6B;AAC7B,eAAO,MAAM,iBAAiB,GAC1B,aAAa,MAAM,EACnB,qBAAqB,MAAM,GAAG,IAAI,EAClC,cAAc,MAAM,EACpB,OAAO,MAAM,KACd,OAEoD,CAAC;AAqBxD,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAA+B,CAAC;AAenF,eAAO,MAAM,cAAc,GACvB,SAAS,MAAM,EACf,MAAK,MAAmB,EACxB,OAAM,MAAU,KACjB,OAgBF,CAAC;AAEF,2EAA2E;AAC3E,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,KAAG,MAAM,GAAG,IAO7D,CAAC;AAgGF;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,EAAE,KAAG,MAAM,EAAE,GAAG,IAQvD,CAAC;AA0CF;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,QAAO,IAG5C,CAAC;AA8NF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,KAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAK3F,CAAC;AAEF,UAAU,QAAQ;IACd,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAiDD;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,QAAQ,KAAG,qBAAqB,GAAG,IAGhE,CAAC;AA0BZ,iBAAiB;AACjB,eAAO,MAAM,kBAAkB,QAAO,IAErC,CAAC;AAWF;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAC3B,MAAM,MAAM,EACZ,WAAW,SAAS,EACpB,UAAU,MAAM,GAAG,IAAI,EACvB,MAAK,MAAmB,KACzB,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,gBAAgB,GAAG,aAAa,CAmB/D,CAAC;AAWF,MAAM,WAAW,YAAY;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACnB;AASD,8EAA8E;AAC9E,eAAO,MAAM,iBAAiB,GAAI,KAAK,OAAO,KAAG,IAWhD,CAAC;AAEF,iBAAiB;AACjB,eAAO,MAAM,mBAAmB,QAAO,IAGtC,CAAC;AAEF,MAAM,WAAW,QAAQ;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GACzB,WAAW,WAAW,CAAC,MAAM,CAAC,EAC9B,UAAS,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAsB,KAC9D,QAAQ,EAgBV,CAAC;AAWF,MAAM,WAAW,aAAa;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,uBAAuB,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,GAAI,KAAK,aAAa,KAAG,cAAc,GAAG,IAmBzE,CAAC;AAgBF;;kDAEkD;AAClD,eAAO,MAAM,mBAAmB,QAAY,CAAC;AAK7C,MAAM,WAAW,gBAAgB;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;4EACwE;IACxE,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,eAAgB,SAAQ,gBAAgB;IACrD;wEACoE;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;sCACkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACnB;AAsGD;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,GAC/B,KAAK,gBAAgB,EACrB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAC9C,OA2CF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GAC9B,KAAK,eAAe,EACpB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAC9C,OA+CF,CAAC;AAWF,MAAM,WAAW,oBAAoB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,6BAA6B,CAAC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GACnC,KAAK,oBAAoB,EACzB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAC9C,OAoDF,CAAC;AAEF,MAAM,WAAW,kBAAkB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAC9B,OAAO,EAAE,2BAA2B,CAAC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;CACd;AA0BD,8DAA8D;AAC9D,eAAO,MAAM,oBAAoB,OAAQ,CAAC;AAI1C;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,GACjC,KAAK,kBAAkB,EACvB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAC9C,OA0DF,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,6BAA6B,CAAC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,0BAA0B,GACnC,KAAK,oBAAoB,EACzB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAC9C,OA6BF,CAAC;AAiBF;+EAC+E;AAC/E,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAG5C,MAAM,WAAW,oBAAoB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IAClC,OAAO,EAAE,+BAA+B,CAAC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;sCACkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;0EACsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;oEACgE;IAChE,SAAS,CAAC,EAAE,yBAAyB,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACtB;AA0CD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,0BAA0B,GACnC,KAAK,oBAAoB,EACzB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAC9C,OA8DF,CAAC;AAsEF,MAAM,WAAW,YAAY;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf;;;oDAGgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,KAAG,YAAY,GAAG,IAY9D,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GACtB,SAAS,MAAM,EACf,QAAQ;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,KACjD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAKlC,CAAC;AASF;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,EAAE,YAAY,MAAM,KAAG,MAClB,CAAC;AAUpD;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,GAChC,aAAa,MAAM,EACnB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,EAC7C,SAAQ,MAAM,EAA8B,KAC7C,IAuBF,CAAC;AAcF,MAAM,WAAW,aAAa;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAQD,eAAO,MAAM,oBAAoB,MAAM,CAAC;AACxC,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAKtC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,eAAe,OAAQ,CAAC;AAOrC,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,aAAa,MAAM,GAAG,IAAI,EAAE,KAAK,MAAM,KAAG,MACgC,CAAC;AAEzG,0DAA0D;AAC1D,MAAM,WAAW,WAAW;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH;;gEAEgE;AAChE,eAAO,MAAM,cAAc,GAAI,QAAQ,WAAW,EAAE,KAAK,MAAM,KAAG,OAGxB,CAAC;AAE3C,eAAO,MAAM,cAAc,GAAI,QAAQ,WAAW,EAAE,KAAK,MAAM,KAAG,OAYjE,CAAC;AAqBF,eAAO,MAAM,eAAe,QAAS,CAAC;AAMtC,eAAO,MAAM,sBAAsB,IAAI,CAAC;AA6GxC,eAAO,MAAM,UAAU,GAAI,aAAa,MAAM,KAAG,IAuBhD,CAAC;AAEF,eAAO,MAAM,cAAc,QAAO,IAGjC,CAAC;AAEF,oFAAoF;AACpF,MAAM,MAAM,kBAAkB,GAAG;IAC7B,OAAO,EAAE,oBAAoB,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,4EAA4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;mEAC+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;4EAMwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;0CAMsC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;0CAOsC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,yBAAyB,CAAC;CACzC,CAAC;AAEF;;;gDAGgD;AAChD,MAAM,MAAM,gBAAgB,GAAG;IAC3B,OAAO,EAAE,oBAAoB,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,iBAAiB,GAAG,kBAAkB,GAAG,gBAAgB,GAAG,cAAc,GAAG,aAAa,GAAG,gBAAgB,CAAC;IACrH;;+EAE2E;IAC3E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;sEAEkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAAI,aAAa,MAAM,KAAG,OAChB,CAAC;AAqC1C;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GACzB,UAAU;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,EACjD,aAAa,MAAM,EACnB,sBAAsB,MAAM,EAC5B,OAAO;IACH,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB,KACF,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAmBnC,CAAC;AAEF;;;;GAIG;AASH,eAAO,MAAM,mBAAmB,GAC5B,KAAK,aAAa,EAClB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAC9C,OAsQF,CAAC;AAWF;;;uBAGuB;AACvB,MAAM,WAAW,iBAAiB;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;+EAI2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;kFAK8E;IAC9E,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;8DAE8D;AAC9D,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC;;uDAEuD;AACvD,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,kFAAkF;AAClF,KAAK,aAAa,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;AAE7D,+BAA+B;AAC/B,KAAK,cAAc,GAAG,cAAc,GAAG;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB;kEAC8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB;wEACoE;IACpE,MAAM,EAAE,OAAO,CAAC;CACnB,CAAC;AAOF,KAAK,UAAU,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAuCtF;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,iBAAiB,KAAG,UAsC7D,CAAC;AAoBF;;;6DAG6D;AAC7D,eAAO,MAAM,eAAe,KAAK,CAAC;AA6ClC;;;;sDAIsD;AACtD,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,QAAO,OAAO,CAAC,IAAI,CAAsB,CAAC;AA6G3E;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC3B,KAAK,iBAAiB,EACtB,MAAM,aAAa,KACpB,OAqCF,CAAC;AAEF,MAAM,WAAW,aAAa;IAC1B,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,0EAA0E;IAC1E,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrD;AAED;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,QAAO,aAuFzC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,QAAO,YAAY,EAAuC,CAAC;AAIvF;;;;;GAKG;AACH,UAAU,kBAAkB;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,QAAQ;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;sEACkE;IAClE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB;sEACkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAChC;AAED,UAAU,cAAc;IACpB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACjD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACpD,0EAA0E;IAC1E,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACxD,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC;IAC1D,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IACtE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,oEAAoE;IACpE,mBAAmB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACzF,+DAA+D;IAC/D,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC7C;sEACkE;IAClE,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AA2BD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAC1B,SAAS,MAAM,EACf,MAAM,MAAM,EACZ,MAAK,MAAM,MAAiB,KAC7B,OAUF,CAAC;AA0DF,eAAO,MAAM,oBAAoB,GAAI,KAAK;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,KAAG,IAE/E,CAAC;AA2FF;;;;GAIG;AACH,eAAO,MAAM,aAAa,GACtB,MAAK,MAAmB,EACxB,MAAK,MAAwB,EAC7B,MAAK,MAA0B,KAChC,MAaF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,GACnB,MAAM,QAAQ,EACd,OAAM,cAAmB,KAC1B,OAAO,CAAC,OAAO,CAgDjB,CAAC;AAcF,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,KAAG,MAIhD,CAAC;AA4DF,eAAO,MAAM,uBAAuB,GAAI,OAAO,MAAM,KAAG,MA2BvD,CAAC;AA8QF;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GACrB,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,EACtC,QAAQ;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAC1B,SAAS,MAAM,KAChB,MAAM,GAAG,IAIX,CAAC;AA6HF,eAAO,MAAM,cAAc,GAAU,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAG,OAAO,CAAC,MAAM,CAwJ3F,CAAC"}
package/dist/listener.js CHANGED
Binary file
@@ -1 +1 @@
1
- {"version":3,"file":"remote-hook.d.ts","sourceRoot":"","sources":["../src/remote-hook.ts"],"names":[],"mappings":"AAwCA;wEACwE;AACxE,eAAO,MAAM,kBAAkB,8BAA+B,CAAC;AAC/D,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAoClE,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,KAAG,GAAG,IAAI,eACE,CAAC;AAE1D;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,OAAO,eAAe,EACtB,OAAO,MAAM,EACb,MAAK,MAAM,CAAC,UAAwB,EACpC,MAAK,MAAM,MAAiB,KAC3B,MAAM,GAAG,IAwCX,CAAC"}
1
+ {"version":3,"file":"remote-hook.d.ts","sourceRoot":"","sources":["../src/remote-hook.ts"],"names":[],"mappings":"AAyCA;wEACwE;AACxE,eAAO,MAAM,kBAAkB,8BAA+B,CAAC;AAC/D,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAoClE,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,KAAG,GAAG,IAAI,eACE,CAAC;AAE1D;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,OAAO,eAAe,EACtB,OAAO,MAAM,EACb,MAAK,MAAM,CAAC,UAAwB,EACpC,MAAK,MAAM,MAAiB,KAC3B,MAAM,GAAG,IA0CX,CAAC"}
@@ -31,6 +31,7 @@ exports.runRemoteHook = exports.isRemoteHookAgent = exports.REMOTE_HOOK_AGENTS =
31
31
  * context and must never block a prompt.
32
32
  */
33
33
  const fs_1 = require("fs");
34
+ const config_js_1 = require("./config.js");
34
35
  const gate_js_1 = require("./gate.js");
35
36
  /** Agents whose prompt-submit hooks this command serves. Claude Code is
36
37
  * NOT here — its hook ships with the plugin (hooks/zeph-remote.sh). */
@@ -54,7 +55,7 @@ const TWO_WAY_CONTEXT = `# System note (Zeph remote-origin detect)
54
55
  This user message arrived from the user's phone via Zeph agent chat (verified by the listener — exact text match). The user is driving this session remotely and is NOT at the terminal. Enter sticky REMOTE mode now (CORE_RULES Rule 9): end EVERY response with \`zeph_ask\` (buttons + free-text) until the user exits — an exit signal (done/stop/exit), or a prompt they type at the terminal, which this hook will tell you about. Plain-text questions are invisible to them.`;
55
56
  const ONE_WAY_CONTEXT = `# System note (Zeph remote-origin detect)
56
57
 
57
- This user message arrived from the user's phone via Zeph agent chat (verified by the listener — exact text match), but ZEPH_HOOK_ID is not set, so two-way tools (zeph_ask/zeph_prompt/zeph_input) are unavailable. Make your final message self-contained — the completion push is the user's only feedback channel. If you have not already mentioned it this session, tell the user once that running \`npx @zeph-to/cli setup\` upgrades this into a two-way remote session (buttons + text replies from the phone).`;
58
+ This user message arrived from the user's phone via Zeph agent chat (verified by the listener — exact text match), but no hook id is configured (neither \`ZEPH_HOOK_ID\` nor \`hookId\` in ~/.zeph/config.json), so two-way tools (zeph_ask/zeph_prompt/zeph_input) are unavailable. Make your final message self-contained — the completion push is the user's only feedback channel. If you have not already mentioned it this session, tell the user once that running \`npx @zeph-to/cli setup\` upgrades this into a two-way remote session (buttons + text replies from the phone).`;
58
59
  /**
59
60
  * The first prompt a remote session's user types at the terminal. Emitted
60
61
  * once — the state is cleared with it, so every later terminal turn is a
@@ -96,14 +97,16 @@ const runRemoteHook = (agent, stdin, env = process.env, now = Date.now) => {
96
97
  // leaves the mode as it was.
97
98
  const origin = remoteOrigin(prompt, cwd, now);
98
99
  if (origin === 'phone') {
99
- if (!env.ZEPH_HOOK_ID)
100
+ // Resolved on the two branches that need it, not up front — the common
101
+ // no-marker path never reads the config file.
102
+ if (!(0, config_js_1.resolveHookId)(env))
100
103
  return emit(ONE_WAY_CONTEXT);
101
104
  // Only a two-way session has a mode to stay in — without zeph_ask there is
102
105
  // nothing for a later turn to be reminded of, so no state is recorded.
103
106
  (0, gate_js_1.touchRemoteActive)(cwd, now);
104
107
  return emit(TWO_WAY_CONTEXT);
105
108
  }
106
- if (origin === 'keyboard' && env.ZEPH_HOOK_ID && (0, gate_js_1.isRemoteActive)(cwd, now)) {
109
+ if (origin === 'keyboard' && (0, gate_js_1.isRemoteActive)(cwd, now) && (0, config_js_1.resolveHookId)(env)) {
107
110
  (0, gate_js_1.clearRemoteActive)(cwd);
108
111
  return emit(EXIT_CONTEXT);
109
112
  }
@@ -7,5 +7,13 @@ export declare const rmGeminiHook: (filePath: string, dry: boolean) => string |
7
7
  * but events the user added since install survive. Exported for tests
8
8
  * (codex is PATH-detected, same as gemini). */
9
9
  export declare const rmCodexHook: (filePath: string, dry: boolean) => string | null;
10
+ /**
11
+ * Remove the login-time LaunchAgent, if one is installed.
12
+ *
13
+ * Async, so it does not fit the sync `Step` shape the per-agent uninstallers
14
+ * use — launchctl has to be waited on. Returns the same "one line or null"
15
+ * contract those steps do.
16
+ */
17
+ export declare const removeServiceStep: (dry: boolean) => Promise<string | null>;
10
18
  export declare const handleUninstall: (args: Record<string, string | boolean>) => Promise<number>;
11
19
  //# sourceMappingURL=uninstall.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"uninstall.d.ts","sourceRoot":"","sources":["../src/uninstall.ts"],"names":[],"mappings":"AAuGA;;iDAEiD;AACjD,eAAO,MAAM,YAAY,GAAI,UAAU,MAAM,EAAE,KAAK,OAAO,KAAG,MAAM,GAAG,IAWtE,CAAC;AAEF;;;gDAGgD;AAChD,eAAO,MAAM,WAAW,GAAI,UAAU,MAAM,EAAE,KAAK,OAAO,KAAG,MAAM,GAAG,IAerE,CAAC;AAuEF,eAAO,MAAM,eAAe,GAAU,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAG,OAAO,CAAC,MAAM,CA8B5F,CAAC"}
1
+ {"version":3,"file":"uninstall.d.ts","sourceRoot":"","sources":["../src/uninstall.ts"],"names":[],"mappings":"AAwGA;;iDAEiD;AACjD,eAAO,MAAM,YAAY,GAAI,UAAU,MAAM,EAAE,KAAK,OAAO,KAAG,MAAM,GAAG,IAWtE,CAAC;AAEF;;;gDAGgD;AAChD,eAAO,MAAM,WAAW,GAAI,UAAU,MAAM,EAAE,KAAK,OAAO,KAAG,MAAM,GAAG,IAerE,CAAC;AAqEF;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAU,KAAK,OAAO,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAM3E,CAAC;AAIF,eAAO,MAAM,eAAe,GAAU,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAG,OAAO,CAAC,MAAM,CAoC5F,CAAC"}
package/dist/uninstall.js CHANGED
@@ -1,11 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleUninstall = exports.rmCodexHook = exports.rmGeminiHook = void 0;
3
+ exports.handleUninstall = exports.removeServiceStep = exports.rmCodexHook = exports.rmGeminiHook = void 0;
4
4
  const child_process_1 = require("child_process");
5
5
  const fs_1 = require("fs");
6
6
  const os_1 = require("os");
7
7
  const path_1 = require("path");
8
8
  const agents_js_1 = require("./agents.js");
9
+ const listener_service_js_1 = require("./listener-service.js");
9
10
  const templates_js_1 = require("./templates.js");
10
11
  const config_js_1 = require("./config.js");
11
12
  const HOME = (0, os_1.homedir)();
@@ -235,6 +236,24 @@ const AGENT_UNINSTALLERS = {
235
236
  () => rmAiderReadDirective((0, path_1.join)(HOME, '.aider.conf.yml'), dry),
236
237
  ]),
237
238
  };
239
+ /**
240
+ * Remove the login-time LaunchAgent, if one is installed.
241
+ *
242
+ * Async, so it does not fit the sync `Step` shape the per-agent uninstallers
243
+ * use — launchctl has to be waited on. Returns the same "one line or null"
244
+ * contract those steps do.
245
+ */
246
+ const removeServiceStep = async (dry) => {
247
+ if (!(0, listener_service_js_1.serviceInstalled)())
248
+ return null;
249
+ if (dry)
250
+ return `would remove the login-time service (${listener_service_js_1.SERVICE_LABEL})`;
251
+ const result = await (0, listener_service_js_1.uninstallService)();
252
+ if (!result.ok)
253
+ return `could not remove the login-time service: ${result.reason}`;
254
+ return `removed the login-time service (${listener_service_js_1.SERVICE_LABEL})`;
255
+ };
256
+ exports.removeServiceStep = removeServiceStep;
238
257
  // ── Entry point ──────────────────────────────────────────────────
239
258
  const handleUninstall = async (args) => {
240
259
  const dry = args['dry-run'] === true;
@@ -248,6 +267,14 @@ const handleUninstall = async (args) => {
248
267
  console.log(` ${agent.name}:`);
249
268
  AGENT_UNINSTALLERS[agent.id]?.(dry);
250
269
  }
270
+ // The LaunchAgent outlives every agent's rules, so removing it is part of
271
+ // uninstall and not of any one agent's teardown.
272
+ console.log('\n Login-time service:');
273
+ const serviceLine = await (0, exports.removeServiceStep)(dry);
274
+ if (serviceLine)
275
+ ok(serviceLine);
276
+ else
277
+ skip('not installed');
251
278
  // ~/.zeph/config.json holds the API key — kept by default so a
252
279
  // re-install doesn't need the key re-entered. --purge removes it.
253
280
  console.log('\n Config:');
@@ -1 +1 @@
1
- {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AA0CA,eAAO,MAAM,YAAY,GAAU,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAG,OAAO,CAAC,MAAM,CAwEzF,CAAC"}
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AA0CA,eAAO,MAAM,YAAY,GAAU,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAG,OAAO,CAAC,MAAM,CAiFzF,CAAC"}
package/dist/verify.js CHANGED
@@ -6,6 +6,7 @@ const os_1 = require("os");
6
6
  const path_1 = require("path");
7
7
  const agents_js_1 = require("./agents.js");
8
8
  const config_js_1 = require("./config.js");
9
+ const listener_service_js_1 = require("./listener-service.js");
9
10
  const zeph_hook_js_1 = require("./zeph-hook.js");
10
11
  const HOME = (0, os_1.homedir)();
11
12
  const pass = (msg) => console.log(` ✓ ${msg}`);
@@ -55,7 +56,7 @@ const handleVerify = async (args) => {
55
56
  console.log(' Credentials:');
56
57
  const config = (0, config_js_1.loadConfig)();
57
58
  const apiKey = (0, config_js_1.resolvedEnv)('ZEPH_API_KEY') || config.apiKey;
58
- const hookId = (0, config_js_1.resolvedEnv)('ZEPH_HOOK_ID') || config.hookId;
59
+ const hookId = (0, config_js_1.resolveHookId)();
59
60
  record(apiKey ? 'ZEPH_API_KEY is set' : 'ZEPH_API_KEY not set (env or ~/.zeph/config.json)', apiKey ? 'pass' : 'fail');
60
61
  record(hookId
61
62
  ? 'ZEPH_HOOK_ID is set (two-way zeph_ask/prompt/input enabled)'
@@ -67,6 +68,15 @@ const handleVerify = async (args) => {
67
68
  record((0, agents_js_1.hasCommand)('zeph')
68
69
  ? 'zeph CLI on PATH'
69
70
  : 'zeph CLI not on PATH (hooks fall back to npx — slower first call)', (0, agents_js_1.hasCommand)('zeph') ? 'pass' : 'warn');
71
+ // ── Login-time service ───────────────────────────────────────
72
+ // Optional, so a missing one is a warning. A broken one is not: every way
73
+ // it breaks still looks installed from the outside.
74
+ const serviceRows = (0, listener_service_js_1.serviceHealthChecks)((0, listener_service_js_1.serviceStatus)());
75
+ if (serviceRows.length > 0) {
76
+ console.log('\n Login-time service:');
77
+ for (const row of serviceRows)
78
+ record(row.label, row.state);
79
+ }
70
80
  // ── Per-agent config ─────────────────────────────────────────
71
81
  console.log('\n Agents:');
72
82
  const detected = (0, agents_js_1.detectAgents)().filter((a) => a.detected);
@@ -1 +1 @@
1
- {"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../src/wrapper.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAOtD,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,QAAO,MAapC,CAAC;AAEF,+DAA+D;AAC/D,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM,KAAG,MAA2B,CAAC;AAI9E;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,KAAG,MAenD,CAAC;AAmCF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,MAAM,GAAG,IAAI,EAAE,WAAW,MAAM,KAAG,OAChC,CAAC;AAEpD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAAa,OAAO,CAAC,IAAI,CAa1D,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAU,OAAO,WAAW,EAAE,QAAO,MAAM,EAAO,KAAG,OAAO,CAAC,MAAM,CAmCjG,CAAC"}
1
+ {"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../src/wrapper.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAOtD,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,QAAO,MAapC,CAAC;AAEF,+DAA+D;AAC/D,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM,KAAG,MAA2B,CAAC;AAI9E;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,KAAG,MAenD,CAAC;AAmCF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,MAAM,GAAG,IAAI,EAAE,WAAW,MAAM,KAAG,OAChC,CAAC;AAEpD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAAa,OAAO,CAAC,IAAI,CAgC1D,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAU,OAAO,WAAW,EAAE,QAAO,MAAM,EAAO,KAAG,OAAO,CAAC,MAAM,CAmCjG,CAAC"}
package/dist/wrapper.js CHANGED
@@ -17,6 +17,7 @@ const path_1 = require("path");
17
17
  const check_update_js_1 = require("./check-update.js");
18
18
  const config_js_1 = require("./config.js");
19
19
  const listener_process_js_1 = require("./listener-process.js");
20
+ const listener_service_js_1 = require("./listener-service.js");
20
21
  const FALLBACK_NAME = 'project';
21
22
  /** basename(), with a stable fallback for edge paths like `/`. */
22
23
  const safeBasename = (path) => (0, path_1.basename)(path) || FALLBACK_NAME;
@@ -126,10 +127,29 @@ exports.listenerVersionDrifted = listenerVersionDrifted;
126
127
  */
127
128
  const ensureListenerRunning = async () => {
128
129
  const pid = (0, listener_process_js_1.runningListenerPid)();
129
- if (pid !== null) {
130
- const running = (0, listener_process_js_1.runningListenerVersion)();
131
- if (!(0, exports.listenerVersionDrifted)(running, config_js_1.VERSION))
130
+ const running = pid === null ? null : (0, listener_process_js_1.runningListenerVersion)();
131
+ if (pid !== null && !(0, exports.listenerVersionDrifted)(running, config_js_1.VERSION))
132
+ return;
133
+ // When the login-time service is installed, launchd owns this process.
134
+ // Stopping its child makes launchd start a replacement while we spawn our
135
+ // own, and the two race through the singleton guard in handleListener —
136
+ // whichever loses exits 0, and if that is launchd's child, KeepAlive
137
+ // either flaps it every ThrottleInterval or reads the clean exit as
138
+ // deliberate and gives up for the rest of the login session. Ask launchd
139
+ // to do it instead; `kickstart -k` replaces a drifted daemon in one step.
140
+ if ((0, listener_service_js_1.serviceInstalled)()) {
141
+ const result = (0, listener_service_js_1.restartService)();
142
+ if (!result.ok) {
143
+ console.error(`zeph: could not start the listener service — ${result.reason}`);
132
144
  return;
145
+ }
146
+ if (pid === null)
147
+ console.log(`zeph: listener started by launchd (log: ${listener_process_js_1.LISTENER_LOG_FILE})`);
148
+ else
149
+ console.log(`zeph: listener ${running ?? '(pre-1.26)'} is stale — launchd restarting on ${config_js_1.VERSION}`);
150
+ return;
151
+ }
152
+ if (pid !== null) {
133
153
  console.log(`zeph: listener ${running ?? '(pre-1.26)'} is stale — restarting on ${config_js_1.VERSION}`);
134
154
  await (0, listener_process_js_1.stopListener)(pid);
135
155
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeph-to/cli",
3
- "version": "2.7.0",
3
+ "version": "2.8.0",
4
4
  "description": "Zeph CLI + push notification SDK for AI agents",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",