@agentdeck/bridge 1.0.24 → 1.2.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 +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/adapters/openclaw.d.ts +74 -4
- package/dist/adapters/openclaw.d.ts.map +1 -1
- package/dist/adapters/openclaw.js +235 -43
- package/dist/adapters/openclaw.js.map +1 -1
- 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/adapters/openclaw-hook.d.ts +38 -10
- package/dist/apme/adapters/openclaw-hook.d.ts.map +1 -1
- package/dist/apme/adapters/openclaw-hook.js +181 -37
- package/dist/apme/adapters/openclaw-hook.js.map +1 -1
- package/dist/apme/collector.d.ts +55 -2
- package/dist/apme/collector.d.ts.map +1 -1
- package/dist/apme/collector.js +324 -37
- package/dist/apme/collector.js.map +1 -1
- package/dist/apme/dashboard-html.d.ts.map +1 -1
- package/dist/apme/dashboard-html.js +275 -35
- package/dist/apme/dashboard-html.js.map +1 -1
- package/dist/apme/http.d.ts.map +1 -1
- package/dist/apme/http.js +33 -5
- package/dist/apme/http.js.map +1 -1
- package/dist/apme/index.d.ts.map +1 -1
- package/dist/apme/index.js +6 -2
- package/dist/apme/index.js.map +1 -1
- package/dist/apme/pareto.d.ts +2 -1
- package/dist/apme/pareto.d.ts.map +1 -1
- package/dist/apme/pareto.js +15 -4
- package/dist/apme/pareto.js.map +1 -1
- package/dist/apme/recommend.d.ts +24 -1
- package/dist/apme/recommend.d.ts.map +1 -1
- package/dist/apme/recommend.js +43 -7
- package/dist/apme/recommend.js.map +1 -1
- package/dist/apme/store.d.ts +47 -1
- package/dist/apme/store.d.ts.map +1 -1
- package/dist/apme/store.js +397 -99
- package/dist/apme/store.js.map +1 -1
- package/dist/bridge-core.d.ts +25 -1
- package/dist/bridge-core.d.ts.map +1 -1
- package/dist/bridge-core.js +50 -9
- package/dist/bridge-core.js.map +1 -1
- package/dist/card-feed.d.ts +46 -1
- package/dist/card-feed.d.ts.map +1 -1
- package/dist/card-feed.js +166 -6
- package/dist/card-feed.js.map +1 -1
- package/dist/cli.d.ts +56 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +442 -34
- 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/daemon-build-identity.d.ts +124 -0
- package/dist/daemon-build-identity.d.ts.map +1 -0
- package/dist/daemon-build-identity.js +310 -0
- package/dist/daemon-build-identity.js.map +1 -0
- package/dist/daemon-server.d.ts +34 -6
- package/dist/daemon-server.d.ts.map +1 -1
- package/dist/daemon-server.js +876 -109
- package/dist/daemon-server.js.map +1 -1
- package/dist/daemon-takeover.d.ts +11 -0
- package/dist/daemon-takeover.d.ts.map +1 -1
- package/dist/daemon-takeover.js +62 -17
- package/dist/daemon-takeover.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/daemon.js.map +1 -1
- package/dist/esp32-serial.d.ts +18 -0
- package/dist/esp32-serial.d.ts.map +1 -1
- package/dist/esp32-serial.js +23 -1
- 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 +5 -1
- package/dist/glance-frame.js.map +1 -1
- package/dist/hook-codex-sessions.d.ts +3 -1
- package/dist/hook-codex-sessions.d.ts.map +1 -1
- package/dist/hook-codex-sessions.js +27 -7
- package/dist/hook-codex-sessions.js.map +1 -1
- 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.js +2 -2
- 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/legacy-rearm-ledger.d.ts +56 -0
- package/dist/legacy-rearm-ledger.d.ts.map +1 -0
- package/dist/legacy-rearm-ledger.js +66 -0
- package/dist/legacy-rearm-ledger.js.map +1 -0
- package/dist/mdns.d.ts +33 -0
- package/dist/mdns.d.ts.map +1 -1
- package/dist/mdns.js +92 -3
- package/dist/mdns.js.map +1 -1
- package/dist/openclaw-session.d.ts.map +1 -1
- package/dist/openclaw-session.js +13 -0
- package/dist/openclaw-session.js.map +1 -1
- package/dist/openclaw-timeline-feed.d.ts +84 -0
- package/dist/openclaw-timeline-feed.d.ts.map +1 -0
- package/dist/openclaw-timeline-feed.js +162 -0
- package/dist/openclaw-timeline-feed.js.map +1 -0
- package/dist/openclaw-transcript-timeline.d.ts +103 -0
- package/dist/openclaw-transcript-timeline.d.ts.map +1 -0
- package/dist/openclaw-transcript-timeline.js +328 -0
- package/dist/openclaw-transcript-timeline.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 +21 -1
- package/dist/passive-observer.d.ts.map +1 -1
- package/dist/passive-observer.js +37 -3
- 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 +98 -0
- package/dist/relayed-usage.d.ts.map +1 -0
- package/dist/relayed-usage.js +103 -0
- package/dist/relayed-usage.js.map +1 -0
- package/dist/session-aggregator.d.ts +3 -0
- package/dist/session-aggregator.d.ts.map +1 -1
- package/dist/session-aggregator.js.map +1 -1
- package/dist/session-registry.d.ts +1 -0
- package/dist/session-registry.d.ts.map +1 -1
- package/dist/surface-protocol.d.ts +84 -0
- package/dist/surface-protocol.d.ts.map +1 -0
- package/dist/surface-protocol.js +237 -0
- package/dist/surface-protocol.js.map +1 -0
- package/dist/timeline-store.d.ts +1 -1
- package/dist/timeline-store.d.ts.map +1 -1
- package/dist/timeline-store.js +16 -1
- package/dist/timeline-store.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/tui/renderer.d.ts.map +1 -1
- package/dist/tui/renderer.js +35 -0
- package/dist/tui/renderer.js.map +1 -1
- package/dist/weather-config.d.ts +17 -0
- package/dist/weather-config.d.ts.map +1 -0
- package/dist/weather-config.js +54 -0
- package/dist/weather-config.js.map +1 -0
- package/dist/weather.d.ts +44 -4
- package/dist/weather.d.ts.map +1 -1
- package/dist/weather.js +223 -15
- package/dist/weather.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 +95 -7
- package/dist/ws-server.js.map +1 -1
- package/fm-helper/AgentDeckFMHelper.swift +51 -5
- package/package.json +4 -3
- package/src/timebox/sync_ble.py +35 -16
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeline rows read from OpenClaw's OWN on-disk session store.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS. OpenClaw reaches this daemon through the Gateway WebSocket
|
|
5
|
+
* event stream and nothing else — the adapter's whole RPC surface is `connect`
|
|
6
|
+
* / `sessions.list` / `chat.send` / `chat.abort` / `exec.approval.*`. That
|
|
7
|
+
* stream carries the prompt, the final response, and the approvals it has to
|
|
8
|
+
* block on. It does NOT carry the individual tool calls, so the timeline showed
|
|
9
|
+
* "OpenClaw said something" with no account of what it actually did, and the
|
|
10
|
+
* only tools that ever appeared were the ones that happened to need approval —
|
|
11
|
+
* a biased sample, not a log.
|
|
12
|
+
*
|
|
13
|
+
* OpenClaw writes a full transcript per session (measured 2026-08-23 against
|
|
14
|
+
* OpenClaw 2026.7.1-2). This reads it, exactly the way `kiro-transcript-timeline`
|
|
15
|
+
* reads Kiro's — an agent that pushes nothing still has a producer.
|
|
16
|
+
*
|
|
17
|
+
* **Scope is deliberately the complement of the Gateway stream, not a
|
|
18
|
+
* duplicate of it.** Only `tool_exec` rows are emitted here. Prompts, responses
|
|
19
|
+
* and approval rows already arrive live over the Gateway and re-deriving them
|
|
20
|
+
* would double every turn in the strip.
|
|
21
|
+
*
|
|
22
|
+
* The record shapes below were copied off a real store, never invented — this
|
|
23
|
+
* repo has a specific history of parsers written against imagined fixtures that
|
|
24
|
+
* passed their tests and matched nothing real, so the test file pins the
|
|
25
|
+
* measured shape verbatim:
|
|
26
|
+
*
|
|
27
|
+
* {"type":"session","version":3,"id":"fef73899-…","timestamp":"2026-08-22T23:52:16.443Z",
|
|
28
|
+
* "cwd":"/Users/x/.openclaw/workspace"}
|
|
29
|
+
* {"type":"message","id":"577c04b8","parentId":"40296bc1",
|
|
30
|
+
* "timestamp":"2026-08-22T23:52:23.315Z",
|
|
31
|
+
* "message":{"role":"assistant","content":[
|
|
32
|
+
* {"type":"thinking","thinking":"…"},
|
|
33
|
+
* {"type":"toolCall","id":"38e16c61-…","name":"exec",
|
|
34
|
+
* "arguments":{"command":"ls ~/github/OpenClaw && …"}}],
|
|
35
|
+
* "provider":"openrouter","model":"stealth/ox-alpha","usage":{…}}}
|
|
36
|
+
* {"type":"message","id":"53079fbe","timestamp":"2026-08-22T23:52:23.376Z",
|
|
37
|
+
* "message":{"role":"toolResult","toolCallId":"38e16c61-…","toolName":"exec",
|
|
38
|
+
* "content":[{"type":"text","text":"CAPTURE_GUIDE.md\n…"}],
|
|
39
|
+
* "details":{"status":"completed","exitCode":0,"durationMs":13},"isError":false}}
|
|
40
|
+
*
|
|
41
|
+
* Three properties of that shape drive the code:
|
|
42
|
+
* - A line carries an ISO `timestamp` AND `message.timestamp` in epoch-ms.
|
|
43
|
+
* They agree; the ISO one is used, with the epoch one as the fallback,
|
|
44
|
+
* because a row with no time cannot be ordered against the live stream.
|
|
45
|
+
* - A tool call and its result are SEPARATE lines linked by `toolCallId`. The
|
|
46
|
+
* call names what was attempted and only the result knows whether it worked,
|
|
47
|
+
* so a row is emitted once, at the call, and completed from the result.
|
|
48
|
+
* - `isError` and `details.exitCode` disagree in the interesting direction: a
|
|
49
|
+
* command can exit 0 while the tool reports an error (and vice versa), so
|
|
50
|
+
* both are consulted rather than picking one.
|
|
51
|
+
*/
|
|
52
|
+
import { readFileSync, readdirSync, statSync } from 'fs';
|
|
53
|
+
import { homedir } from 'os';
|
|
54
|
+
import { join } from 'path';
|
|
55
|
+
import { debug } from './logger.js';
|
|
56
|
+
/** Cap on transcript bytes read, matching the Claude/Kiro readers' posture. */
|
|
57
|
+
const MAX_TRANSCRIPT_BYTES = 4 * 1024 * 1024;
|
|
58
|
+
/** Agent directories scanned under `~/.openclaw/agents`. */
|
|
59
|
+
const MAX_AGENT_DIRS = 32;
|
|
60
|
+
/** Row text budgets — these land on 144×144 keys and 120-byte firmware buffers. */
|
|
61
|
+
const RAW_MAX = 160;
|
|
62
|
+
const DETAIL_MAX = 400;
|
|
63
|
+
export function openClawStateDir(opts = {}) {
|
|
64
|
+
return opts.stateDir ?? join(homedir(), '.openclaw');
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Sessions across every agent, newest-first.
|
|
68
|
+
*
|
|
69
|
+
* Reads each agent's `sessions.json`, whose keys ARE the session keys — the one
|
|
70
|
+
* fact that says whether a given piece of OpenClaw activity is the chat the
|
|
71
|
+
* user is looking at (`agent:main:main`), a scheduled job
|
|
72
|
+
* (`agent:main:cron:<id>`) or a model-eval run (`agent:main:eval-…__r2`). All
|
|
73
|
+
* three arrive at AgentDeck as the single literal "OpenClaw", which is why an
|
|
74
|
+
* approval raised by an eval harness looked like it came from a conversation
|
|
75
|
+
* the user could not find.
|
|
76
|
+
*/
|
|
77
|
+
export function listOpenClawSessions(opts = {}) {
|
|
78
|
+
const agentsRoot = join(openClawStateDir(opts), 'agents');
|
|
79
|
+
let agents;
|
|
80
|
+
try {
|
|
81
|
+
agents = readdirSync(agentsRoot, { withFileTypes: true, encoding: 'utf-8' })
|
|
82
|
+
.filter((e) => e.isDirectory() && !e.isSymbolicLink())
|
|
83
|
+
.slice(0, MAX_AGENT_DIRS)
|
|
84
|
+
.map((e) => e.name);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
const out = [];
|
|
90
|
+
for (const agent of agents) {
|
|
91
|
+
const indexPath = join(agentsRoot, agent, 'sessions', 'sessions.json');
|
|
92
|
+
let parsed;
|
|
93
|
+
try {
|
|
94
|
+
const st = statSync(indexPath);
|
|
95
|
+
if (!st.isFile() || st.size > MAX_TRANSCRIPT_BYTES)
|
|
96
|
+
continue;
|
|
97
|
+
parsed = JSON.parse(readFileSync(indexPath, 'utf-8'));
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
103
|
+
continue;
|
|
104
|
+
for (const [sessionKey, value] of Object.entries(parsed)) {
|
|
105
|
+
if (!value || typeof value !== 'object')
|
|
106
|
+
continue;
|
|
107
|
+
const rec = value;
|
|
108
|
+
if (typeof rec.sessionId !== 'string' || !rec.sessionId)
|
|
109
|
+
continue;
|
|
110
|
+
out.push({
|
|
111
|
+
sessionKey,
|
|
112
|
+
sessionId: rec.sessionId,
|
|
113
|
+
agent,
|
|
114
|
+
updatedAt: typeof rec.updatedAt === 'number' ? rec.updatedAt : 0,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return out.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
119
|
+
}
|
|
120
|
+
function clip(s, max) {
|
|
121
|
+
const flat = s.replace(/\s+/g, ' ').trim();
|
|
122
|
+
return flat.length <= max ? flat : `${flat.slice(0, max - 1)}…`;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Join detail parts into LINES, flattening each one but keeping the breaks.
|
|
126
|
+
*
|
|
127
|
+
* `detail` is ordered most-decisive-first precisely so a narrow surface can
|
|
128
|
+
* take its head line, and every consumer does exactly that (`split('\n')[0]`).
|
|
129
|
+
* Running the whole block through `clip` collapses those breaks and there is no
|
|
130
|
+
* head line left to take — the ordering becomes decorative and the surface
|
|
131
|
+
* shows the first 22 characters of a run-on string instead.
|
|
132
|
+
*/
|
|
133
|
+
function clipLines(parts, max) {
|
|
134
|
+
const lines = [];
|
|
135
|
+
let budget = max;
|
|
136
|
+
for (const part of parts) {
|
|
137
|
+
if (!part)
|
|
138
|
+
continue;
|
|
139
|
+
for (const raw of part.split('\n')) {
|
|
140
|
+
if (budget <= 1)
|
|
141
|
+
return lines.join('\n');
|
|
142
|
+
const line = clip(raw, budget);
|
|
143
|
+
if (!line)
|
|
144
|
+
continue;
|
|
145
|
+
lines.push(line);
|
|
146
|
+
budget -= line.length + 1;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return lines.join('\n');
|
|
150
|
+
}
|
|
151
|
+
/** Epoch-ms for a store line. */
|
|
152
|
+
function lineTs(line) {
|
|
153
|
+
if (typeof line.timestamp === 'string') {
|
|
154
|
+
const parsed = Date.parse(line.timestamp);
|
|
155
|
+
if (Number.isFinite(parsed))
|
|
156
|
+
return parsed;
|
|
157
|
+
}
|
|
158
|
+
const inner = line.message?.timestamp;
|
|
159
|
+
if (typeof inner === 'number' && Number.isFinite(inner))
|
|
160
|
+
return inner;
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The one line that says what a tool call is going to do.
|
|
165
|
+
*
|
|
166
|
+
* `exec` is the overwhelming majority and its `command` is the whole story.
|
|
167
|
+
* Anything else falls back to the first string-valued argument, then to the
|
|
168
|
+
* argument names — a tool this build has never seen must degrade to something
|
|
169
|
+
* true ("browser · url, selector") rather than render as nothing.
|
|
170
|
+
*/
|
|
171
|
+
export function summarizeToolArguments(args) {
|
|
172
|
+
if (typeof args === 'string')
|
|
173
|
+
return args;
|
|
174
|
+
if (!args || typeof args !== 'object' || Array.isArray(args))
|
|
175
|
+
return '';
|
|
176
|
+
const rec = args;
|
|
177
|
+
for (const key of ['command', 'cmd', 'script', 'query', 'path', 'file', 'url', 'prompt']) {
|
|
178
|
+
const v = rec[key];
|
|
179
|
+
if (typeof v === 'string' && v.trim())
|
|
180
|
+
return v.trim();
|
|
181
|
+
}
|
|
182
|
+
for (const v of Object.values(rec)) {
|
|
183
|
+
if (typeof v === 'string' && v.trim())
|
|
184
|
+
return v.trim();
|
|
185
|
+
}
|
|
186
|
+
const names = Object.keys(rec);
|
|
187
|
+
return names.length > 0 ? names.join(', ') : '';
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* `tool_exec` rows for one session's transcript, oldest-first.
|
|
191
|
+
*
|
|
192
|
+
* Emitted at the CALL, then completed in place from the matching result: the
|
|
193
|
+
* call is when the work started and is what orders correctly against the live
|
|
194
|
+
* Gateway rows, while only the result knows the outcome. Building the row at
|
|
195
|
+
* the result instead would stamp every tool with the time it finished and lose
|
|
196
|
+
* any call still running when the read happened.
|
|
197
|
+
*/
|
|
198
|
+
export function openClawTimelineForSession(sessionId, opts = {}) {
|
|
199
|
+
const path = join(openClawStateDir(opts), 'agents');
|
|
200
|
+
let transcript = null;
|
|
201
|
+
let agents = [];
|
|
202
|
+
try {
|
|
203
|
+
agents = readdirSync(path, { withFileTypes: true, encoding: 'utf-8' })
|
|
204
|
+
.filter((e) => e.isDirectory() && !e.isSymbolicLink())
|
|
205
|
+
.slice(0, MAX_AGENT_DIRS)
|
|
206
|
+
.map((e) => e.name);
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
return [];
|
|
210
|
+
}
|
|
211
|
+
for (const agent of agents) {
|
|
212
|
+
const candidate = join(path, agent, 'sessions', `${sessionId}.jsonl`);
|
|
213
|
+
try {
|
|
214
|
+
const st = statSync(candidate);
|
|
215
|
+
if (st.isFile() && st.size <= MAX_TRANSCRIPT_BYTES) {
|
|
216
|
+
transcript = candidate;
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
// not this agent's session
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (!transcript)
|
|
225
|
+
return [];
|
|
226
|
+
let text;
|
|
227
|
+
try {
|
|
228
|
+
text = readFileSync(transcript, 'utf-8');
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
debug('daemon', `openclaw transcript read failed for ${sessionId}: ${String(err)}`);
|
|
232
|
+
return [];
|
|
233
|
+
}
|
|
234
|
+
const rows = [];
|
|
235
|
+
const pending = new Map();
|
|
236
|
+
let model;
|
|
237
|
+
let provider;
|
|
238
|
+
for (const line of text.split('\n')) {
|
|
239
|
+
if (!line.trim())
|
|
240
|
+
continue;
|
|
241
|
+
let rec;
|
|
242
|
+
try {
|
|
243
|
+
rec = JSON.parse(line);
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (rec.type !== 'message')
|
|
249
|
+
continue;
|
|
250
|
+
const msg = rec.message;
|
|
251
|
+
if (!msg || typeof msg !== 'object')
|
|
252
|
+
continue;
|
|
253
|
+
const ts = lineTs(rec);
|
|
254
|
+
if (ts === null)
|
|
255
|
+
continue;
|
|
256
|
+
if (msg.role === 'assistant') {
|
|
257
|
+
// Carried onto the rows this turn produces: which weights actually ran is
|
|
258
|
+
// the fact an eval session exists to vary, and the Gateway stream reports
|
|
259
|
+
// one model for the whole hub.
|
|
260
|
+
if (typeof msg.model === 'string' && msg.model)
|
|
261
|
+
model = msg.model;
|
|
262
|
+
if (typeof msg.provider === 'string' && msg.provider)
|
|
263
|
+
provider = msg.provider;
|
|
264
|
+
const content = Array.isArray(msg.content) ? msg.content : [];
|
|
265
|
+
for (const part of content) {
|
|
266
|
+
if (!part || typeof part !== 'object')
|
|
267
|
+
continue;
|
|
268
|
+
const p = part;
|
|
269
|
+
if (p.type !== 'toolCall')
|
|
270
|
+
continue;
|
|
271
|
+
const toolName = typeof p.name === 'string' && p.name ? p.name : 'tool';
|
|
272
|
+
const summary = summarizeToolArguments(p.arguments);
|
|
273
|
+
const detailParts = [
|
|
274
|
+
model ? `model: ${provider ? `${provider}/${model}` : model}` : '',
|
|
275
|
+
].filter(Boolean);
|
|
276
|
+
rows.push({
|
|
277
|
+
ts,
|
|
278
|
+
type: 'tool_exec',
|
|
279
|
+
raw: clip(summary ? `${toolName} · ${summary}` : toolName, RAW_MAX),
|
|
280
|
+
...(detailParts.length > 0 ? { detail: clipLines(detailParts, DETAIL_MAX) } : {}),
|
|
281
|
+
agentType: 'openclaw',
|
|
282
|
+
});
|
|
283
|
+
if (typeof p.id === 'string' && p.id) {
|
|
284
|
+
pending.set(p.id, { index: rows.length - 1, toolName });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (msg.role === 'toolResult') {
|
|
290
|
+
const callId = typeof msg.toolCallId === 'string' ? msg.toolCallId : '';
|
|
291
|
+
const target = callId ? pending.get(callId) : undefined;
|
|
292
|
+
if (!target)
|
|
293
|
+
continue;
|
|
294
|
+
pending.delete(callId);
|
|
295
|
+
const details = (msg.details ?? {});
|
|
296
|
+
const exitCode = typeof details.exitCode === 'number' ? details.exitCode : undefined;
|
|
297
|
+
const durationMs = typeof details.durationMs === 'number' ? details.durationMs : undefined;
|
|
298
|
+
// `isError` and a non-zero exit are independent signals — a tool can fail
|
|
299
|
+
// to run at all (isError, no exit code) and a command can fail while the
|
|
300
|
+
// tool call itself succeeded. Either one means this did not go well.
|
|
301
|
+
const failed = msg.isError === true || (exitCode !== undefined && exitCode !== 0);
|
|
302
|
+
const first = Array.isArray(msg.content)
|
|
303
|
+
? msg.content.find((c) => c && typeof c === 'object' && c.type === 'text')
|
|
304
|
+
: undefined;
|
|
305
|
+
const output = typeof first?.text === 'string' ? first.text : '';
|
|
306
|
+
const outcome = [
|
|
307
|
+
failed ? 'failed' : 'ok',
|
|
308
|
+
exitCode !== undefined ? `exit ${exitCode}` : '',
|
|
309
|
+
durationMs !== undefined ? `${durationMs}ms` : '',
|
|
310
|
+
].filter(Boolean).join(' · ');
|
|
311
|
+
const existing = rows[target.index];
|
|
312
|
+
// Outcome, then the model, then the output. The first two are short and
|
|
313
|
+
// decisive; the output is unbounded, so putting it ahead of the model
|
|
314
|
+
// pushed "which weights ran" past the clip on any tool that printed more
|
|
315
|
+
// than a line — the one fact an eval session exists to vary.
|
|
316
|
+
rows[target.index] = {
|
|
317
|
+
...existing,
|
|
318
|
+
detail: clipLines([outcome, existing.detail, output], DETAIL_MAX),
|
|
319
|
+
...(failed ? { status: 'denied' } : {}),
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const since = opts.since;
|
|
324
|
+
const filtered = typeof since === 'number' ? rows.filter((r) => r.ts > since) : rows;
|
|
325
|
+
const limit = opts.limit;
|
|
326
|
+
return typeof limit === 'number' && limit >= 0 ? filtered.slice(-limit) : filtered;
|
|
327
|
+
}
|
|
328
|
+
//# sourceMappingURL=openclaw-transcript-timeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openclaw-transcript-timeline.js","sourceRoot":"","sources":["../src/openclaw-transcript-timeline.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"}
|