@zeph-to/mcp-server 1.14.0 → 1.15.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 +63 -18
- package/dist/api-client.d.ts +4 -1
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +5 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +47 -6
- package/dist/crypto.d.ts +2 -1
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +13 -2
- package/dist/index.js +3 -0
- package/dist/tools/notify.d.ts.map +1 -1
- package/dist/tools/notify.js +10 -2
- package/dist/tools/rename.d.ts +5 -0
- package/dist/tools/rename.d.ts.map +1 -0
- package/dist/tools/rename.js +45 -0
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ws-wait.d.ts.map +1 -1
- package/dist/ws-wait.js +12 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,17 +5,27 @@
|
|
|
5
5
|
[](https://nodejs.org)
|
|
6
6
|
[](./LICENSE)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
**Your agent calls `zeph_ask`; the question lands on your phone as buttons + a text field; your reply comes back into the same tool call and the agent keeps going.**
|
|
9
|
+
|
|
10
|
+
Zeph's MCP server is the agent side of that round trip — plus one-way notifications, clipboard, files, and channel broadcasts, all over the [Model Context Protocol](https://modelcontextprotocol.io). Works with Claude Code, Cursor, Windsurf, Gemini CLI, and any MCP client.
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<img src="https://zeph.to/readme/demo.gif" alt="Agent calls zeph_ask; the question hits the phone; you tap Deploy; the tool call returns" width="560"><br>
|
|
14
|
+
<sub><em>Your agent calls <code>zeph_ask</code> → the question hits your phone → you tap <b>Deploy</b> → the call returns and the agent ships.</em></sub>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
Part of the Zeph toolchain: [`@zeph-to/cli`](https://github.com/zeph-to/cli) (installer, push CLI, tmux remote control) · [`zeph-to/plugin`](https://github.com/zeph-to/plugin) (Claude Code plugin bundling this server) · the [Zeph app](https://zeph.to) on your phone.
|
|
9
18
|
|
|
10
19
|
## Setup
|
|
11
20
|
|
|
12
21
|
The easiest way to set up for all agents at once:
|
|
13
22
|
|
|
14
23
|
```bash
|
|
15
|
-
|
|
24
|
+
npm install -g @zeph-to/cli
|
|
25
|
+
zeph install
|
|
16
26
|
```
|
|
17
27
|
|
|
18
|
-
This saves credentials to `~/.zeph/config.json` and configures your agents automatically. The MCP server reads from this file — no env vars needed.
|
|
28
|
+
This saves credentials to `~/.zeph/config.json` and configures your agents automatically. The MCP server reads from this file — no env vars needed. Install globally so `zeph cc` (phone-driven sessions) works and hooks skip an npx cold-start; `npx @zeph-to/cli install` is a notifications-only alternative.
|
|
19
29
|
|
|
20
30
|
### Claude Code (manual)
|
|
21
31
|
|
|
@@ -26,26 +36,26 @@ Add to `~/.claude/settings.json`:
|
|
|
26
36
|
"mcpServers": {
|
|
27
37
|
"zeph": {
|
|
28
38
|
"command": "npx",
|
|
29
|
-
"args": ["-y", "@zeph-to/mcp-server"]
|
|
30
|
-
"env": {
|
|
31
|
-
"ZEPH_API_KEY": "ak_...",
|
|
32
|
-
"ZEPH_HOOK_ID": "hook_...",
|
|
33
|
-
"ZEPH_DEVICE_ID": "dev_..."
|
|
34
|
-
}
|
|
39
|
+
"args": ["-y", "@zeph-to/mcp-server"]
|
|
35
40
|
}
|
|
36
41
|
}
|
|
37
42
|
}
|
|
38
43
|
```
|
|
39
44
|
|
|
45
|
+
No `env` block needed: credentials come from `~/.zeph/config.json` (written
|
|
46
|
+
by `zeph install`). Add env vars only to override the file —
|
|
47
|
+
e.g. a second account:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
"env": { "ZEPH_API_KEY": "ak_other_account" }
|
|
51
|
+
```
|
|
52
|
+
|
|
40
53
|
### Cursor / Other MCP Clients
|
|
41
54
|
|
|
42
55
|
```json
|
|
43
56
|
{
|
|
44
57
|
"command": "npx",
|
|
45
|
-
"args": ["-y", "@zeph-to/mcp-server"]
|
|
46
|
-
"env": {
|
|
47
|
-
"ZEPH_API_KEY": "ak_..."
|
|
48
|
-
}
|
|
58
|
+
"args": ["-y", "@zeph-to/mcp-server"]
|
|
49
59
|
}
|
|
50
60
|
```
|
|
51
61
|
|
|
@@ -57,12 +67,17 @@ Add to `~/.claude/settings.json`:
|
|
|
57
67
|
| `ZEPH_HOOK_ID` | No | Hook ID (optional — only needed for interactive tools like `zeph_ask`/`zeph_prompt`/`zeph_input`) |
|
|
58
68
|
| `ZEPH_DEVICE_ID` | No | Target device ID (optional — only needed for interactive tools like `zeph_ask`/`zeph_prompt`/`zeph_input`). Omit to send to all devices |
|
|
59
69
|
| `ZEPH_BASE_URL` | No | API base URL (default: `https://api.zeph.to/v1`) |
|
|
70
|
+
| `ZEPH_WS_URL` | No | WebSocket endpoint for the hook-response fast path — `zeph_ask`/`zeph_prompt`/`zeph_input` answers arrive the moment the user submits them instead of on the next poll. Falls back to pure polling when unset. Also read from `wsUrl` in `~/.zeph/config.json` |
|
|
60
71
|
| `ZEPH_DISABLE_SESSION_CACHE` | No | Set to `1`/`true` to skip writing the session-id handoff file under `~/.cache/zeph/`. Useful for read-only filesystems, ephemeral CI runners, or sandboxed envs that audit filesystem writes. The plugin's stop hook still works without it (transcript-path UUID extraction is the primary path; the cache is a fallback for older Claude Code versions). |
|
|
72
|
+
| `ZEPH_SESSION_ID` | No | Override the session id attached to pushes (grouping in the app). Auto-detected from the newest Claude Code transcript when unset |
|
|
73
|
+
| `ZEPH_DISABLE_ENCRYPTION` | No | Set to `1`/`true` to force E2E-style push encryption off, even when the account has keys. Useful while cleaning up legacy key state |
|
|
61
74
|
|
|
62
|
-
\* If env vars are not set, the server reads from `~/.zeph/config.json` (created by `
|
|
75
|
+
\* If env vars are not set, the server reads from `~/.zeph/config.json` (created by `zeph install`). Unresolved `${...}` interpolations are also treated as unset.
|
|
63
76
|
|
|
64
77
|
## Tools
|
|
65
78
|
|
|
79
|
+
Push titles are automatically prefixed with the project directory name — `myapp · Build complete` — so the phone feed stays scannable when several sessions push at once.
|
|
80
|
+
|
|
66
81
|
### zeph_notify
|
|
67
82
|
|
|
68
83
|
Send a one-way push notification. Supports optional URL (auto-switches to link type).
|
|
@@ -134,6 +149,16 @@ targetDeviceId: "dev_..." (optional)
|
|
|
134
149
|
|
|
135
150
|
Returns: `{ pushId: "...", fileKey: "...", fileSize: 42 }`
|
|
136
151
|
|
|
152
|
+
### zeph_session_rename
|
|
153
|
+
|
|
154
|
+
Set a custom display name for the **current** agent session, shown in the Zeph app's **Streams › Agents** list. Lets an agent label what it's working on — `"Prod deploy"`, `"Auth refactor"` — so parallel sessions are easy to tell apart on your phone. Renames the session this server runs in (resolved from the listener device id + tmux session name); the name persists until changed.
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
alias: "Prod deploy watcher" (1-60 chars)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Returns: `{ renamed: true, session: "zeph-myapp", alias: "Prod deploy watcher" }`, or `{ renamed: false, reason: "..." }` when there's no active session to rename (not running inside a `zeph listener` tmux session).
|
|
161
|
+
|
|
137
162
|
### zeph_prompt
|
|
138
163
|
|
|
139
164
|
Ask the user to choose from 2-4 options. Blocks until response or timeout.
|
|
@@ -157,6 +182,10 @@ Ask the user a question with optional quick-reply buttons and a text input field
|
|
|
157
182
|
|
|
158
183
|
Requires `ZEPH_HOOK_ID`.
|
|
159
184
|
|
|
185
|
+
<p align="center">
|
|
186
|
+
<img src="https://zeph.to/readme/ask-phone.png" alt="zeph_ask rendered on the phone: a question with tappable answer buttons and a text field" width="300">
|
|
187
|
+
</p>
|
|
188
|
+
|
|
160
189
|
```
|
|
161
190
|
title: "What should we do?"
|
|
162
191
|
body: "3 tests failed in auth module" (optional)
|
|
@@ -188,7 +217,7 @@ Returns: `{ value: "feat: add clipboard sync", timedOut: false }`
|
|
|
188
217
|
|
|
189
218
|
### Client timeouts
|
|
190
219
|
|
|
191
|
-
`zeph_ask`, `zeph_prompt`, and `zeph_input` block until the user responds, up to their `timeout` (max 600s).
|
|
220
|
+
`zeph_ask`, `zeph_prompt`, and `zeph_input` block until the user responds, up to their `timeout` (max 600s). With `ZEPH_WS_URL` configured the response arrives over WebSocket the instant it's submitted; otherwise the server polls. Either way the MCP request stays open the whole time. To keep the client from giving up early, the server emits a `notifications/progress` every 5s while waiting. Clients must either set a per-request timeout above the tool's `timeout`, or reset their timeout on progress notifications. Claude Code does the latter by default.
|
|
192
221
|
|
|
193
222
|
## Resources
|
|
194
223
|
|
|
@@ -207,13 +236,28 @@ Lists channels the user owns or subscribes to. Use to find `channelId` for `zeph
|
|
|
207
236
|
| Situation | Tool | Example |
|
|
208
237
|
|-----------|------|---------|
|
|
209
238
|
| Long task finished | `zeph_notify` | Build complete, test results, deploy done |
|
|
210
|
-
| Need
|
|
211
|
-
|
|
|
239
|
+
| Need a decision (buttons + optional free text) | `zeph_ask` | "Tests green. Deploy?" with a custom-instruction escape hatch |
|
|
240
|
+
| Decision from fixed options only | `zeph_prompt` | Choose deploy target, confirm destructive action |
|
|
241
|
+
| Free-form input only | `zeph_input` | Commit message, env var value, description |
|
|
212
242
|
| Share code/logs | `zeph_file` | Error logs, test reports, generated config |
|
|
213
243
|
| Share snippet | `zeph_clipboard` | API key, URL, shell command |
|
|
244
|
+
| Label this session | `zeph_session_rename` | Name the run "Prod deploy" so parallel sessions stay distinguishable on the phone |
|
|
214
245
|
|
|
215
246
|
### Recommended patterns
|
|
216
247
|
|
|
248
|
+
**Decision gate with an escape hatch (preferred):**
|
|
249
|
+
```
|
|
250
|
+
zeph_ask(
|
|
251
|
+
title: "Tests green. Deploy to production?",
|
|
252
|
+
actions: [
|
|
253
|
+
{ id: "deploy", label: "Deploy", style: "primary" },
|
|
254
|
+
{ id: "hold", label: "Hold", style: "secondary" }
|
|
255
|
+
],
|
|
256
|
+
placeholder: "Or tell me what to do instead...",
|
|
257
|
+
fallback: "hold"
|
|
258
|
+
)
|
|
259
|
+
```
|
|
260
|
+
|
|
217
261
|
**Task completion notification:**
|
|
218
262
|
```
|
|
219
263
|
zeph_notify(
|
|
@@ -271,7 +315,8 @@ The API key needs the following scopes:
|
|
|
271
315
|
|
|
272
316
|
- `push:read` — for `zeph_list`
|
|
273
317
|
- `push:write` — for `zeph_notify`, `zeph_clipboard`, `zeph_dismiss`, `zeph_dismiss_all`, `zeph_file`
|
|
274
|
-
- `hook:write` — for `zeph_prompt
|
|
318
|
+
- `hook:write` — for `zeph_ask`, `zeph_prompt`, and `zeph_input`
|
|
319
|
+
- `device:write` — for `zeph_session_rename`
|
|
275
320
|
- `channel:read` — for `zeph://channels` resource
|
|
276
321
|
|
|
277
322
|
Create an API key with the **MCP** preset in Settings > API Keys for the correct permissions.
|
package/dist/api-client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { McpServerConfig } from './config.js';
|
|
2
|
-
import type { PushResponse, HookTriggerResponse, HookEventResponse, DevicesResponse, PushListResponse, DismissResponse, ChannelsResponse, UploadRequestResponse, AttachedFile } from './types.js';
|
|
2
|
+
import type { PushResponse, HookTriggerResponse, HookEventResponse, DevicesResponse, AgentSessionRenameResponse, PushListResponse, DismissResponse, ChannelsResponse, UploadRequestResponse, AttachedFile } from './types.js';
|
|
3
3
|
export declare class ApiError extends Error {
|
|
4
4
|
readonly code: string;
|
|
5
5
|
readonly status: number;
|
|
@@ -42,6 +42,9 @@ export declare class ZephApiClient {
|
|
|
42
42
|
}): Promise<HookTriggerResponse>;
|
|
43
43
|
getHookEvent(hookId: string, eventId: string): Promise<HookEventResponse>;
|
|
44
44
|
listDevices(): Promise<DevicesResponse>;
|
|
45
|
+
/** Set (or clear, when `alias` is empty) the display name for an agent
|
|
46
|
+
* session, keyed by its tmux `name` on `deviceId`. */
|
|
47
|
+
renameAgentSession(deviceId: string, name: string, alias: string): Promise<AgentSessionRenameResponse>;
|
|
45
48
|
listPushes(params?: {
|
|
46
49
|
limit?: number;
|
|
47
50
|
type?: string;
|
package/dist/api-client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAEV,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACrB,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,qBAAa,QAAS,SAAQ,KAAK;aAGf,IAAI,EAAE,MAAM;aACZ,MAAM,EAAE,MAAM;gBAF9B,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM;CAKjC;AAKD,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,MAAM,EAAE,eAAe;IAK7B,QAAQ,CAAC,MAAM,EAAE;QACrB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;QACvB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,GAAG,OAAO,CAAC,YAAY,CAAC;IAInB,WAAW,CACf,MAAM,EAAE,MAAM,EACd,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACnC,QAAQ,CAAC,EAAE,SAAS,GAAG,aAAa,GAAG,OAAO,GAAG,OAAO,CAAC;QACzD,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;QACvB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,gEAAgE;QAChE,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,GACA,OAAO,CAAC,mBAAmB,CAAC;IAIzB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAIzE,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
|
package/dist/api-client.js
CHANGED
|
@@ -33,6 +33,11 @@ class ZephApiClient {
|
|
|
33
33
|
async listDevices() {
|
|
34
34
|
return this.request('GET', '/devices');
|
|
35
35
|
}
|
|
36
|
+
/** Set (or clear, when `alias` is empty) the display name for an agent
|
|
37
|
+
* session, keyed by its tmux `name` on `deviceId`. */
|
|
38
|
+
async renameAgentSession(deviceId, name, alias) {
|
|
39
|
+
return this.request('PATCH', `/devices/${encodeURIComponent(deviceId)}/agent-sessions/${encodeURIComponent(name)}`, { alias });
|
|
40
|
+
}
|
|
36
41
|
async listPushes(params) {
|
|
37
42
|
const query = new URLSearchParams();
|
|
38
43
|
if (params?.limit)
|
package/dist/config.d.ts
CHANGED
|
@@ -24,4 +24,11 @@ export interface McpServerConfig {
|
|
|
24
24
|
*/
|
|
25
25
|
export declare const formatPushTitle: (projectName: string, title: string) => string;
|
|
26
26
|
export declare const loadConfig: () => McpServerConfig;
|
|
27
|
+
/** Listener device id — MUST match cli `computeListenerDeviceId` so `zeph_ask`
|
|
28
|
+
* hooks land under the same agent-session key as the listener's
|
|
29
|
+
* agent.command/state pushes (otherwise they reach the feed but never thread
|
|
30
|
+
* into the agent chat). cli hashes the platform machine id first and falls
|
|
31
|
+
* back to a hostname hash pinned in a sticky file; mirror that order, reading
|
|
32
|
+
* (never writing) the file so both processes resolve the same id. */
|
|
33
|
+
export declare const listenerDeviceId: () => string;
|
|
27
34
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,eAAe;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB;0EACsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;uCAGmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;CACvB;AAiBD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAI,aAAa,MAAM,EAAE,OAAO,MAAM,KAAG,MAGpE,CAAC;AAuGF,eAAO,MAAM,UAAU,QAAO,eAyB7B,CAAC"}
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,eAAe;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB;0EACsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;uCAGmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;CACvB;AAiBD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAI,aAAa,MAAM,EAAE,OAAO,MAAM,KAAG,MAGpE,CAAC;AAuGF,eAAO,MAAM,UAAU,QAAO,eAyB7B,CAAC;AA4BF;;;;;sEAKsE;AACtE,eAAO,MAAM,gBAAgB,QAAO,MAQnC,CAAC"}
|
package/dist/config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.loadConfig = exports.formatPushTitle = void 0;
|
|
3
|
+
exports.listenerDeviceId = exports.loadConfig = exports.formatPushTitle = void 0;
|
|
4
4
|
const fs_1 = require("fs");
|
|
5
5
|
const os_1 = require("os");
|
|
6
6
|
const crypto_1 = require("crypto");
|
|
@@ -140,16 +140,57 @@ const loadConfig = () => {
|
|
|
140
140
|
hookId: resolvedEnv('ZEPH_HOOK_ID') ?? fileConfig.hookId,
|
|
141
141
|
deviceId: resolvedEnv('ZEPH_DEVICE_ID') ?? fileConfig.deviceId,
|
|
142
142
|
sessionId,
|
|
143
|
-
agentDeviceId: listenerDeviceId(),
|
|
143
|
+
agentDeviceId: (0, exports.listenerDeviceId)(),
|
|
144
144
|
agentSessionName: detectTmuxSessionName(),
|
|
145
145
|
projectName: projectNameFromDir(projectDir),
|
|
146
146
|
};
|
|
147
147
|
};
|
|
148
148
|
exports.loadConfig = loadConfig;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
149
|
+
const LISTENER_ID_FILE = (0, path_1.join)((0, os_1.homedir)(), '.zeph', 'listener-device-id');
|
|
150
|
+
const hashListenerId = (seed) => `dev_listener_${(0, crypto_1.createHash)('sha256').update(seed).digest('hex').slice(0, 8)}`;
|
|
151
|
+
/** Platform machine id (macOS IOPlatformUUID / Linux machine-id), or null.
|
|
152
|
+
* Mirrors cli `readMachineId` so the hash below matches the listener's id. */
|
|
153
|
+
const readMachineId = () => {
|
|
154
|
+
try {
|
|
155
|
+
if (process.platform === 'darwin') {
|
|
156
|
+
const out = (0, child_process_1.execFileSync)('ioreg', ['-rd1', '-c', 'IOPlatformExpertDevice'], { encoding: 'utf-8' });
|
|
157
|
+
const m = out.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/);
|
|
158
|
+
if (m)
|
|
159
|
+
return m[1];
|
|
160
|
+
}
|
|
161
|
+
if (process.platform === 'linux') {
|
|
162
|
+
for (const p of ['/etc/machine-id', '/var/lib/dbus/machine-id']) {
|
|
163
|
+
try {
|
|
164
|
+
const v = (0, fs_1.readFileSync)(p, 'utf-8').trim();
|
|
165
|
+
if (v)
|
|
166
|
+
return v;
|
|
167
|
+
}
|
|
168
|
+
catch { /* try next path */ }
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch { /* no machine id readable — fall through */ }
|
|
173
|
+
return null;
|
|
174
|
+
};
|
|
175
|
+
/** Listener device id — MUST match cli `computeListenerDeviceId` so `zeph_ask`
|
|
176
|
+
* hooks land under the same agent-session key as the listener's
|
|
177
|
+
* agent.command/state pushes (otherwise they reach the feed but never thread
|
|
178
|
+
* into the agent chat). cli hashes the platform machine id first and falls
|
|
179
|
+
* back to a hostname hash pinned in a sticky file; mirror that order, reading
|
|
180
|
+
* (never writing) the file so both processes resolve the same id. */
|
|
181
|
+
const listenerDeviceId = () => {
|
|
182
|
+
const machineId = readMachineId();
|
|
183
|
+
if (machineId)
|
|
184
|
+
return hashListenerId(machineId);
|
|
185
|
+
try {
|
|
186
|
+
const saved = (0, fs_1.readFileSync)(LISTENER_ID_FILE, 'utf-8').trim();
|
|
187
|
+
if (/^dev_listener_[0-9a-f]{8}$/.test(saved))
|
|
188
|
+
return saved;
|
|
189
|
+
}
|
|
190
|
+
catch { /* no sticky file — fall back to hostname */ }
|
|
191
|
+
return hashListenerId((0, os_1.hostname)());
|
|
192
|
+
};
|
|
193
|
+
exports.listenerDeviceId = listenerDeviceId;
|
|
153
194
|
/** The tmux session name the agent runs in (`zeph-<project>`) — stable half of
|
|
154
195
|
* the session key. Undefined outside tmux; the hook then stays feed-only. */
|
|
155
196
|
const detectTmuxSessionName = () => {
|
package/dist/crypto.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Device-shared encryption for MCP server — self-contained ECDH P-256 +
|
|
3
3
|
* AES-256-GCM. Mirrors @zeph/crypto API but bundled inline (no external
|
|
4
|
-
* dependency). Uses Web Crypto API
|
|
4
|
+
* dependency). Uses Web Crypto API via node:crypto webcrypto — Node.js 18+
|
|
5
|
+
* (the `crypto` global only exists unflagged from Node 19, so we import it).
|
|
5
6
|
*
|
|
6
7
|
* Threat model honesty (do not call this "E2E" without a footnote):
|
|
7
8
|
*
|
package/dist/crypto.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AA0JH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,EAAE,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,CAsE5E,CAAC;AAmCF,eAAO,MAAM,UAAU,QAAO,aAAa,GAAG,IAAqB,CAAC;AACpE,eAAO,MAAM,YAAY,QAAO,MAAM,GAAG,IAA+B,CAAC;AAEzE;;GAEG;AACH,eAAO,MAAM,sBAAsB,GACjC,OAAO;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,KACrD,OAAO,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,IAAI,CAAC;CACnB,CAaA,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAC7B,SAAS,MAAM,KACd,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAQlE,CAAC"}
|
package/dist/crypto.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Device-shared encryption for MCP server — self-contained ECDH P-256 +
|
|
4
4
|
* AES-256-GCM. Mirrors @zeph/crypto API but bundled inline (no external
|
|
5
|
-
* dependency). Uses Web Crypto API
|
|
5
|
+
* dependency). Uses Web Crypto API via node:crypto webcrypto — Node.js 18+
|
|
6
|
+
* (the `crypto` global only exists unflagged from Node 19, so we import it).
|
|
6
7
|
*
|
|
7
8
|
* Threat model honesty (do not call this "E2E" without a footnote):
|
|
8
9
|
*
|
|
@@ -33,6 +34,11 @@ exports.encryptFileForSelf = exports.encryptPushBodyForSelf = exports.getPublicK
|
|
|
33
34
|
const fs_1 = require("fs");
|
|
34
35
|
const os_1 = require("os");
|
|
35
36
|
const path_1 = require("path");
|
|
37
|
+
const node_crypto_1 = require("node:crypto");
|
|
38
|
+
// Node 18 has no `crypto` global (unflagged only from 19.0.0) — resolve the
|
|
39
|
+
// Web Crypto implementation explicitly so encryption works on the declared
|
|
40
|
+
// minimum runtime instead of silently staying disabled.
|
|
41
|
+
const crypto = node_crypto_1.webcrypto;
|
|
36
42
|
// ─── Base64 helpers ───
|
|
37
43
|
const toBase64 = (buffer) => Buffer.from(buffer).toString('base64');
|
|
38
44
|
const fromBase64 = (base64) => {
|
|
@@ -208,7 +214,12 @@ exports.initCrypto = initCrypto;
|
|
|
208
214
|
const fetchServerKeys = async (apiKey, baseUrl) => {
|
|
209
215
|
try {
|
|
210
216
|
const url = `${baseUrl.replace(/\/$/, '')}/users/me/keys`;
|
|
211
|
-
|
|
217
|
+
// Bounded: index.ts awaits initCrypto before connecting the MCP
|
|
218
|
+
// transport, so a hanging fetch here would block server startup.
|
|
219
|
+
const res = await fetch(url, {
|
|
220
|
+
headers: { 'X-API-Key': apiKey },
|
|
221
|
+
signal: AbortSignal.timeout(5_000),
|
|
222
|
+
});
|
|
212
223
|
if (!res.ok)
|
|
213
224
|
return null;
|
|
214
225
|
const json = await res.json();
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,7 @@ const dismiss_js_1 = require("./tools/dismiss.js");
|
|
|
18
18
|
const broadcast_js_1 = require("./tools/broadcast.js");
|
|
19
19
|
const file_js_1 = require("./tools/file.js");
|
|
20
20
|
const ask_js_1 = require("./tools/ask.js");
|
|
21
|
+
const rename_js_1 = require("./tools/rename.js");
|
|
21
22
|
const devices_js_1 = require("./resources/devices.js");
|
|
22
23
|
const channels_js_1 = require("./resources/channels.js");
|
|
23
24
|
const getVersion = () => {
|
|
@@ -52,6 +53,7 @@ const createServer = (config) => {
|
|
|
52
53
|
'- zeph_prompt: Ask user to choose from options (requires ZEPH_HOOK_ID)',
|
|
53
54
|
'- zeph_input: Request text input from user (requires ZEPH_HOOK_ID)',
|
|
54
55
|
'- zeph_ask: Ask user with buttons + text input combined (requires ZEPH_HOOK_ID). Prefer this over zeph_prompt/zeph_input when you need both options and free-text.',
|
|
56
|
+
'- zeph_session_rename: Name this agent session so the user can identify it in the app (Streams › Agents).',
|
|
55
57
|
'',
|
|
56
58
|
'Resources:',
|
|
57
59
|
'- zeph://devices: Check which devices are online',
|
|
@@ -68,6 +70,7 @@ const createServer = (config) => {
|
|
|
68
70
|
(0, prompt_js_1.registerPromptTool)(server, client, config, waiter);
|
|
69
71
|
(0, input_js_1.registerInputTool)(server, client, config, waiter);
|
|
70
72
|
(0, ask_js_1.registerAskTool)(server, client, config, waiter);
|
|
73
|
+
(0, rename_js_1.registerRenameTool)(server, client, config);
|
|
71
74
|
(0, devices_js_1.registerDevicesResource)(server, client);
|
|
72
75
|
(0, channels_js_1.registerChannelsResource)(server, client);
|
|
73
76
|
return server;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/tools/notify.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AASrE,eAAO,MAAM,kBAAkB,GAAI,QAAQ,SAAS,EAAE,QAAQ,aAAa,EAAE,QAAQ,eAAe,
|
|
1
|
+
{"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/tools/notify.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AASrE,eAAO,MAAM,kBAAkB,GAAI,QAAQ,SAAS,EAAE,QAAQ,aAAa,EAAE,QAAQ,eAAe,SA2HnG,CAAC"}
|
package/dist/tools/notify.js
CHANGED
|
@@ -48,6 +48,7 @@ const registerNotifyTool = (server, client, config) => {
|
|
|
48
48
|
let uploadContentType = fileType;
|
|
49
49
|
let fileIv;
|
|
50
50
|
let fileEncryptedKey;
|
|
51
|
+
let fileEncrypted = false;
|
|
51
52
|
if (canEncrypt) {
|
|
52
53
|
try {
|
|
53
54
|
const encrypted = await (0, crypto_js_1.encryptFileForSelf)(fileMarkdown);
|
|
@@ -55,6 +56,7 @@ const registerNotifyTool = (server, client, config) => {
|
|
|
55
56
|
uploadContentType = 'application/octet-stream';
|
|
56
57
|
fileIv = encrypted.iv;
|
|
57
58
|
fileEncryptedKey = encrypted.encryptedKey;
|
|
59
|
+
fileEncrypted = true;
|
|
58
60
|
}
|
|
59
61
|
catch (err) {
|
|
60
62
|
console.error('[Crypto] File encryption failed, sending plaintext:', err);
|
|
@@ -74,17 +76,21 @@ const registerNotifyTool = (server, client, config) => {
|
|
|
74
76
|
targetDeviceId: deviceId,
|
|
75
77
|
sessionId: config.sessionId,
|
|
76
78
|
};
|
|
79
|
+
let pushEncrypted = false;
|
|
77
80
|
if (canEncrypt) {
|
|
78
81
|
try {
|
|
79
82
|
const enc = await (0, crypto_js_1.encryptPushBodyForSelf)({ title: pushTitle, body: preview, url });
|
|
80
83
|
pushPayload = { ...pushPayload, title: undefined, body: enc.body, isEncrypted: enc.isEncrypted, encryptedKey: enc.encryptedKey, senderPublicKey: enc.senderPublicKey };
|
|
84
|
+
pushEncrypted = true;
|
|
81
85
|
}
|
|
82
86
|
catch (err) {
|
|
83
87
|
console.error('[Crypto] Push encryption failed, sending plaintext:', err);
|
|
84
88
|
}
|
|
85
89
|
}
|
|
86
90
|
const result = await client.sendPush(pushPayload);
|
|
87
|
-
|
|
91
|
+
// Report what actually went out — an encryption failure above falls
|
|
92
|
+
// back to plaintext, so `canEncrypt` alone would over-claim.
|
|
93
|
+
return (0, error_format_js_1.textResult)({ pushId: result.data.pushId, fileKey: upload.data.fileKey, autoFile: true, encrypted: fileEncrypted && pushEncrypted });
|
|
88
94
|
}
|
|
89
95
|
// Short body — encrypt push only
|
|
90
96
|
let pushPayload = {
|
|
@@ -96,17 +102,19 @@ const registerNotifyTool = (server, client, config) => {
|
|
|
96
102
|
targetDeviceId: deviceId,
|
|
97
103
|
sessionId: config.sessionId,
|
|
98
104
|
};
|
|
105
|
+
let pushEncrypted = false;
|
|
99
106
|
if (canEncrypt) {
|
|
100
107
|
try {
|
|
101
108
|
const enc = await (0, crypto_js_1.encryptPushBodyForSelf)({ title: pushTitle, body: cleanBody, url });
|
|
102
109
|
pushPayload = { ...pushPayload, title: undefined, body: enc.body, isEncrypted: enc.isEncrypted, encryptedKey: enc.encryptedKey, senderPublicKey: enc.senderPublicKey };
|
|
110
|
+
pushEncrypted = true;
|
|
103
111
|
}
|
|
104
112
|
catch (err) {
|
|
105
113
|
console.error('[Crypto] Push encryption failed, sending plaintext:', err);
|
|
106
114
|
}
|
|
107
115
|
}
|
|
108
116
|
const result = await client.sendPush(pushPayload);
|
|
109
|
-
return (0, error_format_js_1.textResult)({ pushId: result.data.pushId, encrypted:
|
|
117
|
+
return (0, error_format_js_1.textResult)({ pushId: result.data.pushId, encrypted: pushEncrypted });
|
|
110
118
|
}
|
|
111
119
|
catch (err) {
|
|
112
120
|
return (0, error_format_js_1.formatToolError)(err);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { ZephApiClient } from '../api-client.js';
|
|
3
|
+
import type { McpServerConfig } from '../config.js';
|
|
4
|
+
export declare const registerRenameTool: (server: McpServer, client: ZephApiClient, config: McpServerConfig) => void;
|
|
5
|
+
//# sourceMappingURL=rename.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rename.d.ts","sourceRoot":"","sources":["../../src/tools/rename.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAKpD,eAAO,MAAM,kBAAkB,GAAI,QAAQ,SAAS,EAAE,QAAQ,aAAa,EAAE,QAAQ,eAAe,SAyCnG,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerRenameTool = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const error_format_js_1 = require("../error-format.js");
|
|
6
|
+
// Mirrors the server cap (zeph apps/server/src/functions/devices.ts).
|
|
7
|
+
const MAX_ALIAS_LENGTH = 60;
|
|
8
|
+
const registerRenameTool = (server, client, config) => {
|
|
9
|
+
server.registerTool('zeph_session_rename', {
|
|
10
|
+
description: "Set a custom display name for THIS agent session in the user's Zeph app (the Streams › Agents list). " +
|
|
11
|
+
'Label what this session is working on — e.g. "Prod deploy" or "Auth refactor" — so the user can tell ' +
|
|
12
|
+
'parallel sessions apart on their phone. Renames the current session; the name persists until changed.',
|
|
13
|
+
annotations: {
|
|
14
|
+
readOnlyHint: false,
|
|
15
|
+
destructiveHint: false,
|
|
16
|
+
openWorldHint: true,
|
|
17
|
+
},
|
|
18
|
+
inputSchema: {
|
|
19
|
+
alias: zod_1.z
|
|
20
|
+
.string()
|
|
21
|
+
.trim()
|
|
22
|
+
.min(1)
|
|
23
|
+
.max(MAX_ALIAS_LENGTH)
|
|
24
|
+
.describe(`Display name for this session (max ${MAX_ALIAS_LENGTH} characters).`),
|
|
25
|
+
},
|
|
26
|
+
}, async ({ alias }) => {
|
|
27
|
+
try {
|
|
28
|
+
const { agentDeviceId, agentSessionName } = config;
|
|
29
|
+
// The session key is resolved once at startup from the listener device
|
|
30
|
+
// id + tmux session name. Absent it, there is nothing to rename.
|
|
31
|
+
if (!agentDeviceId || !agentSessionName) {
|
|
32
|
+
return (0, error_format_js_1.textResult)({
|
|
33
|
+
renamed: false,
|
|
34
|
+
reason: 'No active agent session detected — this tool renames the tmux session it runs in, which requires `zeph listener` and a tmux session.',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
await client.renameAgentSession(agentDeviceId, agentSessionName, alias);
|
|
38
|
+
return (0, error_format_js_1.textResult)({ renamed: true, session: agentSessionName, alias });
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
return (0, error_format_js_1.formatToolError)(err);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
exports.registerRenameTool = registerRenameTool;
|
package/dist/types.d.ts
CHANGED
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/ws-wait.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-wait.d.ts","sourceRoot":"","sources":["../src/ws-wait.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,MAAM,WAAW,MAAM;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,IAAI,IAAI,CAAC;IACd,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAC;KAAE,KAAK,IAAI,GAAG,IAAI,CAAC;CACxF;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;AAMhD,MAAM,WAAW,gBAAgB;IAC7B,2EAA2E;IAC3E,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,+DAA+D;IAC/D,WAAW,IAAI,OAAO,CAAC;IACvB,OAAO,IAAI,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,OAAO,CAAC,EAAE,SAAS,CAAC;CACvB;AAQD,qBAAa,kBAAkB;IAC3B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAY;IACrC,OAAO,CAAC,IAAI,CAAuB;IACnC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IACzD,OAAO,CAAC,SAAS,CAA+C;IAChE,OAAO,CAAC,SAAS,CAA8C;gBAEnD,IAAI,EAAE,yBAAyB;IAM3C,uEAAuE;IACvE,OAAO,IAAI,OAAO;IAIlB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB;IAgB5C,OAAO,CAAC,YAAY;
|
|
1
|
+
{"version":3,"file":"ws-wait.d.ts","sourceRoot":"","sources":["../src/ws-wait.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,MAAM,WAAW,MAAM;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,IAAI,IAAI,CAAC;IACd,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAC;KAAE,KAAK,IAAI,GAAG,IAAI,CAAC;CACxF;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;AAMhD,MAAM,WAAW,gBAAgB;IAC7B,2EAA2E;IAC3E,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,+DAA+D;IAC/D,WAAW,IAAI,OAAO,CAAC;IACvB,OAAO,IAAI,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,OAAO,CAAC,EAAE,SAAS,CAAC;CACvB;AAQD,qBAAa,kBAAkB;IAC3B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAY;IACrC,OAAO,CAAC,IAAI,CAAuB;IACnC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IACzD,OAAO,CAAC,SAAS,CAA+C;IAChE,OAAO,CAAC,SAAS,CAA8C;gBAEnD,IAAI,EAAE,yBAAyB;IAM3C,uEAAuE;IACvE,OAAO,IAAI,OAAO;IAIlB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB;IAgB5C,OAAO,CAAC,YAAY;IAmCpB,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,cAAc;CAUzB"}
|
package/dist/ws-wait.js
CHANGED
|
@@ -72,9 +72,15 @@ class HookResponseWaiter {
|
|
|
72
72
|
return;
|
|
73
73
|
try {
|
|
74
74
|
const sep = this.wsUrl.includes('?') ? '&' : '?';
|
|
75
|
+
// KNOWN TRADEOFF: apiKey rides the query string — the WHATWG
|
|
76
|
+
// WebSocket (Node 21+ global) can't set headers, and the API GW
|
|
77
|
+
// $connect route authenticates on the query param. It can land in
|
|
78
|
+
// gateway access logs; first-message auth needs a server change.
|
|
75
79
|
const sock = this.factory(`${this.wsUrl}${sep}apiKey=${encodeURIComponent(this.apiKey)}`);
|
|
76
80
|
this.sock = sock;
|
|
77
81
|
sock.addEventListener('open', () => {
|
|
82
|
+
if (this.sock !== sock)
|
|
83
|
+
return; // stale socket — already replaced
|
|
78
84
|
this.connected = true;
|
|
79
85
|
this.pingTimer = setInterval(() => {
|
|
80
86
|
try {
|
|
@@ -88,7 +94,12 @@ class HookResponseWaiter {
|
|
|
88
94
|
this.pingTimer.unref?.();
|
|
89
95
|
});
|
|
90
96
|
sock.addEventListener('message', (event) => this.handleMessage(event.data));
|
|
91
|
-
|
|
97
|
+
// Guard against stale events: 'error' and 'close' both fire on a
|
|
98
|
+
// dying socket, and a new subscribe() may have already created a
|
|
99
|
+
// replacement between them — a late event from the old socket
|
|
100
|
+
// must not tear down the new one.
|
|
101
|
+
const drop = () => { if (this.sock === sock)
|
|
102
|
+
this.teardownSocket(); };
|
|
92
103
|
sock.addEventListener('close', drop);
|
|
93
104
|
sock.addEventListener('error', drop);
|
|
94
105
|
}
|
package/package.json
CHANGED