@inbrowser/agent 0.0.0-placeholder → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +270 -0
- package/README.md +117 -2
- package/bin/agent.ts +10 -0
- package/dist/cli/commands/describe.d.ts +14 -0
- package/dist/cli/commands/describe.d.ts.map +1 -0
- package/dist/cli/commands/describe.js +179 -0
- package/dist/cli/commands/describe.js.map +1 -0
- package/dist/cli/commands/events.d.ts +21 -0
- package/dist/cli/commands/events.d.ts.map +1 -0
- package/dist/cli/commands/events.js +59 -0
- package/dist/cli/commands/events.js.map +1 -0
- package/dist/cli/commands/fleet.d.ts +15 -0
- package/dist/cli/commands/fleet.d.ts.map +1 -0
- package/dist/cli/commands/fleet.js +149 -0
- package/dist/cli/commands/fleet.js.map +1 -0
- package/dist/cli/commands/help.d.ts +15 -0
- package/dist/cli/commands/help.d.ts.map +1 -0
- package/dist/cli/commands/help.js +93 -0
- package/dist/cli/commands/help.js.map +1 -0
- package/dist/cli/commands/migrate.d.ts +27 -0
- package/dist/cli/commands/migrate.d.ts.map +1 -0
- package/dist/cli/commands/migrate.js +109 -0
- package/dist/cli/commands/migrate.js.map +1 -0
- package/dist/cli/commands/run.d.ts +38 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +535 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/schema.d.ts +8 -0
- package/dist/cli/commands/schema.d.ts.map +1 -0
- package/dist/cli/commands/schema.js +12 -0
- package/dist/cli/commands/schema.js.map +1 -0
- package/dist/cli/commands/serve.d.ts +39 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/serve.js +65 -0
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/undo.d.ts +36 -0
- package/dist/cli/commands/undo.d.ts.map +1 -0
- package/dist/cli/commands/undo.js +132 -0
- package/dist/cli/commands/undo.js.map +1 -0
- package/dist/cli/fixtures.d.ts +17 -0
- package/dist/cli/fixtures.d.ts.map +1 -0
- package/dist/cli/fixtures.js +107 -0
- package/dist/cli/fixtures.js.map +1 -0
- package/dist/cli/hardening.d.ts +39 -0
- package/dist/cli/hardening.d.ts.map +1 -0
- package/dist/cli/hardening.js +68 -0
- package/dist/cli/hardening.js.map +1 -0
- package/dist/cli/index.d.ts +28 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +19 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/llm/openrouter.d.ts +33 -0
- package/dist/cli/llm/openrouter.d.ts.map +1 -0
- package/dist/cli/llm/openrouter.js +285 -0
- package/dist/cli/llm/openrouter.js.map +1 -0
- package/dist/cli/main.d.ts +32 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +106 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/output.d.ts +36 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +95 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/parse.d.ts +26 -0
- package/dist/cli/parse.d.ts.map +1 -0
- package/dist/cli/parse.js +160 -0
- package/dist/cli/parse.js.map +1 -0
- package/dist/cli/session-log.d.ts +34 -0
- package/dist/cli/session-log.d.ts.map +1 -0
- package/dist/cli/session-log.js +52 -0
- package/dist/cli/session-log.js.map +1 -0
- package/dist/cli/spec.d.ts +62 -0
- package/dist/cli/spec.d.ts.map +1 -0
- package/dist/cli/spec.js +510 -0
- package/dist/cli/spec.js.map +1 -0
- package/dist/cli/ui/RunView.d.ts +134 -0
- package/dist/cli/ui/RunView.d.ts.map +1 -0
- package/dist/cli/ui/RunView.js +341 -0
- package/dist/cli/ui/RunView.js.map +1 -0
- package/dist/events/codec.d.ts +79 -0
- package/dist/events/codec.d.ts.map +1 -0
- package/dist/events/codec.js +142 -0
- package/dist/events/codec.js.map +1 -0
- package/dist/events/log-core.d.ts +76 -0
- package/dist/events/log-core.d.ts.map +1 -0
- package/dist/events/log-core.js +73 -0
- package/dist/events/log-core.js.map +1 -0
- package/dist/events/log.d.ts +60 -0
- package/dist/events/log.d.ts.map +1 -0
- package/dist/events/log.js +193 -0
- package/dist/events/log.js.map +1 -0
- package/dist/events/replay.d.ts +106 -0
- package/dist/events/replay.d.ts.map +1 -0
- package/dist/events/replay.js +137 -0
- package/dist/events/replay.js.map +1 -0
- package/dist/events/wrap.d.ts +100 -0
- package/dist/events/wrap.d.ts.map +1 -0
- package/dist/events/wrap.js +141 -0
- package/dist/events/wrap.js.map +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/llm-adapter.d.ts +96 -0
- package/dist/llm-adapter.d.ts.map +1 -0
- package/dist/llm-adapter.js +132 -0
- package/dist/llm-adapter.js.map +1 -0
- package/dist/mcp/serve.d.ts +70 -0
- package/dist/mcp/serve.d.ts.map +1 -0
- package/dist/mcp/serve.js +154 -0
- package/dist/mcp/serve.js.map +1 -0
- package/dist/metrics/runs.d.ts +58 -0
- package/dist/metrics/runs.d.ts.map +1 -0
- package/dist/metrics/runs.js +99 -0
- package/dist/metrics/runs.js.map +1 -0
- package/dist/metrics.d.ts +38 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +123 -0
- package/dist/metrics.js.map +1 -0
- package/dist/node.d.ts +22 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +22 -0
- package/dist/node.js.map +1 -0
- package/dist/session.d.ts +10 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +179 -0
- package/dist/session.js.map +1 -0
- package/dist/storage.d.ts +14 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +58 -0
- package/dist/storage.js.map +1 -0
- package/dist/strategy.d.ts +26 -0
- package/dist/strategy.d.ts.map +1 -0
- package/dist/strategy.js +200 -0
- package/dist/strategy.js.map +1 -0
- package/dist/tools.d.ts +26 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +129 -0
- package/dist/tools.js.map +1 -0
- package/dist/types/agent.d.ts +94 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +17 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/capabilities.d.ts +17 -0
- package/dist/types/capabilities.d.ts.map +1 -0
- package/dist/types/capabilities.js +13 -0
- package/dist/types/capabilities.js.map +1 -0
- package/dist/types/chat.d.ts +74 -0
- package/dist/types/chat.d.ts.map +1 -0
- package/dist/types/chat.js +10 -0
- package/dist/types/chat.js.map +1 -0
- package/dist/types/events.d.ts +115 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +30 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/llm.d.ts +89 -0
- package/dist/types/llm.d.ts.map +1 -0
- package/dist/types/llm.js +12 -0
- package/dist/types/llm.js.map +1 -0
- package/dist/types/metrics.d.ts +34 -0
- package/dist/types/metrics.d.ts.map +1 -0
- package/dist/types/metrics.js +10 -0
- package/dist/types/metrics.js.map +1 -0
- package/dist/types/observer.d.ts +41 -0
- package/dist/types/observer.d.ts.map +1 -0
- package/dist/types/observer.js +41 -0
- package/dist/types/observer.js.map +1 -0
- package/dist/types/project-context.d.ts +18 -0
- package/dist/types/project-context.d.ts.map +1 -0
- package/dist/types/project-context.js +11 -0
- package/dist/types/project-context.js.map +1 -0
- package/dist/types/runtime.d.ts +71 -0
- package/dist/types/runtime.d.ts.map +1 -0
- package/dist/types/runtime.js +21 -0
- package/dist/types/runtime.js.map +1 -0
- package/dist/types/session.d.ts +103 -0
- package/dist/types/session.d.ts.map +1 -0
- package/dist/types/session.js +11 -0
- package/dist/types/session.js.map +1 -0
- package/dist/types/storage.d.ts +20 -0
- package/dist/types/storage.d.ts.map +1 -0
- package/dist/types/storage.js +41 -0
- package/dist/types/storage.js.map +1 -0
- package/dist/types/strategy.d.ts +76 -0
- package/dist/types/strategy.d.ts.map +1 -0
- package/dist/types/strategy.js +10 -0
- package/dist/types/strategy.js.map +1 -0
- package/dist/types/tools.d.ts +136 -0
- package/dist/types/tools.d.ts.map +1 -0
- package/dist/types/tools.js +11 -0
- package/dist/types/tools.js.map +1 -0
- package/dist/types/trace.d.ts +125 -0
- package/dist/types/trace.d.ts.map +1 -0
- package/dist/types/trace.js +24 -0
- package/dist/types/trace.js.map +1 -0
- package/dist/types/workspace.d.ts +29 -0
- package/dist/types/workspace.d.ts.map +1 -0
- package/dist/types/workspace.js +18 -0
- package/dist/types/workspace.js.map +1 -0
- package/package.json +45 -14
- package/skills/agent-cli.md +218 -0
- package/index.js +0 -2
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Run view — OpenTUI/React renderer for `agent run` when stdout is a
|
|
4
|
+
* TTY. Subscribes to the `AgentSession` event stream and projects it
|
|
5
|
+
* into a four-section layout: header (run metadata) → thinking
|
|
6
|
+
* (scrollable trace) → output (markdown) → tool cards → footer
|
|
7
|
+
* (token/cost/elapsed).
|
|
8
|
+
*
|
|
9
|
+
* Wiring lives in `cli/commands/run.ts` — the runCommand picks this
|
|
10
|
+
* over the NDJSON emitter when `pickMode()` resolves to `text` AND
|
|
11
|
+
* stdout.isTTY. NDJSON / piped / `--output ndjson` keep the existing
|
|
12
|
+
* emitter behavior; agent integrations + tests rely on it.
|
|
13
|
+
*
|
|
14
|
+
* v0 scaffold (this file): state machine + placeholder layout. The
|
|
15
|
+
* scrollbox / markdown / code components plug in over the next two
|
|
16
|
+
* phases.
|
|
17
|
+
*/
|
|
18
|
+
import { RGBA, SyntaxStyle } from '@opentui/core';
|
|
19
|
+
import { useKeyboard } from '@opentui/react';
|
|
20
|
+
import { useEffect, useReducer } from 'react';
|
|
21
|
+
/**
|
|
22
|
+
* Shared syntax-highlighting palette for the `<markdown>` and `<code>`
|
|
23
|
+
* components. Dark-on-default; close enough to GitHub-dark for
|
|
24
|
+
* markdown headings + code fences. Tree-sitter capture names follow
|
|
25
|
+
* the upstream highlight-queries convention (`markup.heading.1`,
|
|
26
|
+
* `keyword`, `string`, …) — fall back to `default` for unmapped
|
|
27
|
+
* captures.
|
|
28
|
+
*/
|
|
29
|
+
const SYNTAX_STYLE = SyntaxStyle.fromStyles({
|
|
30
|
+
default: { fg: RGBA.fromHex('#e6edf3') },
|
|
31
|
+
'markup.heading.1': { fg: RGBA.fromHex('#58a6ff'), bold: true },
|
|
32
|
+
'markup.heading.2': { fg: RGBA.fromHex('#58a6ff'), bold: true },
|
|
33
|
+
'markup.heading.3': { fg: RGBA.fromHex('#58a6ff'), bold: true },
|
|
34
|
+
'markup.list': { fg: RGBA.fromHex('#ff7b72') },
|
|
35
|
+
'markup.bold': { fg: RGBA.fromHex('#e6edf3'), bold: true },
|
|
36
|
+
'markup.italic': { fg: RGBA.fromHex('#e6edf3'), italic: true },
|
|
37
|
+
'markup.raw': { fg: RGBA.fromHex('#a5d6ff') },
|
|
38
|
+
'markup.link': { fg: RGBA.fromHex('#79c0ff'), underline: true },
|
|
39
|
+
keyword: { fg: RGBA.fromHex('#ff7b72') },
|
|
40
|
+
string: { fg: RGBA.fromHex('#a5d6ff') },
|
|
41
|
+
number: { fg: RGBA.fromHex('#79c0ff') },
|
|
42
|
+
function: { fg: RGBA.fromHex('#d2a8ff') },
|
|
43
|
+
comment: { fg: RGBA.fromHex('#8b949e'), italic: true },
|
|
44
|
+
type: { fg: RGBA.fromHex('#ffa657') },
|
|
45
|
+
variable: { fg: RGBA.fromHex('#e6edf3') },
|
|
46
|
+
punctuation: { fg: RGBA.fromHex('#8b949e') },
|
|
47
|
+
});
|
|
48
|
+
export const initialState = {
|
|
49
|
+
sessionId: '',
|
|
50
|
+
scenario: '',
|
|
51
|
+
llmLabel: '',
|
|
52
|
+
promptPreview: '',
|
|
53
|
+
currentTurn: 0,
|
|
54
|
+
thinking: '',
|
|
55
|
+
output: '',
|
|
56
|
+
tools: [],
|
|
57
|
+
status: 'idle',
|
|
58
|
+
tokensIn: 0,
|
|
59
|
+
tokensOut: 0,
|
|
60
|
+
tokensCached: 0,
|
|
61
|
+
tokensReasoning: 0,
|
|
62
|
+
costUsd: 0,
|
|
63
|
+
startedAtMs: Date.now(),
|
|
64
|
+
elapsedMs: 0,
|
|
65
|
+
thinkingCollapsed: false,
|
|
66
|
+
};
|
|
67
|
+
export function reduce(s, a) {
|
|
68
|
+
switch (a.type) {
|
|
69
|
+
case 'session_start':
|
|
70
|
+
return {
|
|
71
|
+
...s,
|
|
72
|
+
sessionId: a.sessionId,
|
|
73
|
+
scenario: a.scenario,
|
|
74
|
+
promptPreview: a.promptPreview,
|
|
75
|
+
status: 'thinking',
|
|
76
|
+
startedAtMs: Date.now(),
|
|
77
|
+
};
|
|
78
|
+
case 'llm_selected':
|
|
79
|
+
return {
|
|
80
|
+
...s,
|
|
81
|
+
llmLabel: a.label,
|
|
82
|
+
...(a.fallbackReason ? { llmFallbackReason: a.fallbackReason } : {}),
|
|
83
|
+
};
|
|
84
|
+
case 'turn_started':
|
|
85
|
+
return { ...s, currentTurn: a.turnNo, status: 'thinking' };
|
|
86
|
+
case 'thinking':
|
|
87
|
+
return { ...s, status: 'thinking', thinking: s.thinking + a.chunk };
|
|
88
|
+
case 'text':
|
|
89
|
+
return { ...s, status: 'streaming', output: s.output + a.chunk };
|
|
90
|
+
case 'tool_started':
|
|
91
|
+
return {
|
|
92
|
+
...s,
|
|
93
|
+
status: 'tool',
|
|
94
|
+
tools: [...s.tools, { callId: a.callId, name: a.name, args: a.args }],
|
|
95
|
+
};
|
|
96
|
+
case 'tool_finished':
|
|
97
|
+
return {
|
|
98
|
+
...s,
|
|
99
|
+
tools: s.tools.map((t) => t.callId === a.callId
|
|
100
|
+
? { ...t, result: { ok: a.ok, ...(a.summary ? { summary: a.summary } : {}) } }
|
|
101
|
+
: t),
|
|
102
|
+
};
|
|
103
|
+
case 'turn_completed':
|
|
104
|
+
return {
|
|
105
|
+
...s,
|
|
106
|
+
tokensIn: s.tokensIn + a.tokensIn,
|
|
107
|
+
tokensOut: s.tokensOut + a.tokensOut,
|
|
108
|
+
tokensCached: s.tokensCached + a.tokensCached,
|
|
109
|
+
tokensReasoning: s.tokensReasoning + a.tokensReasoning,
|
|
110
|
+
costUsd: s.costUsd + a.costUsd,
|
|
111
|
+
};
|
|
112
|
+
case 'session_end':
|
|
113
|
+
return { ...s, status: 'done' };
|
|
114
|
+
case 'error':
|
|
115
|
+
return { ...s, status: 'error', errorMessage: a.message };
|
|
116
|
+
case 'tick':
|
|
117
|
+
return { ...s, elapsedMs: a.nowMs - s.startedAtMs };
|
|
118
|
+
case 'toggle_thinking':
|
|
119
|
+
return { ...s, thinkingCollapsed: !s.thinkingCollapsed };
|
|
120
|
+
default:
|
|
121
|
+
return s;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// ─── Stream ↔ dispatch bridge ────────────────────────────────────────
|
|
125
|
+
/**
|
|
126
|
+
* Consume an async iterable of SessionEvents and dispatch reducer
|
|
127
|
+
* actions. Stops on `completed` or `error`. Caller is responsible for
|
|
128
|
+
* passing the cancellation `signal` through if the user aborts.
|
|
129
|
+
*/
|
|
130
|
+
async function pumpEvents(stream, dispatch) {
|
|
131
|
+
let turnNo = 0;
|
|
132
|
+
for await (const ev of stream) {
|
|
133
|
+
switch (ev.kind) {
|
|
134
|
+
case 'turn_started':
|
|
135
|
+
turnNo += 1;
|
|
136
|
+
dispatch({ type: 'turn_started', turnNo });
|
|
137
|
+
break;
|
|
138
|
+
case 'thinking':
|
|
139
|
+
dispatch({ type: 'thinking', chunk: ev.chunk });
|
|
140
|
+
break;
|
|
141
|
+
case 'text':
|
|
142
|
+
dispatch({ type: 'text', chunk: ev.chunk });
|
|
143
|
+
break;
|
|
144
|
+
case 'tool_started':
|
|
145
|
+
dispatch({
|
|
146
|
+
type: 'tool_started',
|
|
147
|
+
callId: ev.callId,
|
|
148
|
+
name: ev.name,
|
|
149
|
+
args: ev.args,
|
|
150
|
+
});
|
|
151
|
+
break;
|
|
152
|
+
case 'tool_finished':
|
|
153
|
+
dispatch({
|
|
154
|
+
type: 'tool_finished',
|
|
155
|
+
callId: ev.callId,
|
|
156
|
+
ok: ev.result.ok,
|
|
157
|
+
...(ev.result.summary ? { summary: ev.result.summary } : {}),
|
|
158
|
+
});
|
|
159
|
+
break;
|
|
160
|
+
case 'turn_completed':
|
|
161
|
+
dispatch({
|
|
162
|
+
type: 'turn_completed',
|
|
163
|
+
tokensIn: ev.metrics.tokensIn,
|
|
164
|
+
tokensOut: ev.metrics.tokensOut,
|
|
165
|
+
tokensCached: ev.metrics.tokensCached,
|
|
166
|
+
tokensReasoning: ev.metrics.tokensReasoning,
|
|
167
|
+
costUsd: ev.metrics.costUsd,
|
|
168
|
+
});
|
|
169
|
+
break;
|
|
170
|
+
case 'error':
|
|
171
|
+
dispatch({ type: 'error', message: ev.message });
|
|
172
|
+
break;
|
|
173
|
+
case 'completed':
|
|
174
|
+
dispatch({ type: 'session_end' });
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// ─── Component ───────────────────────────────────────────────────────
|
|
180
|
+
/**
|
|
181
|
+
* v0 stub. Phase 4 fills in the actual layout (header, scrollbox of
|
|
182
|
+
* thinking, markdown output, tool cards, footer). For now this just
|
|
183
|
+
* proves the pipe works — reducer dispatch, async-iterable pump, and
|
|
184
|
+
* a single `<box>` showing live status.
|
|
185
|
+
*/
|
|
186
|
+
export function RunView({ events, bootstrap, onQuit }) {
|
|
187
|
+
const [state, dispatch] = useReducer(reduce, {
|
|
188
|
+
...initialState,
|
|
189
|
+
sessionId: bootstrap.sessionId,
|
|
190
|
+
scenario: bootstrap.scenario,
|
|
191
|
+
llmLabel: bootstrap.llmLabel,
|
|
192
|
+
promptPreview: bootstrap.promptPreview,
|
|
193
|
+
...(bootstrap.llmFallbackReason ? { llmFallbackReason: bootstrap.llmFallbackReason } : {}),
|
|
194
|
+
...(bootstrap.logPath ? { logPath: bootstrap.logPath } : {}),
|
|
195
|
+
});
|
|
196
|
+
// Pump events into the reducer. Restarts only if the iterator
|
|
197
|
+
// identity changes (it won't, mid-session).
|
|
198
|
+
useEffect(() => {
|
|
199
|
+
let cancelled = false;
|
|
200
|
+
void (async () => {
|
|
201
|
+
try {
|
|
202
|
+
await pumpEvents(events, (a) => {
|
|
203
|
+
if (!cancelled)
|
|
204
|
+
dispatch(a);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
if (!cancelled) {
|
|
209
|
+
dispatch({
|
|
210
|
+
type: 'error',
|
|
211
|
+
message: err instanceof Error ? err.message : String(err),
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// No onComplete fire here — the TUI stays up after stream end
|
|
216
|
+
// so the user can read the FinalSummary. They exit via `q` /
|
|
217
|
+
// `Escape` (handled below).
|
|
218
|
+
})();
|
|
219
|
+
return () => {
|
|
220
|
+
cancelled = true;
|
|
221
|
+
};
|
|
222
|
+
}, [events]);
|
|
223
|
+
// Elapsed-timer tick — once a second, dispatch a tick so the footer
|
|
224
|
+
// re-renders with a live elapsed counter even when no events fire.
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
if (state.status === 'done' || state.status === 'error')
|
|
227
|
+
return;
|
|
228
|
+
const id = setInterval(() => dispatch({ type: 'tick', nowMs: Date.now() }), 1000);
|
|
229
|
+
return () => clearInterval(id);
|
|
230
|
+
}, [state.status]);
|
|
231
|
+
// Keyboard:
|
|
232
|
+
// `t` — toggle thinking-section visibility (works any time)
|
|
233
|
+
// `q` / Escape — exit, but only after the session has ended.
|
|
234
|
+
// Pressing q mid-stream is a no-op rather than
|
|
235
|
+
// aborting; aborting would need a cancellation
|
|
236
|
+
// handle we don't thread through the view today.
|
|
237
|
+
// Ctrl-C — handled by the renderer (`exitOnCtrlC: true`)
|
|
238
|
+
// in `runWithTui`. Hard exit; runs the post-
|
|
239
|
+
// teardown summary path.
|
|
240
|
+
useKeyboard((key) => {
|
|
241
|
+
if (key.name === 't') {
|
|
242
|
+
dispatch({ type: 'toggle_thinking' });
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (key.name === 'q' || key.name === 'escape') {
|
|
246
|
+
if (state.status === 'done' || state.status === 'error')
|
|
247
|
+
onQuit?.();
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
const done = state.status === 'done' || state.status === 'error';
|
|
251
|
+
return (_jsxs("box", { style: { flexDirection: 'column', padding: 1, gap: 1 }, children: [_jsx(Header, { state: state }), _jsx(ThinkingSection, { state: state, dispatch: dispatch }), _jsx(OutputSection, { state: state }), _jsx(ToolsSection, { state: state }), done ? _jsx(FinalSummary, { state: state }) : _jsx(Footer, { state: state })] }));
|
|
252
|
+
}
|
|
253
|
+
// ─── Sections ────────────────────────────────────────────────────────
|
|
254
|
+
/**
|
|
255
|
+
* Header — session id, llm label, prompt preview. Rendered every tick
|
|
256
|
+
* (cheap) so a single component owns the chrome. The data is set once
|
|
257
|
+
* via bootstrap and `llm_selected` and doesn't change again.
|
|
258
|
+
*/
|
|
259
|
+
function Header({ state }) {
|
|
260
|
+
return (_jsxs("box", { style: { border: true, padding: 1, flexDirection: 'column' }, children: [_jsxs("text", { children: [_jsx("text", { fg: "#888", children: "session " }), state.sessionId] }), _jsxs("text", { children: [_jsx("text", { fg: "#888", children: "llm " }), state.llmLabel] }), state.llmFallbackReason ? _jsxs("text", { fg: "#888", children: [" (", state.llmFallbackReason, ")"] }) : null, _jsxs("text", { children: [_jsx("text", { fg: "#888", children: "prompt " }), state.promptPreview] })] }));
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Thinking — accumulates reasoning chunks into a scrollable trace.
|
|
264
|
+
* Hidden until the first chunk arrives. Auto-scrolls to bottom on new
|
|
265
|
+
* content via `stickyScroll`. A char counter in the heading gives a
|
|
266
|
+
* sense of magnitude even when the trace overflows.
|
|
267
|
+
*/
|
|
268
|
+
function ThinkingSection({ state, dispatch, }) {
|
|
269
|
+
void dispatch; // dispatch isn't used here directly — the `t` keyboard
|
|
270
|
+
// handler in RunView owns toggling. Accepted as a prop
|
|
271
|
+
// for future affordances (clickable header, etc.).
|
|
272
|
+
if (state.thinking.length === 0)
|
|
273
|
+
return null;
|
|
274
|
+
const collapsed = state.thinkingCollapsed;
|
|
275
|
+
return (_jsxs("box", { style: { flexDirection: 'column' }, children: [_jsxs("text", { fg: "#888", children: [collapsed ? '▸' : '▾', " Thinking \u00B7 ", state.thinking.length, " chars", _jsxs("text", { fg: "#666", children: [" [t to ", collapsed ? 'expand' : 'collapse', "]"] })] }), collapsed ? null : (_jsx("scrollbox", { style: { height: 8, border: true, padding: 1 }, stickyScroll: true, children: _jsx("text", { fg: "#888", children: state.thinking }) }))] }));
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Output — the model's answer rendered as markdown so headings, code
|
|
279
|
+
* fences, and lists land formatted instead of as raw prose. While
|
|
280
|
+
* streaming, partial markdown will re-render mid-construct (open code
|
|
281
|
+
* fence, half a header) — that's expected; the render settles when
|
|
282
|
+
* the chunk completes the syntax.
|
|
283
|
+
*/
|
|
284
|
+
function OutputSection({ state }) {
|
|
285
|
+
if (state.output.length === 0) {
|
|
286
|
+
return _jsx("text", { fg: "#888", children: " \u2839 waiting for output\u2026" });
|
|
287
|
+
}
|
|
288
|
+
return (_jsxs("box", { style: { flexDirection: 'column' }, children: [_jsx("text", { fg: "#888", children: "Output" }), _jsx("markdown", { content: state.output, syntaxStyle: SYNTAX_STYLE })] }));
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Tools — one card per tool call. While in flight, the card shows a
|
|
292
|
+
* spinner; on `tool_finished` it switches to ✓/✗ + summary. Args are
|
|
293
|
+
* rendered as JSON via the `<code>` component so they get
|
|
294
|
+
* syntax-highlighted (useful for big payloads).
|
|
295
|
+
*/
|
|
296
|
+
function ToolsSection({ state }) {
|
|
297
|
+
if (state.tools.length === 0)
|
|
298
|
+
return null;
|
|
299
|
+
return (_jsxs("box", { style: { flexDirection: 'column' }, children: [_jsx("text", { fg: "#888", children: "Tools" }), state.tools.map((t) => (_jsxs("box", { style: { border: true, padding: 1, flexDirection: 'column' }, children: [_jsxs("text", { children: [t.result ? (t.result.ok ? '✓' : '✗') : '⠹', " ", t.name, t.result?.summary ? _jsxs("text", { fg: "#aaa", children: [" \u00B7 ", t.result.summary] }) : null] }), _jsx("code", { content: safeStringify(t.args), filetype: "json", syntaxStyle: SYNTAX_STYLE })] }, t.callId)))] }));
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Footer — running totals + elapsed timer + status. Updates every
|
|
303
|
+
* 1s via the `tick` action AND on every event. Freezes on
|
|
304
|
+
* `session_end`; the bottom border + `done`/`error` status make the
|
|
305
|
+
* frozen state obvious so the user knows the run is over.
|
|
306
|
+
*/
|
|
307
|
+
function Footer({ state }) {
|
|
308
|
+
const statusColor = state.status === 'error' ? '#f55' : state.status === 'done' ? '#5f5' : '#aaa';
|
|
309
|
+
return (_jsxs("box", { style: { border: true, padding: 1, flexDirection: 'column' }, children: [_jsxs("text", { children: [_jsx("text", { fg: statusColor, children: state.status }), _jsx("text", { fg: "#888", children: " \u00B7 turn " }), state.currentTurn || 0, _jsx("text", { fg: "#888", children: " \u00B7 " }), state.tokensIn, "+", state.tokensOut, " tok", _jsx("text", { fg: "#888", children: " \u00B7 $" }), state.costUsd.toFixed(6), _jsx("text", { fg: "#888", children: " \u00B7 " }), (state.elapsedMs / 1000).toFixed(1), "s"] }), state.errorMessage ? _jsxs("text", { fg: "#f55", children: ["error: ", state.errorMessage] }) : null] }));
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Final summary — replaces the live Footer once status flips to
|
|
313
|
+
* `done` or `error`. Shows the full token breakdown (cached +
|
|
314
|
+
* reasoning surfaced separately so the user understands provider
|
|
315
|
+
* cost composition), elapsed time, log path, and exit status.
|
|
316
|
+
*
|
|
317
|
+
* Visible only briefly today — runWithTui awaits `onComplete` then
|
|
318
|
+
* immediately destroys the renderer. Phase 6 will keep the TUI alive
|
|
319
|
+
* until the user presses `q`, at which point this is what they read.
|
|
320
|
+
*/
|
|
321
|
+
function FinalSummary({ state }) {
|
|
322
|
+
const statusColor = state.status === 'error' ? '#f55' : '#5f5';
|
|
323
|
+
const tokensTotal = state.tokensIn + state.tokensOut;
|
|
324
|
+
return (_jsxs("box", { style: { border: true, padding: 1, flexDirection: 'column' }, children: [_jsx("text", { children: _jsx("text", { fg: statusColor, children: state.status === 'error' ? '✗ Session failed' : '✓ Session complete' }) }), _jsx("text", { children: " " }), _jsxs("text", { children: [_jsx("text", { fg: "#888", children: "turns " }), state.currentTurn || 0] }), _jsxs("text", { children: [_jsx("text", { fg: "#888", children: "tokens " }), formatNum(tokensTotal), _jsxs("text", { fg: "#888", children: [' ', "(in ", formatNum(state.tokensIn), " \u00B7 out ", formatNum(state.tokensOut)] }), state.tokensCached > 0 ? (_jsxs("text", { fg: "#888", children: [" \u00B7 cached ", formatNum(state.tokensCached)] })) : null, state.tokensReasoning > 0 ? (_jsxs("text", { fg: "#888", children: [" \u00B7 reasoning ", formatNum(state.tokensReasoning)] })) : null, _jsx("text", { fg: "#888", children: ")" })] }), _jsxs("text", { children: [_jsx("text", { fg: "#888", children: "cost " }), "$", state.costUsd.toFixed(6)] }), _jsxs("text", { children: [_jsx("text", { fg: "#888", children: "elapsed " }), (state.elapsedMs / 1000).toFixed(1), "s"] }), state.logPath ? (_jsxs("text", { children: [_jsx("text", { fg: "#888", children: "log " }), state.logPath] })) : null, state.errorMessage ? _jsxs("text", { fg: "#f55", children: ["error: ", state.errorMessage] }) : null, _jsx("text", { children: " " }), _jsx("text", { fg: "#666", children: "Press q or Esc to exit \u00B7 t to toggle thinking" })] }));
|
|
325
|
+
}
|
|
326
|
+
// ─── Utilities ───────────────────────────────────────────────────────
|
|
327
|
+
/** US-style thousands separator. Cheap; no Intl. */
|
|
328
|
+
function formatNum(n) {
|
|
329
|
+
if (!Number.isFinite(n))
|
|
330
|
+
return String(n);
|
|
331
|
+
return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
332
|
+
}
|
|
333
|
+
function safeStringify(v) {
|
|
334
|
+
try {
|
|
335
|
+
return JSON.stringify(v, null, 2);
|
|
336
|
+
}
|
|
337
|
+
catch {
|
|
338
|
+
return String(v);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
//# sourceMappingURL=RunView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RunView.js","sourceRoot":"","sources":["../../../src/cli/ui/RunView.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event value codec — round-trip non-JSON-safe values through the log.
|
|
3
|
+
*
|
|
4
|
+
* The naive approach (`JSON.stringify` everything that lands in
|
|
5
|
+
* `args`, `before`, `after`) is lossy for the exact types real
|
|
6
|
+
* Firebase code uses:
|
|
7
|
+
*
|
|
8
|
+
* - `Date` → ISO string. Replay writes `string` where `Date` was.
|
|
9
|
+
* - `Timestamp` (Firestore) → `{ seconds, nanoseconds }` map. Replay
|
|
10
|
+
* writes a MAP, not a Timestamp. Range queries break silently.
|
|
11
|
+
* - `FieldValue.serverTimestamp()` → `{}`. Sentinel never fires.
|
|
12
|
+
* - `Uint8Array` → array of numbers. Bytes corrupted.
|
|
13
|
+
* - `bigint` → throws (JSON.stringify can't serialize bigint).
|
|
14
|
+
*
|
|
15
|
+
* This module provides a small, extensible codec layer. The default
|
|
16
|
+
* codec handles the universal cases (Date / Uint8Array / bigint) via
|
|
17
|
+
* tagged envelopes. Hosts with domain-specific types (Firestore
|
|
18
|
+
* Timestamp, FieldValue, DocumentReference) compose their own codec
|
|
19
|
+
* on top — see `composeCodecs` + the AGENTS.md recipe.
|
|
20
|
+
*
|
|
21
|
+
* Not handled: `undefined`. Firestore doesn't write `undefined` to
|
|
22
|
+
* docs (it throws), and JSON.stringify drops `undefined`-valued
|
|
23
|
+
* object properties before we ever see them. Encoding it would
|
|
24
|
+
* require a "no-transform" sentinel that the walker collides with;
|
|
25
|
+
* not worth the complexity for a value real Firebase code never
|
|
26
|
+
* produces.
|
|
27
|
+
*
|
|
28
|
+
* Tagged envelope shape: `{ "__pyric": "<tag>", ...fields }`. The
|
|
29
|
+
* `__pyric` prefix makes encoded values self-describing and the
|
|
30
|
+
* round-trip lossless. Hosts using `__pyric` for their own things
|
|
31
|
+
* SHOULD pick a different prefix.
|
|
32
|
+
*/
|
|
33
|
+
export declare const ENVELOPE_KEY: "__pyric";
|
|
34
|
+
export interface EventValueCodec {
|
|
35
|
+
/** Convert a value into a JSON-safe shape. Identity on already-safe values. */
|
|
36
|
+
encode(value: unknown): unknown;
|
|
37
|
+
/** Invert `encode`. Identity on values that have no envelope. */
|
|
38
|
+
decode(value: unknown): unknown;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Walks a value tree, applying a per-node transform. Returns a new
|
|
42
|
+
* tree (the input is NOT mutated). The transform returns `undefined`
|
|
43
|
+
* for nodes it doesn't want to transform; the walker then recurses
|
|
44
|
+
* into the node's children — BUT only when the node is a plain
|
|
45
|
+
* object or an array. Class instances (anything whose prototype is
|
|
46
|
+
* not `Object.prototype`) are returned as-is so composed codecs
|
|
47
|
+
* don't accidentally Object.entries() through a typed value and
|
|
48
|
+
* rebuild it as a bag-of-fields.
|
|
49
|
+
*
|
|
50
|
+
* Exported so codec authors can build their own codecs correctly —
|
|
51
|
+
* see `codec.ts` header for the pattern.
|
|
52
|
+
*/
|
|
53
|
+
export declare function walkValue(value: unknown, transform: (v: unknown) => unknown): unknown;
|
|
54
|
+
/**
|
|
55
|
+
* Default codec — handles universal non-JSON types every consumer
|
|
56
|
+
* needs. Hosts compose Firestore-specific codecs on top via
|
|
57
|
+
* `composeCodecs`.
|
|
58
|
+
*/
|
|
59
|
+
export declare const defaultEventValueCodec: EventValueCodec;
|
|
60
|
+
/**
|
|
61
|
+
* Identity codec — pass-through. Use when the host serializes args
|
|
62
|
+
* itself before passing to a wrapped tool (e.g. tool args are
|
|
63
|
+
* already JSON-safe by contract). Cheaper than `defaultEventValueCodec`
|
|
64
|
+
* because it skips the walk.
|
|
65
|
+
*/
|
|
66
|
+
export declare const identityCodec: EventValueCodec;
|
|
67
|
+
/**
|
|
68
|
+
* Compose two codecs. The outer codec encodes first, decodes last.
|
|
69
|
+
*
|
|
70
|
+
* composeCodecs(outer, inner)
|
|
71
|
+
* encode(v) → outer.encode(inner.encode(v))
|
|
72
|
+
* decode(v) → inner.decode(outer.decode(v))
|
|
73
|
+
*
|
|
74
|
+
* Typical usage: layer a Firestore codec over the default.
|
|
75
|
+
*
|
|
76
|
+
* const codec = composeCodecs(firestoreCodec, defaultEventValueCodec);
|
|
77
|
+
*/
|
|
78
|
+
export declare function composeCodecs(outer: EventValueCodec, inner: EventValueCodec): EventValueCodec;
|
|
79
|
+
//# sourceMappingURL=codec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../src/events/codec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,eAAO,MAAM,YAAY,EAAG,SAAkB,CAAC;AAE/C,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;IAChC,iEAAiE;IACjE,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;CACjC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAiBrF;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,eAqCpC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,eAG3B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,GAAG,eAAe,CAK7F"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event value codec — round-trip non-JSON-safe values through the log.
|
|
3
|
+
*
|
|
4
|
+
* The naive approach (`JSON.stringify` everything that lands in
|
|
5
|
+
* `args`, `before`, `after`) is lossy for the exact types real
|
|
6
|
+
* Firebase code uses:
|
|
7
|
+
*
|
|
8
|
+
* - `Date` → ISO string. Replay writes `string` where `Date` was.
|
|
9
|
+
* - `Timestamp` (Firestore) → `{ seconds, nanoseconds }` map. Replay
|
|
10
|
+
* writes a MAP, not a Timestamp. Range queries break silently.
|
|
11
|
+
* - `FieldValue.serverTimestamp()` → `{}`. Sentinel never fires.
|
|
12
|
+
* - `Uint8Array` → array of numbers. Bytes corrupted.
|
|
13
|
+
* - `bigint` → throws (JSON.stringify can't serialize bigint).
|
|
14
|
+
*
|
|
15
|
+
* This module provides a small, extensible codec layer. The default
|
|
16
|
+
* codec handles the universal cases (Date / Uint8Array / bigint) via
|
|
17
|
+
* tagged envelopes. Hosts with domain-specific types (Firestore
|
|
18
|
+
* Timestamp, FieldValue, DocumentReference) compose their own codec
|
|
19
|
+
* on top — see `composeCodecs` + the AGENTS.md recipe.
|
|
20
|
+
*
|
|
21
|
+
* Not handled: `undefined`. Firestore doesn't write `undefined` to
|
|
22
|
+
* docs (it throws), and JSON.stringify drops `undefined`-valued
|
|
23
|
+
* object properties before we ever see them. Encoding it would
|
|
24
|
+
* require a "no-transform" sentinel that the walker collides with;
|
|
25
|
+
* not worth the complexity for a value real Firebase code never
|
|
26
|
+
* produces.
|
|
27
|
+
*
|
|
28
|
+
* Tagged envelope shape: `{ "__pyric": "<tag>", ...fields }`. The
|
|
29
|
+
* `__pyric` prefix makes encoded values self-describing and the
|
|
30
|
+
* round-trip lossless. Hosts using `__pyric` for their own things
|
|
31
|
+
* SHOULD pick a different prefix.
|
|
32
|
+
*/
|
|
33
|
+
export const ENVELOPE_KEY = '__pyric';
|
|
34
|
+
/**
|
|
35
|
+
* Walks a value tree, applying a per-node transform. Returns a new
|
|
36
|
+
* tree (the input is NOT mutated). The transform returns `undefined`
|
|
37
|
+
* for nodes it doesn't want to transform; the walker then recurses
|
|
38
|
+
* into the node's children — BUT only when the node is a plain
|
|
39
|
+
* object or an array. Class instances (anything whose prototype is
|
|
40
|
+
* not `Object.prototype`) are returned as-is so composed codecs
|
|
41
|
+
* don't accidentally Object.entries() through a typed value and
|
|
42
|
+
* rebuild it as a bag-of-fields.
|
|
43
|
+
*
|
|
44
|
+
* Exported so codec authors can build their own codecs correctly —
|
|
45
|
+
* see `codec.ts` header for the pattern.
|
|
46
|
+
*/
|
|
47
|
+
export function walkValue(value, transform) {
|
|
48
|
+
const replaced = transform(value);
|
|
49
|
+
if (replaced !== undefined)
|
|
50
|
+
return replaced;
|
|
51
|
+
if (value === null || typeof value !== 'object')
|
|
52
|
+
return value;
|
|
53
|
+
if (Array.isArray(value))
|
|
54
|
+
return value.map((v) => walkValue(v, transform));
|
|
55
|
+
// Only descend into plain {} objects. A class instance reaches here
|
|
56
|
+
// either because the transform doesn't recognize it (likely the
|
|
57
|
+
// caller's own type — return unchanged), or because we already
|
|
58
|
+
// decoded an envelope into an instance and a *later* pass shouldn't
|
|
59
|
+
// re-walk its fields.
|
|
60
|
+
const proto = Object.getPrototypeOf(value);
|
|
61
|
+
if (proto !== Object.prototype && proto !== null)
|
|
62
|
+
return value;
|
|
63
|
+
const out = {};
|
|
64
|
+
for (const [k, v] of Object.entries(value)) {
|
|
65
|
+
out[k] = walkValue(v, transform);
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Default codec — handles universal non-JSON types every consumer
|
|
71
|
+
* needs. Hosts compose Firestore-specific codecs on top via
|
|
72
|
+
* `composeCodecs`.
|
|
73
|
+
*/
|
|
74
|
+
export const defaultEventValueCodec = {
|
|
75
|
+
encode(value) {
|
|
76
|
+
return walkValue(value, (v) => {
|
|
77
|
+
if (v instanceof Date) {
|
|
78
|
+
return { [ENVELOPE_KEY]: 'Date', iso: v.toISOString() };
|
|
79
|
+
}
|
|
80
|
+
if (v instanceof Uint8Array) {
|
|
81
|
+
return {
|
|
82
|
+
[ENVELOPE_KEY]: 'Uint8Array',
|
|
83
|
+
b64: Buffer.from(v).toString('base64'),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (typeof v === 'bigint') {
|
|
87
|
+
return { [ENVELOPE_KEY]: 'bigint', value: v.toString() };
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
decode(value) {
|
|
93
|
+
return walkValue(value, (v) => {
|
|
94
|
+
if (v === null || typeof v !== 'object')
|
|
95
|
+
return undefined;
|
|
96
|
+
if (Array.isArray(v))
|
|
97
|
+
return undefined;
|
|
98
|
+
const obj = v;
|
|
99
|
+
const tag = obj[ENVELOPE_KEY];
|
|
100
|
+
if (typeof tag !== 'string')
|
|
101
|
+
return undefined;
|
|
102
|
+
if (tag === 'Date' && typeof obj['iso'] === 'string') {
|
|
103
|
+
return new Date(obj['iso']);
|
|
104
|
+
}
|
|
105
|
+
if (tag === 'Uint8Array' && typeof obj['b64'] === 'string') {
|
|
106
|
+
return new Uint8Array(Buffer.from(obj['b64'], 'base64'));
|
|
107
|
+
}
|
|
108
|
+
if (tag === 'bigint' && typeof obj['value'] === 'string') {
|
|
109
|
+
return BigInt(obj['value']);
|
|
110
|
+
}
|
|
111
|
+
return undefined;
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Identity codec — pass-through. Use when the host serializes args
|
|
117
|
+
* itself before passing to a wrapped tool (e.g. tool args are
|
|
118
|
+
* already JSON-safe by contract). Cheaper than `defaultEventValueCodec`
|
|
119
|
+
* because it skips the walk.
|
|
120
|
+
*/
|
|
121
|
+
export const identityCodec = {
|
|
122
|
+
encode: (v) => v,
|
|
123
|
+
decode: (v) => v,
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Compose two codecs. The outer codec encodes first, decodes last.
|
|
127
|
+
*
|
|
128
|
+
* composeCodecs(outer, inner)
|
|
129
|
+
* encode(v) → outer.encode(inner.encode(v))
|
|
130
|
+
* decode(v) → inner.decode(outer.decode(v))
|
|
131
|
+
*
|
|
132
|
+
* Typical usage: layer a Firestore codec over the default.
|
|
133
|
+
*
|
|
134
|
+
* const codec = composeCodecs(firestoreCodec, defaultEventValueCodec);
|
|
135
|
+
*/
|
|
136
|
+
export function composeCodecs(outer, inner) {
|
|
137
|
+
return {
|
|
138
|
+
encode: (v) => outer.encode(inner.encode(v)),
|
|
139
|
+
decode: (v) => inner.decode(outer.decode(v)),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=codec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec.js","sourceRoot":"","sources":["../../src/events/codec.ts"],"names":[],"mappings":"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"}
|