@yagr/agent 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.
Files changed (215) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +257 -0
  3. package/dist/agent.d.ts +21 -0
  4. package/dist/agent.d.ts.map +1 -0
  5. package/dist/agent.js +47 -0
  6. package/dist/agent.js.map +1 -0
  7. package/dist/cli.d.ts +3 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +293 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/config/init-yagr-home.d.ts +2 -0
  12. package/dist/config/init-yagr-home.d.ts.map +1 -0
  13. package/dist/config/init-yagr-home.js +6 -0
  14. package/dist/config/init-yagr-home.js.map +1 -0
  15. package/dist/config/load-env.d.ts +2 -0
  16. package/dist/config/load-env.d.ts.map +1 -0
  17. package/dist/config/load-env.js +20 -0
  18. package/dist/config/load-env.js.map +1 -0
  19. package/dist/config/load-n8n-engine-config.d.ts +6 -0
  20. package/dist/config/load-n8n-engine-config.d.ts.map +1 -0
  21. package/dist/config/load-n8n-engine-config.js +39 -0
  22. package/dist/config/load-n8n-engine-config.js.map +1 -0
  23. package/dist/config/yagr-config-service.d.ts +51 -0
  24. package/dist/config/yagr-config-service.d.ts.map +1 -0
  25. package/dist/config/yagr-config-service.js +103 -0
  26. package/dist/config/yagr-config-service.js.map +1 -0
  27. package/dist/config/yagr-home.d.ts +4 -0
  28. package/dist/config/yagr-home.d.ts.map +1 -0
  29. package/dist/config/yagr-home.js +25 -0
  30. package/dist/config/yagr-home.js.map +1 -0
  31. package/dist/engine/engine.d.ts +15 -0
  32. package/dist/engine/engine.d.ts.map +1 -0
  33. package/dist/engine/engine.js +2 -0
  34. package/dist/engine/engine.js.map +1 -0
  35. package/dist/engine/n8n-engine.d.ts +26 -0
  36. package/dist/engine/n8n-engine.d.ts.map +1 -0
  37. package/dist/engine/n8n-engine.js +200 -0
  38. package/dist/engine/n8n-engine.js.map +1 -0
  39. package/dist/engine/yagr-engine.d.ts +17 -0
  40. package/dist/engine/yagr-engine.d.ts.map +1 -0
  41. package/dist/engine/yagr-engine.js +37 -0
  42. package/dist/engine/yagr-engine.js.map +1 -0
  43. package/dist/gateway/cli.d.ts +8 -0
  44. package/dist/gateway/cli.d.ts.map +1 -0
  45. package/dist/gateway/cli.js +50 -0
  46. package/dist/gateway/cli.js.map +1 -0
  47. package/dist/gateway/history-viewport.d.ts +14 -0
  48. package/dist/gateway/history-viewport.d.ts.map +1 -0
  49. package/dist/gateway/history-viewport.js +51 -0
  50. package/dist/gateway/history-viewport.js.map +1 -0
  51. package/dist/gateway/interactive-ui.d.ts +4 -0
  52. package/dist/gateway/interactive-ui.d.ts.map +1 -0
  53. package/dist/gateway/interactive-ui.js +430 -0
  54. package/dist/gateway/interactive-ui.js.map +1 -0
  55. package/dist/gateway/manager.d.ts +25 -0
  56. package/dist/gateway/manager.d.ts.map +1 -0
  57. package/dist/gateway/manager.js +204 -0
  58. package/dist/gateway/manager.js.map +1 -0
  59. package/dist/gateway/telegram.d.ts +30 -0
  60. package/dist/gateway/telegram.d.ts.map +1 -0
  61. package/dist/gateway/telegram.js +406 -0
  62. package/dist/gateway/telegram.js.map +1 -0
  63. package/dist/gateway/types.d.ts +19 -0
  64. package/dist/gateway/types.d.ts.map +1 -0
  65. package/dist/gateway/types.js +2 -0
  66. package/dist/gateway/types.js.map +1 -0
  67. package/dist/gateway/webui.d.ts +13 -0
  68. package/dist/gateway/webui.d.ts.map +1 -0
  69. package/dist/gateway/webui.js +663 -0
  70. package/dist/gateway/webui.js.map +1 -0
  71. package/dist/index.d.ts +23 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +15 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/llm/create-language-model.d.ts +31 -0
  76. package/dist/llm/create-language-model.d.ts.map +1 -0
  77. package/dist/llm/create-language-model.js +129 -0
  78. package/dist/llm/create-language-model.js.map +1 -0
  79. package/dist/prompt/build-system-prompt.d.ts +3 -0
  80. package/dist/prompt/build-system-prompt.d.ts.map +1 -0
  81. package/dist/prompt/build-system-prompt.js +64 -0
  82. package/dist/prompt/build-system-prompt.js.map +1 -0
  83. package/dist/runtime/completion-gate.d.ts +21 -0
  84. package/dist/runtime/completion-gate.d.ts.map +1 -0
  85. package/dist/runtime/completion-gate.js +59 -0
  86. package/dist/runtime/completion-gate.js.map +1 -0
  87. package/dist/runtime/context-compaction.d.ts +25 -0
  88. package/dist/runtime/context-compaction.d.ts.map +1 -0
  89. package/dist/runtime/context-compaction.js +197 -0
  90. package/dist/runtime/context-compaction.js.map +1 -0
  91. package/dist/runtime/outcome.d.ts +23 -0
  92. package/dist/runtime/outcome.d.ts.map +1 -0
  93. package/dist/runtime/outcome.js +89 -0
  94. package/dist/runtime/outcome.js.map +1 -0
  95. package/dist/runtime/policy-hooks.d.ts +15 -0
  96. package/dist/runtime/policy-hooks.d.ts.map +1 -0
  97. package/dist/runtime/policy-hooks.js +54 -0
  98. package/dist/runtime/policy-hooks.js.map +1 -0
  99. package/dist/runtime/required-actions.d.ts +5 -0
  100. package/dist/runtime/required-actions.d.ts.map +1 -0
  101. package/dist/runtime/required-actions.js +77 -0
  102. package/dist/runtime/required-actions.js.map +1 -0
  103. package/dist/runtime/run-engine.d.ts +15 -0
  104. package/dist/runtime/run-engine.d.ts.map +1 -0
  105. package/dist/runtime/run-engine.js +624 -0
  106. package/dist/runtime/run-engine.js.map +1 -0
  107. package/dist/setup/setup-wizard.d.ts +52 -0
  108. package/dist/setup/setup-wizard.d.ts.map +1 -0
  109. package/dist/setup/setup-wizard.js +613 -0
  110. package/dist/setup/setup-wizard.js.map +1 -0
  111. package/dist/setup/start-launcher.d.ts +22 -0
  112. package/dist/setup/start-launcher.d.ts.map +1 -0
  113. package/dist/setup/start-launcher.js +76 -0
  114. package/dist/setup/start-launcher.js.map +1 -0
  115. package/dist/setup.d.ts +20 -0
  116. package/dist/setup.d.ts.map +1 -0
  117. package/dist/setup.js +247 -0
  118. package/dist/setup.js.map +1 -0
  119. package/dist/tools/build-tools.d.ts +608 -0
  120. package/dist/tools/build-tools.d.ts.map +1 -0
  121. package/dist/tools/build-tools.js +35 -0
  122. package/dist/tools/build-tools.js.map +1 -0
  123. package/dist/tools/delete-workspace-file.d.ts +38 -0
  124. package/dist/tools/delete-workspace-file.d.ts.map +1 -0
  125. package/dist/tools/delete-workspace-file.js +31 -0
  126. package/dist/tools/delete-workspace-file.js.map +1 -0
  127. package/dist/tools/deploy.d.ts +110 -0
  128. package/dist/tools/deploy.d.ts.map +1 -0
  129. package/dist/tools/deploy.js +28 -0
  130. package/dist/tools/deploy.js.map +1 -0
  131. package/dist/tools/generate-workflow.d.ts +180 -0
  132. package/dist/tools/generate-workflow.d.ts.map +1 -0
  133. package/dist/tools/generate-workflow.js +46 -0
  134. package/dist/tools/generate-workflow.js.map +1 -0
  135. package/dist/tools/index.d.ts +20 -0
  136. package/dist/tools/index.d.ts.map +1 -0
  137. package/dist/tools/index.js +20 -0
  138. package/dist/tools/index.js.map +1 -0
  139. package/dist/tools/list-directory.d.ts +48 -0
  140. package/dist/tools/list-directory.d.ts.map +1 -0
  141. package/dist/tools/list-directory.js +61 -0
  142. package/dist/tools/list-directory.js.map +1 -0
  143. package/dist/tools/list-workflows.d.ts +18 -0
  144. package/dist/tools/list-workflows.d.ts.map +1 -0
  145. package/dist/tools/list-workflows.js +17 -0
  146. package/dist/tools/list-workflows.js.map +1 -0
  147. package/dist/tools/manage-workflow.d.ts +42 -0
  148. package/dist/tools/manage-workflow.d.ts.map +1 -0
  149. package/dist/tools/manage-workflow.js +26 -0
  150. package/dist/tools/manage-workflow.js.map +1 -0
  151. package/dist/tools/move-workspace-file.d.ts +42 -0
  152. package/dist/tools/move-workspace-file.d.ts.map +1 -0
  153. package/dist/tools/move-workspace-file.js +45 -0
  154. package/dist/tools/move-workspace-file.js.map +1 -0
  155. package/dist/tools/n8nac.d.ts +144 -0
  156. package/dist/tools/n8nac.d.ts.map +1 -0
  157. package/dist/tools/n8nac.js +349 -0
  158. package/dist/tools/n8nac.js.map +1 -0
  159. package/dist/tools/node-info.d.ts +18 -0
  160. package/dist/tools/node-info.d.ts.map +1 -0
  161. package/dist/tools/node-info.js +15 -0
  162. package/dist/tools/node-info.js.map +1 -0
  163. package/dist/tools/observer.d.ts +32 -0
  164. package/dist/tools/observer.d.ts.map +1 -0
  165. package/dist/tools/observer.js +10 -0
  166. package/dist/tools/observer.js.map +1 -0
  167. package/dist/tools/read-workspace-file.d.ts +54 -0
  168. package/dist/tools/read-workspace-file.d.ts.map +1 -0
  169. package/dist/tools/read-workspace-file.js +49 -0
  170. package/dist/tools/read-workspace-file.js.map +1 -0
  171. package/dist/tools/replace-in-workspace-file.d.ts +62 -0
  172. package/dist/tools/replace-in-workspace-file.d.ts.map +1 -0
  173. package/dist/tools/replace-in-workspace-file.js +46 -0
  174. package/dist/tools/replace-in-workspace-file.js.map +1 -0
  175. package/dist/tools/report-progress.d.ts +20 -0
  176. package/dist/tools/report-progress.d.ts.map +1 -0
  177. package/dist/tools/report-progress.js +20 -0
  178. package/dist/tools/report-progress.js.map +1 -0
  179. package/dist/tools/request-required-action.d.ts +31 -0
  180. package/dist/tools/request-required-action.d.ts.map +1 -0
  181. package/dist/tools/request-required-action.js +33 -0
  182. package/dist/tools/request-required-action.js.map +1 -0
  183. package/dist/tools/search-nodes.d.ts +18 -0
  184. package/dist/tools/search-nodes.d.ts.map +1 -0
  185. package/dist/tools/search-nodes.js +15 -0
  186. package/dist/tools/search-nodes.js.map +1 -0
  187. package/dist/tools/search-templates.d.ts +18 -0
  188. package/dist/tools/search-templates.d.ts.map +1 -0
  189. package/dist/tools/search-templates.js +15 -0
  190. package/dist/tools/search-templates.js.map +1 -0
  191. package/dist/tools/search-workspace.d.ts +55 -0
  192. package/dist/tools/search-workspace.d.ts.map +1 -0
  193. package/dist/tools/search-workspace.js +86 -0
  194. package/dist/tools/search-workspace.js.map +1 -0
  195. package/dist/tools/validate.d.ts +110 -0
  196. package/dist/tools/validate.d.ts.map +1 -0
  197. package/dist/tools/validate.js +28 -0
  198. package/dist/tools/validate.js.map +1 -0
  199. package/dist/tools/workspace-utils.d.ts +10 -0
  200. package/dist/tools/workspace-utils.d.ts.map +1 -0
  201. package/dist/tools/workspace-utils.js +63 -0
  202. package/dist/tools/workspace-utils.js.map +1 -0
  203. package/dist/tools/write-workspace-file.d.ts +46 -0
  204. package/dist/tools/write-workspace-file.d.ts.map +1 -0
  205. package/dist/tools/write-workspace-file.js +39 -0
  206. package/dist/tools/write-workspace-file.js.map +1 -0
  207. package/dist/types.d.ts +243 -0
  208. package/dist/types.d.ts.map +1 -0
  209. package/dist/types.js +2 -0
  210. package/dist/types.js.map +1 -0
  211. package/dist/webui/app.js +26759 -0
  212. package/dist/webui/app.js.map +7 -0
  213. package/dist/webui/styles.css +740 -0
  214. package/dist/webui/styles.css.map +7 -0
  215. package/package.json +72 -0
