govon 1.2.2 → 1.4.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.
Files changed (80) hide show
  1. package/bin/govon.js +2 -41
  2. package/dist/App.d.ts +22 -0
  3. package/dist/App.d.ts.map +1 -0
  4. package/dist/App.js +307 -0
  5. package/dist/App.js.map +1 -0
  6. package/dist/client.d.ts +102 -0
  7. package/dist/client.d.ts.map +1 -0
  8. package/dist/client.js +332 -0
  9. package/dist/client.js.map +1 -0
  10. package/dist/components/ApprovalPrompt.d.ts +15 -0
  11. package/dist/components/ApprovalPrompt.d.ts.map +1 -0
  12. package/dist/components/ApprovalPrompt.js +35 -0
  13. package/dist/components/ApprovalPrompt.js.map +1 -0
  14. package/dist/components/Banner.d.ts +6 -0
  15. package/dist/components/Banner.d.ts.map +1 -0
  16. package/dist/components/Banner.js +27 -0
  17. package/dist/components/Banner.js.map +1 -0
  18. package/dist/components/InputBar.d.ts +8 -0
  19. package/dist/components/InputBar.d.ts.map +1 -0
  20. package/dist/components/InputBar.js +26 -0
  21. package/dist/components/InputBar.js.map +1 -0
  22. package/dist/components/MarkdownView.d.ts +9 -0
  23. package/dist/components/MarkdownView.d.ts.map +1 -0
  24. package/dist/components/MarkdownView.js +38 -0
  25. package/dist/components/MarkdownView.js.map +1 -0
  26. package/dist/components/MessageBubble.d.ts +14 -0
  27. package/dist/components/MessageBubble.d.ts.map +1 -0
  28. package/dist/components/MessageBubble.js +15 -0
  29. package/dist/components/MessageBubble.js.map +1 -0
  30. package/dist/components/MetadataBar.d.ts +7 -0
  31. package/dist/components/MetadataBar.d.ts.map +1 -0
  32. package/dist/components/MetadataBar.js +19 -0
  33. package/dist/components/MetadataBar.js.map +1 -0
  34. package/dist/components/Spinner.d.ts +7 -0
  35. package/dist/components/Spinner.d.ts.map +1 -0
  36. package/dist/components/Spinner.js +51 -0
  37. package/dist/components/Spinner.js.map +1 -0
  38. package/dist/components/ThinkingBlock.d.ts +9 -0
  39. package/dist/components/ThinkingBlock.d.ts.map +1 -0
  40. package/dist/components/ThinkingBlock.js +9 -0
  41. package/dist/components/ThinkingBlock.js.map +1 -0
  42. package/dist/components/ToolPanel.d.ts +7 -0
  43. package/dist/components/ToolPanel.d.ts.map +1 -0
  44. package/dist/components/ToolPanel.js +19 -0
  45. package/dist/components/ToolPanel.js.map +1 -0
  46. package/dist/config.d.ts +32 -0
  47. package/dist/config.d.ts.map +1 -0
  48. package/dist/config.js +64 -0
  49. package/dist/config.js.map +1 -0
  50. package/dist/data/spinnerVerbs.d.ts +7 -0
  51. package/dist/data/spinnerVerbs.d.ts.map +1 -0
  52. package/dist/data/spinnerVerbs.js +58 -0
  53. package/dist/data/spinnerVerbs.js.map +1 -0
  54. package/dist/hooks/useDaemon.d.ts +16 -0
  55. package/dist/hooks/useDaemon.d.ts.map +1 -0
  56. package/dist/hooks/useDaemon.js +55 -0
  57. package/dist/hooks/useDaemon.js.map +1 -0
  58. package/dist/hooks/useHistory.d.ts +13 -0
  59. package/dist/hooks/useHistory.d.ts.map +1 -0
  60. package/dist/hooks/useHistory.js +170 -0
  61. package/dist/hooks/useHistory.js.map +1 -0
  62. package/dist/hooks/useSSE.d.ts +29 -0
  63. package/dist/hooks/useSSE.d.ts.map +1 -0
  64. package/dist/hooks/useSSE.js +341 -0
  65. package/dist/hooks/useSSE.js.map +1 -0
  66. package/dist/hooks/useTheme.d.ts +39 -0
  67. package/dist/hooks/useTheme.d.ts.map +1 -0
  68. package/dist/hooks/useTheme.js +36 -0
  69. package/dist/hooks/useTheme.js.map +1 -0
  70. package/dist/index.d.ts +7 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +1496 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/types.d.ts +356 -0
  75. package/dist/types.d.ts.map +1 -0
  76. package/dist/types.js +67 -0
  77. package/dist/types.js.map +1 -0
  78. package/package.json +45 -19
  79. package/README.md +0 -45
  80. package/lib/python-check.js +0 -223
