@zeph-to/cli 1.26.1 → 1.27.1
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 +31 -6
- package/dist/crypto.d.ts +12 -0
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +18 -1
- package/dist/remote-agents.d.ts +6 -1
- package/dist/remote-agents.d.ts.map +1 -1
- package/dist/remote-agents.js +15 -3
- package/dist/zeph-hook.d.ts +13 -0
- package/dist/zeph-hook.d.ts.map +1 -1
- package/dist/zeph-hook.js +29 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
**Your agent works, hits a decision, and asks your phone. You tap a button (or type a reply), and the answer lands back in the live session — so the agent keeps going.**
|
|
9
9
|
|
|
10
|
-
`@zeph-to/cli` is the terminal side of that round trip: a zero-dependency push SDK, a `zeph` CLI that wires up 8 AI agents in one command, and a resident listener that lets your phone **drive Claude Code / Codex / Gemini sessions** by typing straight into named tmux sessions.
|
|
10
|
+
`@zeph-to/cli` is the terminal side of that round trip: a zero-dependency push SDK, a `zeph` CLI that wires up 8 AI agents in one command, and a resident listener that lets your phone **drive Claude Code / Codex / Cursor / Gemini sessions** by typing straight into named tmux sessions.
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
13
13
|
<img src="https://zeph.to/readme/demo.gif" alt="Agent asks 'Deploy to prod?' on your phone; you tap Deploy; the session ships" width="560"><br>
|
|
@@ -78,7 +78,7 @@ To **send** notifications:
|
|
|
78
78
|
zeph notify --title "Deploy done" --body "v2.1.0 shipped"
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
To **drive a Claude Code / Codex / Gemini session from your phone**, see
|
|
81
|
+
To **drive a Claude Code / Codex / Cursor / Gemini session from your phone**, see
|
|
82
82
|
[Remote Control](#remote-control) below.
|
|
83
83
|
|
|
84
84
|
## Remote Control
|
|
@@ -111,7 +111,7 @@ tmux session via `tmux send-keys`.
|
|
|
111
111
|
[zeph listener — resident daemon, started by `zeph cc` automatically]
|
|
112
112
|
│ tmux send-keys -l -t zeph-myapp "리팩토링 마무리해줘" + Enter
|
|
113
113
|
▼
|
|
114
|
-
[tmux session "zeph-myapp" running claude / codex / gemini]
|
|
114
|
+
[tmux session "zeph-myapp" running claude / codex / cursor-agent / gemini]
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
The listener polls its tmux session inventory every 5 seconds and
|
|
@@ -136,10 +136,29 @@ an answerable `zeph_ask`).
|
|
|
136
136
|
| Claude Code | `UserPromptSubmit` → plugin's `zeph-remote.sh` | Zeph plugin |
|
|
137
137
|
| Gemini CLI | `BeforeAgent` → `zeph remote-hook gemini` | `zeph setup` |
|
|
138
138
|
| Codex CLI | `UserPromptSubmit` → `zeph remote-hook codex` | `zeph setup` |
|
|
139
|
+
| Cursor CLI | — none yet | — |
|
|
139
140
|
|
|
140
141
|
Detection is exact-match: a terminal keystroke racing a phone message
|
|
141
142
|
can never false-flag. Muted projects are never flagged.
|
|
142
143
|
|
|
144
|
+
`zeph cursor` has no remote-origin hook, so it never enters sticky REMOTE
|
|
145
|
+
mode by itself. **Just ask for it** — one line, once per session:
|
|
146
|
+
|
|
147
|
+
> I'm driving this session from my phone. End every response with
|
|
148
|
+
> `zeph_ask` so I can answer with a button.
|
|
149
|
+
|
|
150
|
+
Nothing else is missing: injection works, and the MCP tools are all
|
|
151
|
+
there, `zeph_ask` included. Check them with `cursor-agent mcp list-tools
|
|
152
|
+
zeph` — `mcp list` prints only the *approved* list, not what's
|
|
153
|
+
configured, so it reads as empty even when the server is wired up.
|
|
154
|
+
|
|
155
|
+
Why it isn't automatic: `hooks.json` is honored by the Cursor IDE but not
|
|
156
|
+
by `cursor-agent`, and `beforeSubmitPrompt`'s output schema is
|
|
157
|
+
`{continue, user_message}` — no context channel to deliver a marker match
|
|
158
|
+
through. Same reason the `stop`-hook auto-push `zeph setup` installs
|
|
159
|
+
covers the Cursor **IDE** but not `zeph cursor` panes; ask for a
|
|
160
|
+
`zeph_notify` when you want one.
|
|
161
|
+
|
|
143
162
|
### Setup
|
|
144
163
|
|
|
145
164
|
1. **Install tmux.** The listener uses `send-keys`; the wrapper spawns
|
|
@@ -162,11 +181,16 @@ can never false-flag. Muted projects are never flagged.
|
|
|
162
181
|
3. **Run agents through the wrapper.** That's it.
|
|
163
182
|
|
|
164
183
|
```bash
|
|
165
|
-
zeph cc # claude
|
|
166
|
-
zeph codex # codex
|
|
167
|
-
zeph
|
|
184
|
+
zeph cc # claude → tmux session "zeph-<project>"
|
|
185
|
+
zeph codex # codex → tmux session "zeph-<project>"
|
|
186
|
+
zeph cursor # cursor-agent → tmux session "zeph-<project>"
|
|
187
|
+
zeph gemini # gemini → tmux session "zeph-<project>"
|
|
168
188
|
```
|
|
169
189
|
|
|
190
|
+
`zeph cursor` runs **`cursor-agent`**, Cursor's terminal agent — a
|
|
191
|
+
separate install from the Cursor IDE (the bare `cursor` on your PATH
|
|
192
|
+
is the editor launcher, which exits immediately and can't be driven).
|
|
193
|
+
|
|
170
194
|
The first `zeph cc` on a machine **auto-spawns a background
|
|
171
195
|
listener** (singleton, PID file at `~/.zeph/listener.pid`,
|
|
172
196
|
stdout/stderr at `~/.zeph/listener.log`). You never run
|
|
@@ -360,6 +384,7 @@ zeph test
|
|
|
360
384
|
# Run an agent in a named tmux session (so the listener can reach it)
|
|
361
385
|
zeph cc # claude
|
|
362
386
|
zeph codex # codex
|
|
387
|
+
zeph cursor # cursor-agent (Cursor CLI, not the IDE)
|
|
363
388
|
zeph gemini # gemini
|
|
364
389
|
|
|
365
390
|
# Run the resident listener (foreground; background it as you like)
|
package/dist/crypto.d.ts
CHANGED
|
@@ -42,6 +42,18 @@ interface EncryptedPayload {
|
|
|
42
42
|
export declare const initCrypto: (apiKey?: string, baseUrl?: string) => Promise<string>;
|
|
43
43
|
export declare const getKeyPair: () => CryptoKeyPair | null;
|
|
44
44
|
export declare const getPublicKey: () => string | null;
|
|
45
|
+
/**
|
|
46
|
+
* Drop the cached keys so every later send goes out as plaintext.
|
|
47
|
+
*
|
|
48
|
+
* Callers use this when the server rejects an encrypted send with
|
|
49
|
+
* `PRO_REQUIRED` (ADR-0008): E2E is a Pro feature, and a long-lived process
|
|
50
|
+
* that initialized crypto before a downgrade would otherwise 403 on every push.
|
|
51
|
+
* Same end state as the "server says encryption disabled" branch in initCrypto.
|
|
52
|
+
* Keys on disk are left alone, but this process will not pick them up again:
|
|
53
|
+
* `initCrypto` is memoized on `initPromise` and `ZephHook.ensureCrypto` short-
|
|
54
|
+
* circuits on `cryptoInitialized`. A restart after an upgrade re-adopts them.
|
|
55
|
+
*/
|
|
56
|
+
export declare const disableCrypto: () => void;
|
|
45
57
|
/**
|
|
46
58
|
* Encrypt push body for a recipient.
|
|
47
59
|
* Returns fields ready to merge into the sendPush payload.
|
package/dist/crypto.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AA+EH,UAAU,gBAAgB;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AA6ED;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,EAAE,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,CAsE5E,CAAC;AA2CF,eAAO,MAAM,UAAU,QAAO,aAAa,GAAG,IAAqB,CAAC;AACpE,eAAO,MAAM,YAAY,QAAO,MAAM,GAAG,IAA+B,CAAC;AAEzE;;;GAGG;AACH,eAAO,MAAM,eAAe,GAC1B,OAAO;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EACtD,uBAAuB,MAAM,KAC5B,OAAO,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,IAAI,CAAC;CACnB,CAeA,CAAC;AAEF;;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;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GAClC,SAAS,MAAM,EACf,uBAAuB,MAAM,KAC5B,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CASlE,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;AAWF;;;;GAIG;AACH,MAAM,WAAW,yBAA0B,SAAQ,gBAAgB;IACjE,eAAe,EAAE,MAAM,CAAC;CACzB;AAsBD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAO,OAAO,CAAC,MAAM,CAoBjD,CAAC;AAEF,eAAO,MAAM,kBAAkB,QAAO,MAAM,GAAG,IAA+B,CAAC;AAE/E;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAC3B,WAAW,MAAM,EACjB,uBAAuB,MAAM,KAC5B,OAAO,CAAC,yBAAyB,CAKnC,CAAC"}
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AA+EH,UAAU,gBAAgB;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AA6ED;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,EAAE,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,CAsE5E,CAAC;AA2CF,eAAO,MAAM,UAAU,QAAO,aAAa,GAAG,IAAqB,CAAC;AACpE,eAAO,MAAM,YAAY,QAAO,MAAM,GAAG,IAA+B,CAAC;AAEzE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,QAAO,IAIhC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAC1B,OAAO;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EACtD,uBAAuB,MAAM,KAC5B,OAAO,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,IAAI,CAAC;CACnB,CAeA,CAAC;AAEF;;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;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GAClC,SAAS,MAAM,EACf,uBAAuB,MAAM,KAC5B,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CASlE,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;AAWF;;;;GAIG;AACH,MAAM,WAAW,yBAA0B,SAAQ,gBAAgB;IACjE,eAAe,EAAE,MAAM,CAAC;CACzB;AAsBD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAO,OAAO,CAAC,MAAM,CAoBjD,CAAC;AAEF,eAAO,MAAM,kBAAkB,QAAO,MAAM,GAAG,IAA+B,CAAC;AAE/E;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAC3B,WAAW,MAAM,EACjB,uBAAuB,MAAM,KAC5B,OAAO,CAAC,yBAAyB,CAKnC,CAAC"}
|
package/dist/crypto.js
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* sensitive-but-not-secret.
|
|
30
30
|
*/
|
|
31
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
-
exports.encryptEphemeral = exports.getDevicePublicKey = exports.initDeviceCrypto = exports.encryptFileForSelf = exports.encryptFileForRecipient = exports.encryptPushBodyForSelf = exports.encryptPushBody = exports.getPublicKey = exports.getKeyPair = exports.initCrypto = void 0;
|
|
32
|
+
exports.encryptEphemeral = exports.getDevicePublicKey = exports.initDeviceCrypto = exports.encryptFileForSelf = exports.encryptFileForRecipient = exports.encryptPushBodyForSelf = exports.encryptPushBody = exports.disableCrypto = exports.getPublicKey = exports.getKeyPair = exports.initCrypto = void 0;
|
|
33
33
|
/// <reference lib="dom" />
|
|
34
34
|
const fs_1 = require("fs");
|
|
35
35
|
const os_1 = require("os");
|
|
@@ -239,6 +239,23 @@ const getKeyPair = () => cachedKeyPair;
|
|
|
239
239
|
exports.getKeyPair = getKeyPair;
|
|
240
240
|
const getPublicKey = () => cachedExportedPublicKey;
|
|
241
241
|
exports.getPublicKey = getPublicKey;
|
|
242
|
+
/**
|
|
243
|
+
* Drop the cached keys so every later send goes out as plaintext.
|
|
244
|
+
*
|
|
245
|
+
* Callers use this when the server rejects an encrypted send with
|
|
246
|
+
* `PRO_REQUIRED` (ADR-0008): E2E is a Pro feature, and a long-lived process
|
|
247
|
+
* that initialized crypto before a downgrade would otherwise 403 on every push.
|
|
248
|
+
* Same end state as the "server says encryption disabled" branch in initCrypto.
|
|
249
|
+
* Keys on disk are left alone, but this process will not pick them up again:
|
|
250
|
+
* `initCrypto` is memoized on `initPromise` and `ZephHook.ensureCrypto` short-
|
|
251
|
+
* circuits on `cryptoInitialized`. A restart after an upgrade re-adopts them.
|
|
252
|
+
*/
|
|
253
|
+
const disableCrypto = () => {
|
|
254
|
+
cachedKeyPair = null;
|
|
255
|
+
cachedExportedPublicKey = null;
|
|
256
|
+
cachedOwnPublicKey = null;
|
|
257
|
+
};
|
|
258
|
+
exports.disableCrypto = disableCrypto;
|
|
242
259
|
/**
|
|
243
260
|
* Encrypt push body for a recipient.
|
|
244
261
|
* Returns fields ready to merge into the sendPush payload.
|
package/dist/remote-agents.d.ts
CHANGED
|
@@ -54,13 +54,18 @@ declare const REMOTE_AGENT_TABLE: readonly [{
|
|
|
54
54
|
readonly displayName: "Codex CLI";
|
|
55
55
|
readonly binary: "codex";
|
|
56
56
|
readonly subcommands: readonly ["codex"];
|
|
57
|
+
}, {
|
|
58
|
+
readonly kind: "cursor";
|
|
59
|
+
readonly displayName: "Cursor CLI";
|
|
60
|
+
readonly binary: "cursor-agent";
|
|
61
|
+
readonly subcommands: readonly ["cursor", "cursor-agent"];
|
|
57
62
|
}, {
|
|
58
63
|
readonly kind: "gemini";
|
|
59
64
|
readonly displayName: "Gemini CLI";
|
|
60
65
|
readonly binary: "gemini";
|
|
61
66
|
readonly subcommands: readonly ["gemini"];
|
|
62
67
|
}];
|
|
63
|
-
/** Closed union of remote-controllable agent kinds ('claude' | 'codex' | 'gemini'). */
|
|
68
|
+
/** Closed union of remote-controllable agent kinds ('claude' | 'codex' | 'cursor' | 'gemini'). */
|
|
64
69
|
export type AgentKind = (typeof REMOTE_AGENT_TABLE)[number]['kind'];
|
|
65
70
|
/** A registry row: the uniform RemoteAgent shape with `kind` narrowed to the closed union. */
|
|
66
71
|
export type RegisteredRemoteAgent = RemoteAgent & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-agents.d.ts","sourceRoot":"","sources":["../src/remote-agents.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"remote-agents.d.ts","sourceRoot":"","sources":["../src/remote-agents.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,WAAW;IACxB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CAC3E;AAyCD;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,IAiB5D,CAAC;AAaF,UAAU,gBAAgB;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACf;AA4ED,4CAA4C;AAC5C,MAAM,WAAW,cAAc;IAC3B,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC7B,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC7B;AAED;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,GACnC,SAAS,MAAM,EACf,SAAS,MAAM,GAAG,IAAI,EACtB,OAAM,cAAmB,KAC1B,MAAM,GAAG,IAOX,CAAC;AAIF,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;EAkCmB,CAAC;AAE5C,kGAAkG;AAClG,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE,8FAA8F;AAC9F,MAAM,MAAM,qBAAqB,GAAG,WAAW,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAEtE,eAAO,MAAM,aAAa,EAAE,SAAS,qBAAqB,EAAuB,CAAC;AAElF,eAAO,MAAM,qBAAqB,GAAI,KAAK,MAAM,KAAG,qBAAqB,GAAG,SAClB,CAAC;AAE3D,eAAO,MAAM,uBAAuB,GAAI,MAAM,MAAM,KAAG,qBAAqB,GAAG,SAG9E,CAAC"}
|
package/dist/remote-agents.js
CHANGED
|
@@ -10,9 +10,11 @@ exports.matchAgentByPaneCommand = exports.findAgentBySubcommand = exports.REMOTE
|
|
|
10
10
|
* and the phone picker, which may validate the enum).
|
|
11
11
|
*
|
|
12
12
|
* This is deliberately NOT merged into `agents.ts`: that table drives
|
|
13
|
-
* install/uninstall/verify detection (8 agents, incl.
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* install/uninstall/verify detection (8 agents, incl. Windsurf, which can
|
|
14
|
+
* never be driven via tmux), and the two tables carry different name axes
|
|
15
|
+
* — install id vs subcommand alias vs pane binary. Cursor is the clearest
|
|
16
|
+
* case for keeping them apart: the `cursor` install id there means the
|
|
17
|
+
* IDE, while the drivable binary here is `cursor-agent`, its terminal TUI.
|
|
16
18
|
*/
|
|
17
19
|
const fs_1 = require("fs");
|
|
18
20
|
const os_1 = require("os");
|
|
@@ -198,6 +200,16 @@ const REMOTE_AGENT_TABLE = [
|
|
|
198
200
|
binary: 'codex',
|
|
199
201
|
subcommands: ['codex'],
|
|
200
202
|
},
|
|
203
|
+
{
|
|
204
|
+
// The pane binary is `cursor-agent`, Cursor's terminal TUI — NOT the
|
|
205
|
+
// bare `cursor` on PATH, which is the IDE launcher script: it opens
|
|
206
|
+
// the app and exits, so a pane started with it dies before the
|
|
207
|
+
// listener can address it.
|
|
208
|
+
kind: 'cursor',
|
|
209
|
+
displayName: 'Cursor CLI',
|
|
210
|
+
binary: 'cursor-agent',
|
|
211
|
+
subcommands: ['cursor', 'cursor-agent'],
|
|
212
|
+
},
|
|
201
213
|
{
|
|
202
214
|
kind: 'gemini',
|
|
203
215
|
displayName: 'Gemini CLI',
|
package/dist/zeph-hook.d.ts
CHANGED
|
@@ -6,7 +6,20 @@ export declare class ZephHook {
|
|
|
6
6
|
private cryptoInitialized;
|
|
7
7
|
constructor(options: ZephOptions);
|
|
8
8
|
private ensureCrypto;
|
|
9
|
+
/**
|
|
10
|
+
* A push must not be lost because E2E turned out to be unavailable.
|
|
11
|
+
*
|
|
12
|
+
* E2E is Pro-only (ADR-0008) and the server rejects `isEncrypted` from a free
|
|
13
|
+
* account with 403 `PRO_REQUIRED`. This process may have initialized crypto
|
|
14
|
+
* while still Pro — a long-lived listener outlives a downgrade — so the
|
|
15
|
+
* rejection is only visible at send time. Drop the keys and run the send
|
|
16
|
+
* again from the top: the retry re-encodes the payload as plaintext and, on
|
|
17
|
+
* the file path, re-uploads the file unencrypted (a payload patch would leave
|
|
18
|
+
* an undecryptable blob in S3). The first, encrypted object stays orphaned —
|
|
19
|
+
* accepted cost; the upload endpoint has no quota counter to burn.
|
|
20
|
+
*/
|
|
9
21
|
notify(payload: NotifyPayload): Promise<NotifyResult>;
|
|
22
|
+
private notifyOnce;
|
|
10
23
|
private notifyWithFile;
|
|
11
24
|
requestUpload(params: {
|
|
12
25
|
fileName: string;
|
package/dist/zeph-hook.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zeph-hook.d.ts","sourceRoot":"","sources":["../src/zeph-hook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAY,gBAAgB,EAAE,gBAAgB,EAAoB,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAoCxL,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,OAAO,CAAC,iBAAiB,CAAS;gBAEtB,OAAO,EAAE,WAAW;YASlB,YAAY;
|
|
1
|
+
{"version":3,"file":"zeph-hook.d.ts","sourceRoot":"","sources":["../src/zeph-hook.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAY,gBAAgB,EAAE,gBAAgB,EAAoB,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAoCxL,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,OAAO,CAAC,iBAAiB,CAAS;gBAEtB,OAAO,EAAE,WAAW;YASlB,YAAY;IAY1B;;;;;;;;;;;OAWG;IACG,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;YAgB7C,UAAU;YAqCV,cAAc;IAsDtB,aAAa,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAK7G,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcrF,IAAI,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAmB9C,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAKlD,UAAU,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAK7C;;;;OAIG;IACG,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;YASxF,OAAO;IAiCrB,OAAO,CAAC,UAAU;CASnB"}
|
package/dist/zeph-hook.js
CHANGED
|
@@ -63,12 +63,40 @@ class ZephHook {
|
|
|
63
63
|
return false;
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* A push must not be lost because E2E turned out to be unavailable.
|
|
68
|
+
*
|
|
69
|
+
* E2E is Pro-only (ADR-0008) and the server rejects `isEncrypted` from a free
|
|
70
|
+
* account with 403 `PRO_REQUIRED`. This process may have initialized crypto
|
|
71
|
+
* while still Pro — a long-lived listener outlives a downgrade — so the
|
|
72
|
+
* rejection is only visible at send time. Drop the keys and run the send
|
|
73
|
+
* again from the top: the retry re-encodes the payload as plaintext and, on
|
|
74
|
+
* the file path, re-uploads the file unencrypted (a payload patch would leave
|
|
75
|
+
* an undecryptable blob in S3). The first, encrypted object stays orphaned —
|
|
76
|
+
* accepted cost; the upload endpoint has no quota counter to burn.
|
|
77
|
+
*/
|
|
66
78
|
async notify(payload) {
|
|
79
|
+
// Resolved once for both attempts: the lookup shells out to tmux, and a
|
|
80
|
+
// retry milliseconds later cannot land in a different session.
|
|
81
|
+
const agentCtx = agentSessionContext();
|
|
82
|
+
try {
|
|
83
|
+
return await this.notifyOnce(payload, agentCtx);
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
// Held keys are what proves this send was encrypted, and dropping them is
|
|
87
|
+
// also the recursion guard — the retry has none, so a second 403 propagates.
|
|
88
|
+
if (!(err instanceof errors_js_1.ZephError) || err.code !== 'PRO_REQUIRED' || !(0, crypto_js_1.getKeyPair)())
|
|
89
|
+
throw err;
|
|
90
|
+
(0, crypto_js_1.disableCrypto)();
|
|
91
|
+
console.error('[Crypto] End-to-end encryption requires Zeph Pro — resending as plaintext.');
|
|
92
|
+
return this.notifyOnce(payload, agentCtx);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async notifyOnce(payload, agentCtx) {
|
|
67
96
|
// Attach the stable session key when in a tmux agent session so the push
|
|
68
97
|
// joins the agent chat. Merged into `payload` here so both the inline and
|
|
69
98
|
// file-upload (notifyWithFile) paths — which each spread `...payload` —
|
|
70
99
|
// carry it. Explicit caller values win.
|
|
71
|
-
const agentCtx = agentSessionContext();
|
|
72
100
|
if (agentCtx)
|
|
73
101
|
payload = { ...agentCtx, ...payload };
|
|
74
102
|
const canEncrypt = await this.ensureCrypto();
|