@@ -0,0 +1,613 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text, render, useApp, useInput, useStdout } from 'ink';
3
+ import { useCallback, useEffect, useRef, useState } from 'react';
4
+ // ─── Palette ──────────────────────────────────────────────────────────────────
5
+ const CURSOR = '▸';
6
+ const CHECK = '✓';
7
+ const DOT = '·';
8
+ const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
9
+ const VALID_PROVIDERS = [
10
+ 'anthropic', 'openai', 'google', 'groq', 'mistral', 'openrouter',
11
+ ];
12
+ const SURFACE_OPTIONS = [
13
+ { value: 'telegram', label: 'Telegram', hint: 'Bot-based chat gateway' },
14
+ { value: 'whatsapp', label: 'WhatsApp', hint: 'Configuration only - runtime coming soon' },
15
+ ];
16
+ export function runSetupWizard(callbacks) {
17
+ return new Promise((resolve) => {
18
+ const { unmount } = render(_jsx(SetupWizard, { callbacks: callbacks, onDone: (result) => { unmount(); resolve(result); } }));
19
+ });
20
+ }
21
+ function sectionFor(phase) {
22
+ if (phase.kind.startsWith('n8n'))
23
+ return 'n8n · Orchestrator';
24
+ if (phase.kind.startsWith('llm'))
25
+ return 'LLM · Language Model';
26
+ if (phase.kind.startsWith('surfaces') || phase.kind.startsWith('telegram'))
27
+ return 'Gateways · Messaging';
28
+ return '';
29
+ }
30
+ function sectionIndex(phase) {
31
+ if (phase.kind.startsWith('n8n'))
32
+ return 1;
33
+ if (phase.kind.startsWith('llm'))
34
+ return 2;
35
+ return 3;
36
+ }
37
+ // ─── Primitive UI components ──────────────────────────────────────────────────
38
+ function Rule() {
39
+ return _jsx(Text, { dimColor: true, children: '─'.repeat(56) });
40
+ }
41
+ function Header({ phase }) {
42
+ const section = sectionFor(phase);
43
+ const idx = sectionIndex(phase);
44
+ const isDone = phase.kind === 'done' || phase.kind === 'cancelled' || phase.kind === 'error';
45
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { color: "cyan", bold: true, children: "\u25C8 Yagr Setup" }), !isDone && (_jsx(Text, { dimColor: true, children: `${idx === 1 ? '●' : '○'}${idx === 2 ? '●' : '○'}${idx === 3 ? '●' : '○'} step ${idx} / 3` }))] }), section ? (_jsx(Text, { color: "cyan", dimColor: true, children: section })) : null, _jsx(Rule, {})] }));
46
+ }
47
+ function HintBar({ hints }) {
48
+ return (_jsxs(Box, { marginTop: 1, children: [_jsx(Rule, {}), _jsx(Box, { children: hints.map((hint, i) => (_jsxs(Text, { dimColor: true, children: [hint, i < hints.length - 1 ? ' ' : ''] }, i))) })] }));
49
+ }
50
+ function FieldLabel({ label, done }) {
51
+ return (_jsx(Box, { marginBottom: 0, children: _jsxs(Text, { color: done ? 'green' : 'white', bold: true, children: [done ? `${CHECK} ` : `${CURSOR} `, label] }) }));
52
+ }
53
+ function ErrorLine({ message }) {
54
+ return (_jsx(Box, { marginTop: 0, children: _jsxs(Text, { color: "red", children: [" \u2715 ", message] }) }));
55
+ }
56
+ function SpinnerDisplay({ message, frame }) {
57
+ return (_jsxs(Box, { children: [_jsxs(Text, { color: "cyan", children: [SPINNER_FRAMES[frame % SPINNER_FRAMES.length], " "] }), _jsx(Text, { dimColor: true, children: message })] }));
58
+ }
59
+ function sanitizeTerminalInputChunk(input) {
60
+ return input
61
+ .replace(/\u001B\[200~/g, '')
62
+ .replace(/\u001B\[201~/g, '')
63
+ .replace(/\r/g, '');
64
+ }
65
+ function getDisplayedModelOptions(models) {
66
+ return [...models, '__custom__'];
67
+ }
68
+ function getVisibleWindow(total, cursor, maxVisible) {
69
+ if (total <= maxVisible) {
70
+ return { start: 0, end: total };
71
+ }
72
+ const clampedCursor = Math.max(0, Math.min(cursor, total - 1));
73
+ const half = Math.floor(maxVisible / 2);
74
+ let start = Math.max(0, clampedCursor - half);
75
+ const maxStart = Math.max(0, total - maxVisible);
76
+ if (start > maxStart) {
77
+ start = maxStart;
78
+ }
79
+ return { start, end: Math.min(total, start + maxVisible) };
80
+ }
81
+ function getListViewportHeight(terminalRows, reservedRows) {
82
+ return Math.max(2, terminalRows - reservedRows - 2);
83
+ }
84
+ function truncateTerminalLine(value, maxWidth) {
85
+ if (maxWidth <= 0) {
86
+ return '';
87
+ }
88
+ if (value.length <= maxWidth) {
89
+ return value;
90
+ }
91
+ if (maxWidth === 1) {
92
+ return '…';
93
+ }
94
+ return `${value.slice(0, maxWidth - 1)}…`;
95
+ }
96
+ function WizardTextInput({ value, onChange, onSubmit, placeholder, mask, }) {
97
+ const valueRef = useRef(value);
98
+ const cursorOffsetRef = useRef(value.length);
99
+ const [renderState, setRenderState] = useState({
100
+ value,
101
+ cursorOffset: value.length,
102
+ });
103
+ useEffect(() => {
104
+ valueRef.current = value;
105
+ cursorOffsetRef.current = Math.min(cursorOffsetRef.current, value.length);
106
+ setRenderState({
107
+ value,
108
+ cursorOffset: Math.min(cursorOffsetRef.current, value.length),
109
+ });
110
+ }, [value]);
111
+ useInput((input, key) => {
112
+ if (key.upArrow || key.downArrow || (key.ctrl && input === 'c') || key.tab || (key.shift && key.tab)) {
113
+ return;
114
+ }
115
+ const currentValue = valueRef.current;
116
+ const currentCursorOffset = cursorOffsetRef.current;
117
+ if (key.return) {
118
+ onSubmit(currentValue);
119
+ return;
120
+ }
121
+ let nextValue = currentValue;
122
+ let nextCursorOffset = currentCursorOffset;
123
+ if (key.leftArrow) {
124
+ nextCursorOffset = Math.max(0, currentCursorOffset - 1);
125
+ }
126
+ else if (key.rightArrow) {
127
+ nextCursorOffset = Math.min(currentValue.length, currentCursorOffset + 1);
128
+ }
129
+ else if (key.backspace || key.delete) {
130
+ if (currentCursorOffset > 0) {
131
+ nextValue = currentValue.slice(0, currentCursorOffset - 1) + currentValue.slice(currentCursorOffset);
132
+ nextCursorOffset = currentCursorOffset - 1;
133
+ }
134
+ }
135
+ else {
136
+ const sanitizedInput = sanitizeTerminalInputChunk(input);
137
+ if (!sanitizedInput) {
138
+ return;
139
+ }
140
+ nextValue = currentValue.slice(0, currentCursorOffset) + sanitizedInput + currentValue.slice(currentCursorOffset);
141
+ nextCursorOffset = currentCursorOffset + sanitizedInput.length;
142
+ }
143
+ valueRef.current = nextValue;
144
+ cursorOffsetRef.current = nextCursorOffset;
145
+ setRenderState({ value: nextValue, cursorOffset: nextCursorOffset });
146
+ if (nextValue !== currentValue) {
147
+ onChange(nextValue);
148
+ }
149
+ });
150
+ const displayValue = mask ? mask.repeat(renderState.value.length) : renderState.value;
151
+ if (displayValue.length === 0) {
152
+ return _jsx(Text, { dimColor: true, children: placeholder ?? '' });
153
+ }
154
+ if (renderState.cursorOffset >= displayValue.length) {
155
+ return _jsxs(Text, { children: [displayValue, _jsx(Text, { inverse: true, children: " " })] });
156
+ }
157
+ return (_jsxs(Text, { children: [displayValue.slice(0, renderState.cursorOffset), _jsx(Text, { inverse: true, children: displayValue.charAt(renderState.cursorOffset) }), displayValue.slice(renderState.cursorOffset + 1)] }));
158
+ }
159
+ function SelectList({ options, cursor, getLabel, getHint, maxVisibleRows, maxLineWidth, }) {
160
+ const visibleRows = Math.max(1, maxVisibleRows ?? options.length);
161
+ const { start, end } = getVisibleWindow(options.length, cursor, visibleRows);
162
+ const visibleOptions = options.slice(start, end);
163
+ const availableWidth = maxLineWidth ?? Number.MAX_SAFE_INTEGER;
164
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 0, marginBottom: 0, children: [start > 0 ? _jsx(Text, { dimColor: true, children: truncateTerminalLine(` ↑ ${start} more`, availableWidth) }) : null, visibleOptions.map((opt, visibleIndex) => {
165
+ const i = start + visibleIndex;
166
+ const active = i === cursor;
167
+ const hint = getHint?.(opt);
168
+ const prefix = active ? ` ${CURSOR} ` : ' ';
169
+ const suffix = hint ? ` ${DOT} ${hint}` : '';
170
+ const line = truncateTerminalLine(`${prefix}${getLabel(opt)}${suffix}`, availableWidth);
171
+ return (_jsx(Box, { children: _jsx(Text, { color: active ? 'cyan' : undefined, bold: active, children: line }) }, i));
172
+ }), end < options.length ? _jsx(Text, { dimColor: true, children: truncateTerminalLine(` ↓ ${options.length - end} more`, availableWidth) }) : null] }));
173
+ }
174
+ function MultiSelectList({ options, cursor, selected, maxVisibleRows, maxLineWidth, }) {
175
+ const visibleRows = Math.max(1, maxVisibleRows ?? options.length);
176
+ const { start, end } = getVisibleWindow(options.length, cursor, visibleRows);
177
+ const visibleOptions = options.slice(start, end);
178
+ const availableWidth = maxLineWidth ?? Number.MAX_SAFE_INTEGER;
179
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 0, marginBottom: 0, children: [start > 0 ? _jsx(Text, { dimColor: true, children: truncateTerminalLine(` ↑ ${start} more`, availableWidth) }) : null, visibleOptions.map((opt, visibleIndex) => {
180
+ const i = start + visibleIndex;
181
+ const active = i === cursor;
182
+ const checked = selected.includes(opt.value);
183
+ const prefix = active ? ` ${CURSOR} ` : ' ';
184
+ const checkbox = checked ? '☑' : '☐';
185
+ const line = truncateTerminalLine(`${prefix}${checkbox} ${opt.label} ${DOT} ${opt.hint}`, availableWidth);
186
+ return (_jsx(Box, { children: _jsx(Text, { color: active || checked ? 'cyan' : undefined, bold: active, children: line }) }, opt.value));
187
+ }), end < options.length ? _jsx(Text, { dimColor: true, children: truncateTerminalLine(` ↓ ${options.length - end} more`, availableWidth) }) : null] }));
188
+ }
189
+ // ─── Main wizard component ────────────────────────────────────────────────────
190
+ function SetupWizard({ callbacks, onDone }) {
191
+ const app = useApp();
192
+ const { stdout } = useStdout();
193
+ const n8nDef = callbacks.getN8nDefaults();
194
+ const llmDef = callbacks.getLlmDefaults();
195
+ const surfDef = callbacks.getSurfaceDefaults();
196
+ const [phase, setPhase] = useState({ kind: 'n8n-url', def: n8nDef.url });
197
+ const [textValue, setTextValue] = useState(n8nDef.url);
198
+ const [spinnerFrame, setSpinnerFrame] = useState(0);
199
+ const asyncGuard = useRef(0);
200
+ const llmApiKeyDraftsRef = useRef({});
201
+ const terminalRows = stdout?.rows ?? process.stdout.rows ?? 24;
202
+ const terminalColumns = stdout?.columns ?? process.stdout.columns ?? 80;
203
+ const listLineWidth = Math.max(12, terminalColumns - 6);
204
+ const isLoading = phase.kind === 'n8n-connecting' || phase.kind === 'n8n-saving'
205
+ || phase.kind === 'llm-models-loading' || phase.kind === 'telegram-connecting';
206
+ useEffect(() => {
207
+ if (!isLoading)
208
+ return;
209
+ const id = setInterval(() => setSpinnerFrame((f) => (f + 1) % SPINNER_FRAMES.length), 80);
210
+ return () => clearInterval(id);
211
+ }, [isLoading]);
212
+ const cancel = useCallback((reason) => {
213
+ setPhase({ kind: 'cancelled', reason });
214
+ setTimeout(() => { onDone({ ok: false }); app.exit(); }, 500);
215
+ }, [app, onDone]);
216
+ useInput((_input, key) => {
217
+ if (key.ctrl && _input === 'c') {
218
+ cancel('Setup cancelled.');
219
+ }
220
+ });
221
+ useEffect(() => {
222
+ if (phase.kind !== 'n8n-connecting')
223
+ return;
224
+ const guard = ++asyncGuard.current;
225
+ void (async () => {
226
+ try {
227
+ const projects = await callbacks.testN8nConnection(phase.url, phase.apiKey);
228
+ if (guard !== asyncGuard.current)
229
+ return;
230
+ if (projects.length === 1) {
231
+ setPhase({
232
+ kind: 'n8n-syncfolder',
233
+ url: phase.url, apiKey: phase.apiKey,
234
+ project: projects[0],
235
+ def: n8nDef.syncFolder ?? 'workflows',
236
+ });
237
+ setTextValue(n8nDef.syncFolder ?? 'workflows');
238
+ }
239
+ else {
240
+ setPhase({ kind: 'n8n-project', url: phase.url, apiKey: phase.apiKey, projects, cursor: 0 });
241
+ }
242
+ }
243
+ catch (err) {
244
+ if (guard !== asyncGuard.current)
245
+ return;
246
+ setPhase({ kind: 'n8n-url', def: phase.url, err: err.message });
247
+ setTextValue(phase.url);
248
+ }
249
+ })();
250
+ }, [phase.kind]); // eslint-disable-line react-hooks/exhaustive-deps
251
+ useEffect(() => {
252
+ if (phase.kind !== 'n8n-saving')
253
+ return;
254
+ const guard = ++asyncGuard.current;
255
+ void (async () => {
256
+ try {
257
+ await callbacks.saveN8nConfig({ url: phase.url, apiKey: phase.apiKey, project: phase.project, syncFolder: phase.syncFolder });
258
+ if (guard !== asyncGuard.current)
259
+ return;
260
+ const llmProvider = llmDef.provider;
261
+ setPhase({ kind: 'llm-provider', initial: llmProvider, cursor: llmProvider ? VALID_PROVIDERS.indexOf(llmProvider) : 0 });
262
+ }
263
+ catch (err) {
264
+ if (guard !== asyncGuard.current)
265
+ return;
266
+ setPhase({ kind: 'error', message: err.message });
267
+ setTimeout(() => { onDone({ ok: false }); app.exit(); }, 2000);
268
+ }
269
+ })();
270
+ }, [phase.kind]); // eslint-disable-line react-hooks/exhaustive-deps
271
+ useEffect(() => {
272
+ if (phase.kind !== 'llm-models-loading')
273
+ return;
274
+ const guard = ++asyncGuard.current;
275
+ void (async () => {
276
+ try {
277
+ const models = await callbacks.fetchModels(phase.provider, phase.apiKey);
278
+ if (guard !== asyncGuard.current)
279
+ return;
280
+ const displayedOptions = getDisplayedModelOptions(models);
281
+ const idx = displayedOptions.indexOf(phase.defModel);
282
+ setPhase({
283
+ kind: 'llm-model',
284
+ provider: phase.provider, apiKey: phase.apiKey,
285
+ models, defModel: phase.defModel,
286
+ cursor: idx >= 0 ? idx : 0,
287
+ });
288
+ }
289
+ catch {
290
+ if (guard !== asyncGuard.current)
291
+ return;
292
+ setPhase({
293
+ kind: 'llm-model',
294
+ provider: phase.provider, apiKey: phase.apiKey,
295
+ models: [], defModel: phase.defModel, cursor: 0,
296
+ });
297
+ }
298
+ })();
299
+ }, [phase.kind]); // eslint-disable-line react-hooks/exhaustive-deps
300
+ useEffect(() => {
301
+ if (phase.kind !== 'telegram-connecting')
302
+ return;
303
+ const guard = ++asyncGuard.current;
304
+ void (async () => {
305
+ try {
306
+ const identity = await callbacks.setupTelegram(phase.token);
307
+ if (guard !== asyncGuard.current)
308
+ return;
309
+ callbacks.saveSurfaces({
310
+ surfaces: phase.surfaces,
311
+ telegram: { token: phase.token, username: identity.username },
312
+ });
313
+ const telegramDeepLink = `https://t.me/${identity.username}`;
314
+ setPhase({ kind: 'done', n8nHost: '', n8nProject: '', provider: '', model: '', surfaces: phase.surfaces, telegramDeepLink });
315
+ setTimeout(() => { onDone({ ok: true, telegramDeepLink }); app.exit(); }, 800);
316
+ }
317
+ catch (err) {
318
+ if (guard !== asyncGuard.current)
319
+ return;
320
+ setPhase({ kind: 'telegram-token', surfaces: phase.surfaces, err: err.message });
321
+ setTextValue('');
322
+ }
323
+ })();
324
+ }, [phase.kind]); // eslint-disable-line react-hooks/exhaustive-deps
325
+ const handleN8nUrlSubmit = useCallback((value) => {
326
+ const url = value.trim().replace(/^['"]|['"]$/g, '');
327
+ if (!url) {
328
+ setPhase((p) => ({ ...p, err: 'A URL is required.' }));
329
+ return;
330
+ }
331
+ try {
332
+ new URL(url);
333
+ }
334
+ catch {
335
+ setPhase((p) => ({ ...p, err: 'Enter a valid URL.' }));
336
+ return;
337
+ }
338
+ const existing = callbacks.getN8nDefaults(url).apiKey;
339
+ if (existing) {
340
+ setPhase({ kind: 'n8n-reuse-apikey', url, existing, cursor: 0 });
341
+ }
342
+ else {
343
+ setPhase({ kind: 'n8n-apikey', url });
344
+ setTextValue('');
345
+ }
346
+ }, [callbacks]);
347
+ const handleN8nApiKeySubmit = useCallback((url) => (value) => {
348
+ const key = value.trim();
349
+ if (!key) {
350
+ setPhase((p) => ({ ...p, err: 'API key is required.' }));
351
+ return;
352
+ }
353
+ setPhase({ kind: 'n8n-connecting', url, apiKey: key });
354
+ }, []);
355
+ const handleSyncFolderSubmit = useCallback((url, apiKey, project) => (value) => {
356
+ const folder = value.trim();
357
+ if (!folder) {
358
+ setPhase((p) => ({ ...p, err: 'Sync folder is required.' }));
359
+ return;
360
+ }
361
+ setPhase({ kind: 'n8n-saving', url, apiKey, project, syncFolder: folder });
362
+ }, []);
363
+ const handleLlmApiKeySubmit = useCallback((provider) => (value) => {
364
+ const key = value.trim();
365
+ if (!key) {
366
+ setPhase((p) => ({ ...p, err: 'API key is required.' }));
367
+ return;
368
+ }
369
+ llmApiKeyDraftsRef.current[provider] = key;
370
+ const defModel = llmDef.getDefaultModel(provider);
371
+ setPhase({ kind: 'llm-models-loading', provider, apiKey: key, defModel });
372
+ }, [llmDef]);
373
+ const handleBaseUrlSubmit = useCallback((provider, apiKey, model) => (value) => {
374
+ const url = value.trim();
375
+ if (url) {
376
+ try {
377
+ new URL(url);
378
+ }
379
+ catch {
380
+ setPhase((p) => ({ ...p, err: 'Enter a valid URL.' }));
381
+ return;
382
+ }
383
+ }
384
+ callbacks.saveLlmConfig({ provider, apiKey, model, baseUrl: url || undefined });
385
+ const currentSurfaces = surfDef.surfaces;
386
+ setPhase({ kind: 'surfaces', cursor: 0, selected: currentSurfaces });
387
+ }, [callbacks, surfDef]);
388
+ const handleTelegramTokenSubmit = useCallback((surfaces) => (value) => {
389
+ const token = value.trim();
390
+ if (!token || !token.includes(':')) {
391
+ setPhase((p) => ({ ...p, err: 'Enter a valid BotFather token.' }));
392
+ return;
393
+ }
394
+ setPhase({ kind: 'telegram-connecting', surfaces, token });
395
+ }, []);
396
+ const handleSelectKey = useCallback((input, key) => {
397
+ if (phase.kind === 'n8n-reuse-apikey') {
398
+ if (key.upArrow)
399
+ setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
400
+ else if (key.downArrow)
401
+ setPhase({ ...phase, cursor: Math.min(1, phase.cursor + 1) });
402
+ else if (key.return) {
403
+ if (phase.cursor === 0) {
404
+ setPhase({ kind: 'n8n-connecting', url: phase.url, apiKey: phase.existing });
405
+ }
406
+ else {
407
+ setPhase({ kind: 'n8n-apikey', url: phase.url });
408
+ setTextValue('');
409
+ }
410
+ }
411
+ else if (key.escape)
412
+ cancel('Setup cancelled.');
413
+ }
414
+ else if (phase.kind === 'n8n-project') {
415
+ if (key.upArrow)
416
+ setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
417
+ else if (key.downArrow)
418
+ setPhase({ ...phase, cursor: Math.min(phase.projects.length - 1, phase.cursor + 1) });
419
+ else if (key.return) {
420
+ const project = phase.projects[phase.cursor];
421
+ setPhase({ kind: 'n8n-syncfolder', url: phase.url, apiKey: phase.apiKey, project, def: n8nDef.syncFolder ?? 'workflows' });
422
+ setTextValue(n8nDef.syncFolder ?? 'workflows');
423
+ }
424
+ else if (key.escape)
425
+ cancel('Setup cancelled.');
426
+ }
427
+ else if (phase.kind === 'llm-provider') {
428
+ if (key.upArrow)
429
+ setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
430
+ else if (key.downArrow)
431
+ setPhase({ ...phase, cursor: Math.min(VALID_PROVIDERS.length - 1, phase.cursor + 1) });
432
+ else if (key.return) {
433
+ const provider = VALID_PROVIDERS[phase.cursor];
434
+ const existing = llmApiKeyDraftsRef.current[provider] ?? llmDef.getApiKey(provider);
435
+ if (existing) {
436
+ setPhase({ kind: 'llm-reuse-apikey', provider, existing, cursor: 0 });
437
+ }
438
+ else {
439
+ setPhase({ kind: 'llm-apikey', provider });
440
+ setTextValue(llmApiKeyDraftsRef.current[provider] ?? '');
441
+ }
442
+ }
443
+ else if (key.escape)
444
+ cancel('Setup cancelled.');
445
+ }
446
+ else if (phase.kind === 'llm-reuse-apikey') {
447
+ if (key.upArrow)
448
+ setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
449
+ else if (key.downArrow)
450
+ setPhase({ ...phase, cursor: Math.min(1, phase.cursor + 1) });
451
+ else if (key.return) {
452
+ if (phase.cursor === 0) {
453
+ const defModel = llmDef.getDefaultModel(phase.provider);
454
+ llmApiKeyDraftsRef.current[phase.provider] = phase.existing;
455
+ setPhase({ kind: 'llm-models-loading', provider: phase.provider, apiKey: phase.existing, defModel });
456
+ }
457
+ else {
458
+ setPhase({ kind: 'llm-apikey', provider: phase.provider });
459
+ setTextValue(llmApiKeyDraftsRef.current[phase.provider] ?? '');
460
+ }
461
+ }
462
+ else if (key.escape)
463
+ cancel('Setup cancelled.');
464
+ }
465
+ else if (phase.kind === 'llm-model') {
466
+ const allOptions = getDisplayedModelOptions(phase.models);
467
+ if (key.upArrow)
468
+ setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
469
+ else if (key.downArrow)
470
+ setPhase({ ...phase, cursor: Math.min(allOptions.length - 1, phase.cursor + 1) });
471
+ else if (key.return) {
472
+ const selected = allOptions[phase.cursor];
473
+ const model = selected === '__custom__' ? phase.defModel : selected;
474
+ const needsUrl = llmDef.needsBaseUrl(phase.provider);
475
+ if (needsUrl || llmDef.getBaseUrl(phase.provider)) {
476
+ setPhase({ kind: 'llm-baseurl', provider: phase.provider, apiKey: phase.apiKey, model, def: llmDef.getBaseUrl(phase.provider) ?? '' });
477
+ setTextValue(llmDef.getBaseUrl(phase.provider) ?? '');
478
+ }
479
+ else {
480
+ callbacks.saveLlmConfig({ provider: phase.provider, apiKey: phase.apiKey, model });
481
+ setPhase({ kind: 'surfaces', cursor: 0, selected: surfDef.surfaces });
482
+ }
483
+ }
484
+ else if (key.escape)
485
+ cancel('Setup cancelled.');
486
+ }
487
+ else if (phase.kind === 'surfaces') {
488
+ const opts = SURFACE_OPTIONS.map((o) => o.value);
489
+ if (key.upArrow)
490
+ setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
491
+ else if (key.downArrow)
492
+ setPhase({ ...phase, cursor: Math.min(opts.length - 1, phase.cursor + 1) });
493
+ else if (input === ' ') {
494
+ const val = opts[phase.cursor];
495
+ const next = phase.selected.includes(val)
496
+ ? phase.selected.filter((s) => s !== val)
497
+ : [...phase.selected, val];
498
+ setPhase({ ...phase, selected: next });
499
+ }
500
+ else if (key.return) {
501
+ const surfaces = phase.selected;
502
+ if (surfaces.includes('telegram')) {
503
+ const existingToken = callbacks.getTelegramToken();
504
+ if (existingToken) {
505
+ setPhase({ kind: 'telegram-reuse-token', surfaces, existing: existingToken, cursor: 0 });
506
+ }
507
+ else {
508
+ setPhase({ kind: 'telegram-token', surfaces });
509
+ setTextValue('');
510
+ }
511
+ }
512
+ else {
513
+ callbacks.saveSurfaces({ surfaces });
514
+ setPhase({ kind: 'done', n8nHost: '', n8nProject: '', provider: '', model: '', surfaces });
515
+ setTimeout(() => { onDone({ ok: true }); app.exit(); }, 500);
516
+ }
517
+ }
518
+ else if (key.escape)
519
+ cancel('Setup cancelled.');
520
+ }
521
+ else if (phase.kind === 'telegram-reuse-token') {
522
+ if (key.upArrow)
523
+ setPhase({ ...phase, cursor: Math.max(0, phase.cursor - 1) });
524
+ else if (key.downArrow)
525
+ setPhase({ ...phase, cursor: Math.min(1, phase.cursor + 1) });
526
+ else if (key.return) {
527
+ if (phase.cursor === 0) {
528
+ callbacks.saveSurfaces({ surfaces: phase.surfaces });
529
+ setPhase({ kind: 'done', n8nHost: '', n8nProject: '', provider: '', model: '', surfaces: phase.surfaces });
530
+ setTimeout(() => { onDone({ ok: true }); app.exit(); }, 500);
531
+ }
532
+ else {
533
+ setPhase({ kind: 'telegram-token', surfaces: phase.surfaces });
534
+ setTextValue('');
535
+ }
536
+ }
537
+ else if (key.escape)
538
+ cancel('Setup cancelled.');
539
+ }
540
+ }, [phase, cancel, callbacks, llmDef, surfDef, n8nDef.syncFolder, app, onDone]);
541
+ const isSelectPhase = ['n8n-reuse-apikey', 'n8n-project', 'llm-provider', 'llm-reuse-apikey', 'surfaces', 'telegram-reuse-token'].includes(phase.kind)
542
+ || (phase.kind === 'llm-model' && phase.models.length > 0);
543
+ useInput((input, key) => {
544
+ if (key.ctrl && input === 'c')
545
+ return;
546
+ if (isSelectPhase)
547
+ handleSelectKey(input, key);
548
+ }, { isActive: isSelectPhase });
549
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsx(Header, { phase: phase }), renderPhase()] }));
550
+ function renderPhase() {
551
+ switch (phase.kind) {
552
+ case 'n8n-url':
553
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "Instance URL" }), _jsx(Box, { marginLeft: 2, children: _jsx(WizardTextInput, { value: textValue, onChange: setTextValue, onSubmit: handleN8nUrlSubmit, placeholder: "https://your-n8n.example.com" }) }), phase.err ? _jsx(ErrorLine, { message: phase.err }) : null, _jsx(HintBar, { hints: ['Enter ↵ confirm', 'Ctrl+C cancel'] })] }));
554
+ case 'n8n-reuse-apikey':
555
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "n8n API key" }), _jsxs(Text, { dimColor: true, children: [" A saved key exists for ", phase.url] }), _jsx(SelectList, { options: ['Keep the saved key', 'Enter a new key'], cursor: phase.cursor, getLabel: (v) => v, maxVisibleRows: getListViewportHeight(terminalRows, 11), maxLineWidth: listLineWidth }), _jsx(HintBar, { hints: ['↑↓ move', 'Enter ↵ confirm', 'Ctrl+C cancel'] })] }));
556
+ case 'n8n-apikey':
557
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "n8n API key" }), _jsx(Box, { marginLeft: 2, children: _jsx(WizardTextInput, { value: textValue, onChange: setTextValue, onSubmit: handleN8nApiKeySubmit(phase.url), mask: "\u25CF", placeholder: "your-api-key" }) }), phase.err ? _jsx(ErrorLine, { message: phase.err }) : null, _jsx(HintBar, { hints: ['Enter ↵ confirm', 'Ctrl+C cancel'] })] }));
558
+ case 'n8n-connecting':
559
+ return (_jsx(Box, { flexDirection: "column", children: _jsx(SpinnerDisplay, { message: `Connecting to ${phase.url}…`, frame: spinnerFrame }) }));
560
+ case 'n8n-project':
561
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "n8n project" }), _jsxs(Text, { dimColor: true, children: [" ", phase.projects.length, " project(s) found"] }), _jsx(SelectList, { options: phase.projects, cursor: phase.cursor, getLabel: (p) => p.name ?? p.id, getHint: (p) => p.id, maxVisibleRows: getListViewportHeight(terminalRows, 11), maxLineWidth: listLineWidth }), _jsx(HintBar, { hints: ['↑↓ move', 'Enter ↵ select', 'Ctrl+C cancel'] })] }));
562
+ case 'n8n-syncfolder':
563
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "Local workflow sync folder" }), _jsx(Box, { marginLeft: 2, children: _jsx(WizardTextInput, { value: textValue, onChange: setTextValue, onSubmit: handleSyncFolderSubmit(phase.url, phase.apiKey, phase.project), placeholder: "workflows" }) }), phase.err ? _jsx(ErrorLine, { message: phase.err }) : null, _jsx(HintBar, { hints: ['Enter ↵ confirm', 'Ctrl+C cancel'] })] }));
564
+ case 'n8n-saving':
565
+ return (_jsx(Box, { flexDirection: "column", children: _jsx(SpinnerDisplay, { message: "Saving n8n configuration and refreshing workspace\u2026", frame: spinnerFrame }) }));
566
+ case 'llm-provider':
567
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "Default LLM provider" }), _jsx(SelectList, { options: VALID_PROVIDERS, cursor: phase.cursor, getLabel: (v) => v, getHint: (v) => v === phase.initial ? 'currently configured' : undefined, maxVisibleRows: getListViewportHeight(terminalRows, 10), maxLineWidth: listLineWidth }), _jsx(HintBar, { hints: ['↑↓ move', 'Enter ↵ select', 'Ctrl+C cancel'] })] }));
568
+ case 'llm-reuse-apikey':
569
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: `${phase.provider} API key` }), _jsxs(Text, { dimColor: true, children: [" A saved key exists for ", phase.provider] }), _jsx(SelectList, { options: ['Keep the saved key', 'Enter a new key'], cursor: phase.cursor, getLabel: (v) => v, maxVisibleRows: getListViewportHeight(terminalRows, 11), maxLineWidth: listLineWidth }), _jsx(HintBar, { hints: ['↑↓ move', 'Enter ↵ confirm', 'Ctrl+C cancel'] })] }));
570
+ case 'llm-apikey':
571
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: `${phase.provider} API key` }), _jsx(Box, { marginLeft: 2, children: _jsx(WizardTextInput, { value: textValue, onChange: (nextValue) => {
572
+ setTextValue(nextValue);
573
+ llmApiKeyDraftsRef.current[phase.provider] = nextValue;
574
+ }, onSubmit: handleLlmApiKeySubmit(phase.provider), mask: "\u25CF", placeholder: "your-api-key" }) }), phase.err ? _jsx(ErrorLine, { message: phase.err }) : null, _jsx(HintBar, { hints: ['Enter ↵ confirm', 'Ctrl+C cancel'] })] }));
575
+ case 'llm-models-loading':
576
+ return (_jsx(Box, { flexDirection: "column", children: _jsx(SpinnerDisplay, { message: `Fetching available models for ${phase.provider}…`, frame: spinnerFrame }) }));
577
+ case 'llm-model': {
578
+ const modelOptions = getDisplayedModelOptions(phase.models);
579
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: `Default model · ${phase.provider}` }), phase.models.length === 0 ? (_jsx(Box, { marginLeft: 2, children: _jsx(WizardTextInput, { value: textValue, onChange: setTextValue, onSubmit: (v) => {
580
+ const m = v.trim() || phase.defModel;
581
+ const needsUrl = llmDef.needsBaseUrl(phase.provider);
582
+ if (needsUrl || llmDef.getBaseUrl(phase.provider)) {
583
+ setPhase({ kind: 'llm-baseurl', provider: phase.provider, apiKey: phase.apiKey, model: m, def: llmDef.getBaseUrl(phase.provider) ?? '' });
584
+ setTextValue(llmDef.getBaseUrl(phase.provider) ?? '');
585
+ }
586
+ else {
587
+ callbacks.saveLlmConfig({ provider: phase.provider, apiKey: phase.apiKey, model: m });
588
+ setPhase({ kind: 'surfaces', cursor: 0, selected: surfDef.surfaces });
589
+ }
590
+ }, placeholder: phase.defModel }) })) : (_jsx(SelectList, { options: modelOptions, cursor: phase.cursor, getLabel: (v) => v === '__custom__' ? '⌨ enter manually…' : v, getHint: (v) => v === phase.defModel ? 'recommended' : undefined, maxVisibleRows: getListViewportHeight(terminalRows, 10), maxLineWidth: listLineWidth })), _jsx(HintBar, { hints: phase.models.length > 0 ? ['↑↓ move', 'Enter ↵ select', 'Ctrl+C cancel'] : ['Enter ↵ confirm', 'Ctrl+C cancel'] })] }));
591
+ }
592
+ case 'llm-baseurl':
593
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: `${phase.provider} base URL` }), _jsx(Text, { dimColor: true, children: " Leave empty to use the default endpoint" }), _jsx(Box, { marginLeft: 2, children: _jsx(WizardTextInput, { value: textValue, onChange: setTextValue, onSubmit: handleBaseUrlSubmit(phase.provider, phase.apiKey, phase.model), placeholder: phase.def || 'https://api.example.com/v1' }) }), phase.err ? _jsx(ErrorLine, { message: phase.err }) : null, _jsx(HintBar, { hints: ['Enter ↵ confirm (empty = default)', 'Ctrl+C cancel'] })] }));
594
+ case 'surfaces':
595
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "Optional messaging gateways" }), _jsx(Text, { dimColor: true, children: " These enable Yagr to receive work from external channels" }), _jsx(Box, { marginTop: 1, children: _jsx(MultiSelectList, { options: SURFACE_OPTIONS, cursor: phase.cursor, selected: phase.selected, maxVisibleRows: getListViewportHeight(terminalRows, 11), maxLineWidth: listLineWidth }) }), _jsx(HintBar, { hints: ['↑↓ move', 'Space toggle', 'Enter ↵ confirm', 'Ctrl+C cancel'] })] }));
596
+ case 'telegram-reuse-token':
597
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "Telegram bot token" }), _jsx(Text, { dimColor: true, children: " A saved token is already configured" }), _jsx(SelectList, { options: ['Keep the saved token', 'Enter a new token'], cursor: phase.cursor, getLabel: (v) => v, maxVisibleRows: getListViewportHeight(terminalRows, 11), maxLineWidth: listLineWidth }), _jsx(HintBar, { hints: ['↑↓ move', 'Enter ↵ confirm', 'Ctrl+C cancel'] })] }));
598
+ case 'telegram-token':
599
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(FieldLabel, { label: "Telegram bot token" }), _jsx(Text, { dimColor: true, children: " Open Telegram, search for @BotFather, then start a chat." }), _jsx(Text, { dimColor: true, children: " Run /newbot, choose a bot name, then choose a unique username ending in \"bot\"." }), _jsx(Text, { dimColor: true, children: " BotFather will reply with an HTTP API token like \"123456789:ABCdef...\". Paste that token here." }), _jsx(Box, { marginLeft: 2, children: _jsx(WizardTextInput, { value: textValue, onChange: setTextValue, onSubmit: handleTelegramTokenSubmit(phase.surfaces), mask: "\u25CF", placeholder: "123456789:ABCdef..." }) }), phase.err ? _jsx(ErrorLine, { message: phase.err }) : null, _jsx(HintBar, { hints: ['Enter ↵ confirm', 'Ctrl+C cancel'] })] }));
600
+ case 'telegram-connecting':
601
+ return (_jsx(Box, { flexDirection: "column", children: _jsx(SpinnerDisplay, { message: "Verifying Telegram bot token\u2026", frame: spinnerFrame }) }));
602
+ case 'done':
603
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "green", bold: true, children: [CHECK, " Setup complete"] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [phase.n8nHost ? _jsxs(Text, { dimColor: true, children: [" n8n ", DOT, " ", phase.n8nHost] }) : null, phase.provider ? _jsxs(Text, { dimColor: true, children: [" LLM ", DOT, " ", phase.provider, phase.model ? ` / ${phase.model}` : ''] }) : null, phase.surfaces.length > 0
604
+ ? _jsxs(Text, { dimColor: true, children: [" Gates ", DOT, " ", phase.surfaces.join(', ')] })
605
+ : _jsxs(Text, { dimColor: true, children: [" Gates ", DOT, " none"] })] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "Next: " }), _jsx(Text, { color: "cyan", children: "yagr start" })] })] }));
606
+ case 'cancelled':
607
+ return (_jsx(Box, { children: _jsx(Text, { color: "yellow", children: " Setup cancelled." }) }));
608
+ case 'error':
609
+ return (_jsx(Box, { children: _jsxs(Text, { color: "red", children: [" \u2715 ", phase.message] }) }));
610
+ }
611
+ }
612
+ }
613
+ //# sourceMappingURL=setup-wizard.js.map