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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MetadataBar.js","sourceRoot":"","sources":["../../src/components/MetadataBar.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAO5C,MAAM,UAAU,WAAW,CAAC,EAAE,QAAQ,EAAoB;IACxD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,QAAQ,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;QAC5C,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,gBAAgB,aAAa,CAAC,CAAC;IACxD,CAAC;IACD,IAAI,QAAQ,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;QAC5C,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,gBAAgB,QAAQ,CAAC,CAAC;IACnD,CAAC;IACD,IAAI,QAAQ,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;QAC5C,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;IAC5E,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEpC,OAAO,CACL,KAAC,GAAG,IAAC,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,YAC9B,MAAC,IAAI,IAAC,KAAK,EAAE,YAAY,CAAC,KAAK,EAAE,QAAQ,8BACpC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IACf,GACH,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ interface SpinnerProps {
2
+ /** Token count to display alongside elapsed time. */
3
+ tokens?: number;
4
+ }
5
+ export declare function Spinner({ tokens }: SpinnerProps): import("react/jsx-runtime").JSX.Element;
6
+ export {};
7
+ //# sourceMappingURL=Spinner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Spinner.d.ts","sourceRoot":"","sources":["../../src/components/Spinner.tsx"],"names":[],"mappings":"AAKA,UAAU,YAAY;IACpB,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAqBD,wBAAgB,OAAO,CAAC,EAAE,MAAU,EAAE,EAAE,YAAY,2CAmCnD"}
@@ -0,0 +1,51 @@
1
+ import { jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect, useRef } from 'react';
3
+ import { Text } from 'ink';
4
+ import { SPINNER } from '../config.js';
5
+ import { SPINNER_VERBS } from '../data/spinnerVerbs.js';
6
+ /** Format elapsed seconds into a human-readable duration string. */
7
+ function formatElapsed(seconds) {
8
+ if (seconds < 60)
9
+ return `${seconds}s`;
10
+ const m = Math.floor(seconds / 60);
11
+ const s = seconds % 60;
12
+ return `${m}m ${String(s).padStart(2, '0')}s`;
13
+ }
14
+ /** Abbreviate large token counts with a 'k' suffix. */
15
+ function formatTokens(count) {
16
+ if (count >= 1000)
17
+ return `${(count / 1000).toFixed(1)}k`;
18
+ return String(count);
19
+ }
20
+ /** Pick a random Korean proverb from the verb list. */
21
+ function randomVerb() {
22
+ return SPINNER_VERBS[Math.floor(Math.random() * SPINNER_VERBS.length)];
23
+ }
24
+ export function Spinner({ tokens = 0 }) {
25
+ const [frame, setFrame] = useState(0);
26
+ const [verb, setVerb] = useState(randomVerb);
27
+ const tickRef = useRef(0);
28
+ // Record the mount time so elapsed seconds are accurate even after re-renders
29
+ const startTime = useRef(Date.now());
30
+ useEffect(() => {
31
+ const interval = setInterval(() => {
32
+ // Advance frame counter (pure updater — no side effects)
33
+ setFrame((f) => f + 1);
34
+ // Rotate proverb outside the updater to keep it pure (React StrictMode safe)
35
+ tickRef.current += 1;
36
+ if (tickRef.current % SPINNER.verbChangeInterval === 0) {
37
+ setVerb(randomVerb());
38
+ }
39
+ }, 1000 / SPINNER.fps);
40
+ return () => clearInterval(interval);
41
+ }, []);
42
+ const char = SPINNER.chars[frame % SPINNER.chars.length];
43
+ const elapsed = Math.floor((Date.now() - startTime.current) / 1000);
44
+ const elapsedStr = formatElapsed(elapsed);
45
+ // Append token count only when the backend has reported usage
46
+ const suffix = tokens > 0
47
+ ? ` (${elapsedStr} · ↓ ${formatTokens(tokens)} tokens)`
48
+ : ` (${elapsedStr})`;
49
+ return (_jsxs(Text, { dimColor: true, children: [char, " ", verb, "\u2026", suffix] }));
50
+ }
51
+ //# sourceMappingURL=Spinner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Spinner.js","sourceRoot":"","sources":["../../src/components/Spinner.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ interface ThinkingBlockProps {
2
+ /** Accumulated thinking text (may be streaming). */
3
+ content: string;
4
+ /** Whether thinking is still in progress. */
5
+ streaming?: boolean;
6
+ }
7
+ export declare function ThinkingBlock({ content, streaming }: ThinkingBlockProps): import("react/jsx-runtime").JSX.Element | null;
8
+ export {};
9
+ //# sourceMappingURL=ThinkingBlock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThinkingBlock.d.ts","sourceRoot":"","sources":["../../src/components/ThinkingBlock.tsx"],"names":[],"mappings":"AAIA,UAAU,kBAAkB;IAC1B,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,EAAE,OAAO,EAAE,SAAiB,EAAE,EAAE,kBAAkB,kDAe/E"}
@@ -0,0 +1,9 @@
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 ThinkingBlock({ content, streaming = false }) {
5
+ if (!content)
6
+ return null;
7
+ return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Text, { color: THEME_COLORS.muted, dimColor: true, children: ['💭 ', streaming ? '사고 중…' : '사고 완료'] }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: THEME_COLORS.dimmed, dimColor: true, wrap: "wrap", children: content }) })] }));
8
+ }
9
+ //# sourceMappingURL=ThinkingBlock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThinkingBlock.js","sourceRoot":"","sources":["../../src/components/ThinkingBlock.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAS5C,MAAM,UAAU,aAAa,CAAC,EAAE,OAAO,EAAE,SAAS,GAAG,KAAK,EAAsB;IAC9E,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE1B,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,UAAU,EAAE,CAAC,aACvC,MAAC,IAAI,IAAC,KAAK,EAAE,YAAY,CAAC,KAAK,EAAE,QAAQ,mBACtC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,IAChC,EACP,KAAC,GAAG,IAAC,UAAU,EAAE,CAAC,YAChB,KAAC,IAAI,IAAC,KAAK,EAAE,YAAY,CAAC,MAAM,EAAE,QAAQ,QAAC,IAAI,EAAC,MAAM,YACnD,OAAO,GACH,GACH,IACF,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { ToolInvocation } from '../types.js';
2
+ interface ToolPanelProps {
3
+ tools: ToolInvocation[];
4
+ }
5
+ export declare function ToolPanel({ tools }: ToolPanelProps): import("react/jsx-runtime").JSX.Element | null;
6
+ export {};
7
+ //# sourceMappingURL=ToolPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolPanel.d.ts","sourceRoot":"","sources":["../../src/components/ToolPanel.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,UAAU,cAAc;IACtB,KAAK,EAAE,cAAc,EAAE,CAAC;CACzB;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,cAAc,kDAyBlD"}
@@ -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
+ import { TOOL_DISPLAY_NAMES } from '../types.js';
5
+ export function ToolPanel({ tools }) {
6
+ if (tools.length === 0)
7
+ return null;
8
+ return (_jsx(Box, { flexDirection: "column", marginLeft: 2, children: tools.map((tool, i) => {
9
+ const displayName = TOOL_DISPLAY_NAMES[tool.tool] ?? tool.tool;
10
+ if (tool.pending) {
11
+ return (_jsxs(Text, { color: THEME_COLORS.warning, children: ["\u250C\u2500 \u2699 ", tool.tool, " (", displayName, ") \uC2E4\uD589 \uC911\u2026"] }, i));
12
+ }
13
+ const statusColor = tool.success !== false ? THEME_COLORS.success : THEME_COLORS.error;
14
+ const statusIcon = tool.success !== false ? '✦' : '✘';
15
+ const statusText = tool.success !== false ? '완료' : '실패';
16
+ return (_jsxs(Text, { color: statusColor, children: ["\u2514\u2500 ", statusIcon, " ", tool.tool, " (", displayName, ") ", statusText] }, i));
17
+ }) }));
18
+ }
19
+ //# sourceMappingURL=ToolPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToolPanel.js","sourceRoot":"","sources":["../../src/components/ToolPanel.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAOjD,MAAM,UAAU,SAAS,CAAC,EAAE,KAAK,EAAkB;IACjD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEpC,OAAO,CACL,KAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,UAAU,EAAE,CAAC,YACtC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;YACrB,MAAM,WAAW,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC;YAC/D,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjB,OAAO,CACL,MAAC,IAAI,IAAS,KAAK,EAAE,YAAY,CAAC,OAAO,qCACjC,IAAI,CAAC,IAAI,QAAI,WAAW,oCADrB,CAAC,CAEL,CACR,CAAC;YACJ,CAAC;YACD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC;YACvF,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;YACtD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;YACxD,OAAO,CACL,MAAC,IAAI,IAAS,KAAK,EAAE,WAAW,8BAC1B,UAAU,OAAG,IAAI,CAAC,IAAI,QAAI,WAAW,QAAI,UAAU,KAD9C,CAAC,CAEL,CACR,CAAC;QACJ,CAAC,CAAC,GACE,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Runtime configuration loaded from environment variables.
3
+ * All timeouts are in milliseconds.
4
+ */
5
+ /** Resolve and validate the backend base URL from GOVON_RUNTIME_URL. */
6
+ export declare function getBaseUrl(): string;
7
+ export declare const TIMEOUTS: {
8
+ readonly connect: 10000;
9
+ readonly read: 300000;
10
+ /** Blocking /v2/agent/run and /v3/agent/run request timeout. */
11
+ readonly run: 120000;
12
+ /** Default timeout for simple requests (health, approve, cancel). */
13
+ readonly default: 30000;
14
+ readonly coldStart: number;
15
+ readonly coldStartInterval: number;
16
+ };
17
+ export declare const THEME_COLORS: {
18
+ readonly primary: "#2D5A3D";
19
+ readonly accent: "#F5E6C8";
20
+ readonly muted: "#7A8B7E";
21
+ readonly error: "#D32F2F";
22
+ readonly warning: "#FFA000";
23
+ readonly success: "#388E3C";
24
+ readonly info: "#1976D2";
25
+ readonly dimmed: "#666666";
26
+ };
27
+ export declare const SPINNER: {
28
+ readonly fps: 30;
29
+ readonly verbChangeInterval: 90;
30
+ readonly chars: readonly ["·", "✻", "✽", "✶", "✳", "✢"];
31
+ };
32
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,wEAAwE;AACxE,wBAAgB,UAAU,IAAI,MAAM,CAuBnC;AAGD,eAAO,MAAM,QAAQ;;;IAGnB,gEAAgE;;IAEhE,qEAAqE;;;;CAI7D,CAAC;AAGX,eAAO,MAAM,YAAY;;;;;;;;;CASf,CAAC;AAGX,eAAO,MAAM,OAAO;;;;CAIV,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Runtime configuration loaded from environment variables.
3
+ * All timeouts are in milliseconds.
4
+ */
5
+ /** Parse a positive integer from env, clamped to [1, maxVal]. Falls back to defaultVal. */
6
+ function parsePosInt(envVal, defaultVal, maxVal = 86_400) {
7
+ const parsed = parseInt(envVal ?? String(defaultVal), 10);
8
+ if (!Number.isFinite(parsed) || parsed <= 0 || parsed > maxVal) {
9
+ return defaultVal;
10
+ }
11
+ return parsed;
12
+ }
13
+ /** Resolve and validate the backend base URL from GOVON_RUNTIME_URL. */
14
+ export function getBaseUrl() {
15
+ const raw = (process.env.GOVON_RUNTIME_URL ?? 'http://127.0.0.1:8000').replace(/\/+$/, '');
16
+ try {
17
+ const parsed = new URL(raw);
18
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
19
+ throw new Error(`GOVON_RUNTIME_URL must use http or https scheme, got: ${parsed.protocol}`);
20
+ }
21
+ // Warn when sending plaintext to a non-localhost remote
22
+ if (parsed.hostname !== '127.0.0.1' &&
23
+ parsed.hostname !== 'localhost' &&
24
+ parsed.protocol === 'http:') {
25
+ process.stderr.write('[warn] GOVON_RUNTIME_URL uses http:// for a non-localhost host. ' +
26
+ 'Use https:// to protect query content in transit.\n');
27
+ }
28
+ return raw;
29
+ }
30
+ catch (err) {
31
+ if (err instanceof Error && err.message.startsWith('GOVON_RUNTIME_URL'))
32
+ throw err;
33
+ throw new Error(`GOVON_RUNTIME_URL is not a valid URL: ${raw}`);
34
+ }
35
+ }
36
+ // Timeout configuration in milliseconds
37
+ export const TIMEOUTS = {
38
+ connect: 10_000,
39
+ read: 300_000,
40
+ /** Blocking /v2/agent/run and /v3/agent/run request timeout. */
41
+ run: 120_000,
42
+ /** Default timeout for simple requests (health, approve, cancel). */
43
+ default: 30_000,
44
+ coldStart: parsePosInt(process.env.GOVON_COLD_START_TIMEOUT, 600) * 1000,
45
+ coldStartInterval: parsePosInt(process.env.GOVON_COLD_START_INTERVAL, 5) * 1000,
46
+ };
47
+ // Brand color palette
48
+ export const THEME_COLORS = {
49
+ primary: '#2D5A3D',
50
+ accent: '#F5E6C8',
51
+ muted: '#7A8B7E',
52
+ error: '#D32F2F',
53
+ warning: '#FFA000',
54
+ success: '#388E3C',
55
+ info: '#1976D2',
56
+ dimmed: '#666666',
57
+ };
58
+ // Spinner animation config
59
+ export const SPINNER = {
60
+ fps: 30,
61
+ verbChangeInterval: 90, // frames (~3 seconds at 30fps)
62
+ chars: ['\u00b7', '\u273b', '\u273d', '\u2736', '\u2733', '\u2722'],
63
+ };
64
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,2FAA2F;AAC3F,SAAS,WAAW,CAAC,MAA0B,EAAE,UAAkB,EAAE,MAAM,GAAG,MAAM;IAClF,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,IAAI,MAAM,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,IAAI,MAAM,GAAG,MAAM,EAAE,CAAC;QAC/D,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,UAAU;IACxB,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,uBAAuB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC3F,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAChE,MAAM,IAAI,KAAK,CAAC,yDAAyD,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC9F,CAAC;QACD,wDAAwD;QACxD,IACE,MAAM,CAAC,QAAQ,KAAK,WAAW;YAC/B,MAAM,CAAC,QAAQ,KAAK,WAAW;YAC/B,MAAM,CAAC,QAAQ,KAAK,OAAO,EAC3B,CAAC;YACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,kEAAkE;gBAChE,qDAAqD,CACxD,CAAC;QACJ,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,mBAAmB,CAAC;YAAE,MAAM,GAAG,CAAC;QACnF,MAAM,IAAI,KAAK,CAAC,yCAAyC,GAAG,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC;AAED,wCAAwC;AACxC,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,OAAO,EAAE,MAAM;IACf,IAAI,EAAE,OAAO;IACb,gEAAgE;IAChE,GAAG,EAAE,OAAO;IACZ,qEAAqE;IACrE,OAAO,EAAE,MAAM;IACf,SAAS,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,GAAG,CAAC,GAAG,IAAI;IACxE,iBAAiB,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE,CAAC,CAAC,GAAG,IAAI;CACvE,CAAC;AAEX,sBAAsB;AACtB,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,SAAS;IACjB,KAAK,EAAE,SAAS;IAChB,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,SAAS;CACT,CAAC;AAEX,2BAA2B;AAC3B,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,GAAG,EAAE,EAAE;IACP,kBAAkB,EAAE,EAAE,EAAE,+BAA+B;IACvD,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAU;CACpE,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Korean proverbs, idioms (사자성어), and fun facts displayed
3
+ * in the spinner while waiting for agent responses.
4
+ * Ported from src/cli/spinner.py STATUS_VERBS.
5
+ */
6
+ export declare const SPINNER_VERBS: readonly string[];
7
+ //# sourceMappingURL=spinnerVerbs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spinnerVerbs.d.ts","sourceRoot":"","sources":["../../src/data/spinnerVerbs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EAmDjC,CAAC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Korean proverbs, idioms (사자성어), and fun facts displayed
3
+ * in the spinner while waiting for agent responses.
4
+ * Ported from src/cli/spinner.py STATUS_VERBS.
5
+ */
6
+ export const SPINNER_VERBS = [
7
+ // 사자성어 (Four-character idioms)
8
+ '온고지신 (溫故知新)',
9
+ '유비무환 (有備無患)',
10
+ '일석이조 (一石二鳥)',
11
+ '대기만성 (大器晩成)',
12
+ '자강불식 (自強不息)',
13
+ '형설지공 (螢雪之功)',
14
+ '우공이산 (愚公移山)',
15
+ '마부위침 (磨斧爲針)',
16
+ '견인불발 (堅忍不拔)',
17
+ '금상첨화 (錦上添花)',
18
+ '선견지명 (先見之明)',
19
+ '타산지석 (他山之石)',
20
+ '화룡점정 (畫龍點睛)',
21
+ '사필귀정 (事必歸正)',
22
+ '전화위복 (轉禍爲福)',
23
+ '고진감래 (苦盡甘來)',
24
+ '불철주야 (不撤晝夜)',
25
+ '절차탁마 (切磋琢磨)',
26
+ '심사숙고 (深思熟考)',
27
+ '천재일우 (千載一遇)',
28
+ '이심전심 (以心傳心)',
29
+ '백문불여일견 (百聞不如一見)',
30
+ '각골난망 (刻骨難忘)',
31
+ '결초보은 (結草報恩)',
32
+ '개과천선 (改過遷善)',
33
+ // 속담 (Proverbs)
34
+ '천 리 길도 한 걸음부터',
35
+ '구슬이 서 말이라도 꿰어야 보배',
36
+ '돌다리도 두들겨 보고 건너라',
37
+ '뜻이 있는 곳에 길이 있다',
38
+ '공든 탑이 무너지랴',
39
+ '티끌 모아 태산',
40
+ '하늘은 스스로 돕는 자를 돕는다',
41
+ '실패는 성공의 어머니',
42
+ '세 살 버릇 여든까지 간다',
43
+ '빈 수레가 요란하다',
44
+ '등잔 밑이 어둡다',
45
+ '호랑이도 제 말 하면 온다',
46
+ '원숭이도 나무에서 떨어진다',
47
+ '소 잃고 외양간 고친다',
48
+ '가는 말이 고와야 오는 말이 곱다',
49
+ // 재미있는 상식 (Fun facts)
50
+ '꿀벌은 한 숟갈의 꿀을 위해 평생 난다',
51
+ '문어의 심장은 세 개다',
52
+ '바나나는 식물학적으로 장과(berry)이다',
53
+ '지구에서 가장 오래된 나무는 5000살이 넘는다',
54
+ '인간의 뇌는 60%가 지방이다',
55
+ '해파리는 뇌가 없다',
56
+ '낙타의 혹에는 물이 아니라 지방이 있다',
57
+ ];
58
+ //# sourceMappingURL=spinnerVerbs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spinnerVerbs.js","sourceRoot":"","sources":["../../src/data/spinnerVerbs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAsB;IAC9C,+BAA+B;IAC/B,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,iBAAiB;IACjB,aAAa;IACb,aAAa;IACb,aAAa;IACb,gBAAgB;IAChB,eAAe;IACf,mBAAmB;IACnB,iBAAiB;IACjB,gBAAgB;IAChB,YAAY;IACZ,UAAU;IACV,mBAAmB;IACnB,aAAa;IACb,gBAAgB;IAChB,YAAY;IACZ,WAAW;IACX,gBAAgB;IAChB,gBAAgB;IAChB,cAAc;IACd,oBAAoB;IACpB,sBAAsB;IACtB,uBAAuB;IACvB,cAAc;IACd,yBAAyB;IACzB,4BAA4B;IAC5B,kBAAkB;IAClB,YAAY;IACZ,uBAAuB;CACf,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { GovOnClient } from '../client.js';
2
+ interface DaemonState {
3
+ /** Server is confirmed ready. */
4
+ ready: boolean;
5
+ /** Currently waiting for server. */
6
+ waiting: boolean;
7
+ /** Error message if server could not be reached. */
8
+ error: string | null;
9
+ }
10
+ /**
11
+ * Check server health on mount. If the server is sleeping (e.g. HF Space),
12
+ * call client.waitForReady() which polls with status messages to stderr.
13
+ */
14
+ export declare function useDaemon(client: GovOnClient): DaemonState;
15
+ export {};
16
+ //# sourceMappingURL=useDaemon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDaemon.d.ts","sourceRoot":"","sources":["../../src/hooks/useDaemon.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,UAAU,WAAW;IACnB,iCAAiC;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,oCAAoC;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,oDAAoD;IACpD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAgD1D"}
@@ -0,0 +1,55 @@
1
+ import { useState, useEffect, useRef } from 'react';
2
+ /**
3
+ * Check server health on mount. If the server is sleeping (e.g. HF Space),
4
+ * call client.waitForReady() which polls with status messages to stderr.
5
+ */
6
+ export function useDaemon(client) {
7
+ const [state, setState] = useState({
8
+ ready: false,
9
+ waiting: true,
10
+ error: null,
11
+ });
12
+ const controllerRef = useRef(new AbortController());
13
+ useEffect(() => {
14
+ let mounted = true;
15
+ async function check() {
16
+ try {
17
+ // Quick health check first
18
+ await client.health();
19
+ if (mounted)
20
+ setState({ ready: true, waiting: false, error: null });
21
+ }
22
+ catch {
23
+ // Server not immediately available — try cold start wait
24
+ if (mounted)
25
+ setState((s) => ({ ...s, waiting: true }));
26
+ try {
27
+ const ok = await client.waitForReady();
28
+ if (mounted) {
29
+ setState({
30
+ ready: ok,
31
+ waiting: false,
32
+ error: ok ? null : 'Server connection timed out',
33
+ });
34
+ }
35
+ }
36
+ catch (err) {
37
+ if (mounted) {
38
+ setState({
39
+ ready: false,
40
+ waiting: false,
41
+ error: err instanceof Error ? err.message : String(err),
42
+ });
43
+ }
44
+ }
45
+ }
46
+ }
47
+ check();
48
+ return () => {
49
+ mounted = false;
50
+ controllerRef.current.abort();
51
+ };
52
+ }, [client]);
53
+ return state;
54
+ }
55
+ //# sourceMappingURL=useDaemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDaemon.js","sourceRoot":"","sources":["../../src/hooks/useDaemon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAYpD;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,MAAmB;IAC3C,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAc;QAC9C,KAAK,EAAE,KAAK;QACZ,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,IAAI;KACZ,CAAC,CAAC;IACH,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,eAAe,EAAE,CAAC,CAAC;IAEpD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,OAAO,GAAG,IAAI,CAAC;QAEnB,KAAK,UAAU,KAAK;YAClB,IAAI,CAAC;gBACH,2BAA2B;gBAC3B,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;gBACtB,IAAI,OAAO;oBAAE,QAAQ,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACtE,CAAC;YAAC,MAAM,CAAC;gBACP,yDAAyD;gBACzD,IAAI,OAAO;oBAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;gBACxD,IAAI,CAAC;oBACH,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;oBACvC,IAAI,OAAO,EAAE,CAAC;wBACZ,QAAQ,CAAC;4BACP,KAAK,EAAE,EAAE;4BACT,OAAO,EAAE,KAAK;4BACd,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,6BAA6B;yBACjD,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,IAAI,OAAO,EAAE,CAAC;wBACZ,QAAQ,CAAC;4BACP,KAAK,EAAE,KAAK;4BACZ,OAAO,EAAE,KAAK;4BACd,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;yBACxD,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,KAAK,EAAE,CAAC;QACR,OAAO,GAAG,EAAE;YACV,OAAO,GAAG,KAAK,CAAC;YAChB,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAChC,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Load and persist CLI input history from ~/.govon/history.
3
+ * Returns the history entries, a push function, a navigate function, and the
4
+ * current cursor position (-1 means "at current input").
5
+ */
6
+ export declare function useHistory(): {
7
+ entries: string[];
8
+ push: (entry: string) => void;
9
+ navigate: (direction: "up" | "down") => string | undefined;
10
+ resetCursor: () => void;
11
+ cursor: number;
12
+ };
13
+ //# sourceMappingURL=useHistory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useHistory.d.ts","sourceRoot":"","sources":["../../src/hooks/useHistory.ts"],"names":[],"mappings":"AA8GA;;;;GAIG;AACH,wBAAgB,UAAU;;kBAsDS,MAAM;0BAQzB,IAAI,GAAG,MAAM,KAAG,MAAM,GAAG,SAAS;;;EAgCjD"}
@@ -0,0 +1,170 @@
1
+ import { useReducer, useCallback, useEffect, useRef } from 'react';
2
+ import { writeFile } from 'node:fs/promises';
3
+ import { readFileSync, mkdirSync, lstatSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { homedir } from 'node:os';
6
+ const HISTORY_DIR = join(homedir(), '.govon');
7
+ const HISTORY_FILE = join(HISTORY_DIR, 'history');
8
+ const MAX_ENTRIES = 500;
9
+ const MAX_ENTRY_BYTES = 4096;
10
+ const DEBOUNCE_MS = 300;
11
+ // ---------------------------------------------------------------------------
12
+ // Reducer — makes push (entries + cursor) atomic
13
+ // ---------------------------------------------------------------------------
14
+ function historyReducer(state, action) {
15
+ switch (action.type) {
16
+ case 'PUSH': {
17
+ const trimmed = action.entry.trim();
18
+ if (!trimmed)
19
+ return state;
20
+ if (Buffer.byteLength(trimmed, 'utf8') > MAX_ENTRY_BYTES)
21
+ return state;
22
+ // Deduplicate consecutive identical entries
23
+ const next = state.entries[state.entries.length - 1] === trimmed
24
+ ? state.entries
25
+ : [...state.entries, trimmed];
26
+ return { entries: next.slice(-MAX_ENTRIES), cursor: -1 };
27
+ }
28
+ case 'NAVIGATE': {
29
+ const { entries, cursor } = state;
30
+ if (entries.length === 0)
31
+ return state;
32
+ if (action.direction === 'up') {
33
+ const next = cursor === -1 ? entries.length - 1 : Math.max(0, cursor - 1);
34
+ return { ...state, cursor: next };
35
+ }
36
+ // direction === 'down'
37
+ if (cursor === -1)
38
+ return state; // already at current input — nothing to do
39
+ if (cursor + 1 >= entries.length) {
40
+ return { ...state, cursor: -1 }; // back to empty input
41
+ }
42
+ return { ...state, cursor: cursor + 1 };
43
+ }
44
+ case 'RESET_CURSOR':
45
+ return { ...state, cursor: -1 };
46
+ default:
47
+ return state;
48
+ }
49
+ }
50
+ // ---------------------------------------------------------------------------
51
+ // Initial state loader
52
+ // ---------------------------------------------------------------------------
53
+ function loadInitialEntries() {
54
+ try {
55
+ const raw = readFileSync(HISTORY_FILE, 'utf-8');
56
+ return raw.split('\n').filter(Boolean).slice(-MAX_ENTRIES);
57
+ }
58
+ catch {
59
+ return [];
60
+ }
61
+ }
62
+ // ---------------------------------------------------------------------------
63
+ // Async persist helper — symlink-safe
64
+ // ---------------------------------------------------------------------------
65
+ async function persistEntries(entries) {
66
+ try {
67
+ mkdirSync(HISTORY_DIR, { recursive: true });
68
+ // Reject symlinks to prevent symlink-based write attacks
69
+ try {
70
+ const stat = lstatSync(HISTORY_FILE);
71
+ if (!stat.isFile())
72
+ return; // reject symlinks or other non-regular files
73
+ }
74
+ catch {
75
+ // File does not exist yet — safe to create
76
+ }
77
+ await writeFile(HISTORY_FILE, entries.join('\n') + '\n', 'utf-8');
78
+ }
79
+ catch {
80
+ // Silently ignore write failures (read-only fs, etc.)
81
+ }
82
+ }
83
+ // ---------------------------------------------------------------------------
84
+ // Hook
85
+ // ---------------------------------------------------------------------------
86
+ /**
87
+ * Load and persist CLI input history from ~/.govon/history.
88
+ * Returns the history entries, a push function, a navigate function, and the
89
+ * current cursor position (-1 means "at current input").
90
+ */
91
+ export function useHistory() {
92
+ const [state, dispatch] = useReducer(historyReducer, undefined, () => ({
93
+ entries: loadInitialEntries(),
94
+ cursor: -1,
95
+ }));
96
+ const { entries, cursor } = state;
97
+ // Debounce + overlap-guard refs for async persistence
98
+ const debounceTimerRef = useRef(null);
99
+ const writeInProgressRef = useRef(false);
100
+ const pendingWriteRef = useRef(false);
101
+ const latestEntriesRef = useRef(entries);
102
+ // Keep latest entries ref in sync
103
+ latestEntriesRef.current = entries;
104
+ // Persist entries whenever they change, with debounce and trailing-write guard
105
+ useEffect(() => {
106
+ if (debounceTimerRef.current !== null) {
107
+ clearTimeout(debounceTimerRef.current);
108
+ }
109
+ debounceTimerRef.current = setTimeout(() => {
110
+ debounceTimerRef.current = null;
111
+ if (writeInProgressRef.current) {
112
+ // A write is running — queue a trailing write so the latest state is persisted
113
+ pendingWriteRef.current = true;
114
+ return;
115
+ }
116
+ writeInProgressRef.current = true;
117
+ persistEntries(latestEntriesRef.current).finally(() => {
118
+ writeInProgressRef.current = false;
119
+ // If entries changed while the write was in progress, flush the latest
120
+ if (pendingWriteRef.current) {
121
+ pendingWriteRef.current = false;
122
+ writeInProgressRef.current = true;
123
+ void persistEntries(latestEntriesRef.current).finally(() => {
124
+ writeInProgressRef.current = false;
125
+ });
126
+ }
127
+ });
128
+ }, DEBOUNCE_MS);
129
+ return () => {
130
+ if (debounceTimerRef.current !== null) {
131
+ clearTimeout(debounceTimerRef.current);
132
+ debounceTimerRef.current = null;
133
+ }
134
+ };
135
+ }, [entries]);
136
+ const push = useCallback((entry) => {
137
+ dispatch({ type: 'PUSH', entry });
138
+ }, []);
139
+ // Navigate up/down through history.
140
+ // Returns the selected entry string, '' when returning to current input, or
141
+ // undefined when there is nothing to navigate.
142
+ const navigate = useCallback((direction) => {
143
+ if (entries.length === 0)
144
+ return undefined;
145
+ const prevCursor = cursor;
146
+ if (direction === 'down') {
147
+ if (prevCursor === -1)
148
+ return undefined; // already at current input
149
+ if (prevCursor + 1 >= entries.length) {
150
+ dispatch({ type: 'NAVIGATE', direction: 'down' });
151
+ return ''; // signal: restore empty input
152
+ }
153
+ }
154
+ dispatch({ type: 'NAVIGATE', direction });
155
+ // Compute the next cursor locally so we can return the correct entry
156
+ // without waiting for the next render cycle.
157
+ if (direction === 'up') {
158
+ const next = prevCursor === -1 ? entries.length - 1 : Math.max(0, prevCursor - 1);
159
+ return entries[next];
160
+ }
161
+ else {
162
+ return entries[prevCursor + 1];
163
+ }
164
+ }, [entries, cursor]);
165
+ const resetCursor = useCallback(() => {
166
+ dispatch({ type: 'RESET_CURSOR' });
167
+ }, []);
168
+ return { entries, push, navigate, resetCursor, cursor };
169
+ }
170
+ //# sourceMappingURL=useHistory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useHistory.js","sourceRoot":"","sources":["../../src/hooks/useHistory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * useSSE — React hook managing the SSE streaming lifecycle.
3
+ *
4
+ * Mirrors the fallback chain in src/cli/shell.py _try_process_query (lines 164-202):
5
+ * 1. Try v3 fine-grained streaming (client.streamV3)
6
+ * 2. On failure → try v2 node-level streaming (client.stream)
7
+ * 3. On failure → try blocking run (client.run)
8
+ *
9
+ * Cancellation is handled via an AbortController stored in a ref.
10
+ * The controller.signal is forwarded to every client call so that aborting the
11
+ * controller immediately cancels the underlying fetch (no zombie requests).
12
+ *
13
+ * An unmount guard (mountedRef) prevents dispatching into unmounted components.
14
+ */
15
+ import type { GovOnClient } from '../client.js';
16
+ import type { Action } from '../types.js';
17
+ export interface UseSSEOptions {
18
+ client: GovOnClient;
19
+ dispatch: React.Dispatch<Action>;
20
+ sessionId: string | null;
21
+ }
22
+ export interface UseSSEReturn {
23
+ /** Submit a user query. Resolves when the run finishes (success or error). */
24
+ submit: (query: string) => Promise<void>;
25
+ /** Abort the in-flight request immediately. */
26
+ cancel: () => void;
27
+ }
28
+ export declare function useSSE({ client, dispatch, sessionId }: UseSSEOptions): UseSSEReturn;
29
+ //# sourceMappingURL=useSSE.d.ts.map