@zeph-to/cli 2.6.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.
@@ -14,7 +14,7 @@
14
14
  * another program.
15
15
  */
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.sessionDirectoryExists = exports.knownSessionsPath = exports.isKnownSession = exports.rememberSessions = exports.recallSession = exports.knownSessions = exports.KNOWN_SESSION_TTL_MS = exports.MAX_KNOWN_SESSIONS = void 0;
17
+ exports.sessionDirectoryExists = exports.knownSessionsPath = exports.isKnownSession = exports.forgetSession = exports.rememberSessions = exports.recallSession = exports.knownSessions = exports.KNOWN_SESSION_TTL_MS = exports.MAX_KNOWN_SESSIONS = void 0;
18
18
  const fs_1 = require("fs");
19
19
  const path_1 = require("path");
20
20
  const gate_js_1 = require("./gate.js");
@@ -100,6 +100,27 @@ const rememberSessions = (live, now = Date.now()) => {
100
100
  writeAll(entries);
101
101
  };
102
102
  exports.rememberSessions = rememberSessions;
103
+ /**
104
+ * Forget one session, by name. Returns false when this machine never knew it.
105
+ *
106
+ * This is what deleting a past session means on the machine that ran it: the
107
+ * entry leaves the file, so the phone stops being offered the session AND —
108
+ * since this file is the resume whitelist — stops being able to start it.
109
+ * That is the intended pair, not a side effect.
110
+ *
111
+ * It stays forgotten: `rememberSessions` only writes down sessions that are
112
+ * running, so nothing re-adds an entry for a session that has ended. Running
113
+ * that name again is what brings it back, which is also the only way back.
114
+ */
115
+ const forgetSession = (name) => {
116
+ const entries = readAll();
117
+ const kept = entries.filter((e) => e.name !== name);
118
+ if (kept.length === entries.length)
119
+ return false;
120
+ writeAll(kept);
121
+ return true;
122
+ };
123
+ exports.forgetSession = forgetSession;
103
124
  /** Whether the registry knows this name — the resume whitelist check. */
104
125
  const isKnownSession = (name, now = Date.now()) => (0, exports.recallSession)(name, now) !== null;
105
126
  exports.isKnownSession = isKnownSession;
@@ -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.6.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",