@agentdeck/bridge 1.1.0 → 1.2.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 +14 -6
- package/assets/fonts/NOTICE-PocketSansWorld.md +10 -0
- package/assets/fonts/OFL-PocketSansWorld.txt +93 -0
- package/assets/fonts/PocketSansWorld_12.cpfont +0 -0
- package/assets/fonts/pocket-sans-world.manifest.json +49 -0
- package/assets/learning/LICENSE-CC-BY-SA-4.0.txt +10 -0
- package/assets/learning/NOTICE.md +33 -0
- package/assets/learning/jp-n3-ko.manifest.json +27 -0
- package/assets/learning/jp-n3-ko.pdl +0 -0
- package/dist/agent-cli-diagnostics.d.ts +20 -0
- package/dist/agent-cli-diagnostics.d.ts.map +1 -0
- package/dist/agent-cli-diagnostics.js +100 -0
- package/dist/agent-cli-diagnostics.js.map +1 -0
- package/dist/apme/classifier.d.ts.map +1 -1
- package/dist/apme/classifier.js +18 -10
- package/dist/apme/classifier.js.map +1 -1
- package/dist/apme/claude-transcript-reader.d.ts +36 -0
- package/dist/apme/claude-transcript-reader.d.ts.map +1 -1
- package/dist/apme/claude-transcript-reader.js +78 -2
- package/dist/apme/claude-transcript-reader.js.map +1 -1
- package/dist/apme/collector.d.ts +126 -3
- package/dist/apme/collector.d.ts.map +1 -1
- package/dist/apme/collector.js +450 -41
- package/dist/apme/collector.js.map +1 -1
- package/dist/apme/dashboard-html.d.ts.map +1 -1
- package/dist/apme/dashboard-html.js +278 -36
- package/dist/apme/dashboard-html.js.map +1 -1
- package/dist/apme/graph.d.ts +1 -1
- package/dist/apme/graph.d.ts.map +1 -1
- package/dist/apme/graph.js +51 -1
- package/dist/apme/graph.js.map +1 -1
- package/dist/apme/http.d.ts.map +1 -1
- package/dist/apme/http.js +37 -5
- package/dist/apme/http.js.map +1 -1
- package/dist/apme/index.d.ts +4 -0
- package/dist/apme/index.d.ts.map +1 -1
- package/dist/apme/index.js +19 -3
- package/dist/apme/index.js.map +1 -1
- package/dist/apme/runner.d.ts +90 -0
- package/dist/apme/runner.d.ts.map +1 -1
- package/dist/apme/runner.js +524 -48
- package/dist/apme/runner.js.map +1 -1
- package/dist/apme/sample-to-timeline.d.ts.map +1 -1
- package/dist/apme/sample-to-timeline.js +5 -0
- package/dist/apme/sample-to-timeline.js.map +1 -1
- package/dist/apme/settings.d.ts +8 -0
- package/dist/apme/settings.d.ts.map +1 -1
- package/dist/apme/settings.js +4 -0
- package/dist/apme/settings.js.map +1 -1
- package/dist/apme/store.d.ts +118 -9
- package/dist/apme/store.d.ts.map +1 -1
- package/dist/apme/store.js +342 -44
- package/dist/apme/store.js.map +1 -1
- package/dist/apme/task-gradeability.d.ts +82 -0
- package/dist/apme/task-gradeability.d.ts.map +1 -0
- package/dist/apme/task-gradeability.js +126 -0
- package/dist/apme/task-gradeability.js.map +1 -0
- package/dist/awaiting-overlay.d.ts +13 -26
- package/dist/awaiting-overlay.d.ts.map +1 -1
- package/dist/awaiting-overlay.js +7 -40
- package/dist/awaiting-overlay.js.map +1 -1
- package/dist/bridge-core.d.ts +15 -0
- package/dist/bridge-core.d.ts.map +1 -1
- package/dist/bridge-core.js +30 -4
- package/dist/bridge-core.js.map +1 -1
- package/dist/claude-permission-rules.d.ts +20 -32
- package/dist/claude-permission-rules.d.ts.map +1 -1
- package/dist/claude-permission-rules.js +19 -102
- package/dist/claude-permission-rules.js.map +1 -1
- package/dist/claude-usage-recovery.d.ts +54 -0
- package/dist/claude-usage-recovery.d.ts.map +1 -0
- package/dist/claude-usage-recovery.js +200 -0
- package/dist/claude-usage-recovery.js.map +1 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +81 -14
- package/dist/cli.js.map +1 -1
- package/dist/codex-rate-limits-live.d.ts +180 -10
- package/dist/codex-rate-limits-live.d.ts.map +1 -1
- package/dist/codex-rate-limits-live.js +475 -22
- package/dist/codex-rate-limits-live.js.map +1 -1
- package/dist/codex-rollout-response.d.ts +43 -0
- package/dist/codex-rollout-response.d.ts.map +1 -1
- package/dist/codex-rollout-response.js +67 -0
- package/dist/codex-rollout-response.js.map +1 -1
- package/dist/coordination-evidence.d.ts +150 -0
- package/dist/coordination-evidence.d.ts.map +1 -0
- package/dist/coordination-evidence.js +455 -0
- package/dist/coordination-evidence.js.map +1 -0
- package/dist/daemon-server.d.ts.map +1 -1
- package/dist/daemon-server.js +592 -35
- package/dist/daemon-server.js.map +1 -1
- package/dist/daemon-ws-client.d.ts +1 -0
- package/dist/daemon-ws-client.d.ts.map +1 -1
- package/dist/daemon-ws-client.js +25 -16
- package/dist/daemon-ws-client.js.map +1 -1
- package/dist/daemon.js +1 -1
- package/dist/deferred-reply-read.d.ts +24 -0
- package/dist/deferred-reply-read.d.ts.map +1 -0
- package/dist/deferred-reply-read.js +42 -0
- package/dist/deferred-reply-read.js.map +1 -0
- package/dist/esp32-serial.d.ts +15 -0
- package/dist/esp32-serial.d.ts.map +1 -1
- package/dist/esp32-serial.js +61 -2
- package/dist/esp32-serial.js.map +1 -1
- package/dist/font-pack.d.ts +15 -0
- package/dist/font-pack.d.ts.map +1 -0
- package/dist/font-pack.js +75 -0
- package/dist/font-pack.js.map +1 -0
- package/dist/glance-frame.d.ts.map +1 -1
- package/dist/glance-frame.js +2 -0
- package/dist/glance-frame.js.map +1 -1
- package/dist/hook-codex-sessions.d.ts +9 -0
- package/dist/hook-codex-sessions.d.ts.map +1 -1
- package/dist/hook-codex-sessions.js +35 -2
- package/dist/hook-codex-sessions.js.map +1 -1
- package/dist/hook-opencode-sessions.d.ts +62 -0
- package/dist/hook-opencode-sessions.d.ts.map +1 -0
- package/dist/hook-opencode-sessions.js +180 -0
- package/dist/hook-opencode-sessions.js.map +1 -0
- package/dist/hook-response-source.d.ts +40 -0
- package/dist/hook-response-source.d.ts.map +1 -0
- package/dist/hook-response-source.js +31 -0
- package/dist/hook-response-source.js.map +1 -0
- package/dist/http-auth-gate.d.ts.map +1 -1
- package/dist/http-auth-gate.js +9 -1
- package/dist/http-auth-gate.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/learning-pack.d.ts +24 -0
- package/dist/learning-pack.d.ts.map +1 -0
- package/dist/learning-pack.js +125 -0
- package/dist/learning-pack.js.map +1 -0
- package/dist/mdns.js +1 -1
- package/dist/mdns.js.map +1 -1
- package/dist/native-dependency-diagnostics.d.ts +29 -0
- package/dist/native-dependency-diagnostics.d.ts.map +1 -0
- package/dist/native-dependency-diagnostics.js +95 -0
- package/dist/native-dependency-diagnostics.js.map +1 -0
- package/dist/observed-steering.d.ts +4 -4
- package/dist/observed-steering.d.ts.map +1 -1
- package/dist/observed-steering.js +28 -32
- package/dist/observed-steering.js.map +1 -1
- package/dist/openclaw-gateway-stability.d.ts +94 -0
- package/dist/openclaw-gateway-stability.d.ts.map +1 -0
- package/dist/openclaw-gateway-stability.js +176 -0
- package/dist/openclaw-gateway-stability.js.map +1 -0
- package/dist/pairing-knocks.d.ts +80 -0
- package/dist/pairing-knocks.d.ts.map +1 -0
- package/dist/pairing-knocks.js +312 -0
- package/dist/pairing-knocks.js.map +1 -0
- package/dist/passive-observer.d.ts +5 -0
- package/dist/passive-observer.d.ts.map +1 -1
- package/dist/passive-observer.js +6 -0
- package/dist/passive-observer.js.map +1 -1
- package/dist/pixoo/micro-glyphs.d.ts +4 -0
- package/dist/pixoo/micro-glyphs.d.ts.map +1 -1
- package/dist/pixoo/micro-glyphs.js +29 -0
- package/dist/pixoo/micro-glyphs.js.map +1 -1
- package/dist/pixoo/pixoo-font.d.ts.map +1 -1
- package/dist/pixoo/pixoo-font.js +3 -1
- package/dist/pixoo/pixoo-font.js.map +1 -1
- package/dist/pixoo/pixoo-renderer.d.ts +1 -1
- package/dist/pixoo/pixoo-renderer.d.ts.map +1 -1
- package/dist/pixoo/pixoo-renderer.js +17 -2
- package/dist/pixoo/pixoo-renderer.js.map +1 -1
- package/dist/pty-manager.d.ts +9 -0
- package/dist/pty-manager.d.ts.map +1 -1
- package/dist/pty-manager.js +63 -4
- package/dist/pty-manager.js.map +1 -1
- package/dist/relayed-usage.d.ts +30 -0
- package/dist/relayed-usage.d.ts.map +1 -1
- package/dist/relayed-usage.js +25 -2
- package/dist/relayed-usage.js.map +1 -1
- package/dist/review-runner.js +1 -1
- package/dist/subagent-timeline.d.ts +27 -0
- package/dist/subagent-timeline.d.ts.map +1 -1
- package/dist/subagent-timeline.js +52 -5
- package/dist/subagent-timeline.js.map +1 -1
- package/dist/surface-protocol.d.ts.map +1 -1
- package/dist/surface-protocol.js +2 -0
- package/dist/surface-protocol.js.map +1 -1
- package/dist/tui/dashboard.d.ts.map +1 -1
- package/dist/tui/dashboard.js +8 -1
- package/dist/tui/dashboard.js.map +1 -1
- package/dist/usage-api.d.ts +3 -0
- package/dist/usage-api.d.ts.map +1 -1
- package/dist/usage-api.js +101 -36
- package/dist/usage-api.js.map +1 -1
- package/dist/usage-event.d.ts.map +1 -1
- package/dist/usage-event.js +2 -1
- package/dist/usage-event.js.map +1 -1
- package/dist/ws-server.d.ts +18 -0
- package/dist/ws-server.d.ts.map +1 -1
- package/dist/ws-server.js +40 -2
- package/dist/ws-server.js.map +1 -1
- package/package.json +6 -5
- package/src/timebox/sync_ble.py +35 -16
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-dependency-diagnostics.js","sourceRoot":"","sources":["../src/native-dependency-diagnostics.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,9 +23,8 @@
|
|
|
23
23
|
* Keyed by the Claude session UUID (same key as awaiting-overlay.ts). Lives
|
|
24
24
|
* outside the state machine for the same reason: per-session attribution.
|
|
25
25
|
*/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
export declare function gateSignature(tool: string, toolInput: Record<string, unknown> | undefined): string;
|
|
26
|
+
import { gateSignature } from '@agentdeck/shared';
|
|
27
|
+
export { gateSignature };
|
|
29
28
|
/** Human-readable question for the gate's awaiting overlay — device-native
|
|
30
29
|
* semantics ("Allow Bash: git push …?"), never a fabricated mirror of the
|
|
31
30
|
* TUI prompt's option labels. Overlay caps length at 120 chars. */
|
|
@@ -124,6 +123,7 @@ export declare function gateReleased(sid: string, requestId: string, opts: {
|
|
|
124
123
|
undecided: boolean;
|
|
125
124
|
tool: string;
|
|
126
125
|
toolInput?: Record<string, unknown>;
|
|
126
|
+
toolUseId?: string;
|
|
127
127
|
}): void;
|
|
128
128
|
/** A permission_prompt Notification arrived — every recent undecided release
|
|
129
129
|
* was a GENUINE prompt, so nothing should be learned as auto-approved. */
|
|
@@ -134,7 +134,7 @@ export declare function notePermissionPromptShown(sid: string): void;
|
|
|
134
134
|
* (session-scoped "always allow" we cannot read) — suppress the signature so
|
|
135
135
|
* it is never held again this session.
|
|
136
136
|
*/
|
|
137
|
-
export declare function noteToolEnd(sid: string, tool: string | undefined): void;
|
|
137
|
+
export declare function noteToolEnd(sid: string, tool: string | undefined, toolUseId?: string): void;
|
|
138
138
|
/** Enrichment snapshot for sessions_list (devices render STOPPING / queue badges). */
|
|
139
139
|
export declare function steeringSnapshot(sid: string): {
|
|
140
140
|
stopRequested: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observed-steering.d.ts","sourceRoot":"","sources":["../src/observed-steering.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;
|
|
1
|
+
{"version":3,"file":"observed-steering.d.ts","sourceRoot":"","sources":["../src/observed-steering.ts"],"names":[],"mappings":"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"}
|
|
@@ -24,17 +24,20 @@
|
|
|
24
24
|
* outside the state machine for the same reason: per-session attribution.
|
|
25
25
|
*/
|
|
26
26
|
import { randomUUID } from 'crypto';
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
27
|
+
import { GATE_LEARN_WINDOW_MS, gateSignature, predictPreToolUseHold, } from '@agentdeck/shared';
|
|
28
|
+
import { loadMergedPermissionRules } from './claude-permission-rules.js';
|
|
29
29
|
import { debug } from './logger.js';
|
|
30
|
+
export { gateSignature };
|
|
30
31
|
const STOP_FLAG_TTL_MS = 10 * 60_000; // stale STOP must not deny a tool an hour later
|
|
31
32
|
const DIRECTIVE_TTL_MS = 60 * 60_000;
|
|
32
33
|
const DIRECTIVE_QUEUE_CAP = 3;
|
|
33
34
|
/** After a hold releases undecided, how long we correlate PostToolUse-without-
|
|
34
35
|
* Notification to learn "this signature was auto-approved" (session
|
|
35
36
|
* "always allow" answers live only in Claude's memory — this is the only way
|
|
36
|
-
* to see them).
|
|
37
|
-
|
|
37
|
+
* to see them). SSOT in @agentdeck/shared: a `permission_prompt`
|
|
38
|
+
* Notification clears the pending release, so this bounds only how slow the
|
|
39
|
+
* TOOL may be — and 8 s was shorter than a routine `curl`. */
|
|
40
|
+
const ASK_RELEASE_LEARN_WINDOW_MS = GATE_LEARN_WINDOW_MS;
|
|
38
41
|
const sessions = new Map();
|
|
39
42
|
function ses(sid) {
|
|
40
43
|
let s = sessions.get(sid);
|
|
@@ -44,15 +47,6 @@ function ses(sid) {
|
|
|
44
47
|
}
|
|
45
48
|
return s;
|
|
46
49
|
}
|
|
47
|
-
/** Bash signature = first two command tokens (the granularity of Claude's own
|
|
48
|
-
* "always allow `git push`"-style session approvals); other tools = tool name. */
|
|
49
|
-
export function gateSignature(tool, toolInput) {
|
|
50
|
-
if (tool === 'Bash' && typeof toolInput?.command === 'string') {
|
|
51
|
-
const head = toolInput.command.trim().split(/\s+/).slice(0, 2).join(' ');
|
|
52
|
-
return `Bash|${head}`;
|
|
53
|
-
}
|
|
54
|
-
return tool;
|
|
55
|
-
}
|
|
56
50
|
/** Human-readable question for the gate's awaiting overlay — device-native
|
|
57
51
|
* semantics ("Allow Bash: git push …?"), never a fabricated mirror of the
|
|
58
52
|
* TUI prompt's option labels. Overlay caps length at 120 chars. */
|
|
@@ -199,31 +193,26 @@ export function shouldHoldPreToolUse(ctx) {
|
|
|
199
193
|
return { hold: false, reason: 'no clients' };
|
|
200
194
|
if (!ctx.tool)
|
|
201
195
|
return { hold: false, reason: 'no tool name' };
|
|
202
|
-
if (ctx.tool.startsWith('mcp__'))
|
|
203
|
-
return { hold: false, reason: 'mcp tool (trust state unknown)' };
|
|
204
|
-
if (isNeverPromptTool(ctx.tool))
|
|
205
|
-
return { hold: false, reason: 'never-prompt tool' };
|
|
206
|
-
if (!isPromptProneTool(ctx.tool))
|
|
207
|
-
return { hold: false, reason: 'not prompt-prone' };
|
|
208
|
-
if (!shouldGatePreToolUse(ctx.permissionMode, ctx.tool)) {
|
|
209
|
-
return { hold: false, reason: `permission_mode=${ctx.permissionMode ?? 'default'} auto-approves` };
|
|
210
|
-
}
|
|
211
196
|
const s = ses(ctx.sessionId);
|
|
212
197
|
const signature = gateSignature(ctx.tool, ctx.toolInput);
|
|
213
198
|
if (s.suppressed.has(signature))
|
|
214
199
|
return { hold: false, reason: 'signature learned auto-approved' };
|
|
215
200
|
if (s.heldRequestId)
|
|
216
201
|
return { hold: false, reason: 'another gate already held' };
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
202
|
+
// The stateless prediction (tool sets, mode gate, allow/deny/ask rules,
|
|
203
|
+
// built-in read-only and acceptEdits auto-approvals) is the shared SSOT the
|
|
204
|
+
// Swift daemon runs as generated code — one decision, two daemons.
|
|
205
|
+
const prediction = predictPreToolUseHold({
|
|
206
|
+
tool: ctx.tool,
|
|
207
|
+
toolInput: ctx.toolInput,
|
|
208
|
+
permissionMode: ctx.permissionMode,
|
|
209
|
+
rules: loadMergedPermissionRules(ctx.cwd),
|
|
210
|
+
});
|
|
211
|
+
if (!prediction.hold)
|
|
212
|
+
return { hold: false, reason: prediction.reason };
|
|
224
213
|
const requestId = randomUUID();
|
|
225
214
|
s.heldRequestId = requestId;
|
|
226
|
-
return { hold: true, requestId, reason:
|
|
215
|
+
return { hold: true, requestId, reason: prediction.reason };
|
|
227
216
|
}
|
|
228
217
|
/**
|
|
229
218
|
* Register a hold for an AskUserQuestion PreToolUse so a device can answer it.
|
|
@@ -267,6 +256,7 @@ export function gateReleased(sid, requestId, opts) {
|
|
|
267
256
|
tool: opts.tool,
|
|
268
257
|
signature: gateSignature(opts.tool, opts.toolInput),
|
|
269
258
|
ts: Date.now(),
|
|
259
|
+
toolUseId: opts.toolUseId,
|
|
270
260
|
});
|
|
271
261
|
if (s.recentAskReleases.length > 8)
|
|
272
262
|
s.recentAskReleases.shift();
|
|
@@ -285,7 +275,7 @@ export function notePermissionPromptShown(sid) {
|
|
|
285
275
|
* (session-scoped "always allow" we cannot read) — suppress the signature so
|
|
286
276
|
* it is never held again this session.
|
|
287
277
|
*/
|
|
288
|
-
export function noteToolEnd(sid, tool) {
|
|
278
|
+
export function noteToolEnd(sid, tool, toolUseId) {
|
|
289
279
|
const s = sessions.get(sid);
|
|
290
280
|
if (!s || !tool || s.recentAskReleases.length === 0)
|
|
291
281
|
return;
|
|
@@ -294,7 +284,13 @@ export function noteToolEnd(sid, tool) {
|
|
|
294
284
|
for (const r of s.recentAskReleases) {
|
|
295
285
|
if (now - r.ts > ASK_RELEASE_LEARN_WINDOW_MS)
|
|
296
286
|
continue; // expired
|
|
297
|
-
|
|
287
|
+
// The held call and its PostToolUse share a `tool_use_id`, so a release
|
|
288
|
+
// that recorded one learns ONLY from its own completion — a parallel
|
|
289
|
+
// allowlisted Bash finishing inside the window must not teach the held
|
|
290
|
+
// signature (the window is 15 min now, wide enough for that to happen).
|
|
291
|
+
// Releases without an id (older Claude) fall back to the tool name.
|
|
292
|
+
const matches = r.toolUseId ? r.toolUseId === toolUseId : r.tool === tool;
|
|
293
|
+
if (matches) {
|
|
298
294
|
s.suppressed.add(r.signature);
|
|
299
295
|
debug('steering', `learned auto-approved signature for ${sid}: ${r.signature}`);
|
|
300
296
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observed-steering.js","sourceRoot":"","sources":["../src/observed-steering.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"observed-steering.js","sourceRoot":"","sources":["../src/observed-steering.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw Gateway instability — detect a restart storm and say why.
|
|
3
|
+
*
|
|
4
|
+
* A Gateway restart is invisible from inside the daemon: the socket closes,
|
|
5
|
+
* `disconnectGatewayAdapter` tears the virtual session down, the probe finds
|
|
6
|
+
* the port again a few seconds later and everything reconnects. Each cycle
|
|
7
|
+
* looks like a healthy recovery, and a surface that watched it saw the
|
|
8
|
+
* OpenClaw row flip to `gateway_not_found` and back. On 2026-09-02 that
|
|
9
|
+
* happened eleven times in ninety minutes and nothing anywhere said so —
|
|
10
|
+
* the daemon log carried eleven `connected` lines, every dashboard showed
|
|
11
|
+
* a connected Gateway between flaps, and the cause (two OpenClaw installs
|
|
12
|
+
* fighting over `~/.openclaw/state`, a launchd KeepAlive respawning a
|
|
13
|
+
* Gateway whose startup migration could not run because the other copy
|
|
14
|
+
* held the state, and a plugin install restarting it on top) was written
|
|
15
|
+
* only in OpenClaw's own log, which no surface reads.
|
|
16
|
+
*
|
|
17
|
+
* Two pieces, both pure so a test can drive them with a clock and a string:
|
|
18
|
+
*
|
|
19
|
+
* - `GatewayFlapTracker` counts disconnects inside a rolling window. Three
|
|
20
|
+
* in ten minutes is the line between "a plugin install restarted it" and
|
|
21
|
+
* "something is wrong": a single deliberate restart never crosses it, and
|
|
22
|
+
* the measured storm crossed it inside four minutes.
|
|
23
|
+
* - `diagnoseGatewayLog` reads the Gateway's own log tail for the sentences
|
|
24
|
+
* OpenClaw prints when it knows what is happening. Structural causes
|
|
25
|
+
* (another Gateway owns the state directory, the restart-loop breaker
|
|
26
|
+
* tripped, a startup migration refused to complete) outrank routine ones
|
|
27
|
+
* (a config-change restart, a plain SIGTERM) regardless of which line is
|
|
28
|
+
* newer, because the routine restart is usually the symptom that the
|
|
29
|
+
* structural cause produced.
|
|
30
|
+
*
|
|
31
|
+
* The daemon surfaces the result three ways so it cannot hide: a loud log
|
|
32
|
+
* line, an `instability` field on `/status` and `/health`, and one `error`
|
|
33
|
+
* timeline row per escalation on the virtual `openclaw-gateway` session —
|
|
34
|
+
* the timeline is the one channel every surface already renders, Android
|
|
35
|
+
* and e-ink included, without a protocol change.
|
|
36
|
+
*/
|
|
37
|
+
export declare const GATEWAY_FLAP_WINDOW_MS: number;
|
|
38
|
+
export declare const GATEWAY_FLAP_THRESHOLD = 3;
|
|
39
|
+
/** Bytes read from the end of the Gateway log when diagnosing a storm. */
|
|
40
|
+
export declare const GATEWAY_LOG_TAIL_BYTES: number;
|
|
41
|
+
export type GatewayInstabilityKind = 'state_owner_conflict' | 'restart_loop' | 'migration_failed' | 'config_restart' | 'external_sigterm';
|
|
42
|
+
export interface GatewayLogDiagnosis {
|
|
43
|
+
kind: GatewayInstabilityKind;
|
|
44
|
+
/** The Gateway log line that carries the reason, trimmed. */
|
|
45
|
+
line: string;
|
|
46
|
+
/** What an operator should do about it, one sentence. */
|
|
47
|
+
hint: string;
|
|
48
|
+
}
|
|
49
|
+
export interface GatewayInstability {
|
|
50
|
+
flapsInWindow: number;
|
|
51
|
+
windowMs: number;
|
|
52
|
+
/** Epoch ms of the oldest disconnect still inside the window. */
|
|
53
|
+
since: number;
|
|
54
|
+
/** Epoch ms of the disconnect that produced this assessment. */
|
|
55
|
+
lastFlapAt: number;
|
|
56
|
+
diagnosis: GatewayLogDiagnosis | null;
|
|
57
|
+
}
|
|
58
|
+
export declare class GatewayFlapTracker {
|
|
59
|
+
private readonly windowMs;
|
|
60
|
+
private readonly threshold;
|
|
61
|
+
private stamps;
|
|
62
|
+
constructor(windowMs?: number, threshold?: number);
|
|
63
|
+
/** Record a disconnect at `nowMs`; returns the count inside the window including it. */
|
|
64
|
+
record(nowMs: number): number;
|
|
65
|
+
/**
|
|
66
|
+
* How many disconnects are inside the window, and whether that crosses the
|
|
67
|
+
* line. `since` is the oldest one still counted — the start of the storm.
|
|
68
|
+
*/
|
|
69
|
+
assess(nowMs: number): {
|
|
70
|
+
flapsInWindow: number;
|
|
71
|
+
unstable: boolean;
|
|
72
|
+
since: number | null;
|
|
73
|
+
};
|
|
74
|
+
private prune;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Scan a Gateway log tail for the sentence that explains a restart storm.
|
|
78
|
+
* Returns the highest-priority kind present, carrying its most recent line.
|
|
79
|
+
* `null` means the tail has no known sentinel — the storm is real but the
|
|
80
|
+
* log does not say why, and the surfaces should say exactly that.
|
|
81
|
+
*/
|
|
82
|
+
export declare function diagnoseGatewayLog(text: string): GatewayLogDiagnosis | null;
|
|
83
|
+
/** Default locations of the Gateway's launchd stdout log, most likely first. */
|
|
84
|
+
export declare function defaultGatewayLogPaths(home?: string): string[];
|
|
85
|
+
/**
|
|
86
|
+
* Read the last `maxBytes` of the first readable log path. Best-effort:
|
|
87
|
+
* an unreadable or absent log yields an empty string, never a throw —
|
|
88
|
+
* the storm is still reported, only without a reason.
|
|
89
|
+
*/
|
|
90
|
+
export declare function readGatewayLogTail(paths?: string[], maxBytes?: number): Promise<string>;
|
|
91
|
+
/** One line for the timeline row and the daemon log; ≤ ~180 chars. */
|
|
92
|
+
export declare function formatGatewayInstability(i: GatewayInstability): string;
|
|
93
|
+
export declare function describeKind(kind: GatewayInstabilityKind): string;
|
|
94
|
+
//# sourceMappingURL=openclaw-gateway-stability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openclaw-gateway-stability.d.ts","sourceRoot":"","sources":["../src/openclaw-gateway-stability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAMH,eAAO,MAAM,sBAAsB,QAAc,CAAC;AAClD,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,0EAA0E;AAC1E,eAAO,MAAM,sBAAsB,QAAY,CAAC;AAEhD,MAAM,MAAM,sBAAsB,GAC9B,sBAAsB,GACtB,cAAc,GACd,kBAAkB,GAClB,gBAAgB,GAChB,kBAAkB,CAAC;AAEvB,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,mBAAmB,GAAG,IAAI,CAAC;CACvC;AA8CD,qBAAa,kBAAkB;IAI3B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAJ5B,OAAO,CAAC,MAAM,CAAgB;gBAGX,QAAQ,GAAE,MAA+B,EACzC,SAAS,GAAE,MAA+B;IAG7D,wFAAwF;IACxF,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAM7B;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;IASzF,OAAO,CAAC,KAAK;CAId;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAW3E;AAED,gFAAgF;AAChF,wBAAgB,sBAAsB,CAAC,IAAI,GAAE,MAAkB,GAAG,MAAM,EAAE,CAEzE;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,GAAE,MAAM,EAA6B,EAC1C,QAAQ,GAAE,MAA+B,GACxC,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAED,sEAAsE;AACtE,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAKtE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,sBAAsB,GAAG,MAAM,CAQjE"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw Gateway instability — detect a restart storm and say why.
|
|
3
|
+
*
|
|
4
|
+
* A Gateway restart is invisible from inside the daemon: the socket closes,
|
|
5
|
+
* `disconnectGatewayAdapter` tears the virtual session down, the probe finds
|
|
6
|
+
* the port again a few seconds later and everything reconnects. Each cycle
|
|
7
|
+
* looks like a healthy recovery, and a surface that watched it saw the
|
|
8
|
+
* OpenClaw row flip to `gateway_not_found` and back. On 2026-09-02 that
|
|
9
|
+
* happened eleven times in ninety minutes and nothing anywhere said so —
|
|
10
|
+
* the daemon log carried eleven `connected` lines, every dashboard showed
|
|
11
|
+
* a connected Gateway between flaps, and the cause (two OpenClaw installs
|
|
12
|
+
* fighting over `~/.openclaw/state`, a launchd KeepAlive respawning a
|
|
13
|
+
* Gateway whose startup migration could not run because the other copy
|
|
14
|
+
* held the state, and a plugin install restarting it on top) was written
|
|
15
|
+
* only in OpenClaw's own log, which no surface reads.
|
|
16
|
+
*
|
|
17
|
+
* Two pieces, both pure so a test can drive them with a clock and a string:
|
|
18
|
+
*
|
|
19
|
+
* - `GatewayFlapTracker` counts disconnects inside a rolling window. Three
|
|
20
|
+
* in ten minutes is the line between "a plugin install restarted it" and
|
|
21
|
+
* "something is wrong": a single deliberate restart never crosses it, and
|
|
22
|
+
* the measured storm crossed it inside four minutes.
|
|
23
|
+
* - `diagnoseGatewayLog` reads the Gateway's own log tail for the sentences
|
|
24
|
+
* OpenClaw prints when it knows what is happening. Structural causes
|
|
25
|
+
* (another Gateway owns the state directory, the restart-loop breaker
|
|
26
|
+
* tripped, a startup migration refused to complete) outrank routine ones
|
|
27
|
+
* (a config-change restart, a plain SIGTERM) regardless of which line is
|
|
28
|
+
* newer, because the routine restart is usually the symptom that the
|
|
29
|
+
* structural cause produced.
|
|
30
|
+
*
|
|
31
|
+
* The daemon surfaces the result three ways so it cannot hide: a loud log
|
|
32
|
+
* line, an `instability` field on `/status` and `/health`, and one `error`
|
|
33
|
+
* timeline row per escalation on the virtual `openclaw-gateway` session —
|
|
34
|
+
* the timeline is the one channel every surface already renders, Android
|
|
35
|
+
* and e-ink included, without a protocol change.
|
|
36
|
+
*/
|
|
37
|
+
import { open } from 'node:fs/promises';
|
|
38
|
+
import { homedir } from 'node:os';
|
|
39
|
+
import { join } from 'node:path';
|
|
40
|
+
export const GATEWAY_FLAP_WINDOW_MS = 10 * 60_000;
|
|
41
|
+
export const GATEWAY_FLAP_THRESHOLD = 3;
|
|
42
|
+
/** Bytes read from the end of the Gateway log when diagnosing a storm. */
|
|
43
|
+
export const GATEWAY_LOG_TAIL_BYTES = 64 * 1024;
|
|
44
|
+
/**
|
|
45
|
+
* Sentinels in priority order. The first kind found anywhere in the tail
|
|
46
|
+
* wins, so a structural cause explains a storm even when the most recent
|
|
47
|
+
* line is the routine restart it caused.
|
|
48
|
+
*/
|
|
49
|
+
const SENTINELS = [
|
|
50
|
+
{
|
|
51
|
+
kind: 'state_owner_conflict',
|
|
52
|
+
pattern: /another Gateway owns that state directory/i,
|
|
53
|
+
hint: 'Two OpenClaw Gateways are using one ~/.openclaw/state — usually two installs ' +
|
|
54
|
+
'(a global npm/pnpm copy beside the app-managed ~/.openclaw/tools runtime). ' +
|
|
55
|
+
'Keep one; make sure `openclaw` on PATH is ~/.openclaw/bin/openclaw.',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
kind: 'restart_loop',
|
|
59
|
+
pattern: /restart-loop breaker tripped/i,
|
|
60
|
+
hint: 'OpenClaw\'s own breaker saw repeated unclean boots; launchd KeepAlive is ' +
|
|
61
|
+
'respawning a Gateway that cannot come up. Read ~/Library/Logs/openclaw/gateway.log.',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
kind: 'migration_failed',
|
|
65
|
+
pattern: /startup migrations did not complete cleanly|migration requires stopped-writer maintenance/i,
|
|
66
|
+
hint: 'A startup migration refused to run because another writer holds the state. ' +
|
|
67
|
+
'Stop every other Gateway, then restart this one once.',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
kind: 'config_restart',
|
|
71
|
+
pattern: /config change requires gateway restart|received SIGUSR1; restarting|Restart the gateway to (apply|load)/i,
|
|
72
|
+
hint: 'A config or plugin change asked the Gateway to restart. Expected once; a storm of these is not.',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
kind: 'external_sigterm',
|
|
76
|
+
pattern: /received SIGTERM; shutting down/i,
|
|
77
|
+
hint: 'Something sent the Gateway SIGTERM (a CLI restart, an updater, or launchd). Check who.',
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
export class GatewayFlapTracker {
|
|
81
|
+
windowMs;
|
|
82
|
+
threshold;
|
|
83
|
+
stamps = [];
|
|
84
|
+
constructor(windowMs = GATEWAY_FLAP_WINDOW_MS, threshold = GATEWAY_FLAP_THRESHOLD) {
|
|
85
|
+
this.windowMs = windowMs;
|
|
86
|
+
this.threshold = threshold;
|
|
87
|
+
}
|
|
88
|
+
/** Record a disconnect at `nowMs`; returns the count inside the window including it. */
|
|
89
|
+
record(nowMs) {
|
|
90
|
+
this.stamps.push(nowMs);
|
|
91
|
+
this.prune(nowMs);
|
|
92
|
+
return this.stamps.length;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* How many disconnects are inside the window, and whether that crosses the
|
|
96
|
+
* line. `since` is the oldest one still counted — the start of the storm.
|
|
97
|
+
*/
|
|
98
|
+
assess(nowMs) {
|
|
99
|
+
this.prune(nowMs);
|
|
100
|
+
return {
|
|
101
|
+
flapsInWindow: this.stamps.length,
|
|
102
|
+
unstable: this.stamps.length >= this.threshold,
|
|
103
|
+
since: this.stamps.length > 0 ? this.stamps[0] : null,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
prune(nowMs) {
|
|
107
|
+
const cutoff = nowMs - this.windowMs;
|
|
108
|
+
while (this.stamps.length > 0 && this.stamps[0] < cutoff)
|
|
109
|
+
this.stamps.shift();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Scan a Gateway log tail for the sentence that explains a restart storm.
|
|
114
|
+
* Returns the highest-priority kind present, carrying its most recent line.
|
|
115
|
+
* `null` means the tail has no known sentinel — the storm is real but the
|
|
116
|
+
* log does not say why, and the surfaces should say exactly that.
|
|
117
|
+
*/
|
|
118
|
+
export function diagnoseGatewayLog(text) {
|
|
119
|
+
if (!text)
|
|
120
|
+
return null;
|
|
121
|
+
const lines = text.split(/\r?\n/);
|
|
122
|
+
for (const sentinel of SENTINELS) {
|
|
123
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
124
|
+
if (sentinel.pattern.test(lines[i])) {
|
|
125
|
+
return { kind: sentinel.kind, line: lines[i].trim().slice(0, 300), hint: sentinel.hint };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
/** Default locations of the Gateway's launchd stdout log, most likely first. */
|
|
132
|
+
export function defaultGatewayLogPaths(home = homedir()) {
|
|
133
|
+
return [join(home, 'Library', 'Logs', 'openclaw', 'gateway.log')];
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Read the last `maxBytes` of the first readable log path. Best-effort:
|
|
137
|
+
* an unreadable or absent log yields an empty string, never a throw —
|
|
138
|
+
* the storm is still reported, only without a reason.
|
|
139
|
+
*/
|
|
140
|
+
export async function readGatewayLogTail(paths = defaultGatewayLogPaths(), maxBytes = GATEWAY_LOG_TAIL_BYTES) {
|
|
141
|
+
for (const path of paths) {
|
|
142
|
+
let handle;
|
|
143
|
+
try {
|
|
144
|
+
handle = await open(path, 'r');
|
|
145
|
+
const { size } = await handle.stat();
|
|
146
|
+
const length = Math.min(size, maxBytes);
|
|
147
|
+
const buffer = Buffer.alloc(length);
|
|
148
|
+
await handle.read(buffer, 0, length, size - length);
|
|
149
|
+
return buffer.toString('utf8');
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
await handle?.close().catch(() => { });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return '';
|
|
159
|
+
}
|
|
160
|
+
/** One line for the timeline row and the daemon log; ≤ ~180 chars. */
|
|
161
|
+
export function formatGatewayInstability(i) {
|
|
162
|
+
const minutes = Math.max(1, Math.round((i.lastFlapAt - i.since) / 60_000));
|
|
163
|
+
const head = `OpenClaw Gateway unstable · ${i.flapsInWindow} restarts in ${minutes} min`;
|
|
164
|
+
const why = i.diagnosis ? describeKind(i.diagnosis.kind) : 'reason not in the Gateway log';
|
|
165
|
+
return `${head} — ${why}`;
|
|
166
|
+
}
|
|
167
|
+
export function describeKind(kind) {
|
|
168
|
+
switch (kind) {
|
|
169
|
+
case 'state_owner_conflict': return 'two Gateways own ~/.openclaw/state (duplicate install?)';
|
|
170
|
+
case 'restart_loop': return 'OpenClaw restart-loop breaker tripped';
|
|
171
|
+
case 'migration_failed': return 'startup migration blocked by another writer';
|
|
172
|
+
case 'config_restart': return 'config/plugin change restarts';
|
|
173
|
+
case 'external_sigterm': return 'repeated external SIGTERM';
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=openclaw-gateway-stability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openclaw-gateway-stability.js","sourceRoot":"","sources":["../src/openclaw-gateway-stability.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A knock is worth showing for this long after its last attempt. Long enough
|
|
3
|
+
* that the operator can walk to the machine; short enough that a device carried
|
|
4
|
+
* out of the building stops being offered.
|
|
5
|
+
*/
|
|
6
|
+
export declare const KNOCK_TTL_MS: number;
|
|
7
|
+
/** Cap on simultaneously-tracked peers. */
|
|
8
|
+
export declare const MAX_KNOCKS = 12;
|
|
9
|
+
export interface PairingKnock {
|
|
10
|
+
/**
|
|
11
|
+
* What an approval will be recorded against. `device:<id>` when the client
|
|
12
|
+
* identified itself, `ip:<addr>` otherwise — never the bare string, so the
|
|
13
|
+
* two can never collide or be confused downstream.
|
|
14
|
+
*/
|
|
15
|
+
key: string;
|
|
16
|
+
ip: string;
|
|
17
|
+
deviceId: string | null;
|
|
18
|
+
attempts: number;
|
|
19
|
+
firstSeen: number;
|
|
20
|
+
lastSeen: number;
|
|
21
|
+
/**
|
|
22
|
+
* True when the peer presented a token we do not accept, rather than no token
|
|
23
|
+
* at all. Reads very differently to an operator: usually a provisioned device
|
|
24
|
+
* whose credential went stale, not a new device.
|
|
25
|
+
*/
|
|
26
|
+
staleToken: boolean;
|
|
27
|
+
/** Display only — `key` already encodes which kind this is. */
|
|
28
|
+
deviceScoped: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface PairingApproval {
|
|
31
|
+
key: string;
|
|
32
|
+
/**
|
|
33
|
+
* Where it last connected from. Display only — an approval keyed on a device
|
|
34
|
+
* id must not start depending on the address, or it would silently reacquire
|
|
35
|
+
* the NAT and DHCP problems it exists to escape.
|
|
36
|
+
*/
|
|
37
|
+
lastIP: string;
|
|
38
|
+
approvedAt: number;
|
|
39
|
+
deviceId: string | null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The one place a key is spelled, so the gate, the store and the UI cannot
|
|
43
|
+
* disagree about what "this peer" means.
|
|
44
|
+
*/
|
|
45
|
+
export declare function peerKey(ip: string, deviceId: unknown): string;
|
|
46
|
+
/**
|
|
47
|
+
* Called from the refusal path. Cheap on purpose — it runs for every rejected
|
|
48
|
+
* handshake, and a looping device produces one every few seconds.
|
|
49
|
+
*/
|
|
50
|
+
export declare function recordKnock(ip: string, deviceId: unknown, staleToken: boolean, now?: number): void;
|
|
51
|
+
/** Live knocks, newest first. Expiry is applied here — see the header. */
|
|
52
|
+
export declare function pairingKnocks(now?: number): PairingKnock[];
|
|
53
|
+
/**
|
|
54
|
+
* Drop a knock without approving it. Not a block: the peer may knock again,
|
|
55
|
+
* which is deliberate — a permanent denylist keyed on an address a stranger can
|
|
56
|
+
* change is security theatre, and it would silently strand a real device that
|
|
57
|
+
* later inherits that address.
|
|
58
|
+
*/
|
|
59
|
+
export declare function dismissKnock(key: string): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Approving is what a pairing code would have done, minus the typing: from here
|
|
62
|
+
* on this peer authenticates by its key. Persisted immediately.
|
|
63
|
+
*/
|
|
64
|
+
export declare function approvePeer(key: string, now?: number): boolean;
|
|
65
|
+
export declare function revokePeer(key: string): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* The question the WebSocket gate asks.
|
|
68
|
+
*
|
|
69
|
+
* A device id wins when the client sent one; the address is consulted only as
|
|
70
|
+
* the legacy path, so a client that adopts the header stops depending on its
|
|
71
|
+
* address the moment it does. Both are checked rather than one, because a
|
|
72
|
+
* device approved by address before it learned to send an id must not be locked
|
|
73
|
+
* out by the upgrade that taught it.
|
|
74
|
+
*/
|
|
75
|
+
export declare function isApprovedPeer(ip: string, deviceId: unknown): boolean;
|
|
76
|
+
export declare function pairingApprovals(): PairingApproval[];
|
|
77
|
+
/** Read the device id a client offered on the handshake, if any. */
|
|
78
|
+
export declare function deviceIdFromHeaders(headers: Record<string, string | string[] | undefined> | undefined, headerName: string): string | null;
|
|
79
|
+
export declare function resetPairingKnocksForTests(): void;
|
|
80
|
+
//# sourceMappingURL=pairing-knocks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairing-knocks.d.ts","sourceRoot":"","sources":["../src/pairing-knocks.ts"],"names":[],"mappings":"AAgEA;;;;GAIG;AACH,eAAO,MAAM,YAAY,QAAc,CAAC;AAExC,2CAA2C;AAC3C,eAAO,MAAM,UAAU,KAAK,CAAC;AAE7B,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB,+DAA+D;IAC/D,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CAI7D;AA8ID;;;GAGG;AACH,wBAAgB,WAAW,CACzB,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,OAAO,EACjB,UAAU,EAAE,OAAO,EACnB,GAAG,SAAa,GACf,IAAI,CAwBN;AAED,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,GAAG,SAAa,GAAG,YAAY,EAAE,CAK9D;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,OAAO,CAQlE;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAK/C;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAMrE;AAED,wBAAgB,gBAAgB,IAAI,eAAe,EAAE,CAEpD;AAED,oEAAoE;AACpE,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,SAAS,EAClE,UAAU,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,CAGf;AAED,wBAAgB,0BAA0B,IAAI,IAAI,CAGjD"}
|