@zeph-to/cli 2.3.1 → 2.4.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 +41 -5
- package/dist/remote-agents.d.ts +9 -3
- package/dist/remote-agents.d.ts.map +1 -1
- package/dist/remote-agents.js +14 -2
- package/dist/remote-hook.js +10 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
**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.**
|
|
10
10
|
|
|
11
|
-
`@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
|
|
11
|
+
`@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 any agent session it can start** by typing straight into named tmux sessions.
|
|
12
12
|
|
|
13
13
|
<p align="center">
|
|
14
14
|
<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>
|
|
@@ -83,13 +83,13 @@ To **send** notifications:
|
|
|
83
83
|
zeph notify --title "Deploy done" --body "v2.1.0 shipped"
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
To **drive
|
|
86
|
+
To **drive an agent session from your phone**, see
|
|
87
87
|
[Remote Control](#remote-control) below.
|
|
88
88
|
|
|
89
89
|
## Remote Control
|
|
90
90
|
|
|
91
|
-
> Send messages from your phone *into* a live
|
|
92
|
-
>
|
|
91
|
+
> Send messages from your phone *into* a live agent session — even after
|
|
92
|
+
> a `zeph_ask` polling window has expired.
|
|
93
93
|
|
|
94
94
|
<p align="center">
|
|
95
95
|
<img src="https://zeph.to/readme/ask-phone.png" alt="A Zeph hook on the phone: a question with tappable answer buttons and a text field" width="300">
|
|
@@ -116,7 +116,7 @@ tmux session via `tmux send-keys`.
|
|
|
116
116
|
[zeph listener — resident daemon, started by `zeph cc` automatically]
|
|
117
117
|
│ tmux send-keys -l -t zeph-myapp "리팩토링 마무리해줘" + Enter
|
|
118
118
|
▼
|
|
119
|
-
[tmux session "zeph-myapp" running claude / codex / cursor-agent / gemini]
|
|
119
|
+
[tmux session "zeph-myapp" running claude / codex / cursor-agent / gemini / hermes]
|
|
120
120
|
```
|
|
121
121
|
|
|
122
122
|
The listener polls its tmux session inventory every 5 seconds and
|
|
@@ -164,6 +164,26 @@ through. Same reason the `stop`-hook auto-push `zeph setup` installs
|
|
|
164
164
|
covers the Cursor **IDE** but not `zeph cursor` panes; ask for a
|
|
165
165
|
`zeph_notify` when you want one.
|
|
166
166
|
|
|
167
|
+
`zeph hermes` is in the same position, and asking for it works the same
|
|
168
|
+
way. `zeph install` does not wire Hermes either — add the MCP server to
|
|
169
|
+
`~/.hermes/config.yaml` by hand, as one entry under `mcp_servers`:
|
|
170
|
+
|
|
171
|
+
```yaml
|
|
172
|
+
mcp_servers:
|
|
173
|
+
zeph:
|
|
174
|
+
command: "npx"
|
|
175
|
+
args: ["-y", "@zeph-to/mcp-server"]
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Add the entry; do not replace the file. `mcp_servers` is shared with
|
|
179
|
+
every other server you have configured, and nothing under `~/.hermes` is
|
|
180
|
+
in version control. Then `/reload-mcp` inside Hermes.
|
|
181
|
+
|
|
182
|
+
`zeph_ask` needs a hook id, which the MCP server reads from
|
|
183
|
+
`ZEPH_HOOK_ID` or from `hookId` in `~/.zeph/config.json` — `zeph install`
|
|
184
|
+
writes the latter, so a machine that has run it once needs no `env`
|
|
185
|
+
block here.
|
|
186
|
+
|
|
167
187
|
### Setup
|
|
168
188
|
|
|
169
189
|
1. **Install tmux.** The listener uses `send-keys`; the wrapper spawns
|
|
@@ -190,12 +210,27 @@ covers the Cursor **IDE** but not `zeph cursor` panes; ask for a
|
|
|
190
210
|
zeph codex # codex → tmux session "zeph-<project>"
|
|
191
211
|
zeph cursor # cursor-agent → tmux session "zeph-<project>"
|
|
192
212
|
zeph gemini # gemini → tmux session "zeph-<project>"
|
|
213
|
+
zeph hermes # hermes → tmux session "zeph-<project>"
|
|
193
214
|
```
|
|
194
215
|
|
|
195
216
|
`zeph cursor` runs **`cursor-agent`**, Cursor's terminal agent — a
|
|
196
217
|
separate install from the Cursor IDE (the bare `cursor` on your PATH
|
|
197
218
|
is the editor launcher, which exits immediately and can't be driven).
|
|
198
219
|
|
|
220
|
+
`zeph hermes` runs Hermes in its classic REPL, which is the default.
|
|
221
|
+
Launching it in TUI mode instead (`zeph hermes --tui`, or `HERMES_TUI=1`
|
|
222
|
+
in the environment) puts the pane on the terminal's alternate screen,
|
|
223
|
+
and the alternate screen has no scrollback — the live mirror on your
|
|
224
|
+
phone still draws, but scrolling up in it comes back empty.
|
|
225
|
+
|
|
226
|
+
If a Hermes session disappears from the phone's picker, the cause is
|
|
227
|
+
tmux having dropped the pane's `start_command`: all the listener sees
|
|
228
|
+
then is the interpreter Hermes execs (`python3.11` at time of writing),
|
|
229
|
+
which names no agent. `zeph listener` run in the foreground prints the
|
|
230
|
+
reason (`no agent in pane (start=…, current=python3.11)`). Kill the
|
|
231
|
+
session and start it again with `zeph hermes` to recover. `claude` sits
|
|
232
|
+
on the same edge for the same reason — it execs node.
|
|
233
|
+
|
|
199
234
|
The first `zeph cc` on a machine **auto-spawns a background
|
|
200
235
|
listener** (singleton, PID file at `~/.zeph/listener.pid`,
|
|
201
236
|
stdout/stderr at `~/.zeph/listener.log`). You never run
|
|
@@ -407,6 +442,7 @@ zeph cc # claude
|
|
|
407
442
|
zeph codex # codex
|
|
408
443
|
zeph cursor # cursor-agent (Cursor CLI, not the IDE)
|
|
409
444
|
zeph gemini # gemini
|
|
445
|
+
zeph hermes # hermes
|
|
410
446
|
|
|
411
447
|
# Run the resident listener (foreground; background it as you like)
|
|
412
448
|
zeph listener
|
package/dist/remote-agents.d.ts
CHANGED
|
@@ -23,8 +23,9 @@ export interface RemoteAgent {
|
|
|
23
23
|
/**
|
|
24
24
|
* Resolve the agent's own session id from the pane's cwd (+ pane pid
|
|
25
25
|
* when the caller knows it — enables exact process-tree matching).
|
|
26
|
-
* EXTENSION POINT:
|
|
27
|
-
*
|
|
26
|
+
* EXTENSION POINT: carried only by Claude Code. Every other row omits
|
|
27
|
+
* it until that agent's session-file format is confirmed — the listener
|
|
28
|
+
* then reports agentSessionId: null.
|
|
28
29
|
*/
|
|
29
30
|
resolveSessionId?: (paneCwd: string, panePid?: number) => string | null;
|
|
30
31
|
}
|
|
@@ -76,8 +77,13 @@ declare const REMOTE_AGENT_TABLE: readonly [{
|
|
|
76
77
|
readonly displayName: "Gemini CLI";
|
|
77
78
|
readonly binary: "gemini";
|
|
78
79
|
readonly subcommands: readonly ["gemini"];
|
|
80
|
+
}, {
|
|
81
|
+
readonly kind: "hermes";
|
|
82
|
+
readonly displayName: "Hermes";
|
|
83
|
+
readonly binary: "hermes";
|
|
84
|
+
readonly subcommands: readonly ["hermes"];
|
|
79
85
|
}];
|
|
80
|
-
/** Closed union of remote-controllable agent kinds
|
|
86
|
+
/** Closed union of remote-controllable agent kinds, derived from the table above. */
|
|
81
87
|
export type AgentKind = (typeof REMOTE_AGENT_TABLE)[number]['kind'];
|
|
82
88
|
/** A registry row: the uniform RemoteAgent shape with `kind` narrowed to the closed union. */
|
|
83
89
|
export type RegisteredRemoteAgent = RemoteAgent & {
|
|
@@ -1 +1 @@
|
|
|
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;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC
|
|
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;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC;;;;;;OAMG;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;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+CmB,CAAC;AAE5C,qFAAqF;AACrF,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
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.matchAgentByPaneCommand = exports.findAgentBySubcommand = exports.REMOTE_AGENTS = exports.detectClaudeSessionIdByPid = exports.detectClaudeSessionId = void 0;
|
|
4
4
|
/**
|
|
5
|
-
* Remote-control agent registry — the single table behind
|
|
6
|
-
* `zeph
|
|
5
|
+
* Remote-control agent registry — the single table behind every
|
|
6
|
+
* `zeph <agent>` subcommand, the listener's pane matching, and the
|
|
7
7
|
* per-agent session-id enrichment. Adding a remote-controllable agent is
|
|
8
8
|
* one row here (plus, for a genuinely new kind, backend/phone support:
|
|
9
9
|
* `kind` is a wire contract — AgentSession.agentKind flows to the server
|
|
@@ -217,6 +217,18 @@ const REMOTE_AGENT_TABLE = [
|
|
|
217
217
|
binary: 'gemini',
|
|
218
218
|
subcommands: ['gemini'],
|
|
219
219
|
},
|
|
220
|
+
{
|
|
221
|
+
// `hermes` on PATH is a bash script that execs a venv Python, so the
|
|
222
|
+
// pane's *current* command reads the interpreter (`python3.11` as
|
|
223
|
+
// measured). Only `pane_start_command` identifies it, which is the
|
|
224
|
+
// same bargain `claude` (node) already makes. Deliberately no
|
|
225
|
+
// paneMatchAliases: matching interpreter names would adopt every
|
|
226
|
+
// Python REPL on the machine as a Hermes session.
|
|
227
|
+
kind: 'hermes',
|
|
228
|
+
displayName: 'Hermes',
|
|
229
|
+
binary: 'hermes',
|
|
230
|
+
subcommands: ['hermes'],
|
|
231
|
+
},
|
|
220
232
|
];
|
|
221
233
|
exports.REMOTE_AGENTS = REMOTE_AGENT_TABLE;
|
|
222
234
|
const findAgentBySubcommand = (cmd) => exports.REMOTE_AGENTS.find((a) => a.subcommands.includes(cmd));
|
package/dist/remote-hook.js
CHANGED
|
@@ -126,8 +126,17 @@ const remoteOrigin = (prompt, cwd, now) => {
|
|
|
126
126
|
return 'unclear';
|
|
127
127
|
// Marker format: "<epochSec> <sha256hex>\n" (listener.ts writeRemoteMarker).
|
|
128
128
|
const record = content.match(/^(\d+) ([0-9a-f]{64})\n?$/);
|
|
129
|
-
if (!record)
|
|
129
|
+
if (!record) {
|
|
130
|
+
// Junk can only ever be re-read and re-rejected — same housekeeping a
|
|
131
|
+
// stale marker gets. Twin: zeph-remote.sh's `rm -f` on the parse branches.
|
|
132
|
+
try {
|
|
133
|
+
(0, fs_1.unlinkSync)(marker);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
/* best-effort housekeeping */
|
|
137
|
+
}
|
|
130
138
|
return 'keyboard';
|
|
139
|
+
}
|
|
131
140
|
if (Math.floor(now() / 1000) - Number(record[1]) > FRESH_WINDOW_SEC) {
|
|
132
141
|
// Stale markers are dead weight (can never flag) — delete on sight.
|
|
133
142
|
try {
|