package/dist/client.js ADDED
@@ -0,0 +1,332 @@
1
+ /**
2
+ * GovOn local daemon API HTTP client.
3
+ *
4
+ * TypeScript port of src/cli/http_client.py.
5
+ * Wraps the REST/SSE API of the local daemon (uvicorn).
6
+ * Core endpoints: health / run / stream / approve / cancel.
7
+ */
8
+ import { EventSourceParserStream } from 'eventsource-parser/stream';
9
+ import { getBaseUrl, TIMEOUTS } from './config.js';
10
+ // ---------------------------------------------------------------------------
11
+ // Internal timeout helpers
12
+ // ---------------------------------------------------------------------------
13
+ /** Milliseconds to AbortSignal with combined connect + read budget. */
14
+ function makeSignal(ms) {
15
+ return AbortSignal.timeout(ms);
16
+ }
17
+ // ---------------------------------------------------------------------------
18
+ // GovOnClient
19
+ // ---------------------------------------------------------------------------
20
+ export class GovOnClient {
21
+ _baseUrl;
22
+ /** Cold-start polling timeout in ms (from config). */
23
+ static _COLD_START_TIMEOUT_MS = TIMEOUTS.coldStart;
24
+ /** Cold-start polling interval in ms (from config). */
25
+ static _COLD_START_INTERVAL_MS = TIMEOUTS.coldStartInterval;
26
+ /**
27
+ * @param baseUrl - Daemon base URL (e.g. "http://127.0.0.1:8000").
28
+ * Trailing slash is stripped automatically.
29
+ */
30
+ constructor(baseUrl = getBaseUrl()) {
31
+ this._baseUrl = baseUrl.replace(/\/+$/, '');
32
+ }
33
+ // -------------------------------------------------------------------------
34
+ // Public API
35
+ // -------------------------------------------------------------------------
36
+ /**
37
+ * GET /health — check daemon status.
38
+ *
39
+ * @returns Health response from the server.
40
+ * @throws Error when daemon is unreachable.
41
+ */
42
+ async health() {
43
+ return this._get('/health', TIMEOUTS.default);
44
+ }
45
+ /**
46
+ * Poll GET /health until the server responds with HTTP 200.
47
+ *
48
+ * Handles cold-start / sleeping remote servers (e.g. HF Space) by
49
+ * printing Korean status messages to stderr and waiting patiently.
50
+ *
51
+ * @returns true when server is ready, false on timeout.
52
+ */
53
+ async waitForReady() {
54
+ const url = `${this._baseUrl}/health`;
55
+ const deadline = Date.now() + GovOnClient._COLD_START_TIMEOUT_MS;
56
+ const intervalMs = GovOnClient._COLD_START_INTERVAL_MS;
57
+ let lastStatus = '';
58
+ let attempt = 0;
59
+ while (Date.now() < deadline) {
60
+ let newStatus;
61
+ try {
62
+ const signal = makeSignal(10_000);
63
+ const resp = await fetch(url, { signal });
64
+ if (resp.status === 200) {
65
+ if (attempt > 0) {
66
+ process.stderr.write('\r✦ 서버 준비 완료. \n');
67
+ }
68
+ return true;
69
+ }
70
+ if (resp.status === 503) {
71
+ newStatus = '⊛ 서버 시작 중… (503 응답 대기)';
72
+ }
73
+ else {
74
+ newStatus = `⊛ 서버 응답 대기 중… (HTTP ${resp.status})`;
75
+ }
76
+ }
77
+ catch (err) {
78
+ const msg = err instanceof Error ? err.message : String(err);
79
+ if (msg.includes('ECONNREFUSED') ||
80
+ msg.includes('ENOTFOUND') ||
81
+ msg.includes('fetch failed') ||
82
+ msg.includes('connect')) {
83
+ newStatus = '⊛ 서버에 연결 중… (sleeping 상태에서 깨어나는 중)';
84
+ }
85
+ else if (msg.includes('TimeoutError') || msg.includes('timed out') || msg.includes('abort')) {
86
+ newStatus = '⊛ 서버 응답 대기 중… (빌드 또는 모델 로딩 중)';
87
+ }
88
+ else {
89
+ newStatus = '⊛ 서버에 연결 중… (sleeping 상태에서 깨어나는 중)';
90
+ }
91
+ }
92
+ const elapsed = Math.round((Date.now() - (deadline - GovOnClient._COLD_START_TIMEOUT_MS)) / 1000);
93
+ if (newStatus !== lastStatus || attempt % 6 === 0) {
94
+ process.stderr.write(`\r⏳ ${newStatus} (${elapsed}s)`);
95
+ lastStatus = newStatus;
96
+ }
97
+ attempt += 1;
98
+ await sleep(intervalMs);
99
+ }
100
+ process.stderr.write('\r✘ 서버 연결 시간 초과. \n');
101
+ return false;
102
+ }
103
+ /**
104
+ * POST /v3/agent/stream — v3 ReAct fine-grained SSE streaming.
105
+ *
106
+ * @param query - User input query.
107
+ * @param sessionId - Session ID to resume an existing session.
108
+ * @param maxIterations - Maximum ReAct loop iterations.
109
+ * @param signal - Optional external AbortSignal; composed with the internal timeout.
110
+ * @yields Parsed SSE event objects. Use the `type` key to distinguish them.
111
+ */
112
+ async *streamV3(query, sessionId, maxIterations, signal) {
113
+ const body = { query };
114
+ if (sessionId !== undefined)
115
+ body['session_id'] = sessionId;
116
+ if (maxIterations !== undefined)
117
+ body['max_iterations'] = maxIterations;
118
+ const url = `${this._baseUrl}/v3/agent/stream`;
119
+ yield* this._sseStream(url, body, 'stream_v3', signal);
120
+ }
121
+ /**
122
+ * POST /v2/agent/stream — per-node SSE streaming.
123
+ *
124
+ * @param query - User input query.
125
+ * @param sessionId - Session ID to resume an existing session.
126
+ * @param signal - Optional external AbortSignal; composed with the internal timeout.
127
+ * @yields Parsed SSE event objects. Contains at least `node` and `status` keys.
128
+ */
129
+ async *stream(query, sessionId, signal) {
130
+ const body = { query };
131
+ if (sessionId !== undefined)
132
+ body['session_id'] = sessionId;
133
+ const url = `${this._baseUrl}/v2/agent/stream`;
134
+ yield* this._sseStream(url, body, 'stream', signal);
135
+ }
136
+ /**
137
+ * POST /v2/agent/run — blocking agent execution.
138
+ *
139
+ * @param query - User input query.
140
+ * @param sessionId - Session ID to resume an existing session.
141
+ * @param signal - Optional external AbortSignal; composed with the internal timeout.
142
+ * @returns Server response including thread_id, status, etc.
143
+ */
144
+ async run(query, sessionId, signal) {
145
+ const body = { query };
146
+ if (sessionId !== undefined)
147
+ body['session_id'] = sessionId;
148
+ return this._post('/v2/agent/run', body, TIMEOUTS.run, signal);
149
+ }
150
+ /**
151
+ * POST /v3/agent/run — v3 ReAct blocking execution.
152
+ *
153
+ * @param query - User input query.
154
+ * @param sessionId - Session ID to resume an existing session.
155
+ * @param maxIterations - Maximum ReAct loop iterations.
156
+ * @param signal - Optional external AbortSignal; composed with the internal timeout.
157
+ * @returns Server response including metadata.
158
+ */
159
+ async runV3(query, sessionId, maxIterations, signal) {
160
+ const body = { query };
161
+ if (sessionId !== undefined)
162
+ body['session_id'] = sessionId;
163
+ if (maxIterations !== undefined)
164
+ body['max_iterations'] = maxIterations;
165
+ return this._post('/v3/agent/run', body, TIMEOUTS.run, signal);
166
+ }
167
+ /**
168
+ * POST /v2/agent/approve — approve or reject a pending tool call.
169
+ *
170
+ * Note: uses QUERY PARAMETERS (thread_id, approved), not request body.
171
+ *
172
+ * @param threadId - Graph thread ID to approve or reject.
173
+ * @param approved - true to approve, false to reject.
174
+ * @returns Server response.
175
+ */
176
+ async approve(threadId, approved) {
177
+ const params = new URLSearchParams({
178
+ thread_id: threadId,
179
+ approved: String(approved).toLowerCase(),
180
+ });
181
+ return this._postParams('/v2/agent/approve', params, TIMEOUTS.default);
182
+ }
183
+ /**
184
+ * POST /v2/agent/cancel — cancel a running session.
185
+ *
186
+ * Note: uses QUERY PARAMETER (thread_id), not request body.
187
+ *
188
+ * @param threadId - Graph thread ID to cancel.
189
+ * @returns Server response.
190
+ */
191
+ async cancel(threadId) {
192
+ const params = new URLSearchParams({ thread_id: threadId });
193
+ return this._postParams('/v2/agent/cancel', params, TIMEOUTS.default);
194
+ }
195
+ // -------------------------------------------------------------------------
196
+ // Internal helpers
197
+ // -------------------------------------------------------------------------
198
+ /** Shared SSE streaming implementation used by stream() and streamV3(). */
199
+ async *_sseStream(url, body, label, externalSignal) {
200
+ // Compose the caller-provided signal with the internal read timeout so that
201
+ // whichever fires first aborts the fetch.
202
+ const timeoutSignal = AbortSignal.timeout(TIMEOUTS.read);
203
+ const signal = externalSignal
204
+ ? AbortSignal.any([externalSignal, timeoutSignal])
205
+ : timeoutSignal;
206
+ let response;
207
+ try {
208
+ response = await fetch(url, {
209
+ method: 'POST',
210
+ headers: { 'Content-Type': 'application/json' },
211
+ body: JSON.stringify(body),
212
+ signal,
213
+ });
214
+ }
215
+ catch (err) {
216
+ const msg = err instanceof Error ? err.message : String(err);
217
+ if (msg.includes('ECONNREFUSED') ||
218
+ msg.includes('ENOTFOUND') ||
219
+ msg.includes('fetch failed') ||
220
+ msg.includes('connect')) {
221
+ throw new Error(`daemon is not running. (${this._baseUrl})`);
222
+ }
223
+ throw err;
224
+ }
225
+ if (!response.ok) {
226
+ throw new Error(`[${label}] HTTP ${response.status}: ${url}`);
227
+ }
228
+ if (!response.body) {
229
+ throw new Error(`[${label}] Response body is null: ${url}`);
230
+ }
231
+ const sseStream = response.body
232
+ .pipeThrough(new TextDecoderStream())
233
+ .pipeThrough(new EventSourceParserStream());
234
+ for await (const event of sseStream) {
235
+ // EventSourceParserStream yields EventSourceMessage objects with { event?, data }
236
+ if (event.data) {
237
+ try {
238
+ yield JSON.parse(event.data);
239
+ }
240
+ catch {
241
+ process.stderr.write(`[${label}] SSE JSON parse failed: len=${event.data.length}\n`);
242
+ // Continue — do not break the stream on a single malformed frame
243
+ }
244
+ }
245
+ }
246
+ }
247
+ /** GET helper with connection-error normalisation. */
248
+ async _get(path, timeoutMs) {
249
+ const url = `${this._baseUrl}${path}`;
250
+ let response;
251
+ try {
252
+ response = await fetch(url, { signal: makeSignal(timeoutMs) });
253
+ }
254
+ catch (err) {
255
+ const msg = err instanceof Error ? err.message : String(err);
256
+ if (msg.includes('ECONNREFUSED') ||
257
+ msg.includes('ENOTFOUND') ||
258
+ msg.includes('fetch failed') ||
259
+ msg.includes('connect')) {
260
+ throw new Error(`daemon is not running. (${this._baseUrl})`);
261
+ }
262
+ throw err;
263
+ }
264
+ if (!response.ok) {
265
+ throw new Error(`[http_client] HTTP ${response.status}: ${url}`);
266
+ }
267
+ return response.json();
268
+ }
269
+ /** POST with JSON body helper. */
270
+ async _post(path, body, timeoutMs, externalSignal) {
271
+ const url = `${this._baseUrl}${path}`;
272
+ const timeoutSignal = makeSignal(timeoutMs);
273
+ const signal = externalSignal
274
+ ? AbortSignal.any([externalSignal, timeoutSignal])
275
+ : timeoutSignal;
276
+ let response;
277
+ try {
278
+ response = await fetch(url, {
279
+ method: 'POST',
280
+ headers: { 'Content-Type': 'application/json' },
281
+ body: JSON.stringify(body),
282
+ signal,
283
+ });
284
+ }
285
+ catch (err) {
286
+ const msg = err instanceof Error ? err.message : String(err);
287
+ if (msg.includes('ECONNREFUSED') ||
288
+ msg.includes('ENOTFOUND') ||
289
+ msg.includes('fetch failed') ||
290
+ msg.includes('connect')) {
291
+ throw new Error(`daemon is not running. (${this._baseUrl})`);
292
+ }
293
+ throw err;
294
+ }
295
+ if (!response.ok) {
296
+ throw new Error(`[http_client] HTTP ${response.status}: ${url}`);
297
+ }
298
+ return response.json();
299
+ }
300
+ /** POST with query-parameter helper (used by /approve and /cancel). */
301
+ async _postParams(path, params, timeoutMs) {
302
+ const url = `${this._baseUrl}${path}?${params.toString()}`;
303
+ let response;
304
+ try {
305
+ response = await fetch(url, {
306
+ method: 'POST',
307
+ signal: makeSignal(timeoutMs),
308
+ });
309
+ }
310
+ catch (err) {
311
+ const msg = err instanceof Error ? err.message : String(err);
312
+ if (msg.includes('ECONNREFUSED') ||
313
+ msg.includes('ENOTFOUND') ||
314
+ msg.includes('fetch failed') ||
315
+ msg.includes('connect')) {
316
+ throw new Error(`daemon is not running. (${this._baseUrl})`);
317
+ }
318
+ throw err;
319
+ }
320
+ if (!response.ok) {
321
+ throw new Error(`[http_client] HTTP ${response.status}: ${url}`);
322
+ }
323
+ return response.json();
324
+ }
325
+ }
326
+ // ---------------------------------------------------------------------------
327
+ // Utility
328
+ // ---------------------------------------------------------------------------
329
+ function sleep(ms) {
330
+ return new Promise((resolve) => setTimeout(resolve, ms));
331
+ }
332
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * ApprovalPrompt — renders a human-approval gate dialog.
3
+ *
4
+ * Shown when the backend emits an `awaiting_approval` event.
5
+ * The user presses 'y' to approve or 'n' to reject.
6
+ * Calls onApprove(true) or onApprove(false) accordingly.
7
+ */
8
+ import type { ApprovalRequest } from '../types.js';
9
+ interface ApprovalPromptProps {
10
+ request: ApprovalRequest;
11
+ onApprove: (approved: boolean) => void;
12
+ }
13
+ export declare function ApprovalPrompt({ request, onApprove }: ApprovalPromptProps): import("react/jsx-runtime").JSX.Element;
14
+ export {};
15
+ //# sourceMappingURL=ApprovalPrompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApprovalPrompt.d.ts","sourceRoot":"","sources":["../../src/components/ApprovalPrompt.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAInD,UAAU,mBAAmB;IAC3B,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC;AAID,wBAAgB,cAAc,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,mBAAmB,2CAsEzE"}
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * ApprovalPrompt — renders a human-approval gate dialog.
4
+ *
5
+ * Shown when the backend emits an `awaiting_approval` event.
6
+ * The user presses 'y' to approve or 'n' to reject.
7
+ * Calls onApprove(true) or onApprove(false) accordingly.
8
+ */
9
+ import { useCallback, useRef } from 'react';
10
+ import { Box, Text, useInput } from 'ink';
11
+ import { TASK_TYPE_LABELS, TASK_TYPE_STYLES } from '../types.js';
12
+ import { THEME_COLORS } from '../config.js';
13
+ const MAX_DESCRIPTION_LENGTH = 1000;
14
+ export function ApprovalPrompt({ request, onApprove }) {
15
+ const safeDescription = request.description?.slice(0, MAX_DESCRIPTION_LENGTH);
16
+ // Prevent duplicate approval submissions from repeated keypresses
17
+ const decidedRef = useRef(false);
18
+ useInput(useCallback((input) => {
19
+ if (decidedRef.current)
20
+ return;
21
+ const key = input.toLowerCase();
22
+ if (key === 'y') {
23
+ decidedRef.current = true;
24
+ onApprove(true);
25
+ }
26
+ else if (key === 'n') {
27
+ decidedRef.current = true;
28
+ onApprove(false);
29
+ }
30
+ }, [onApprove]));
31
+ const taskLabel = TASK_TYPE_LABELS[request.task_type] ?? TASK_TYPE_LABELS['default'];
32
+ const taskColor = TASK_TYPE_STYLES[request.task_type] ?? TASK_TYPE_STYLES['default'];
33
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: THEME_COLORS.warning, paddingX: 2, paddingY: 1, marginTop: 1, children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { bold: true, color: THEME_COLORS.warning, children: '⚠ 승인 요청 ' }), _jsxs(Text, { color: taskColor, children: ["[", taskLabel, "]"] })] }), safeDescription && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { wrap: "wrap", children: safeDescription }) })), request.planned_tools && request.planned_tools.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: THEME_COLORS.muted, children: '실행 예정 도구:' }), request.planned_tools.map((tool, i) => (_jsx(Text, { color: THEME_COLORS.muted, children: ` • ${tool}` }, i)))] })), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { bold: true, children: ['승인하시겠습니까? ', _jsx(Text, { color: "green", children: 'y' }), ' / ', _jsx(Text, { color: "red", children: 'n' })] }) })] }));
34
+ }
35
+ //# sourceMappingURL=ApprovalPrompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApprovalPrompt.js","sourceRoot":"","sources":["../../src/components/ApprovalPrompt.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ interface BannerProps {
2
+ version: string;
3
+ }
4
+ export declare function Banner({ version }: BannerProps): import("react/jsx-runtime").JSX.Element;
5
+ export {};
6
+ //# sourceMappingURL=Banner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Banner.d.ts","sourceRoot":"","sources":["../../src/components/Banner.tsx"],"names":[],"mappings":"AAcA,UAAU,WAAW;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,MAAM,CAAC,EAAE,OAAO,EAAE,EAAE,WAAW,2CAiE9C"}
@@ -0,0 +1,27 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import { THEME_COLORS, getBaseUrl } from '../config.js';
4
+ // ASCII art for the 'G' logo, one line per row
5
+ const LOGO_ART = [
6
+ ' ██████╗ ',
7
+ ' ██╔════╝ ',
8
+ ' ██║ ████╗',
9
+ ' ██║ ╚═██║',
10
+ ' ╚██████╔╝',
11
+ ' ╚═════╝ ',
12
+ ];
13
+ export function Banner({ version }) {
14
+ const baseUrl = getBaseUrl();
15
+ // Derive a short label describing the current backend target
16
+ let modeLabel;
17
+ try {
18
+ const url = new URL(baseUrl);
19
+ const isLocal = url.hostname === '127.0.0.1' || url.hostname === 'localhost';
20
+ modeLabel = isLocal ? '로컬 모드' : `원격: ${url.hostname}`;
21
+ }
22
+ catch {
23
+ modeLabel = '로컬 모드';
24
+ }
25
+ return (_jsxs(Box, { borderStyle: "round", borderColor: THEME_COLORS.primary, paddingX: 2, paddingY: 1, flexDirection: "row", children: [_jsxs(Box, { flexDirection: "column", marginRight: 3, children: [LOGO_ART.map((line, i) => (_jsx(Text, { color: THEME_COLORS.accent, children: line }, i))), _jsx(Text, { children: " " }), _jsxs(Text, { bold: true, color: THEME_COLORS.accent, children: ["GovOn v", version] }), _jsx(Text, { color: THEME_COLORS.muted, children: modeLabel })] }), _jsx(Box, { borderStyle: "single", borderLeft: true, borderRight: false, borderTop: false, borderBottom: false, borderColor: THEME_COLORS.primary, marginRight: 3 }), _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, color: THEME_COLORS.accent, children: "\uC2DC\uC791 \uAC00\uC774\uB4DC" }), _jsx(Text, { color: THEME_COLORS.muted, children: "\uC9C8\uBB38\uC744 \uC785\uB825\uD558\uBA74 AI \uC5D0\uC774\uC804\uD2B8\uAC00 \uBD84\uC11D\uD558\uACE0 \uB3C4\uAD6C\uB97C \uC0AC\uC6A9\uD569\uB2C8\uB2E4" }), _jsx(Text, { color: THEME_COLORS.muted, children: "/help \uB85C \uBA85\uB839\uC5B4 \uBAA9\uB85D \uD655\uC778" }), _jsx(Text, { color: THEME_COLORS.muted, children: "/exit \uB610\uB294 Ctrl+D \uB85C \uC885\uB8CC" }), _jsx(Text, { children: " " }), _jsx(Text, { color: THEME_COLORS.muted, children: "\uC2B9\uC778 \uD544\uC694 \uB3C4\uAD6C\uB294 \uC2E4\uD589 \uC804 \uD655\uC778\uC744 \uC694\uCCAD\uD569\uB2C8\uB2E4" }), _jsx(Text, { color: THEME_COLORS.muted, children: "Esc \uB85C \uC9C4\uD589 \uC911\uC778 \uC791\uC5C5 \uCDE8\uC18C" })] })] }));
26
+ }
27
+ //# sourceMappingURL=Banner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Banner.js","sourceRoot":"","sources":["../../src/components/Banner.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAExD,+CAA+C;AAC/C,MAAM,QAAQ,GAAG;IACf,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,YAAY;CACb,CAAC;AAMF,MAAM,UAAU,MAAM,CAAC,EAAE,OAAO,EAAe;IAC7C,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAE7B,6DAA6D;IAC7D,IAAI,SAAiB,CAAC;IACtB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;QAC7B,MAAM,OAAO,GACX,GAAG,CAAC,QAAQ,KAAK,WAAW,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,CAAC;QAC/D,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,SAAS,GAAG,OAAO,CAAC;IACtB,CAAC;IAED,OAAO,CACL,MAAC,GAAG,IACF,WAAW,EAAC,OAAO,EACnB,WAAW,EAAE,YAAY,CAAC,OAAO,EACjC,QAAQ,EAAE,CAAC,EACX,QAAQ,EAAE,CAAC,EACX,aAAa,EAAC,KAAK,aAGnB,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,WAAW,EAAE,CAAC,aACvC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CACzB,KAAC,IAAI,IAAS,KAAK,EAAE,YAAY,CAAC,MAAM,YACrC,IAAI,IADI,CAAC,CAEL,CACR,CAAC,EACF,KAAC,IAAI,oBAAS,EACd,MAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAE,YAAY,CAAC,MAAM,wBAC3B,OAAO,IACV,EACP,KAAC,IAAI,IAAC,KAAK,EAAE,YAAY,CAAC,KAAK,YAAG,SAAS,GAAQ,IAC/C,EAGN,KAAC,GAAG,IACF,WAAW,EAAC,QAAQ,EACpB,UAAU,QACV,WAAW,EAAE,KAAK,EAClB,SAAS,EAAE,KAAK,EAChB,YAAY,EAAE,KAAK,EACnB,WAAW,EAAE,YAAY,CAAC,OAAO,EACjC,WAAW,EAAE,CAAC,GACd,EAGF,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,KAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAE,YAAY,CAAC,MAAM,gDAE9B,EACP,KAAC,IAAI,IAAC,KAAK,EAAE,YAAY,CAAC,KAAK,yKAExB,EACP,KAAC,IAAI,IAAC,KAAK,EAAE,YAAY,CAAC,KAAK,0EAA0B,EACzD,KAAC,IAAI,IAAC,KAAK,EAAE,YAAY,CAAC,KAAK,8DAA6B,EAC5D,KAAC,IAAI,oBAAS,EACd,KAAC,IAAI,IAAC,KAAK,EAAE,YAAY,CAAC,KAAK,mIAExB,EACP,KAAC,IAAI,IAAC,KAAK,EAAE,YAAY,CAAC,KAAK,+EAA0B,IACrD,IACF,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ interface InputBarProps {
2
+ onSubmit: (query: string) => void;
3
+ /** When true, the input field is read-only and shows a busy placeholder. */
4
+ disabled?: boolean;
5
+ }
6
+ export declare function InputBar({ onSubmit, disabled }: InputBarProps): import("react/jsx-runtime").JSX.Element;
7
+ export {};
8
+ //# sourceMappingURL=InputBar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InputBar.d.ts","sourceRoot":"","sources":["../../src/components/InputBar.tsx"],"names":[],"mappings":"AAIA,UAAU,aAAa;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,QAAQ,CAAC,EAAE,QAAQ,EAAE,QAAgB,EAAE,EAAE,aAAa,2CAuCrE"}
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useCallback, useEffect } from 'react';
3
+ import { Box, Text } from 'ink';
4
+ import TextInput from 'ink-text-input';
5
+ export function InputBar({ onSubmit, disabled = false }) {
6
+ const [value, setValue] = useState('');
7
+ // Clear stale input when entering disabled state
8
+ useEffect(() => {
9
+ if (disabled)
10
+ setValue('');
11
+ }, [disabled]);
12
+ // Block input changes while disabled
13
+ const handleChange = useCallback((next) => {
14
+ if (!disabled)
15
+ setValue(next);
16
+ }, [disabled]);
17
+ const handleSubmit = useCallback((val) => {
18
+ const trimmed = val.trim();
19
+ if (!trimmed || disabled)
20
+ return;
21
+ onSubmit(trimmed);
22
+ setValue('');
23
+ }, [onSubmit, disabled]);
24
+ return (_jsxs(Box, { children: [_jsx(Text, { bold: true, color: disabled ? 'gray' : 'green', children: '❯ ' }), _jsx(TextInput, { value: value, onChange: handleChange, onSubmit: handleSubmit, placeholder: disabled ? '처리 중…' : '질문을 입력하세요' })] }));
25
+ }
26
+ //# sourceMappingURL=InputBar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InputBar.js","sourceRoot":"","sources":["../../src/components/InputBar.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAChE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,SAAS,MAAM,gBAAgB,CAAC;AAQvC,MAAM,UAAU,QAAQ,CAAC,EAAE,QAAQ,EAAE,QAAQ,GAAG,KAAK,EAAiB;IACpE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IAEvC,iDAAiD;IACjD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,QAAQ;YAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC7B,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,qCAAqC;IACrC,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,IAAY,EAAE,EAAE;QACf,IAAI,CAAC,QAAQ;YAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC,EACD,CAAC,QAAQ,CAAC,CACX,CAAC;IAEF,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,GAAW,EAAE,EAAE;QACd,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3B,IAAI,CAAC,OAAO,IAAI,QAAQ;YAAE,OAAO;QACjC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAClB,QAAQ,CAAC,EAAE,CAAC,CAAC;IACf,CAAC,EACD,CAAC,QAAQ,EAAE,QAAQ,CAAC,CACrB,CAAC;IAEF,OAAO,CACL,MAAC,GAAG,eACF,KAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,YAC1C,IAAI,GACA,EACP,KAAC,SAAS,IACR,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,YAAY,EACtB,QAAQ,EAAE,YAAY,EACtB,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,GAC7C,IACE,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ interface MarkdownViewProps {
2
+ /** Raw markdown text to render. */
3
+ content: string;
4
+ /** Whether more content is still streaming. */
5
+ streaming?: boolean;
6
+ }
7
+ export declare function MarkdownView({ content, streaming }: MarkdownViewProps): import("react/jsx-runtime").JSX.Element | null;
8
+ export {};
9
+ //# sourceMappingURL=MarkdownView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MarkdownView.d.ts","sourceRoot":"","sources":["../../src/components/MarkdownView.tsx"],"names":[],"mappings":"AAgBA,UAAU,iBAAiB;IACzB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAQD,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,SAAiB,EAAE,EAAE,iBAAiB,kDAsB7E"}
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
3
+ import { Box, Text } from 'ink';
4
+ import { marked } from 'marked';
5
+ import { markedTerminal } from 'marked-terminal';
6
+ // Configure marked with the terminal renderer once at module load time.
7
+ // markedTerminal() returns a marked extension object compatible with the
8
+ // marked.use() API introduced in marked v5+ and still current in v15.
9
+ marked.use(markedTerminal({
10
+ reflowText: true,
11
+ width: process.stdout.columns || 80,
12
+ showSectionPrefix: false,
13
+ }));
14
+ // Strip ANSI escape sequences from untrusted server content
15
+ function stripAnsi(str) {
16
+ // eslint-disable-next-line no-control-regex
17
+ return str.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
18
+ }
19
+ export function MarkdownView({ content, streaming = false }) {
20
+ const rendered = useMemo(() => {
21
+ if (!content)
22
+ return '';
23
+ try {
24
+ // marked.parse() is synchronous when the terminal renderer extension is active.
25
+ const result = marked.parse(stripAnsi(content));
26
+ // Remove trailing newlines produced by marked's block-level output.
27
+ return typeof result === 'string' ? result.trimEnd() : '';
28
+ }
29
+ catch {
30
+ // Fall back to raw text so the UI never goes blank on a parse error.
31
+ return content;
32
+ }
33
+ }, [content]);
34
+ if (!rendered)
35
+ return null;
36
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { wrap: "wrap", children: rendered }), streaming && _jsx(Text, { dimColor: true, children: '▍' })] }));
37
+ }
38
+ //# sourceMappingURL=MarkdownView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MarkdownView.js","sourceRoot":"","sources":["../../src/components/MarkdownView.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,wEAAwE;AACxE,yEAAyE;AACzE,sEAAsE;AACtE,MAAM,CAAC,GAAG,CACR,cAAc,CAAC;IACb,UAAU,EAAE,IAAI;IAChB,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE;IACnC,iBAAiB,EAAE,KAAK;CACzB,CAAC,CACH,CAAC;AASF,4DAA4D;AAC5D,SAAS,SAAS,CAAC,GAAW;IAC5B,4CAA4C;IAC5C,OAAO,GAAG,CAAC,OAAO,CAAC,wBAAwB,EAAE,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAAE,OAAO,EAAE,SAAS,GAAG,KAAK,EAAqB;IAC5E,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE;QAC5B,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC;YACH,gFAAgF;YAChF,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;YAChD,oEAAoE;YACpE,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,CAAC;QAAC,MAAM,CAAC;YACP,qEAAqE;YACrE,OAAO,OAAO,CAAC;QACjB,CAAC;IACH,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE3B,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,KAAC,IAAI,IAAC,IAAI,EAAC,MAAM,YAAE,QAAQ,GAAQ,EAClC,SAAS,IAAI,KAAC,IAAI,IAAC,QAAQ,kBAAE,GAAG,GAAQ,IACrC,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * MessageBubble — renders a single completed chat message (user or assistant).
3
+ *
4
+ * For user messages: plain text with a ❯ prefix.
5
+ * For assistant messages: markdown-rendered content, optional thinking steps,
6
+ * optional tool invocations, optional evidence/metadata footers.
7
+ */
8
+ import type { Message } from '../types.js';
9
+ interface MessageBubbleProps {
10
+ message: Message;
11
+ }
12
+ export declare function MessageBubble({ message }: MessageBubbleProps): import("react/jsx-runtime").JSX.Element;
13
+ export {};
14
+ //# sourceMappingURL=MessageBubble.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageBubble.d.ts","sourceRoot":"","sources":["../../src/components/MessageBubble.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAO3C,UAAU,kBAAkB;IAC1B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,aAAa,CAAC,EAAE,OAAO,EAAE,EAAE,kBAAkB,2CAmD5D"}
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import { THEME_COLORS } from '../config.js';
4
+ import { MarkdownView } from './MarkdownView.js';
5
+ import { ThinkingBlock } from './ThinkingBlock.js';
6
+ import { ToolPanel } from './ToolPanel.js';
7
+ import { MetadataBar } from './MetadataBar.js';
8
+ export function MessageBubble({ message }) {
9
+ if (message.role === 'user') {
10
+ return (_jsxs(Box, { flexDirection: "row", marginTop: 1, children: [_jsx(Text, { bold: true, color: "green", children: '❯ ' }), _jsx(Text, { wrap: "wrap", children: message.content })] }));
11
+ }
12
+ // Assistant message
13
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { bold: true, color: THEME_COLORS.primary, children: 'GovOn' }), message.thinking && message.thinking.length > 0 && (_jsx(Box, { flexDirection: "column", children: message.thinking.map((step, i) => (_jsx(ThinkingBlock, { content: step.content, streaming: false }, i))) })), message.tools && message.tools.length > 0 && (_jsx(ToolPanel, { tools: message.tools })), message.error ? (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { color: THEME_COLORS.error, children: ['오류: ', message.error] }) })) : (_jsx(Box, { marginLeft: 2, children: _jsx(MarkdownView, { content: message.content, streaming: false }) })), message.metadata && (_jsx(MetadataBar, { metadata: message.metadata }))] }));
14
+ }
15
+ //# sourceMappingURL=MessageBubble.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageBubble.js","sourceRoot":"","sources":["../../src/components/MessageBubble.tsx"],"names":[],"mappings":";AASA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAEhC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAM/C,MAAM,UAAU,aAAa,CAAC,EAAE,OAAO,EAAsB;IAC3D,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,KAAK,EAAC,SAAS,EAAE,CAAC,aACnC,KAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,OAAO,YAAE,IAAI,GAAQ,EACtC,KAAC,IAAI,IAAC,IAAI,EAAC,MAAM,YAAE,OAAO,CAAC,OAAO,GAAQ,IACtC,CACP,CAAC;IACJ,CAAC;IAED,oBAAoB;IACpB,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,SAAS,EAAE,CAAC,aAEtC,KAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAE,YAAY,CAAC,OAAO,YAAG,OAAO,GAAQ,EAGvD,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CAClD,KAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,YACxB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CACjC,KAAC,aAAa,IAEZ,OAAO,EAAE,IAAI,CAAC,OAAO,EACrB,SAAS,EAAE,KAAK,IAFX,CAAC,CAGN,CACH,CAAC,GACE,CACP,EAGA,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CAC5C,KAAC,SAAS,IAAC,KAAK,EAAE,OAAO,CAAC,KAAK,GAAI,CACpC,EAGA,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CACf,KAAC,GAAG,IAAC,UAAU,EAAE,CAAC,YAChB,MAAC,IAAI,IAAC,KAAK,EAAE,YAAY,CAAC,KAAK,aAAG,MAAM,EAAE,OAAO,CAAC,KAAK,IAAQ,GAC3D,CACP,CAAC,CAAC,CAAC,CACF,KAAC,GAAG,IAAC,UAAU,EAAE,CAAC,YAChB,KAAC,YAAY,IAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,GAAI,GACxD,CACP,EAGA,OAAO,CAAC,QAAQ,IAAI,CACnB,KAAC,WAAW,IAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,GAAI,CAC5C,IACG,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { RunMetadata } from '../types.js';
2
+ interface MetadataBarProps {
3
+ metadata: RunMetadata;
4
+ }
5
+ export declare function MetadataBar({ metadata }: MetadataBarProps): import("react/jsx-runtime").JSX.Element | null;
6
+ export {};
7
+ //# sourceMappingURL=MetadataBar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MetadataBar.d.ts","sourceRoot":"","sources":["../../src/components/MetadataBar.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,UAAU,gBAAgB;IACxB,QAAQ,EAAE,WAAW,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE,gBAAgB,kDAqBzD"}
@@ -0,0 +1,19 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import { THEME_COLORS } from '../config.js';
4
+ export function MetadataBar({ metadata }) {
5
+ const parts = [];
6
+ if (metadata.total_iterations !== undefined) {
7
+ parts.push(`${metadata.total_iterations} iterations`);
8
+ }
9
+ if (metadata.total_tool_calls !== undefined) {
10
+ parts.push(`${metadata.total_tool_calls} tools`);
11
+ }
12
+ if (metadata.total_latency_ms !== undefined) {
13
+ parts.push(`${Math.round(metadata.total_latency_ms).toLocaleString()}ms`);
14
+ }
15
+ if (parts.length === 0)
16
+ return null;
17
+ return (_jsx(Box, { marginLeft: 2, marginTop: 1, children: _jsxs(Text, { color: THEME_COLORS.muted, dimColor: true, children: ["\u2500 ", parts.join(' · ')] }) }));
18
+ }
19
+ //# sourceMappingURL=MetadataBar.js.map