@zeph-to/cli 1.27.1 → 2.1.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 +59 -29
- package/dist/cli.js +6 -1
- package/dist/crypto.d.ts +80 -55
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +178 -184
- package/dist/gate.d.ts +47 -2
- package/dist/gate.d.ts.map +1 -1
- package/dist/gate.js +50 -4
- package/dist/input-sequencer.d.ts +66 -0
- package/dist/input-sequencer.d.ts.map +1 -0
- package/dist/input-sequencer.js +127 -0
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +7 -3
- package/dist/listener.d.ts +119 -2
- package/dist/listener.d.ts.map +1 -1
- package/dist/listener.js +549 -40
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +15 -5
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/zeph-hook.d.ts +9 -0
- package/dist/zeph-hook.d.ts.map +1 -1
- package/dist/zeph-hook.js +81 -46
- package/package.json +1 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inbound ordering guard for keys/text arriving over the ephemeral relay.
|
|
3
|
+
*
|
|
4
|
+
* The outbound stream stamps every frame with `seq` + `epoch` so the viewer
|
|
5
|
+
* can drop what it has already painted (see listener.ts buildStreamFrame's
|
|
6
|
+
* send site); this is the inverse for the inbound direction. A dropped or
|
|
7
|
+
* reordered keystroke is not a cosmetic glitch the way a stale frame is —
|
|
8
|
+
* it lands in a real editor buffer — so the rules differ:
|
|
9
|
+
*
|
|
10
|
+
* - in-order seq delivers immediately (typing latency is the whole point of
|
|
11
|
+
* this path; nothing is buffered on the happy path)
|
|
12
|
+
* - a gap holds later keys for `holdMs`, then delivers what arrived anyway.
|
|
13
|
+
* Approximate order beats dropped keys: the relay is at-most-once, so a
|
|
14
|
+
* never-arriving seq must not wedge everything behind it forever.
|
|
15
|
+
* - a new epoch (sender restart) resets the high-water mark, and the old
|
|
16
|
+
* epoch's held keys are discarded rather than typed into the new run
|
|
17
|
+
* - a seq at or below the high-water mark is a straggler the hold already
|
|
18
|
+
* flushed past — typing it now would put it out of order. The transport is
|
|
19
|
+
* at-most-once, so this is never a transport duplicate; the sender is told
|
|
20
|
+
* (`stale`) so it can decide whether the key still matters.
|
|
21
|
+
*
|
|
22
|
+
* Every outcome that drops a message is reported — as a non-'ok' AcceptResult
|
|
23
|
+
* for the message being accepted, or through `onDiscard` for held messages
|
|
24
|
+
* swept out by an epoch change or reset. The caller's contract ("every
|
|
25
|
+
* refusal reaches the sender") is only as honest as this accounting.
|
|
26
|
+
*/
|
|
27
|
+
/** How long a key waits for the gap in front of it to fill. */
|
|
28
|
+
export declare const INPUT_HOLD_MS = 500;
|
|
29
|
+
/** Ceiling on the reorder buffer. A gap that never fills lets every later key
|
|
30
|
+
* pile up for the whole hold, and the flush then delivers them back to back —
|
|
31
|
+
* one blocking tmux inject each. 32 is far past any real burst at
|
|
32
|
+
* INPUT_HOLD_MS, so this only bites on a stuck or hostile sender. */
|
|
33
|
+
export declare const MAX_PENDING_INPUTS = 32;
|
|
34
|
+
/** Non-'ok' means this message was dropped and the caller owes its sender a
|
|
35
|
+
* refusal: `overflow` (buffer full), `superseded` (older epoch than the
|
|
36
|
+
* current run), `stale` (at or below the high-water mark — the hold already
|
|
37
|
+
* flushed past it). 'ok' covers delivered AND held; held messages that are
|
|
38
|
+
* later swept out surface through `onDiscard` instead. */
|
|
39
|
+
export type AcceptResult = 'ok' | 'overflow' | 'superseded' | 'stale';
|
|
40
|
+
/** Cancels a pending flush. */
|
|
41
|
+
type CancelFlush = () => void;
|
|
42
|
+
/** Injectable timer so tests drive the hold deadline instead of sleeping. */
|
|
43
|
+
export type ScheduleFlush = (fn: () => void, ms: number) => CancelFlush;
|
|
44
|
+
export type SequencedInput = {
|
|
45
|
+
/** Sender-side monotonic counter within `epoch`. */
|
|
46
|
+
seq: number;
|
|
47
|
+
/** Sender incarnation — a restart bumps it and restarts `seq`. */
|
|
48
|
+
epoch: number;
|
|
49
|
+
};
|
|
50
|
+
export type InputSequencerOptions<T> = {
|
|
51
|
+
holdMs?: number;
|
|
52
|
+
schedule?: ScheduleFlush;
|
|
53
|
+
/** Called for each HELD message dropped without delivery — an epoch change
|
|
54
|
+
* or reset() sweeping the buffer. Accept-time drops are reported via the
|
|
55
|
+
* AcceptResult instead, so no message can vanish through both cracks. */
|
|
56
|
+
onDiscard?: (msg: T) => void;
|
|
57
|
+
};
|
|
58
|
+
export type InputSequencer<T extends SequencedInput> = {
|
|
59
|
+
accept: (msg: T) => AcceptResult;
|
|
60
|
+
/** Drop everything held (reported via onDiscard) and forget the epoch —
|
|
61
|
+
* the run is over. */
|
|
62
|
+
reset: () => void;
|
|
63
|
+
};
|
|
64
|
+
export declare const createInputSequencer: <T extends SequencedInput>(deliver: (msg: T) => void, opts?: InputSequencerOptions<T>) => InputSequencer<T>;
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=input-sequencer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-sequencer.d.ts","sourceRoot":"","sources":["../src/input-sequencer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,+DAA+D;AAC/D,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC;;;sEAGsE;AACtE,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;2DAI2D;AAC3D,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,UAAU,GAAG,YAAY,GAAG,OAAO,CAAC;AAEtE,+BAA+B;AAC/B,KAAK,WAAW,GAAG,MAAM,IAAI,CAAC;AAE9B,6EAA6E;AAC7E,MAAM,MAAM,aAAa,GAAG,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK,WAAW,CAAC;AAExE,MAAM,MAAM,cAAc,GAAG;IACzB,oDAAoD;IACpD,GAAG,EAAE,MAAM,CAAC;IACZ,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,qBAAqB,CAAC,CAAC,IAAI;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB;;8EAE0E;IAC1E,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,cAAc,IAAI;IACnD,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,YAAY,CAAC;IACjC;2BACuB;IACvB,KAAK,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AASF,eAAO,MAAM,oBAAoB,GAAI,CAAC,SAAS,cAAc,EACzD,SAAS,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,EACzB,OAAM,qBAAqB,CAAC,CAAC,CAAM,KACpC,cAAc,CAAC,CAAC,CAmFlB,CAAC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Inbound ordering guard for keys/text arriving over the ephemeral relay.
|
|
4
|
+
*
|
|
5
|
+
* The outbound stream stamps every frame with `seq` + `epoch` so the viewer
|
|
6
|
+
* can drop what it has already painted (see listener.ts buildStreamFrame's
|
|
7
|
+
* send site); this is the inverse for the inbound direction. A dropped or
|
|
8
|
+
* reordered keystroke is not a cosmetic glitch the way a stale frame is —
|
|
9
|
+
* it lands in a real editor buffer — so the rules differ:
|
|
10
|
+
*
|
|
11
|
+
* - in-order seq delivers immediately (typing latency is the whole point of
|
|
12
|
+
* this path; nothing is buffered on the happy path)
|
|
13
|
+
* - a gap holds later keys for `holdMs`, then delivers what arrived anyway.
|
|
14
|
+
* Approximate order beats dropped keys: the relay is at-most-once, so a
|
|
15
|
+
* never-arriving seq must not wedge everything behind it forever.
|
|
16
|
+
* - a new epoch (sender restart) resets the high-water mark, and the old
|
|
17
|
+
* epoch's held keys are discarded rather than typed into the new run
|
|
18
|
+
* - a seq at or below the high-water mark is a straggler the hold already
|
|
19
|
+
* flushed past — typing it now would put it out of order. The transport is
|
|
20
|
+
* at-most-once, so this is never a transport duplicate; the sender is told
|
|
21
|
+
* (`stale`) so it can decide whether the key still matters.
|
|
22
|
+
*
|
|
23
|
+
* Every outcome that drops a message is reported — as a non-'ok' AcceptResult
|
|
24
|
+
* for the message being accepted, or through `onDiscard` for held messages
|
|
25
|
+
* swept out by an epoch change or reset. The caller's contract ("every
|
|
26
|
+
* refusal reaches the sender") is only as honest as this accounting.
|
|
27
|
+
*/
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.createInputSequencer = exports.MAX_PENDING_INPUTS = exports.INPUT_HOLD_MS = void 0;
|
|
30
|
+
/** How long a key waits for the gap in front of it to fill. */
|
|
31
|
+
exports.INPUT_HOLD_MS = 500;
|
|
32
|
+
/** Ceiling on the reorder buffer. A gap that never fills lets every later key
|
|
33
|
+
* pile up for the whole hold, and the flush then delivers them back to back —
|
|
34
|
+
* one blocking tmux inject each. 32 is far past any real burst at
|
|
35
|
+
* INPUT_HOLD_MS, so this only bites on a stuck or hostile sender. */
|
|
36
|
+
exports.MAX_PENDING_INPUTS = 32;
|
|
37
|
+
const defaultSchedule = (fn, ms) => {
|
|
38
|
+
const timer = setTimeout(fn, ms);
|
|
39
|
+
// A pending flush must not keep the daemon's event loop alive.
|
|
40
|
+
timer.unref?.();
|
|
41
|
+
return () => clearTimeout(timer);
|
|
42
|
+
};
|
|
43
|
+
const createInputSequencer = (deliver, opts = {}) => {
|
|
44
|
+
const holdMs = opts.holdMs ?? exports.INPUT_HOLD_MS;
|
|
45
|
+
const schedule = opts.schedule ?? defaultSchedule;
|
|
46
|
+
let epoch = null;
|
|
47
|
+
let highWater = 0;
|
|
48
|
+
let cancelFlush = null;
|
|
49
|
+
const pending = new Map();
|
|
50
|
+
const disarm = () => {
|
|
51
|
+
cancelFlush?.();
|
|
52
|
+
cancelFlush = null;
|
|
53
|
+
};
|
|
54
|
+
/** Sweep the hold buffer without delivering — each swept message is a
|
|
55
|
+
* keystroke its sender still waits on, so each is reported. */
|
|
56
|
+
const discardPending = () => {
|
|
57
|
+
const swept = [...pending.values()];
|
|
58
|
+
pending.clear();
|
|
59
|
+
disarm();
|
|
60
|
+
for (const msg of swept)
|
|
61
|
+
opts.onDiscard?.(msg);
|
|
62
|
+
};
|
|
63
|
+
const emit = (msg) => {
|
|
64
|
+
highWater = msg.seq;
|
|
65
|
+
deliver(msg);
|
|
66
|
+
};
|
|
67
|
+
/** Release the unbroken run sitting on top of the high-water mark. */
|
|
68
|
+
const drain = () => {
|
|
69
|
+
for (;;) {
|
|
70
|
+
const next = pending.get(highWater + 1);
|
|
71
|
+
if (!next)
|
|
72
|
+
break;
|
|
73
|
+
pending.delete(next.seq);
|
|
74
|
+
emit(next);
|
|
75
|
+
}
|
|
76
|
+
if (!pending.size)
|
|
77
|
+
disarm();
|
|
78
|
+
};
|
|
79
|
+
/** Hold expired: the missing seq is not coming. Deliver the rest in seq
|
|
80
|
+
* order, which also moves the high-water mark past the hole. */
|
|
81
|
+
const flush = () => {
|
|
82
|
+
cancelFlush = null;
|
|
83
|
+
const held = [...pending.values()].sort((a, b) => a.seq - b.seq);
|
|
84
|
+
pending.clear();
|
|
85
|
+
for (const msg of held)
|
|
86
|
+
emit(msg);
|
|
87
|
+
};
|
|
88
|
+
const accept = (msg) => {
|
|
89
|
+
if (epoch === null || msg.epoch > epoch) {
|
|
90
|
+
// First message of a run — whatever seq it carries is in order by
|
|
91
|
+
// definition, so joining a stream mid-flight costs no hold.
|
|
92
|
+
discardPending();
|
|
93
|
+
epoch = msg.epoch;
|
|
94
|
+
highWater = msg.seq - 1;
|
|
95
|
+
}
|
|
96
|
+
else if (msg.epoch < epoch) {
|
|
97
|
+
return 'superseded';
|
|
98
|
+
}
|
|
99
|
+
if (msg.seq <= highWater || pending.has(msg.seq))
|
|
100
|
+
return 'stale';
|
|
101
|
+
if (msg.seq === highWater + 1) {
|
|
102
|
+
emit(msg);
|
|
103
|
+
drain();
|
|
104
|
+
return 'ok';
|
|
105
|
+
}
|
|
106
|
+
// Drop the NEW message, not a held one: the buffer already holds the
|
|
107
|
+
// keys closest to the gap, and evicting those would reorder what is
|
|
108
|
+
// about to be typed.
|
|
109
|
+
if (pending.size >= exports.MAX_PENDING_INPUTS)
|
|
110
|
+
return 'overflow';
|
|
111
|
+
pending.set(msg.seq, msg);
|
|
112
|
+
// One deadline per gap: it dates from when the hole opened, so a
|
|
113
|
+
// steady stream of later keys can't extend the wait indefinitely.
|
|
114
|
+
if (!cancelFlush)
|
|
115
|
+
cancelFlush = schedule(flush, holdMs);
|
|
116
|
+
return 'ok';
|
|
117
|
+
};
|
|
118
|
+
return {
|
|
119
|
+
accept,
|
|
120
|
+
reset: () => {
|
|
121
|
+
discardPending();
|
|
122
|
+
epoch = null;
|
|
123
|
+
highWater = 0;
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
exports.createInputSequencer = createInputSequencer;
|
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;;;;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,
|
|
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,CAgH1F,CAAC"}
|
package/dist/installer.js
CHANGED
|
@@ -489,9 +489,13 @@ const handleInstall = async (args) => {
|
|
|
489
489
|
// Hooks fire for every session of every configured agent, so say it here —
|
|
490
490
|
// at the moment the hooks get installed — together with the volume dials.
|
|
491
491
|
console.log(' Notifications now fire for EVERY session of each agent above');
|
|
492
|
-
console.log(' (not only sessions launched with `zeph cc`).
|
|
493
|
-
console.log('
|
|
494
|
-
console.log('
|
|
492
|
+
console.log(' (not only sessions launched with `zeph cc`).');
|
|
493
|
+
console.log('');
|
|
494
|
+
console.log(' In Claude Code the default is QUIET: you get pushed when the agent');
|
|
495
|
+
console.log(' asks you something and when a session finishes, not on every turn.');
|
|
496
|
+
console.log(' Dial it any time:');
|
|
497
|
+
console.log(' /zeph-normal push on every turn that did real work');
|
|
498
|
+
console.log(' /zeph-loud push on every turn');
|
|
495
499
|
console.log(' /zeph-mute full silence, current project');
|
|
496
500
|
console.log(' /zeph-status show what is in effect\n');
|
|
497
501
|
return 0;
|
package/dist/listener.d.ts
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
import { type AgentKind, type RegisteredRemoteAgent } from './remote-agents.js';
|
|
24
24
|
import { type AgentState } from './agent-state.js';
|
|
25
25
|
import { type EncryptedEphemeralPayload } from './crypto.js';
|
|
26
|
+
import { type SequencedInput } from './input-sequencer.js';
|
|
26
27
|
export declare const SESSION_REPORT_HEARTBEAT_MS = 30000;
|
|
27
28
|
interface AgentSession {
|
|
28
29
|
name: string;
|
|
@@ -181,6 +182,29 @@ export interface StreamControl {
|
|
|
181
182
|
*/
|
|
182
183
|
renew?: boolean;
|
|
183
184
|
}
|
|
185
|
+
export declare const STREAM_INTERVAL_MS = 400;
|
|
186
|
+
export declare const BURST_INTERVAL_MS = 120;
|
|
187
|
+
export declare const BURST_WINDOW_MS = 2500;
|
|
188
|
+
export declare const MAX_FRAMES_PER_SEC = 8;
|
|
189
|
+
/**
|
|
190
|
+
* Delay before the next capture: burst while the last input is still echoing,
|
|
191
|
+
* idle otherwise. Exclusive at the boundary — exactly BURST_WINDOW_MS after
|
|
192
|
+
* the input is already idle, so the window can't stretch. A lastInputAt in the
|
|
193
|
+
* future (wall-clock jump) reads as fresh input rather than as an expired
|
|
194
|
+
* window: bursting is the recoverable side of that.
|
|
195
|
+
*/
|
|
196
|
+
export declare const streamCadence: (lastInputAt: number | null, now: number) => number;
|
|
197
|
+
/** Rolling one-second send budget for a single stream. */
|
|
198
|
+
export interface FrameBudget {
|
|
199
|
+
windowStartedAt: number;
|
|
200
|
+
sent: number;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Claim one send from the budget, rolling the window over when the second has
|
|
204
|
+
* passed. False means this tick must skip WITHOUT marking the frame as sent —
|
|
205
|
+
* the diff-gate would otherwise swallow that content for good.
|
|
206
|
+
*/
|
|
207
|
+
export declare const claimFrameSend: (budget: FrameBudget, now: number) => boolean;
|
|
184
208
|
export declare const STREAM_LEASE_MS = 15000;
|
|
185
209
|
export declare const MAX_CONCURRENT_STREAMS = 3;
|
|
186
210
|
export declare const stopStream: (sessionName: string) => void;
|
|
@@ -201,11 +225,23 @@ export type StreamFramePayload = {
|
|
|
201
225
|
/** E2EE envelope — replaces `content` on encrypted streams. */
|
|
202
226
|
encrypted?: EncryptedEphemeralPayload;
|
|
203
227
|
};
|
|
204
|
-
/** Wire shape of a live-mirror error frame — same subtype, no pane data.
|
|
228
|
+
/** Wire shape of a live-mirror error frame — same subtype, no pane data.
|
|
229
|
+
* `input_rejected` refuses an ephemeral `agent.command.input`; it rides this
|
|
230
|
+
* frame so a viewer already handling stream errors can fall back to the REST
|
|
231
|
+
* push path without a second error channel. */
|
|
205
232
|
export type StreamErrorFrame = {
|
|
206
233
|
subtype: 'agent.stream.frame';
|
|
207
234
|
sessionName: string;
|
|
208
|
-
error: 'unknown_session' | 'e2ee_unavailable' | 'encrypt_failed' | 'stream_limit' | 'stream_gone';
|
|
235
|
+
error: 'unknown_session' | 'e2ee_unavailable' | 'encrypt_failed' | 'stream_limit' | 'stream_gone' | 'input_rejected';
|
|
236
|
+
/** `input_rejected` only: the refused message's ordering stamp echoed back.
|
|
237
|
+
* A sender with several keystrokes in flight can't tell which one was
|
|
238
|
+
* refused without it. Absent when the message carried no usable stamp. */
|
|
239
|
+
seq?: number;
|
|
240
|
+
epoch?: number;
|
|
241
|
+
/** `input_rejected` only: the refused sender's deviceId, echoed so a second
|
|
242
|
+
* device whose (seq, epoch) happens to collide doesn't claim the refusal
|
|
243
|
+
* and fire a REST resend for an input that was never refused. */
|
|
244
|
+
inputDeviceId?: string;
|
|
209
245
|
};
|
|
210
246
|
/**
|
|
211
247
|
* Per-listener concurrency guard, as a pure predicate so the boundary is
|
|
@@ -228,6 +264,87 @@ export declare const buildStreamFrame: (captured: {
|
|
|
228
264
|
* path). start is idempotent — a repeat restarts the loop.
|
|
229
265
|
*/
|
|
230
266
|
export declare const handleStreamControl: (req: StreamControl, send: (data: Record<string, unknown>) => void) => boolean;
|
|
267
|
+
/** Wire shape of one inbound injection. Every field is optional, and none of
|
|
268
|
+
* them is proven: this shape is an unchecked cast over relay JSON, so
|
|
269
|
+
* validateInputMessage re-checks the types it acts on rather than trusting
|
|
270
|
+
* the declaration. */
|
|
271
|
+
export interface AgentCommandInput {
|
|
272
|
+
subtype?: string;
|
|
273
|
+
targetDeviceId?: string;
|
|
274
|
+
sessionName?: string;
|
|
275
|
+
/** Which device's seq/epoch run this is. Current relays stamp it from the
|
|
276
|
+
* sending connection unconditionally; relays older than that overwrite
|
|
277
|
+
* only a missing value, so a sender there can claim another device's id.
|
|
278
|
+
* Treat it as an ordering namespace, never as authentication — the worst
|
|
279
|
+
* a forged id buys is a different reorder lane for its own keystrokes. */
|
|
280
|
+
deviceId?: string;
|
|
281
|
+
/** Named keys, ALLOWED_KEYS-validated. Mutually exclusive with `body`. */
|
|
282
|
+
keys?: string[];
|
|
283
|
+
/** Literal text; the injector appends the Enter, as on the REST path. */
|
|
284
|
+
body?: string;
|
|
285
|
+
/** Sender's monotonic counter, and the incarnation it counts within. */
|
|
286
|
+
seq?: number;
|
|
287
|
+
epoch?: number;
|
|
288
|
+
/** E2EE envelope sealing `{ keys | body }` plus a copy of the
|
|
289
|
+
* `sessionName`/`seq`/`epoch` stamp as JSON — the only way in on an
|
|
290
|
+
* encrypted stream, where `keys`/`body` above are refused. Present means
|
|
291
|
+
* the plaintext pair is ignored entirely: on a message the relay can
|
|
292
|
+
* append to, only the ciphertext decides what gets typed, and the sealed
|
|
293
|
+
* stamp is what proves the plaintext one was not re-written for a replay. */
|
|
294
|
+
encrypted?: unknown;
|
|
295
|
+
}
|
|
296
|
+
/** Parity with the REST path, which refuses more than MAX_KEYS_PER_COMMAND per
|
|
297
|
+
* agent.command (zeph apps/server/src/functions/pushes.ts). The lower-latency
|
|
298
|
+
* door into the same pane must not also be the wider one. */
|
|
299
|
+
export declare const MAX_INPUT_KEYS = 10;
|
|
300
|
+
/** The REST path caps no body length, so this bound is ours alone: an
|
|
301
|
+
* unbounded body is one tmux send-keys argv of unbounded size, and what it
|
|
302
|
+
* lands in is an agent prompt, not a paste buffer. */
|
|
303
|
+
export declare const MAX_INPUT_BODY_CHARS = 4096;
|
|
304
|
+
/** The relay socket a message arrived on, and the only way back to its sender. */
|
|
305
|
+
type SendEphemeral = (data: Record<string, unknown>) => void;
|
|
306
|
+
/** One validated injection. */
|
|
307
|
+
type ValidatedInput = SequencedInput & {
|
|
308
|
+
sessionName: string;
|
|
309
|
+
/** Sender's device, when the relay stamped one — rides along so a refusal
|
|
310
|
+
* frame can name whose input was refused (inputDeviceId). */
|
|
311
|
+
deviceId?: string;
|
|
312
|
+
/** Exactly one of these is set. */
|
|
313
|
+
tokens: string[] | null;
|
|
314
|
+
text: string | null;
|
|
315
|
+
};
|
|
316
|
+
type InputCheck = {
|
|
317
|
+
ok: true;
|
|
318
|
+
input: ValidatedInput;
|
|
319
|
+
} | {
|
|
320
|
+
ok: false;
|
|
321
|
+
reason: string;
|
|
322
|
+
};
|
|
323
|
+
/**
|
|
324
|
+
* Parse and whitelist an inbound input message. Pure — the lease gate and the
|
|
325
|
+
* tmux-side guards stay in the caller, so this is testable on its own.
|
|
326
|
+
*/
|
|
327
|
+
export declare const validateInputMessage: (msg: AgentCommandInput) => InputCheck;
|
|
328
|
+
/** Ceiling on concurrent sender lanes. The lane key includes a relay-stamped
|
|
329
|
+
* deviceId, but a relay older than the unconditional stamp lets a sender vary
|
|
330
|
+
* it per message — without a cap that grows the map for the lease's whole
|
|
331
|
+
* life. Real senders are one per device; 16 is generous. */
|
|
332
|
+
export declare const MAX_INPUT_LANES = 16;
|
|
333
|
+
/** Ceiling on queued decrypts. The subscriber-key binding is a string compare
|
|
334
|
+
* against a public key the relay fanned out to every same-account connection,
|
|
335
|
+
* so it gates WHO can enqueue an ECDH derive, not how many — a flooding
|
|
336
|
+
* client would otherwise grow the chain without bound. Mirrors
|
|
337
|
+
* MAX_PENDING_INPUTS' role on the plaintext side. */
|
|
338
|
+
export declare const MAX_PENDING_DECRYPTS = 32;
|
|
339
|
+
/** Resolves once every encrypted input accepted so far has been routed. */
|
|
340
|
+
export declare const pendingInputDecrypts: () => Promise<void>;
|
|
341
|
+
/**
|
|
342
|
+
* Handle agent.command.input. Returns true when the message was ours to
|
|
343
|
+
* answer, so the caller stops routing it. Encrypted messages finish
|
|
344
|
+
* asynchronously — the return value reports routing, not delivery, exactly as
|
|
345
|
+
* it already did for a message the sequencer holds.
|
|
346
|
+
*/
|
|
347
|
+
export declare const handleCommandInput: (msg: AgentCommandInput, send: SendEphemeral) => boolean;
|
|
231
348
|
export interface CollectResult {
|
|
232
349
|
sessions: AgentSession[];
|
|
233
350
|
/** Diagnostic notes per rejected session — surfaced under `--verbose`. */
|
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;AAoBH,OAAO,EAA2B,KAAK,SAAS,EAAE,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AACzG,OAAO,EAAiD,KAAK,UAAU,EAA4C,MAAM,kBAAkB,CAAC;AAE5I,OAAO,
|
|
1
|
+
{"version":3,"file":"listener.d.ts","sourceRoot":"","sources":["../src/listener.ts"],"names":[],"mappings":"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"}
|