@zeph-to/hook-sdk 1.10.0 → 1.10.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 +5 -0
- package/dist/listener.d.ts +2 -1
- package/dist/listener.d.ts.map +1 -1
- package/dist/listener.js +50 -6
- package/dist/wrapper.d.ts.map +1 -1
- package/dist/wrapper.js +18 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# @zeph-to/hook-sdk
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@zeph-to/hook-sdk)
|
|
4
|
+
[](https://www.npmjs.com/package/@zeph-to/hook-sdk)
|
|
5
|
+
[](https://nodejs.org)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
|
|
3
8
|
Push notification SDK + CLI for [Zeph](https://zeph.to), with an optional
|
|
4
9
|
resident listener that **drives Claude Code / Codex / Gemini sessions
|
|
5
10
|
from your phone** by injecting messages into named tmux sessions.
|
package/dist/listener.d.ts
CHANGED
|
@@ -50,7 +50,8 @@ export declare const parseSessionName: (name: string) => {
|
|
|
50
50
|
* directory of a tmux pane. Mirrors `mcp-server/config.ts`'s
|
|
51
51
|
* detectClaudeSessionId: CC writes per-session jsonl files at
|
|
52
52
|
* `~/.claude/projects/<projectHash>/<UUID>.jsonl` where the hash is
|
|
53
|
-
* the cwd with `/` replaced by `-`.
|
|
53
|
+
* the cwd with `/` replaced by `-`. Cached for 60s — see
|
|
54
|
+
* claudeSessionCache.
|
|
54
55
|
*/
|
|
55
56
|
export declare const detectClaudeSessionId: (cwd: string) => string | null;
|
|
56
57
|
export interface CollectResult {
|
package/dist/listener.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listener.d.ts","sourceRoot":"","sources":["../src/listener.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAuBH,KAAK,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAG/C,UAAU,YAAY;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AA2BD,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,EAAE,MAAK,MAAmB,KAAG,OAgB1E,CAAC;AAEF,2EAA2E;AAC3E,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,KAAG,MAAM,GAAG,IAO7D,CAAC;AA6QF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,KAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAK3F,CAAC;
|
|
1
|
+
{"version":3,"file":"listener.d.ts","sourceRoot":"","sources":["../src/listener.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAuBH,KAAK,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAG/C,UAAU,YAAY;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AA2BD,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,EAAE,MAAK,MAAmB,KAAG,OAgB1E,CAAC;AAEF,2EAA2E;AAC3E,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,KAAG,MAAM,GAAG,IAO7D,CAAC;AA6QF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,KAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAK3F,CAAC;AAuCF;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,IAiB5D,CAAC;AAoEF,MAAM,WAAW,aAAa;IAC1B,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,0EAA0E;IAC1E,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrD;AAED;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,QAAO,aA0DzC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,QAAO,YAAY,EAAuC,CAAC;AAIvF,UAAU,QAAQ;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,UAAU,cAAc;IACpB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACjD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACpD,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IACzC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAgCD;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,GACnB,MAAM,QAAQ,EACd,OAAM,cAAmB,KAC1B,OAQF,CAAC;AA2BF;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GAAI,OAAM,MAAmB,KAAG,MAGnE,CAAC;AAyLF,eAAO,MAAM,cAAc,GAAU,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAG,OAAO,CAAC,MAAM,CAyF3F,CAAC"}
|
package/dist/listener.js
CHANGED
|
@@ -379,13 +379,20 @@ const parseSessionName = (name) => {
|
|
|
379
379
|
exports.parseSessionName = parseSessionName;
|
|
380
380
|
const CLAUDE_PROJECTS_DIR = (0, path_1.join)((0, os_1.homedir)(), '.claude', 'projects');
|
|
381
381
|
/**
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
*
|
|
382
|
+
* Cache for detectClaudeSessionId. The function walks every jsonl file
|
|
383
|
+
* in `~/.claude/projects/<hash>/` on each call — after weeks of CC use
|
|
384
|
+
* that directory holds hundreds of session files, and we were calling
|
|
385
|
+
* this per tmux session per 5-second report cycle. Heavy disk I/O
|
|
386
|
+
* compounded with multiple sessions caused the report cycle to spike
|
|
387
|
+
* CPU and starve the host shell.
|
|
388
|
+
*
|
|
389
|
+
* The current-session UUID only changes when a new CC session starts
|
|
390
|
+
* in that directory (rare, on the order of hours), so a 60-second TTL
|
|
391
|
+
* is safe and cuts the per-cycle stat count by ~12×.
|
|
387
392
|
*/
|
|
388
|
-
const
|
|
393
|
+
const claudeSessionCache = new Map();
|
|
394
|
+
const CLAUDE_SESSION_CACHE_TTL_MS = 60_000;
|
|
395
|
+
const doDetectClaudeSessionId = (cwd) => {
|
|
389
396
|
try {
|
|
390
397
|
const projectHash = cwd.replace(/\//g, '-');
|
|
391
398
|
const sessionsDir = (0, path_1.join)(CLAUDE_PROJECTS_DIR, projectHash);
|
|
@@ -407,6 +414,32 @@ const detectClaudeSessionId = (cwd) => {
|
|
|
407
414
|
return null;
|
|
408
415
|
}
|
|
409
416
|
};
|
|
417
|
+
/**
|
|
418
|
+
* Locate the most recent Claude Code session UUID for the working
|
|
419
|
+
* directory of a tmux pane. Mirrors `mcp-server/config.ts`'s
|
|
420
|
+
* detectClaudeSessionId: CC writes per-session jsonl files at
|
|
421
|
+
* `~/.claude/projects/<projectHash>/<UUID>.jsonl` where the hash is
|
|
422
|
+
* the cwd with `/` replaced by `-`. Cached for 60s — see
|
|
423
|
+
* claudeSessionCache.
|
|
424
|
+
*/
|
|
425
|
+
const detectClaudeSessionId = (cwd) => {
|
|
426
|
+
const now = Date.now();
|
|
427
|
+
const cached = claudeSessionCache.get(cwd);
|
|
428
|
+
if (cached && cached.expiresAt > now)
|
|
429
|
+
return cached.sessionId;
|
|
430
|
+
// Cap cache size so a long-lived listener that's seen many cwds
|
|
431
|
+
// doesn't grow unbounded. 64 is plenty for any realistic setup.
|
|
432
|
+
if (claudeSessionCache.size >= 64) {
|
|
433
|
+
// Evict the oldest-expiring entry — Map iteration order is
|
|
434
|
+
// insertion order, so the first key we hit is the oldest.
|
|
435
|
+
const firstKey = claudeSessionCache.keys().next().value;
|
|
436
|
+
if (firstKey !== undefined)
|
|
437
|
+
claudeSessionCache.delete(firstKey);
|
|
438
|
+
}
|
|
439
|
+
const sessionId = doDetectClaudeSessionId(cwd);
|
|
440
|
+
claudeSessionCache.set(cwd, { sessionId, expiresAt: now + CLAUDE_SESSION_CACHE_TTL_MS });
|
|
441
|
+
return sessionId;
|
|
442
|
+
};
|
|
410
443
|
exports.detectClaudeSessionId = detectClaudeSessionId;
|
|
411
444
|
// U+241F "Symbol for Unit Separator" — a *printable* Unicode glyph
|
|
412
445
|
// (3-byte UTF-8) that visually represents the C0 Unit Separator but is
|
|
@@ -842,6 +875,17 @@ const handleListener = async (args) => {
|
|
|
842
875
|
log(`zeph listener starting — ${wsUrl}`);
|
|
843
876
|
log(`device=${(0, exports.computeListenerDeviceId)()} host=${(0, os_1.hostname)()} pid=${process.pid}`);
|
|
844
877
|
log("Waiting for 'agent.command' pushes from the phone picker. Ctrl-C to stop.");
|
|
878
|
+
// Heartbeat memory log — once an hour. Lets the user (and us) spot
|
|
879
|
+
// gradual growth in a long-running daemon before it gets bad enough
|
|
880
|
+
// to make the host shell unresponsive. The MB counter is human-
|
|
881
|
+
// readable and tiny enough not to bloat the log.
|
|
882
|
+
const HEAP_LOG_INTERVAL_MS = 60 * 60 * 1000;
|
|
883
|
+
const heapLogTimer = setInterval(() => {
|
|
884
|
+
const m = process.memoryUsage();
|
|
885
|
+
const mb = (n) => Math.round(n / 1024 / 1024);
|
|
886
|
+
log(`heap: rss=${mb(m.rss)}MB heapUsed=${mb(m.heapUsed)}MB external=${mb(m.external)}MB`);
|
|
887
|
+
}, HEAP_LOG_INTERVAL_MS);
|
|
888
|
+
heapLogTimer.unref();
|
|
845
889
|
let shuttingDown = false;
|
|
846
890
|
let activeHandle = null;
|
|
847
891
|
const stop = (sig) => {
|
package/dist/wrapper.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../src/wrapper.ts"],"names":[],"mappings":"AAwBA,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;
|
|
1
|
+
{"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../src/wrapper.ts"],"names":[],"mappings":"AAwBA,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;AAmHF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,EAAE,QAAO,MAAM,EAAO,KAAG,OAAO,CAAC,MAAM,CAmCtF,CAAC"}
|
package/dist/wrapper.js
CHANGED
|
@@ -145,6 +145,23 @@ const resolveCliPath = () => {
|
|
|
145
145
|
* Failure here is non-fatal — `zeph cc` still launches the agent. The
|
|
146
146
|
* user just loses the phone-bridge feature until they restart.
|
|
147
147
|
*/
|
|
148
|
+
/**
|
|
149
|
+
* Rotate the listener log once it grows past 5 MB. The daemon runs for
|
|
150
|
+
* days and writes 2-3 lines per 5-s cycle, so without rotation the file
|
|
151
|
+
* climbs into the tens of megabytes range pretty quickly. We keep the
|
|
152
|
+
* previous run's tail under `.old` for post-mortem and start fresh.
|
|
153
|
+
*/
|
|
154
|
+
const LISTENER_LOG_MAX_BYTES = 5 * 1024 * 1024;
|
|
155
|
+
const rotateListenerLogIfLarge = () => {
|
|
156
|
+
try {
|
|
157
|
+
if (!(0, fs_1.existsSync)(LISTENER_LOG_FILE))
|
|
158
|
+
return;
|
|
159
|
+
if ((0, fs_1.statSync)(LISTENER_LOG_FILE).size <= LISTENER_LOG_MAX_BYTES)
|
|
160
|
+
return;
|
|
161
|
+
(0, fs_1.renameSync)(LISTENER_LOG_FILE, LISTENER_LOG_FILE + '.old');
|
|
162
|
+
}
|
|
163
|
+
catch { /* best-effort */ }
|
|
164
|
+
};
|
|
148
165
|
const ensureListenerRunning = () => {
|
|
149
166
|
if (listenerAlive())
|
|
150
167
|
return;
|
|
@@ -153,6 +170,7 @@ const ensureListenerRunning = () => {
|
|
|
153
170
|
return;
|
|
154
171
|
try {
|
|
155
172
|
(0, fs_1.mkdirSync)(ZEPH_DIR, { recursive: true });
|
|
173
|
+
rotateListenerLogIfLarge();
|
|
156
174
|
const out = (0, fs_1.openSync)(LISTENER_LOG_FILE, 'a');
|
|
157
175
|
const child = (0, child_process_1.spawn)(process.execPath, [cliPath, 'listener'], {
|
|
158
176
|
detached: true,
|