@zeph-to/cli 1.20.0 → 1.22.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 +36 -22
- package/dist/cli.js +4 -1
- package/dist/installer.d.ts +6 -1
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +22 -24
- package/dist/listener.d.ts +11 -0
- package/dist/listener.d.ts.map +1 -1
- package/dist/listener.js +79 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,36 +33,50 @@ npx @zeph-to/cli notify --title "Hello"
|
|
|
33
33
|
|
|
34
34
|
## Quick Start
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
# Sign in via browser — auto-fetches your API key + hook into
|
|
38
|
-
# ~/.zeph/config.json (no copy-paste)
|
|
39
|
-
npx @zeph-to/cli login
|
|
36
|
+
Install the CLI globally, then run setup:
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
```bash
|
|
39
|
+
npm install -g @zeph-to/cli
|
|
40
|
+
zeph install
|
|
44
41
|
```
|
|
45
42
|
|
|
46
|
-
`
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
43
|
+
`zeph install` opens a browser sign-in on a fresh machine — the web app
|
|
44
|
+
issues an API key + hook as a matched pair and the CLI writes them to
|
|
45
|
+
`~/.zeph/config.json` (no copy-paste). Then it installs rules + hooks +
|
|
46
|
+
MCP for every detected agent (Claude Code / Cursor / Windsurf / Gemini /
|
|
47
|
+
Codex / …). Safe to re-run: a saved login is reused untouched (no
|
|
48
|
+
prompts), so a re-run just refreshes the agent integrations. To switch
|
|
49
|
+
account, `zeph install --relogin` forces a fresh sign-in.
|
|
50
|
+
|
|
51
|
+
**Why global, not `npx`?** `zeph cc` (drive a session from your phone)
|
|
52
|
+
needs `zeph` on your `PATH`, and the agent hooks this installs are
|
|
53
|
+
`$(command -v zeph || npx …)` — a global binary skips an npx cold-start
|
|
54
|
+
on *every* notification. For notifications only, with no phone control,
|
|
55
|
+
`npx @zeph-to/cli install` is a lighter alternative that skips the global
|
|
56
|
+
binary.
|
|
51
57
|
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
`~/.zeph/config.json` is the single source of truth — the CLI, the MCP
|
|
59
|
+
server, the plugin hooks, and the listener all read it. You never need
|
|
60
|
+
`ZEPH_API_KEY`-style env vars for a normal setup; they exist as overrides
|
|
61
|
+
(second account, CI).
|
|
62
|
+
|
|
63
|
+
Variants:
|
|
54
64
|
|
|
55
65
|
```bash
|
|
56
|
-
|
|
57
|
-
|
|
66
|
+
# Headless box (no browser): paste credentials from another machine's
|
|
67
|
+
# ~/.zeph/config.json
|
|
68
|
+
zeph install --key ak_... --hook hook_...
|
|
58
69
|
|
|
59
|
-
|
|
60
|
-
|
|
70
|
+
# Skip the interactive agent picker
|
|
71
|
+
zeph install --only claude,cursor
|
|
61
72
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
# Switch account — force a fresh browser sign-in over a saved login
|
|
74
|
+
zeph install --relogin
|
|
75
|
+
|
|
76
|
+
# Refresh credentials only (no agent re-install) — assumes you already
|
|
77
|
+
# ran `zeph install` once; on its own it does NOT wire MCP into agents
|
|
78
|
+
zeph login
|
|
79
|
+
```
|
|
66
80
|
|
|
67
81
|
To **send** notifications:
|
|
68
82
|
|
package/dist/cli.js
CHANGED
|
@@ -107,10 +107,13 @@ Login options:
|
|
|
107
107
|
|
|
108
108
|
Install options:
|
|
109
109
|
(no --key, no saved config → opens browser login automatically;
|
|
110
|
-
|
|
110
|
+
a saved login is reused untouched on re-run; headless falls back
|
|
111
|
+
to manual key entry)
|
|
111
112
|
--key <api-key> API key (non-interactive; skips browser login)
|
|
112
113
|
--hook <hook-id> Hook ID (non-interactive)
|
|
113
114
|
--base-url <url> Base URL (non-interactive)
|
|
115
|
+
--relogin Force a fresh browser sign-in even if a login is
|
|
116
|
+
already saved (switch account)
|
|
114
117
|
--web-url <url> Login web app URL [default: https://app.zeph.to]
|
|
115
118
|
--only <agents> Comma-separated agent ids to install for
|
|
116
119
|
(claude,cursor,windsurf,gemini,codex,copilot,cline,aider).
|
package/dist/installer.d.ts
CHANGED
|
@@ -3,7 +3,12 @@ import type { Agent } from './agents.js';
|
|
|
3
3
|
* True when install should auto-open browser login (ADR 0002): interactive
|
|
4
4
|
* context with no existing credential (--key/env/config all absent).
|
|
5
5
|
*/
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Interactive install, past the `--key` path: open a browser login only when
|
|
8
|
+
* there's nothing saved yet, or the user explicitly asked to re-authenticate
|
|
9
|
+
* with `--relogin`. A plain re-run keeps the saved login untouched.
|
|
10
|
+
*/
|
|
11
|
+
export declare const shouldReauth: (currentKey: string | undefined, relogin: boolean) => boolean;
|
|
7
12
|
/** Exported for tests (installGemini shells out to the real `gemini` binary,
|
|
8
13
|
* so the merge semantics can't be covered through it deterministically). */
|
|
9
14
|
export declare const mergeJsonFile: (filePath: string, patch: Record<string, unknown>) => void;
|
package/dist/installer.d.ts.map
CHANGED
|
@@ -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,eAAO,MAAM,
|
|
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,CAkG1F,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.
|
|
3
|
+
exports.handleInstall = 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");
|
|
@@ -19,8 +19,13 @@ const fail = (msg) => console.log(` - ${msg}`);
|
|
|
19
19
|
* True when install should auto-open browser login (ADR 0002): interactive
|
|
20
20
|
* context with no existing credential (--key/env/config all absent).
|
|
21
21
|
*/
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Interactive install, past the `--key` path: open a browser login only when
|
|
24
|
+
* there's nothing saved yet, or the user explicitly asked to re-authenticate
|
|
25
|
+
* with `--relogin`. A plain re-run keeps the saved login untouched.
|
|
26
|
+
*/
|
|
27
|
+
const shouldReauth = (currentKey, relogin) => !currentKey || relogin;
|
|
28
|
+
exports.shouldReauth = shouldReauth;
|
|
24
29
|
const promptInput = (question) => {
|
|
25
30
|
const rl = (0, readline_1.createInterface)({ input: process.stdin, output: process.stdout });
|
|
26
31
|
return new Promise((resolve) => {
|
|
@@ -348,26 +353,12 @@ const testConnection = async (apiKey, baseUrl) => {
|
|
|
348
353
|
return false;
|
|
349
354
|
}
|
|
350
355
|
};
|
|
351
|
-
/** Interactive path when a credential already exists: prompt to keep/replace. */
|
|
352
|
-
const promptExistingCredentials = async (currentKey, existing) => {
|
|
353
|
-
if (currentKey)
|
|
354
|
-
console.log(` Current API Key: ${currentKey.slice(0, 12)}...`);
|
|
355
|
-
const keyInput = await promptInput(currentKey ? ' New API Key (Enter to keep): ' : ' API Key (from app > Settings > API Keys): ');
|
|
356
|
-
const currentHook = (0, config_js_1.resolvedEnv)('ZEPH_HOOK_ID') || existing.hookId;
|
|
357
|
-
if (currentHook)
|
|
358
|
-
console.log(` Current Hook ID: ${currentHook}`);
|
|
359
|
-
const hookInput = await promptInput(currentHook ? ' New Hook ID (Enter to keep, "none" to remove): ' : ' Hook ID (optional, for prompt/input): ');
|
|
360
|
-
return {
|
|
361
|
-
apiKey: keyInput || currentKey,
|
|
362
|
-
hookId: hookInput === 'none' ? undefined : (hookInput || currentHook),
|
|
363
|
-
baseUrl: existing.baseUrl,
|
|
364
|
-
};
|
|
365
|
-
};
|
|
366
356
|
/**
|
|
367
|
-
* Resolve API key + hook for install. Priority: --key/env/config (non-interactive
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
357
|
+
* Resolve API key + hook for install. Priority: --key/env/config (non-interactive)
|
|
358
|
+
* → a saved login is reused untouched on re-run → otherwise a brand-new
|
|
359
|
+
* interactive install opens browser login (ADR 0002), falling back to manual
|
|
360
|
+
* paste when headless. wsUrl/deviceId from a login are persisted by
|
|
361
|
+
* runLoginFlow and re-read at config-save time.
|
|
371
362
|
*/
|
|
372
363
|
const collectCredentials = async (args, installArgs, nonInteractive, existing) => {
|
|
373
364
|
if (nonInteractive) {
|
|
@@ -379,8 +370,15 @@ const collectCredentials = async (args, installArgs, nonInteractive, existing) =
|
|
|
379
370
|
}
|
|
380
371
|
console.log('');
|
|
381
372
|
const currentKey = (0, config_js_1.resolvedEnv)('ZEPH_API_KEY') || existing.apiKey;
|
|
382
|
-
|
|
383
|
-
|
|
373
|
+
// Re-run with a saved login: reuse it silently and move on to agent wiring.
|
|
374
|
+
// Switching accounts is `zeph login` or `zeph install --relogin`.
|
|
375
|
+
if (!(0, exports.shouldReauth)(currentKey, args.relogin === true)) {
|
|
376
|
+
console.log(` Using saved login (${currentKey.slice(0, 12)}…). Run \`zeph install --relogin\` to switch account.`);
|
|
377
|
+
return {
|
|
378
|
+
apiKey: currentKey,
|
|
379
|
+
hookId: (0, config_js_1.resolvedEnv)('ZEPH_HOOK_ID') || existing.hookId,
|
|
380
|
+
baseUrl: existing.baseUrl,
|
|
381
|
+
};
|
|
384
382
|
}
|
|
385
383
|
const result = await (0, login_js_1.runLoginFlow)({
|
|
386
384
|
webUrl: (0, login_js_1.resolveWebUrl)(args['web-url']),
|
package/dist/listener.d.ts
CHANGED
|
@@ -266,6 +266,17 @@ export declare const gcAttachments: (now?: number, dir?: string, ttl?: number) =
|
|
|
266
266
|
export declare const handlePush: (push: PushItem, deps?: HandlePushDeps) => Promise<boolean>;
|
|
267
267
|
export declare const computeBackoff: (attempt: number) => number;
|
|
268
268
|
export declare const computeListenerDeviceId: (host?: string) => string;
|
|
269
|
+
/**
|
|
270
|
+
* A user who only set ZEPH_API_KEY (no `zeph login`, no config file) gets the
|
|
271
|
+
* prod socket by default — mirroring the DEFAULT_API_BASE fallback for REST.
|
|
272
|
+
* The default only applies when the base URL is also the prod default: with a
|
|
273
|
+
* custom base URL, silently pointing the listener at the prod socket while
|
|
274
|
+
* REST talks to another stage would split the device across environments, so
|
|
275
|
+
* that combination still errors loudly.
|
|
276
|
+
*/
|
|
277
|
+
export declare const resolveWsUrl: (args: Record<string, string | boolean>, config: {
|
|
278
|
+
wsUrl?: string;
|
|
279
|
+
}, baseUrl: string) => string | null;
|
|
269
280
|
export declare const handleListener: (args: Record<string, string | boolean>) => Promise<number>;
|
|
270
281
|
export {};
|
|
271
282
|
//# sourceMappingURL=listener.d.ts.map
|
package/dist/listener.d.ts.map
CHANGED
|
@@ -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":"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"}
|
package/dist/listener.js
CHANGED
|
@@ -25,7 +25,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
25
25
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
26
26
|
};
|
|
27
27
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
-
exports.handleListener = exports.computeListenerDeviceId = exports.computeBackoff = exports.handlePush = exports.gcAttachments = exports.setAttachmentContext = exports.writeRemoteMarker = exports.collectSessions = exports.collectSessionsVerbose = exports.scheduleInjectSnapshots = exports.handleScreenRequest = exports.collectWatchHits = exports.resetPatternWatches = exports.setPatternWatches = exports.deriveSessionState = exports.resetSessionStates = exports.detectRemoteAgent = exports.parseSessionName = exports.invalidateTmuxSocketCache = exports.resolveKeys = exports.paneCurrentCommand = exports.checkRateLimit = exports.AUTH_FAILURE_CODES = exports.sessionsReportDue = exports.sessionsFingerprint = exports.SESSION_REPORT_HEARTBEAT_MS = void 0;
|
|
28
|
+
exports.handleListener = exports.resolveWsUrl = exports.computeListenerDeviceId = exports.computeBackoff = exports.handlePush = exports.gcAttachments = exports.setAttachmentContext = exports.writeRemoteMarker = exports.collectSessions = exports.collectSessionsVerbose = exports.scheduleInjectSnapshots = exports.handleScreenRequest = exports.collectWatchHits = exports.resetPatternWatches = exports.setPatternWatches = exports.deriveSessionState = exports.resetSessionStates = exports.detectRemoteAgent = exports.parseSessionName = exports.invalidateTmuxSocketCache = exports.resolveKeys = exports.paneCurrentCommand = exports.checkRateLimit = exports.AUTH_FAILURE_CODES = exports.sessionsReportDue = exports.sessionsFingerprint = exports.SESSION_REPORT_HEARTBEAT_MS = void 0;
|
|
29
29
|
const child_process_1 = require("child_process");
|
|
30
30
|
const crypto_1 = require("crypto");
|
|
31
31
|
const fs_1 = require("fs");
|
|
@@ -1082,23 +1082,75 @@ exports.computeBackoff = computeBackoff;
|
|
|
1082
1082
|
* time `zeph listener` rebinds). `dev_listener_<sha8(hostname)>` keeps it
|
|
1083
1083
|
* human-recognisable in dev logs without leaking the raw hostname.
|
|
1084
1084
|
*
|
|
1085
|
-
* The no-arg result
|
|
1086
|
-
*
|
|
1087
|
-
*
|
|
1088
|
-
*
|
|
1089
|
-
*
|
|
1085
|
+
* The no-arg result must identify the MACHINE, stably:
|
|
1086
|
+
* - NOT the hostname: macOS rewrites it on network changes (observed
|
|
1087
|
+
* flip-flopping between 'Mac' and '<name>.local' within hours), which
|
|
1088
|
+
* split one machine into several device rows — duplicated session lists
|
|
1089
|
+
* on the phone, and screen-peek requests addressed to an id no live
|
|
1090
|
+
* process answers for.
|
|
1091
|
+
* - NOT a file under ~/.zeph alone: users copy ~/.zeph to a new machine
|
|
1092
|
+
* to carry their API key, and a copied id makes two machines answer for
|
|
1093
|
+
* each other.
|
|
1094
|
+
* So: hash the platform machine id (IOPlatformUUID / /etc/machine-id) —
|
|
1095
|
+
* deterministic per machine, immune to both. The sticky file is only the
|
|
1096
|
+
* fallback for platforms where no machine id is readable.
|
|
1090
1097
|
*/
|
|
1091
1098
|
let processListenerDeviceId;
|
|
1099
|
+
const LISTENER_ID_FILE = (0, path_1.join)((0, os_1.homedir)(), '.zeph', 'listener-device-id');
|
|
1100
|
+
const hashListenerId = (seed) => `dev_listener_${(0, crypto_1.createHash)('sha256').update(seed).digest('hex').slice(0, 8)}`;
|
|
1101
|
+
const readMachineId = () => {
|
|
1102
|
+
try {
|
|
1103
|
+
if (process.platform === 'darwin') {
|
|
1104
|
+
const r = (0, child_process_1.spawnSync)('ioreg', ['-rd1', '-c', 'IOPlatformExpertDevice'], {
|
|
1105
|
+
encoding: 'utf-8',
|
|
1106
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
1107
|
+
});
|
|
1108
|
+
const m = r.stdout?.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/);
|
|
1109
|
+
if (m)
|
|
1110
|
+
return m[1];
|
|
1111
|
+
}
|
|
1112
|
+
if (process.platform === 'linux') {
|
|
1113
|
+
for (const p of ['/etc/machine-id', '/var/lib/dbus/machine-id']) {
|
|
1114
|
+
try {
|
|
1115
|
+
const v = (0, fs_1.readFileSync)(p, 'utf-8').trim();
|
|
1116
|
+
if (v)
|
|
1117
|
+
return v;
|
|
1118
|
+
}
|
|
1119
|
+
catch { /* try next */ }
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
catch { /* fall through to the sticky-file fallback */ }
|
|
1124
|
+
return null;
|
|
1125
|
+
};
|
|
1092
1126
|
const computeListenerDeviceId = (host) => {
|
|
1093
|
-
if (host !== undefined)
|
|
1094
|
-
|
|
1095
|
-
|
|
1127
|
+
if (host !== undefined)
|
|
1128
|
+
return hashListenerId(host);
|
|
1129
|
+
if (processListenerDeviceId)
|
|
1130
|
+
return processListenerDeviceId;
|
|
1131
|
+
const machineId = readMachineId();
|
|
1132
|
+
if (machineId) {
|
|
1133
|
+
processListenerDeviceId = hashListenerId(machineId);
|
|
1134
|
+
return processListenerDeviceId;
|
|
1096
1135
|
}
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1136
|
+
// No readable machine id — pin the first hostname-derived id to a file
|
|
1137
|
+
// so at least hostname drift can't split this machine.
|
|
1138
|
+
try {
|
|
1139
|
+
const saved = (0, fs_1.readFileSync)(LISTENER_ID_FILE, 'utf-8').trim();
|
|
1140
|
+
if (/^dev_listener_[0-9a-f]{8}$/.test(saved)) {
|
|
1141
|
+
processListenerDeviceId = saved;
|
|
1142
|
+
return saved;
|
|
1143
|
+
}
|
|
1100
1144
|
}
|
|
1101
|
-
|
|
1145
|
+
catch { /* first run — fall through to compute + persist */ }
|
|
1146
|
+
const fresh = hashListenerId((0, os_1.hostname)());
|
|
1147
|
+
try {
|
|
1148
|
+
(0, fs_1.mkdirSync)((0, path_1.join)((0, os_1.homedir)(), '.zeph'), { recursive: true });
|
|
1149
|
+
(0, fs_1.writeFileSync)(LISTENER_ID_FILE, fresh);
|
|
1150
|
+
}
|
|
1151
|
+
catch { /* persistence is best-effort; in-memory pin still holds */ }
|
|
1152
|
+
processListenerDeviceId = fresh;
|
|
1153
|
+
return fresh;
|
|
1102
1154
|
};
|
|
1103
1155
|
exports.computeListenerDeviceId = computeListenerDeviceId;
|
|
1104
1156
|
/**
|
|
@@ -1337,10 +1389,21 @@ const streamSession = (wsUrl, apiKey) => {
|
|
|
1337
1389
|
terminate: () => { ws?.terminate(); },
|
|
1338
1390
|
};
|
|
1339
1391
|
};
|
|
1340
|
-
const
|
|
1392
|
+
const DEFAULT_WS_URL = 'wss://ws.zeph.to';
|
|
1393
|
+
/**
|
|
1394
|
+
* A user who only set ZEPH_API_KEY (no `zeph login`, no config file) gets the
|
|
1395
|
+
* prod socket by default — mirroring the DEFAULT_API_BASE fallback for REST.
|
|
1396
|
+
* The default only applies when the base URL is also the prod default: with a
|
|
1397
|
+
* custom base URL, silently pointing the listener at the prod socket while
|
|
1398
|
+
* REST talks to another stage would split the device across environments, so
|
|
1399
|
+
* that combination still errors loudly.
|
|
1400
|
+
*/
|
|
1401
|
+
const resolveWsUrl = (args, config, baseUrl) => {
|
|
1341
1402
|
const fromArg = typeof args['ws-url'] === 'string' ? args['ws-url'] : null;
|
|
1342
|
-
return fromArg || (0, config_js_1.resolvedEnv)('ZEPH_WS_URL') || config.wsUrl
|
|
1403
|
+
return fromArg || (0, config_js_1.resolvedEnv)('ZEPH_WS_URL') || config.wsUrl
|
|
1404
|
+
|| (baseUrl === DEFAULT_API_BASE ? DEFAULT_WS_URL : null);
|
|
1343
1405
|
};
|
|
1406
|
+
exports.resolveWsUrl = resolveWsUrl;
|
|
1344
1407
|
// ── Singleton guard (PID file) ──────────────────────────────────────
|
|
1345
1408
|
const ZEPH_DIR = (0, path_1.join)((0, os_1.homedir)(), '.zeph');
|
|
1346
1409
|
const LISTENER_PID_FILE = (0, path_1.join)(ZEPH_DIR, 'listener.pid');
|
|
@@ -1411,7 +1474,7 @@ const handleListener = async (args) => {
|
|
|
1411
1474
|
// resolution order as the rest of the CLI; falls back to the prod API.
|
|
1412
1475
|
const baseUrl = args['base-url'] || (0, config_js_1.resolvedEnv)('ZEPH_BASE_URL') || config.baseUrl || DEFAULT_API_BASE;
|
|
1413
1476
|
(0, exports.setAttachmentContext)({ apiKey, baseUrl });
|
|
1414
|
-
const wsUrl = resolveWsUrl(args, config);
|
|
1477
|
+
const wsUrl = (0, exports.resolveWsUrl)(args, config, baseUrl);
|
|
1415
1478
|
if (!wsUrl) {
|
|
1416
1479
|
console.error('zeph listener: WebSocket URL not set. Either:\n' +
|
|
1417
1480
|
' • add "wsUrl": "wss://..." to ~/.zeph/config.json\n' +
|