@zeph-to/cli 2.0.0 → 2.1.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/dist/agent-rules-fetch.d.ts.map +1 -1
- package/dist/agent-rules-fetch.js +7 -2
- package/dist/crypto.d.ts +15 -3
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +53 -5
- package/dist/input-sequencer.d.ts +65 -0
- package/dist/input-sequencer.d.ts.map +1 -0
- package/dist/input-sequencer.js +126 -0
- package/dist/listener.d.ts +123 -2
- package/dist/listener.d.ts.map +1 -1
- package/dist/listener.js +565 -40
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-rules-fetch.d.ts","sourceRoot":"","sources":["../src/agent-rules-fetch.ts"],"names":[],"mappings":"AAuBA,OAAO,EAEH,KAAK,iBAAiB,EACzB,MAAM,kBAAkB,CAAC;AAG1B,eAAO,MAAM,gBAAgB,QAAuC,CAAC;AACrE,eAAO,MAAM,yBAAyB,QAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-rules-fetch.d.ts","sourceRoot":"","sources":["../src/agent-rules-fetch.ts"],"names":[],"mappings":"AAuBA,OAAO,EAEH,KAAK,iBAAiB,EACzB,MAAM,kBAAkB,CAAC;AAG1B,eAAO,MAAM,gBAAgB,QAAuC,CAAC;AACrE,eAAO,MAAM,yBAAyB,QAAqB,CAAC;AAkD5D;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,OAAO,KAAG,iBAAiB,GAAG,IAWrE,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAU9D,CAAC;AAIF,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAK5D,eAAO,MAAM,iBAAiB,QAAO,iBAAmC,CAAC;AACzE,eAAO,MAAM,uBAAuB,QAAO,cAA8B,CAAC;AAS1E,iBAAiB;AACjB,eAAO,MAAM,mBAAmB,QAAO,IAEtC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAAO,cAWxC,CAAC;AAWF,eAAO,MAAM,QAAQ,QAAO,MAK3B,CAAC;AAEF,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,cAAc,CAAC;IACvB,6FAA6F;IAC7F,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAU,YAAW,OAAO,KAAa,KAAG,OAAO,CAAC,aAAa,CA4C5F,CAAC"}
|
|
@@ -55,11 +55,16 @@ const isRule = (v) => {
|
|
|
55
55
|
return false;
|
|
56
56
|
if (typeof r.state !== 'string' || !VALID_STATES.has(r.state))
|
|
57
57
|
return false;
|
|
58
|
-
|
|
58
|
+
// Safe integers, not merely finite. These numbers arrive from the server
|
|
59
|
+
// and decide behavior: a fractional priority orders rules by values no
|
|
60
|
+
// author can reason about, 1e21 outranks every rule ever written including
|
|
61
|
+
// the kill-switch, and a fractional tailLines slices a capture at a
|
|
62
|
+
// fraction of a line.
|
|
63
|
+
if (!Number.isSafeInteger(r.priority))
|
|
59
64
|
return false;
|
|
60
65
|
if (r.region !== undefined && r.region !== 'tail' && r.region !== 'whole')
|
|
61
66
|
return false;
|
|
62
|
-
if (r.tailLines !== undefined && (
|
|
67
|
+
if (r.tailLines !== undefined && (!Number.isSafeInteger(r.tailLines) || r.tailLines < 1 || r.tailLines > 200))
|
|
63
68
|
return false;
|
|
64
69
|
if (r.contains !== undefined && !isStringArray(r.contains))
|
|
65
70
|
return false;
|
package/dist/crypto.d.ts
CHANGED
|
@@ -122,10 +122,22 @@ export interface EncryptedEphemeralPayload extends EncryptedPayload {
|
|
|
122
122
|
*/
|
|
123
123
|
export declare const initDeviceCrypto: () => Promise<string>;
|
|
124
124
|
export declare const getDevicePublicKey: () => string | null;
|
|
125
|
+
export declare const encryptEphemeral: (plaintext: string, recipientPublicKeyRaw: string) => Promise<EncryptedEphemeralPayload>;
|
|
125
126
|
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
127
|
+
* Open an ephemeral envelope addressed to this device — the exact inverse of
|
|
128
|
+
* encryptEphemeral, and of the web's `encrypt` from @zeph/crypto, which
|
|
129
|
+
* produces the same five fields.
|
|
130
|
+
*
|
|
131
|
+
* Rejects rather than returning null: AES-GCM is authenticated, so a throw
|
|
132
|
+
* here means the envelope was sealed for another key, tampered with, or is not
|
|
133
|
+
* an envelope at all. Callers must treat every rejection as a refusal — there
|
|
134
|
+
* is no partial result to fall back on. Requires initDeviceCrypto().
|
|
135
|
+
*
|
|
136
|
+
* Note that opening an envelope proves only that its sender holds the private
|
|
137
|
+
* half of `senderPublicKey`; nothing signs that field, so it authenticates the
|
|
138
|
+
* key pairing and not the sender. A caller that needs to know *which* peer
|
|
139
|
+
* sent this must compare `senderPublicKey` against a key it already trusts.
|
|
128
140
|
*/
|
|
129
|
-
export declare const
|
|
141
|
+
export declare const decryptEphemeral: (payload: EncryptedEphemeralPayload) => Promise<string>;
|
|
130
142
|
export {};
|
|
131
143
|
//# sourceMappingURL=crypto.d.ts.map
|
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;
|
|
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;AA0ED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,EAAE,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,CAyB5E,CAAC;AAsCF,wDAAwD;AACxD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,mFAAmF;AACnF,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAElD;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,GAC3B,SAAS;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,KAClD,eAAe,EAG8C,CAAC;AA2CjE,eAAO,MAAM,UAAU,QAAO,aAAa,GAAG,IAAkD,CAAC;AACjG,eAAO,MAAM,YAAY,QAAO,MAAM,GAAG,IAA4D,CAAC;AAEtG;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,QAAO,IAEhC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,GACpC,OAAO;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EACtD,YAAY,eAAe,EAAE,KAC5B,OAAO,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,YAAY,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,IAAI,CAAC;CACnB,CAkBA,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAChC,SAAS,MAAM,GAAG,MAAM,EACxB,YAAY,eAAe,EAAE,KAC5B,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,YAAY,CAAA;CAAE,CAsBxE,CAAC;AAcF;;;;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;AAyB/E,eAAO,MAAM,gBAAgB,GAC3B,WAAW,MAAM,EACjB,uBAAuB,MAAM,KAC5B,OAAO,CAAC,yBAAyB,CAKnC,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,GAC3B,SAAS,yBAAyB,KACjC,OAAO,CAAC,MAAM,CAIhB,CAAC"}
|
package/dist/crypto.js
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* account keypair are gone.
|
|
30
30
|
*/
|
|
31
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
-
exports.encryptEphemeral = exports.getDevicePublicKey = exports.initDeviceCrypto = exports.encryptFileForDevices = exports.encryptPushBodyForDevices = exports.disableCrypto = exports.getPublicKey = exports.getKeyPair = exports.selectRecipients = exports.initCrypto = void 0;
|
|
32
|
+
exports.decryptEphemeral = exports.encryptEphemeral = exports.getDevicePublicKey = exports.initDeviceCrypto = exports.encryptFileForDevices = exports.encryptPushBodyForDevices = exports.disableCrypto = exports.getPublicKey = exports.getKeyPair = exports.selectRecipients = exports.initCrypto = void 0;
|
|
33
33
|
/// <reference lib="dom" />
|
|
34
34
|
const fs_1 = require("fs");
|
|
35
35
|
const os_1 = require("os");
|
|
@@ -76,11 +76,13 @@ const importKeyPair = async (exported) => {
|
|
|
76
76
|
return { publicKey, privateKey };
|
|
77
77
|
};
|
|
78
78
|
const deriveAesKey = async (privateKey, publicKey) => crypto.subtle.deriveKey({ name: 'ECDH', public: publicKey }, privateKey, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']);
|
|
79
|
-
const encrypt = async (plaintext, senderPrivateKey, recipientPublicKey
|
|
79
|
+
const encrypt = async (plaintext, senderPrivateKey, recipientPublicKey,
|
|
80
|
+
/** Pre-derived ECDH secret for this pair, when the caller keeps one. */
|
|
81
|
+
shared) => {
|
|
80
82
|
const messageKey = await crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']);
|
|
81
83
|
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
82
84
|
const ciphertext = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, messageKey, new TextEncoder().encode(plaintext));
|
|
83
|
-
const sharedKey = await deriveAesKey(senderPrivateKey, recipientPublicKey);
|
|
85
|
+
const sharedKey = shared ?? await deriveAesKey(senderPrivateKey, recipientPublicKey);
|
|
84
86
|
const rawMessageKey = await crypto.subtle.exportKey('raw', messageKey);
|
|
85
87
|
const keyIv = crypto.getRandomValues(new Uint8Array(12));
|
|
86
88
|
const encryptedKey = await crypto.subtle.encrypt({ name: 'AES-GCM', iv: keyIv }, sharedKey, rawMessageKey);
|
|
@@ -91,6 +93,15 @@ const encrypt = async (plaintext, senderPrivateKey, recipientPublicKey) => {
|
|
|
91
93
|
keyIv: toBase64(keyIv.buffer),
|
|
92
94
|
};
|
|
93
95
|
};
|
|
96
|
+
const decrypt = async (payload, recipientPrivateKey, senderPublicKey,
|
|
97
|
+
/** Pre-derived ECDH secret for this pair, when the caller keeps one. */
|
|
98
|
+
shared) => {
|
|
99
|
+
const sharedKey = shared ?? await deriveAesKey(recipientPrivateKey, senderPublicKey);
|
|
100
|
+
const rawMessageKey = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: new Uint8Array(fromBase64(payload.keyIv)) }, sharedKey, fromBase64(payload.encryptedKey));
|
|
101
|
+
const messageKey = await crypto.subtle.importKey('raw', rawMessageKey, { name: 'AES-GCM' }, false, ['decrypt']);
|
|
102
|
+
const plaintext = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: new Uint8Array(fromBase64(payload.iv)) }, messageKey, fromBase64(payload.ciphertext));
|
|
103
|
+
return new TextDecoder().decode(plaintext);
|
|
104
|
+
};
|
|
94
105
|
// ─── Superseded account keystore ───
|
|
95
106
|
//
|
|
96
107
|
// Held the escrowed account keypair. Nothing reads it any more; it is deleted
|
|
@@ -334,11 +345,48 @@ exports.getDevicePublicKey = getDevicePublicKey;
|
|
|
334
345
|
* Encrypt an ephemeral payload (e.g. a stream frame) for one recipient
|
|
335
346
|
* device. Requires initDeviceCrypto() to have completed.
|
|
336
347
|
*/
|
|
348
|
+
/**
|
|
349
|
+
* One imported peer key plus the ECDH secret derived with it. A stream talks to
|
|
350
|
+
* exactly one subscriber, and the input half binds to that same key, so both
|
|
351
|
+
* directions reuse one entry for the stream's life — deriving per frame AND per
|
|
352
|
+
* keystroke was two WebCrypto operations repeating the same answer.
|
|
353
|
+
*/
|
|
354
|
+
let peerCache = null;
|
|
355
|
+
const peerFor = async (raw, privateKey) => {
|
|
356
|
+
if (peerCache?.b64 === raw)
|
|
357
|
+
return peerCache;
|
|
358
|
+
const key = await importPublicKey(raw);
|
|
359
|
+
const shared = await deriveAesKey(privateKey, key);
|
|
360
|
+
peerCache = { b64: raw, key, shared };
|
|
361
|
+
return peerCache;
|
|
362
|
+
};
|
|
337
363
|
const encryptEphemeral = async (plaintext, recipientPublicKeyRaw) => {
|
|
338
364
|
if (!deviceKeyPair || !deviceExportedPublicKey)
|
|
339
365
|
throw new Error('Device crypto not initialized');
|
|
340
|
-
const
|
|
341
|
-
const payload = await encrypt(plaintext, deviceKeyPair.privateKey,
|
|
366
|
+
const peer = await peerFor(recipientPublicKeyRaw, deviceKeyPair.privateKey);
|
|
367
|
+
const payload = await encrypt(plaintext, deviceKeyPair.privateKey, peer.key, peer.shared);
|
|
342
368
|
return { ...payload, senderPublicKey: deviceExportedPublicKey };
|
|
343
369
|
};
|
|
344
370
|
exports.encryptEphemeral = encryptEphemeral;
|
|
371
|
+
/**
|
|
372
|
+
* Open an ephemeral envelope addressed to this device — the exact inverse of
|
|
373
|
+
* encryptEphemeral, and of the web's `encrypt` from @zeph/crypto, which
|
|
374
|
+
* produces the same five fields.
|
|
375
|
+
*
|
|
376
|
+
* Rejects rather than returning null: AES-GCM is authenticated, so a throw
|
|
377
|
+
* here means the envelope was sealed for another key, tampered with, or is not
|
|
378
|
+
* an envelope at all. Callers must treat every rejection as a refusal — there
|
|
379
|
+
* is no partial result to fall back on. Requires initDeviceCrypto().
|
|
380
|
+
*
|
|
381
|
+
* Note that opening an envelope proves only that its sender holds the private
|
|
382
|
+
* half of `senderPublicKey`; nothing signs that field, so it authenticates the
|
|
383
|
+
* key pairing and not the sender. A caller that needs to know *which* peer
|
|
384
|
+
* sent this must compare `senderPublicKey` against a key it already trusts.
|
|
385
|
+
*/
|
|
386
|
+
const decryptEphemeral = async (payload) => {
|
|
387
|
+
if (!deviceKeyPair)
|
|
388
|
+
throw new Error('Device crypto not initialized');
|
|
389
|
+
const peer = await peerFor(payload.senderPublicKey, deviceKeyPair.privateKey);
|
|
390
|
+
return decrypt(payload, deviceKeyPair.privateKey, peer.key, peer.shared);
|
|
391
|
+
};
|
|
392
|
+
exports.decryptEphemeral = decryptEphemeral;
|
|
@@ -0,0 +1,65 @@
|
|
|
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 INPUT_HOLD_MS, 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
|
+
schedule?: ScheduleFlush;
|
|
52
|
+
/** Called for each HELD message dropped without delivery — an epoch change
|
|
53
|
+
* or reset() sweeping the buffer. Accept-time drops are reported via the
|
|
54
|
+
* AcceptResult instead, so no message can vanish through both cracks. */
|
|
55
|
+
onDiscard?: (msg: T) => void;
|
|
56
|
+
};
|
|
57
|
+
export type InputSequencer<T extends SequencedInput> = {
|
|
58
|
+
accept: (msg: T) => AcceptResult;
|
|
59
|
+
/** Drop everything held (reported via onDiscard) and forget the epoch —
|
|
60
|
+
* the run is over. */
|
|
61
|
+
reset: () => void;
|
|
62
|
+
};
|
|
63
|
+
export declare const createInputSequencer: <T extends SequencedInput>(deliver: (msg: T) => void, opts?: InputSequencerOptions<T>) => InputSequencer<T>;
|
|
64
|
+
export {};
|
|
65
|
+
//# 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,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,CAkFlB,CAAC"}
|
|
@@ -0,0 +1,126 @@
|
|
|
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 INPUT_HOLD_MS, 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 schedule = opts.schedule ?? defaultSchedule;
|
|
45
|
+
let epoch = null;
|
|
46
|
+
let highWater = 0;
|
|
47
|
+
let cancelFlush = null;
|
|
48
|
+
const pending = new Map();
|
|
49
|
+
const disarm = () => {
|
|
50
|
+
cancelFlush?.();
|
|
51
|
+
cancelFlush = null;
|
|
52
|
+
};
|
|
53
|
+
/** Sweep the hold buffer without delivering — each swept message is a
|
|
54
|
+
* keystroke its sender still waits on, so each is reported. */
|
|
55
|
+
const discardPending = () => {
|
|
56
|
+
const swept = [...pending.values()];
|
|
57
|
+
pending.clear();
|
|
58
|
+
disarm();
|
|
59
|
+
for (const msg of swept)
|
|
60
|
+
opts.onDiscard?.(msg);
|
|
61
|
+
};
|
|
62
|
+
const emit = (msg) => {
|
|
63
|
+
highWater = msg.seq;
|
|
64
|
+
deliver(msg);
|
|
65
|
+
};
|
|
66
|
+
/** Release the unbroken run sitting on top of the high-water mark. */
|
|
67
|
+
const drain = () => {
|
|
68
|
+
for (;;) {
|
|
69
|
+
const next = pending.get(highWater + 1);
|
|
70
|
+
if (!next)
|
|
71
|
+
break;
|
|
72
|
+
pending.delete(next.seq);
|
|
73
|
+
emit(next);
|
|
74
|
+
}
|
|
75
|
+
if (!pending.size)
|
|
76
|
+
disarm();
|
|
77
|
+
};
|
|
78
|
+
/** Hold expired: the missing seq is not coming. Deliver the rest in seq
|
|
79
|
+
* order, which also moves the high-water mark past the hole. */
|
|
80
|
+
const flush = () => {
|
|
81
|
+
cancelFlush = null;
|
|
82
|
+
const held = [...pending.values()].sort((a, b) => a.seq - b.seq);
|
|
83
|
+
pending.clear();
|
|
84
|
+
for (const msg of held)
|
|
85
|
+
emit(msg);
|
|
86
|
+
};
|
|
87
|
+
const accept = (msg) => {
|
|
88
|
+
if (epoch === null || msg.epoch > epoch) {
|
|
89
|
+
// First message of a run — whatever seq it carries is in order by
|
|
90
|
+
// definition, so joining a stream mid-flight costs no hold.
|
|
91
|
+
discardPending();
|
|
92
|
+
epoch = msg.epoch;
|
|
93
|
+
highWater = msg.seq - 1;
|
|
94
|
+
}
|
|
95
|
+
else if (msg.epoch < epoch) {
|
|
96
|
+
return 'superseded';
|
|
97
|
+
}
|
|
98
|
+
if (msg.seq <= highWater || pending.has(msg.seq))
|
|
99
|
+
return 'stale';
|
|
100
|
+
if (msg.seq === highWater + 1) {
|
|
101
|
+
emit(msg);
|
|
102
|
+
drain();
|
|
103
|
+
return 'ok';
|
|
104
|
+
}
|
|
105
|
+
// Drop the NEW message, not a held one: the buffer already holds the
|
|
106
|
+
// keys closest to the gap, and evicting those would reorder what is
|
|
107
|
+
// about to be typed.
|
|
108
|
+
if (pending.size >= exports.MAX_PENDING_INPUTS)
|
|
109
|
+
return 'overflow';
|
|
110
|
+
pending.set(msg.seq, msg);
|
|
111
|
+
// One deadline per gap: it dates from when the hole opened, so a
|
|
112
|
+
// steady stream of later keys can't extend the wait indefinitely.
|
|
113
|
+
if (!cancelFlush)
|
|
114
|
+
cancelFlush = schedule(flush, exports.INPUT_HOLD_MS);
|
|
115
|
+
return 'ok';
|
|
116
|
+
};
|
|
117
|
+
return {
|
|
118
|
+
accept,
|
|
119
|
+
reset: () => {
|
|
120
|
+
discardPending();
|
|
121
|
+
epoch = null;
|
|
122
|
+
highWater = 0;
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
exports.createInputSequencer = createInputSequencer;
|
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,33 @@ 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
|
+
/** Is there budget left this second? A peek, so a tick can decline to pay for a
|
|
208
|
+
* blocking capture it could not send anyway — spending the token here instead
|
|
209
|
+
* would burn budget on ticks the diff-gate goes on to skip. */
|
|
210
|
+
export declare const hasFrameBudget: (budget: FrameBudget, now: number) => boolean;
|
|
211
|
+
export declare const claimFrameSend: (budget: FrameBudget, now: number) => boolean;
|
|
184
212
|
export declare const STREAM_LEASE_MS = 15000;
|
|
185
213
|
export declare const MAX_CONCURRENT_STREAMS = 3;
|
|
186
214
|
export declare const stopStream: (sessionName: string) => void;
|
|
@@ -201,11 +229,23 @@ export type StreamFramePayload = {
|
|
|
201
229
|
/** E2EE envelope — replaces `content` on encrypted streams. */
|
|
202
230
|
encrypted?: EncryptedEphemeralPayload;
|
|
203
231
|
};
|
|
204
|
-
/** Wire shape of a live-mirror error frame — same subtype, no pane data.
|
|
232
|
+
/** Wire shape of a live-mirror error frame — same subtype, no pane data.
|
|
233
|
+
* `input_rejected` refuses an ephemeral `agent.command.input`; it rides this
|
|
234
|
+
* frame so a viewer already handling stream errors can fall back to the REST
|
|
235
|
+
* push path without a second error channel. */
|
|
205
236
|
export type StreamErrorFrame = {
|
|
206
237
|
subtype: 'agent.stream.frame';
|
|
207
238
|
sessionName: string;
|
|
208
|
-
error: 'unknown_session' | 'e2ee_unavailable' | 'encrypt_failed' | 'stream_limit' | 'stream_gone';
|
|
239
|
+
error: 'unknown_session' | 'e2ee_unavailable' | 'encrypt_failed' | 'stream_limit' | 'stream_gone' | 'input_rejected';
|
|
240
|
+
/** `input_rejected` only: the refused message's ordering stamp echoed back.
|
|
241
|
+
* A sender with several keystrokes in flight can't tell which one was
|
|
242
|
+
* refused without it. Absent when the message carried no usable stamp. */
|
|
243
|
+
seq?: number;
|
|
244
|
+
epoch?: number;
|
|
245
|
+
/** `input_rejected` only: the refused sender's deviceId, echoed so a second
|
|
246
|
+
* device whose (seq, epoch) happens to collide doesn't claim the refusal
|
|
247
|
+
* and fire a REST resend for an input that was never refused. */
|
|
248
|
+
inputDeviceId?: string;
|
|
209
249
|
};
|
|
210
250
|
/**
|
|
211
251
|
* Per-listener concurrency guard, as a pure predicate so the boundary is
|
|
@@ -228,6 +268,87 @@ export declare const buildStreamFrame: (captured: {
|
|
|
228
268
|
* path). start is idempotent — a repeat restarts the loop.
|
|
229
269
|
*/
|
|
230
270
|
export declare const handleStreamControl: (req: StreamControl, send: (data: Record<string, unknown>) => void) => boolean;
|
|
271
|
+
/** Wire shape of one inbound injection. Every field is optional, and none of
|
|
272
|
+
* them is proven: this shape is an unchecked cast over relay JSON, so
|
|
273
|
+
* validateInputMessage re-checks the types it acts on rather than trusting
|
|
274
|
+
* the declaration. */
|
|
275
|
+
export interface AgentCommandInput {
|
|
276
|
+
subtype?: string;
|
|
277
|
+
targetDeviceId?: string;
|
|
278
|
+
sessionName?: string;
|
|
279
|
+
/** Which device's seq/epoch run this is. Current relays stamp it from the
|
|
280
|
+
* sending connection unconditionally; relays older than that overwrite
|
|
281
|
+
* only a missing value, so a sender there can claim another device's id.
|
|
282
|
+
* Treat it as an ordering namespace, never as authentication — the worst
|
|
283
|
+
* a forged id buys is a different reorder lane for its own keystrokes. */
|
|
284
|
+
deviceId?: string;
|
|
285
|
+
/** Named keys, ALLOWED_KEYS-validated. Mutually exclusive with `body`. */
|
|
286
|
+
keys?: string[];
|
|
287
|
+
/** Literal text; the injector appends the Enter, as on the REST path. */
|
|
288
|
+
body?: string;
|
|
289
|
+
/** Sender's monotonic counter, and the incarnation it counts within. */
|
|
290
|
+
seq?: number;
|
|
291
|
+
epoch?: number;
|
|
292
|
+
/** E2EE envelope sealing `{ keys | body }` plus a copy of the
|
|
293
|
+
* `sessionName`/`seq`/`epoch` stamp as JSON — the only way in on an
|
|
294
|
+
* encrypted stream, where `keys`/`body` above are refused. Present means
|
|
295
|
+
* the plaintext pair is ignored entirely: on a message the relay can
|
|
296
|
+
* append to, only the ciphertext decides what gets typed, and the sealed
|
|
297
|
+
* stamp is what proves the plaintext one was not re-written for a replay. */
|
|
298
|
+
encrypted?: unknown;
|
|
299
|
+
}
|
|
300
|
+
/** Parity with the REST path, which refuses more than MAX_KEYS_PER_COMMAND per
|
|
301
|
+
* agent.command (zeph apps/server/src/functions/pushes.ts). The lower-latency
|
|
302
|
+
* door into the same pane must not also be the wider one. */
|
|
303
|
+
export declare const MAX_INPUT_KEYS = 10;
|
|
304
|
+
/** The REST path caps no body length, so this bound is ours alone: an
|
|
305
|
+
* unbounded body is one tmux send-keys argv of unbounded size, and what it
|
|
306
|
+
* lands in is an agent prompt, not a paste buffer. */
|
|
307
|
+
export declare const MAX_INPUT_BODY_CHARS = 4096;
|
|
308
|
+
/** The relay socket a message arrived on, and the only way back to its sender. */
|
|
309
|
+
type SendEphemeral = (data: Record<string, unknown>) => void;
|
|
310
|
+
/** One validated injection. */
|
|
311
|
+
type ValidatedInput = SequencedInput & {
|
|
312
|
+
sessionName: string;
|
|
313
|
+
/** Sender's device, when the relay stamped one — rides along so a refusal
|
|
314
|
+
* frame can name whose input was refused (inputDeviceId). */
|
|
315
|
+
deviceId?: string;
|
|
316
|
+
/** Exactly one of these is set. */
|
|
317
|
+
tokens: string[] | null;
|
|
318
|
+
text: string | null;
|
|
319
|
+
};
|
|
320
|
+
type InputCheck = {
|
|
321
|
+
ok: true;
|
|
322
|
+
input: ValidatedInput;
|
|
323
|
+
} | {
|
|
324
|
+
ok: false;
|
|
325
|
+
reason: string;
|
|
326
|
+
};
|
|
327
|
+
/**
|
|
328
|
+
* Parse and whitelist an inbound input message. Pure — the lease gate and the
|
|
329
|
+
* tmux-side guards stay in the caller, so this is testable on its own.
|
|
330
|
+
*/
|
|
331
|
+
export declare const validateInputMessage: (msg: AgentCommandInput) => InputCheck;
|
|
332
|
+
/** Ceiling on concurrent sender lanes. The lane key includes a relay-stamped
|
|
333
|
+
* deviceId, but a relay older than the unconditional stamp lets a sender vary
|
|
334
|
+
* it per message — without a cap that grows the map for the lease's whole
|
|
335
|
+
* life. Real senders are one per device; 16 is generous. */
|
|
336
|
+
export declare const MAX_INPUT_LANES = 16;
|
|
337
|
+
/** Ceiling on queued decrypts. The subscriber-key binding is a string compare
|
|
338
|
+
* against a public key the relay fanned out to every same-account connection,
|
|
339
|
+
* so it gates WHO can enqueue an ECDH derive, not how many — a flooding
|
|
340
|
+
* client would otherwise grow the chain without bound. Mirrors
|
|
341
|
+
* MAX_PENDING_INPUTS' role on the plaintext side. */
|
|
342
|
+
export declare const MAX_PENDING_DECRYPTS = 32;
|
|
343
|
+
/** Resolves once every encrypted input accepted so far has been routed. */
|
|
344
|
+
export declare const pendingInputDecrypts: () => Promise<void>;
|
|
345
|
+
/**
|
|
346
|
+
* Handle agent.command.input. Returns true when the message was ours to
|
|
347
|
+
* answer, so the caller stops routing it. Encrypted messages finish
|
|
348
|
+
* asynchronously — the return value reports routing, not delivery, exactly as
|
|
349
|
+
* it already did for a message the sequencer holds.
|
|
350
|
+
*/
|
|
351
|
+
export declare const handleCommandInput: (msg: AgentCommandInput, send: SendEphemeral) => boolean;
|
|
231
352
|
export interface CollectResult {
|
|
232
353
|
sessions: AgentSession[];
|
|
233
354
|
/** 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":"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,EAA4E,KAAK,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACvI,OAAO,EAA6C,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAmCtG,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAElD,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;IACxB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAAI,UAAU,YAAY,EAAE,KAAG,MAS7C,CAAC;AAEnB;6BAC6B;AAC7B,eAAO,MAAM,iBAAiB,GAC1B,aAAa,MAAM,EACnB,qBAAqB,MAAM,GAAG,IAAI,EAClC,cAAc,MAAM,EACpB,OAAO,MAAM,KACd,OAEoD,CAAC;AAYxD,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAA+B,CAAC;AAenF,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;AAyCF;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,EAAE,KAAG,MAAM,EAAE,GAAG,IAQvD,CAAC;AA0CF;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,QAAO,IAG5C,CAAC;AA8NF;;;;;;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;AAEF,UAAU,QAAQ;IACd,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAiDD;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,QAAQ,KAAG,qBAAqB,GAAG,IAGhE,CAAC;AA0BZ,iBAAiB;AACjB,eAAO,MAAM,kBAAkB,QAAO,IAErC,CAAC;AAWF;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAC3B,MAAM,MAAM,EACZ,WAAW,SAAS,EACpB,UAAU,MAAM,GAAG,IAAI,EACvB,MAAK,MAAmB,KACzB,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,gBAAgB,GAAG,aAAa,CAmB/D,CAAC;AAWF,MAAM,WAAW,YAAY;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACnB;AASD,8EAA8E;AAC9E,eAAO,MAAM,iBAAiB,GAAI,KAAK,OAAO,KAAG,IAWhD,CAAC;AAEF,iBAAiB;AACjB,eAAO,MAAM,mBAAmB,QAAO,IAGtC,CAAC;AAEF,MAAM,WAAW,QAAQ;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GACzB,WAAW,WAAW,CAAC,MAAM,CAAC,EAC9B,UAAS,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAsB,KAC9D,QAAQ,EAgBV,CAAC;AAWF,MAAM,WAAW,aAAa;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,uBAAuB,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,GAAI,KAAK,aAAa,KAAG,cAAc,GAAG,IAmBzE,CAAC;AAyCF;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,GAChC,aAAa,MAAM,EACnB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,EAC7C,SAAQ,MAAM,EAA8B,KAC7C,IAuBF,CAAC;AAcF,MAAM,WAAW,aAAa;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AASD,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAKtC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,eAAe,OAAQ,CAAC;AAOrC,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,aAAa,MAAM,GAAG,IAAI,EAAE,KAAK,MAAM,KAAG,MACgC,CAAC;AAEzG,0DAA0D;AAC1D,MAAM,WAAW,WAAW;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH;;gEAEgE;AAChE,eAAO,MAAM,cAAc,GAAI,QAAQ,WAAW,EAAE,KAAK,MAAM,KAAG,OAGxB,CAAC;AAE3C,eAAO,MAAM,cAAc,GAAI,QAAQ,WAAW,EAAE,KAAK,MAAM,KAAG,OAYjE,CAAC;AAqBF,eAAO,MAAM,eAAe,QAAS,CAAC;AAMtC,eAAO,MAAM,sBAAsB,IAAI,CAAC;AA6GxC,eAAO,MAAM,UAAU,GAAI,aAAa,MAAM,KAAG,IAuBhD,CAAC;AAEF,eAAO,MAAM,cAAc,QAAO,IAGjC,CAAC;AAEF,oFAAoF;AACpF,MAAM,MAAM,kBAAkB,GAAG;IAC7B,OAAO,EAAE,oBAAoB,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,4EAA4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;mEAC+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,yBAAyB,CAAC;CACzC,CAAC;AAEF;;;gDAGgD;AAChD,MAAM,MAAM,gBAAgB,GAAG;IAC3B,OAAO,EAAE,oBAAoB,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,iBAAiB,GAAG,kBAAkB,GAAG,gBAAgB,GAAG,cAAc,GAAG,aAAa,GAAG,gBAAgB,CAAC;IACrH;;+EAE2E;IAC3E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;sEAEkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAAI,aAAa,MAAM,KAAG,OAChB,CAAC;AAqC1C;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GACzB,UAAU;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,EACjD,aAAa,MAAM,EACnB,sBAAsB,MAAM,KAC7B,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAenC,CAAC;AAEF;;;;GAIG;AASH,eAAO,MAAM,mBAAmB,GAC5B,KAAK,aAAa,EAClB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAC9C,OA8NF,CAAC;AAWF;;;uBAGuB;AACvB,MAAM,WAAW,iBAAiB;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;+EAI2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;kFAK8E;IAC9E,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;8DAE8D;AAC9D,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC;;uDAEuD;AACvD,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,kFAAkF;AAClF,KAAK,aAAa,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;AAE7D,+BAA+B;AAC/B,KAAK,cAAc,GAAG,cAAc,GAAG;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB;kEAC8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAOF,KAAK,UAAU,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAuCtF;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,iBAAiB,KAAG,UA4B7D,CAAC;AAoBF;;;6DAG6D;AAC7D,eAAO,MAAM,eAAe,KAAK,CAAC;AA6ClC;;;;sDAIsD;AACtD,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,QAAO,OAAO,CAAC,IAAI,CAAsB,CAAC;AA4G3E;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC3B,KAAK,iBAAiB,EACtB,MAAM,aAAa,KACpB,OAqCF,CAAC;AAEF,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,aAiEzC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,QAAO,YAAY,EAAuC,CAAC;AAIvF;;;;;GAKG;AACH,UAAU,kBAAkB;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,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;IAC1B;sEACkE;IAClE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,uEAAuE;IACvE,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAChC;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,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC;IAC1D,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IACzC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,oEAAoE;IACpE,mBAAmB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACzF,+DAA+D;IAC/D,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC7C;sEACkE;IAClE,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AA2BD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAC1B,SAAS,MAAM,EACf,MAAM,MAAM,EACZ,MAAK,MAAM,MAAiB,KAC7B,OAUF,CAAC;AA0CF,eAAO,MAAM,oBAAoB,GAAI,KAAK;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,KAAG,IAE/E,CAAC;AA2FF;;;;GAIG;AACH,eAAO,MAAM,aAAa,GACtB,MAAK,MAAmB,EACxB,MAAK,MAAwB,EAC7B,MAAK,MAA0B,KAChC,MAaF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,GACnB,MAAM,QAAQ,EACd,OAAM,cAAmB,KAC1B,OAAO,CAAC,OAAO,CAoCjB,CAAC;AAcF,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,KAAG,MAIhD,CAAC;AA4DF,eAAO,MAAM,uBAAuB,GAAI,OAAO,MAAM,KAAG,MA2BvD,CAAC;AAsPF;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GACrB,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,EACtC,QAAQ;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAC1B,SAAS,MAAM,KAChB,MAAM,GAAG,IAIX,CAAC;AA0DF,eAAO,MAAM,cAAc,GAAU,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAG,OAAO,CAAC,MAAM,CAsI3F,CAAC"}
|