@agentuity/cli 0.0.51 → 0.0.53

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 (299) hide show
  1. package/dist/api.js +68 -0
  2. package/dist/api.js.map +1 -0
  3. package/dist/auth.js +225 -0
  4. package/dist/auth.js.map +1 -0
  5. package/dist/banner.js +35 -0
  6. package/dist/banner.js.map +1 -0
  7. package/dist/cli-logger.js +72 -0
  8. package/dist/cli-logger.js.map +1 -0
  9. package/dist/cli.js +822 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/cmd/ai/capabilities/index.js +10 -0
  12. package/dist/cmd/ai/capabilities/index.js.map +1 -0
  13. package/dist/cmd/ai/capabilities/show.js +221 -0
  14. package/dist/cmd/ai/capabilities/show.js.map +1 -0
  15. package/dist/cmd/ai/index.js +11 -0
  16. package/dist/cmd/ai/index.js.map +1 -0
  17. package/dist/cmd/ai/prompt/index.js +10 -0
  18. package/dist/cmd/ai/prompt/index.js.map +1 -0
  19. package/dist/cmd/ai/prompt/llm.js +365 -0
  20. package/dist/cmd/ai/prompt/llm.js.map +1 -0
  21. package/dist/cmd/ai/schema/index.js +10 -0
  22. package/dist/cmd/ai/schema/index.js.map +1 -0
  23. package/dist/cmd/ai/schema/show.js +23 -0
  24. package/dist/cmd/ai/schema/show.js.map +1 -0
  25. package/dist/cmd/auth/api.js +85 -0
  26. package/dist/cmd/auth/api.js.map +1 -0
  27. package/dist/cmd/auth/index.js +13 -0
  28. package/dist/cmd/auth/index.js.map +1 -0
  29. package/dist/cmd/auth/login.js +84 -0
  30. package/dist/cmd/auth/login.js.map +1 -0
  31. package/dist/cmd/auth/logout.js +17 -0
  32. package/dist/cmd/auth/logout.js.map +1 -0
  33. package/dist/cmd/auth/signup.js +55 -0
  34. package/dist/cmd/auth/signup.js.map +1 -0
  35. package/dist/cmd/auth/ssh/add.js +239 -0
  36. package/dist/cmd/auth/ssh/add.js.map +1 -0
  37. package/dist/cmd/auth/ssh/api.js +53 -0
  38. package/dist/cmd/auth/ssh/api.js.map +1 -0
  39. package/dist/cmd/auth/ssh/delete.js +126 -0
  40. package/dist/cmd/auth/ssh/delete.js.map +1 -0
  41. package/dist/cmd/auth/ssh/index.js +11 -0
  42. package/dist/cmd/auth/ssh/index.js.map +1 -0
  43. package/dist/cmd/auth/ssh/list.js +70 -0
  44. package/dist/cmd/auth/ssh/list.js.map +1 -0
  45. package/dist/cmd/auth/whoami.js +68 -0
  46. package/dist/cmd/auth/whoami.js.map +1 -0
  47. package/dist/cmd/build/ast.js +608 -0
  48. package/dist/cmd/build/ast.js.map +1 -0
  49. package/dist/cmd/build/ast.test.js +389 -0
  50. package/dist/cmd/build/ast.test.js.map +1 -0
  51. package/dist/cmd/build/bundler.js +304 -0
  52. package/dist/cmd/build/bundler.js.map +1 -0
  53. package/dist/cmd/build/file.js +10 -0
  54. package/dist/cmd/build/file.js.map +1 -0
  55. package/dist/cmd/build/fix-duplicate-exports.js +167 -0
  56. package/dist/cmd/build/fix-duplicate-exports.js.map +1 -0
  57. package/dist/cmd/build/fix-duplicate-exports.test.js +300 -0
  58. package/dist/cmd/build/fix-duplicate-exports.test.js.map +1 -0
  59. package/dist/cmd/build/index.d.ts.map +1 -1
  60. package/dist/cmd/build/index.js +81 -0
  61. package/dist/cmd/build/index.js.map +1 -0
  62. package/dist/cmd/build/patch/_util.js +42 -0
  63. package/dist/cmd/build/patch/_util.js.map +1 -0
  64. package/dist/cmd/build/patch/aisdk.js +65 -0
  65. package/dist/cmd/build/patch/aisdk.js.map +1 -0
  66. package/dist/cmd/build/patch/index.js +97 -0
  67. package/dist/cmd/build/patch/index.js.map +1 -0
  68. package/dist/cmd/build/patch/llm.js +18 -0
  69. package/dist/cmd/build/patch/llm.js.map +1 -0
  70. package/dist/cmd/build/plugin.d.ts.map +1 -1
  71. package/dist/cmd/build/plugin.js +581 -0
  72. package/dist/cmd/build/plugin.js.map +1 -0
  73. package/dist/cmd/cloud/agents/index.js +133 -0
  74. package/dist/cmd/cloud/agents/index.js.map +1 -0
  75. package/dist/cmd/cloud/deploy.js +341 -0
  76. package/dist/cmd/cloud/deploy.js.map +1 -0
  77. package/dist/cmd/cloud/deployment/index.js +20 -0
  78. package/dist/cmd/cloud/deployment/index.js.map +1 -0
  79. package/dist/cmd/cloud/deployment/list.js +89 -0
  80. package/dist/cmd/cloud/deployment/list.js.map +1 -0
  81. package/dist/cmd/cloud/deployment/remove.js +60 -0
  82. package/dist/cmd/cloud/deployment/remove.js.map +1 -0
  83. package/dist/cmd/cloud/deployment/rollback.js +80 -0
  84. package/dist/cmd/cloud/deployment/rollback.js.map +1 -0
  85. package/dist/cmd/cloud/deployment/show.js +106 -0
  86. package/dist/cmd/cloud/deployment/show.js.map +1 -0
  87. package/dist/cmd/cloud/deployment/undeploy.js +45 -0
  88. package/dist/cmd/cloud/deployment/undeploy.js.map +1 -0
  89. package/dist/cmd/cloud/deployment/utils.js +10 -0
  90. package/dist/cmd/cloud/deployment/utils.js.map +1 -0
  91. package/dist/cmd/cloud/domain.js +77 -0
  92. package/dist/cmd/cloud/domain.js.map +1 -0
  93. package/dist/cmd/cloud/env/delete.js +50 -0
  94. package/dist/cmd/cloud/env/delete.js.map +1 -0
  95. package/dist/cmd/cloud/env/get.js +65 -0
  96. package/dist/cmd/cloud/env/get.js.map +1 -0
  97. package/dist/cmd/cloud/env/import.js +113 -0
  98. package/dist/cmd/cloud/env/import.js.map +1 -0
  99. package/dist/cmd/cloud/env/index.js +24 -0
  100. package/dist/cmd/cloud/env/index.js.map +1 -0
  101. package/dist/cmd/cloud/env/list.js +58 -0
  102. package/dist/cmd/cloud/env/list.js.map +1 -0
  103. package/dist/cmd/cloud/env/pull.js +81 -0
  104. package/dist/cmd/cloud/env/pull.js.map +1 -0
  105. package/dist/cmd/cloud/env/push.js +61 -0
  106. package/dist/cmd/cloud/env/push.js.map +1 -0
  107. package/dist/cmd/cloud/env/set.js +73 -0
  108. package/dist/cmd/cloud/env/set.js.map +1 -0
  109. package/dist/cmd/cloud/index.js +31 -0
  110. package/dist/cmd/cloud/index.js.map +1 -0
  111. package/dist/cmd/cloud/keyvalue/create-namespace.js +41 -0
  112. package/dist/cmd/cloud/keyvalue/create-namespace.js.map +1 -0
  113. package/dist/cmd/cloud/keyvalue/delete-namespace.js +64 -0
  114. package/dist/cmd/cloud/keyvalue/delete-namespace.js.map +1 -0
  115. package/dist/cmd/cloud/keyvalue/delete.js +47 -0
  116. package/dist/cmd/cloud/keyvalue/delete.js.map +1 -0
  117. package/dist/cmd/cloud/keyvalue/get.js +65 -0
  118. package/dist/cmd/cloud/keyvalue/get.js.map +1 -0
  119. package/dist/cmd/cloud/keyvalue/index.js +32 -0
  120. package/dist/cmd/cloud/keyvalue/index.js.map +1 -0
  121. package/dist/cmd/cloud/keyvalue/keys.js +50 -0
  122. package/dist/cmd/cloud/keyvalue/keys.js.map +1 -0
  123. package/dist/cmd/cloud/keyvalue/list-namespaces.js +37 -0
  124. package/dist/cmd/cloud/keyvalue/list-namespaces.js.map +1 -0
  125. package/dist/cmd/cloud/keyvalue/repl.js +277 -0
  126. package/dist/cmd/cloud/keyvalue/repl.js.map +1 -0
  127. package/dist/cmd/cloud/keyvalue/search.js +72 -0
  128. package/dist/cmd/cloud/keyvalue/search.js.map +1 -0
  129. package/dist/cmd/cloud/keyvalue/set.js +59 -0
  130. package/dist/cmd/cloud/keyvalue/set.js.map +1 -0
  131. package/dist/cmd/cloud/keyvalue/stats.js +82 -0
  132. package/dist/cmd/cloud/keyvalue/stats.js.map +1 -0
  133. package/dist/cmd/cloud/keyvalue/util.js +19 -0
  134. package/dist/cmd/cloud/keyvalue/util.js.map +1 -0
  135. package/dist/cmd/cloud/objectstore/delete-bucket.js +66 -0
  136. package/dist/cmd/cloud/objectstore/delete-bucket.js.map +1 -0
  137. package/dist/cmd/cloud/objectstore/delete.js +56 -0
  138. package/dist/cmd/cloud/objectstore/delete.js.map +1 -0
  139. package/dist/cmd/cloud/objectstore/get.js +64 -0
  140. package/dist/cmd/cloud/objectstore/get.js.map +1 -0
  141. package/dist/cmd/cloud/objectstore/index.js +28 -0
  142. package/dist/cmd/cloud/objectstore/index.js.map +1 -0
  143. package/dist/cmd/cloud/objectstore/list-buckets.js +37 -0
  144. package/dist/cmd/cloud/objectstore/list-buckets.js.map +1 -0
  145. package/dist/cmd/cloud/objectstore/list-keys.js +52 -0
  146. package/dist/cmd/cloud/objectstore/list-keys.js.map +1 -0
  147. package/dist/cmd/cloud/objectstore/put.js +57 -0
  148. package/dist/cmd/cloud/objectstore/put.js.map +1 -0
  149. package/dist/cmd/cloud/objectstore/repl.js +219 -0
  150. package/dist/cmd/cloud/objectstore/repl.js.map +1 -0
  151. package/dist/cmd/cloud/objectstore/url.js +55 -0
  152. package/dist/cmd/cloud/objectstore/url.js.map +1 -0
  153. package/dist/cmd/cloud/objectstore/util.js +18 -0
  154. package/dist/cmd/cloud/objectstore/util.js.map +1 -0
  155. package/dist/cmd/cloud/resource/add.js +70 -0
  156. package/dist/cmd/cloud/resource/add.js.map +1 -0
  157. package/dist/cmd/cloud/resource/delete.js +126 -0
  158. package/dist/cmd/cloud/resource/delete.js.map +1 -0
  159. package/dist/cmd/cloud/resource/index.js +12 -0
  160. package/dist/cmd/cloud/resource/index.js.map +1 -0
  161. package/dist/cmd/cloud/resource/list.js +89 -0
  162. package/dist/cmd/cloud/resource/list.js.map +1 -0
  163. package/dist/cmd/cloud/scp/download.js +72 -0
  164. package/dist/cmd/cloud/scp/download.js.map +1 -0
  165. package/dist/cmd/cloud/scp/index.js +10 -0
  166. package/dist/cmd/cloud/scp/index.js.map +1 -0
  167. package/dist/cmd/cloud/scp/upload.js +75 -0
  168. package/dist/cmd/cloud/scp/upload.js.map +1 -0
  169. package/dist/cmd/cloud/secret/delete.js +50 -0
  170. package/dist/cmd/cloud/secret/delete.js.map +1 -0
  171. package/dist/cmd/cloud/secret/get.js +69 -0
  172. package/dist/cmd/cloud/secret/get.js.map +1 -0
  173. package/dist/cmd/cloud/secret/import.js +88 -0
  174. package/dist/cmd/cloud/secret/import.js.map +1 -0
  175. package/dist/cmd/cloud/secret/index.js +24 -0
  176. package/dist/cmd/cloud/secret/index.js.map +1 -0
  177. package/dist/cmd/cloud/secret/list.js +58 -0
  178. package/dist/cmd/cloud/secret/list.js.map +1 -0
  179. package/dist/cmd/cloud/secret/pull.js +81 -0
  180. package/dist/cmd/cloud/secret/pull.js.map +1 -0
  181. package/dist/cmd/cloud/secret/push.js +61 -0
  182. package/dist/cmd/cloud/secret/push.js.map +1 -0
  183. package/dist/cmd/cloud/secret/set.js +57 -0
  184. package/dist/cmd/cloud/secret/set.js.map +1 -0
  185. package/dist/cmd/cloud/session/get.d.ts.map +1 -1
  186. package/dist/cmd/cloud/session/get.js +155 -0
  187. package/dist/cmd/cloud/session/get.js.map +1 -0
  188. package/dist/cmd/cloud/session/index.js +11 -0
  189. package/dist/cmd/cloud/session/index.js.map +1 -0
  190. package/dist/cmd/cloud/session/list.js +132 -0
  191. package/dist/cmd/cloud/session/list.js.map +1 -0
  192. package/dist/cmd/cloud/session/logs.js +56 -0
  193. package/dist/cmd/cloud/session/logs.js.map +1 -0
  194. package/dist/cmd/cloud/ssh.js +67 -0
  195. package/dist/cmd/cloud/ssh.js.map +1 -0
  196. package/dist/cmd/dev/agents.js +103 -0
  197. package/dist/cmd/dev/agents.js.map +1 -0
  198. package/dist/cmd/dev/api.js +26 -0
  199. package/dist/cmd/dev/api.js.map +1 -0
  200. package/dist/cmd/dev/download.js +77 -0
  201. package/dist/cmd/dev/download.js.map +1 -0
  202. package/dist/cmd/dev/index.js +745 -0
  203. package/dist/cmd/dev/index.js.map +1 -0
  204. package/dist/cmd/dev/sync.js +229 -0
  205. package/dist/cmd/dev/sync.js.map +1 -0
  206. package/dist/cmd/dev/templates.js +75 -0
  207. package/dist/cmd/dev/templates.js.map +1 -0
  208. package/dist/cmd/index.js +49 -0
  209. package/dist/cmd/index.js.map +1 -0
  210. package/dist/cmd/profile/create.js +89 -0
  211. package/dist/cmd/profile/create.js.map +1 -0
  212. package/dist/cmd/profile/delete.js +63 -0
  213. package/dist/cmd/profile/delete.js.map +1 -0
  214. package/dist/cmd/profile/index.js +14 -0
  215. package/dist/cmd/profile/index.js.map +1 -0
  216. package/dist/cmd/profile/list.js +28 -0
  217. package/dist/cmd/profile/list.js.map +1 -0
  218. package/dist/cmd/profile/show.js +68 -0
  219. package/dist/cmd/profile/show.js.map +1 -0
  220. package/dist/cmd/profile/use.js +37 -0
  221. package/dist/cmd/profile/use.js.map +1 -0
  222. package/dist/cmd/project/create.js +92 -0
  223. package/dist/cmd/project/create.js.map +1 -0
  224. package/dist/cmd/project/delete.js +117 -0
  225. package/dist/cmd/project/delete.js.map +1 -0
  226. package/dist/cmd/project/download.js +217 -0
  227. package/dist/cmd/project/download.js.map +1 -0
  228. package/dist/cmd/project/index.js +12 -0
  229. package/dist/cmd/project/index.js.map +1 -0
  230. package/dist/cmd/project/list.js +51 -0
  231. package/dist/cmd/project/list.js.map +1 -0
  232. package/dist/cmd/project/show.js +54 -0
  233. package/dist/cmd/project/show.js.map +1 -0
  234. package/dist/cmd/project/template-flow.js +315 -0
  235. package/dist/cmd/project/template-flow.js.map +1 -0
  236. package/dist/cmd/project/templates.js +31 -0
  237. package/dist/cmd/project/templates.js.map +1 -0
  238. package/dist/cmd/repl/index.js +444 -0
  239. package/dist/cmd/repl/index.js.map +1 -0
  240. package/dist/cmd/version/index.js +29 -0
  241. package/dist/cmd/version/index.js.map +1 -0
  242. package/dist/command-prefix.js +37 -0
  243. package/dist/command-prefix.js.map +1 -0
  244. package/dist/config.js +536 -0
  245. package/dist/config.js.map +1 -0
  246. package/dist/crypto/box.js +382 -0
  247. package/dist/crypto/box.js.map +1 -0
  248. package/dist/crypto/box.test.js +317 -0
  249. package/dist/crypto/box.test.js.map +1 -0
  250. package/dist/download.js +64 -0
  251. package/dist/download.js.map +1 -0
  252. package/dist/env-util.js +219 -0
  253. package/dist/env-util.js.map +1 -0
  254. package/dist/env-util.test.js +146 -0
  255. package/dist/env-util.test.js.map +1 -0
  256. package/dist/errors.js +177 -0
  257. package/dist/errors.js.map +1 -0
  258. package/dist/explain.js +90 -0
  259. package/dist/explain.js.map +1 -0
  260. package/dist/index.js +23 -0
  261. package/dist/index.js.map +1 -0
  262. package/dist/json.js +29 -0
  263. package/dist/json.js.map +1 -0
  264. package/dist/legacy-check.js +104 -0
  265. package/dist/legacy-check.js.map +1 -0
  266. package/dist/output.js +207 -0
  267. package/dist/output.js.map +1 -0
  268. package/dist/repl.js +1176 -0
  269. package/dist/repl.js.map +1 -0
  270. package/dist/runtime.js +19 -0
  271. package/dist/runtime.js.map +1 -0
  272. package/dist/schema-generator.js +289 -0
  273. package/dist/schema-generator.js.map +1 -0
  274. package/dist/schema-parser.js +145 -0
  275. package/dist/schema-parser.js.map +1 -0
  276. package/dist/sound.js +44 -0
  277. package/dist/sound.js.map +1 -0
  278. package/dist/steps.js +293 -0
  279. package/dist/steps.js.map +1 -0
  280. package/dist/terminal.js +130 -0
  281. package/dist/terminal.js.map +1 -0
  282. package/dist/tui.js +1124 -0
  283. package/dist/tui.js.map +1 -0
  284. package/dist/types.js +163 -0
  285. package/dist/types.js.map +1 -0
  286. package/dist/utils/detectSubagent.js +25 -0
  287. package/dist/utils/detectSubagent.js.map +1 -0
  288. package/dist/utils/format.js +21 -0
  289. package/dist/utils/format.js.map +1 -0
  290. package/dist/utils/zip.js +33 -0
  291. package/dist/utils/zip.js.map +1 -0
  292. package/dist/version.js +24 -0
  293. package/dist/version.js.map +1 -0
  294. package/package.json +6 -6
  295. package/src/banner.ts +1 -1
  296. package/src/cmd/build/index.ts +18 -22
  297. package/src/cmd/build/plugin.ts +95 -64
  298. package/src/cmd/cloud/session/get.ts +20 -14
  299. package/src/cmd/cloud/session/list.ts +1 -1
package/dist/tui.js ADDED
@@ -0,0 +1,1124 @@
1
+ /**
2
+ * Terminal UI utilities for formatted, colorized output
3
+ *
4
+ * Provides semantic helpers for console output with automatic icons and colors.
5
+ * Uses Bun's built-in color support and ANSI escape codes.
6
+ */
7
+ import { stringWidth } from 'bun';
8
+ import { colorize } from 'json-colorizer';
9
+ import enquirer from 'enquirer';
10
+ import { projectList } from '@agentuity/server';
11
+ import * as readline from 'readline';
12
+ import { getExitCode } from './errors';
13
+ // Icons
14
+ const ICONS = {
15
+ success: '✓',
16
+ error: '✗',
17
+ warning: '⚠',
18
+ info: 'ℹ',
19
+ arrow: '→',
20
+ bullet: '•',
21
+ };
22
+ export function shouldUseColors() {
23
+ return (!process.env.NO_COLOR &&
24
+ !process.env.CI &&
25
+ process.env.TERM !== 'dumb' &&
26
+ !!process.stdout.isTTY);
27
+ }
28
+ // Color definitions (light/dark adaptive) using Bun.color
29
+ function getColors() {
30
+ const USE_COLORS = shouldUseColors();
31
+ if (!USE_COLORS) {
32
+ return {
33
+ success: { light: '', dark: '' },
34
+ error: { light: '', dark: '' },
35
+ warning: { light: '', dark: '' },
36
+ info: { light: '', dark: '' },
37
+ muted: { light: '', dark: '' },
38
+ bold: { light: '', dark: '' },
39
+ link: { light: '', dark: '' },
40
+ reset: '',
41
+ };
42
+ }
43
+ return {
44
+ success: {
45
+ light: Bun.color('#008000', 'ansi') || '\x1b[32m', // green
46
+ dark: Bun.color('#00FF00', 'ansi') || '\x1b[92m', // bright green
47
+ },
48
+ error: {
49
+ light: Bun.color('#CC0000', 'ansi') || '\x1b[31m', // red
50
+ dark: Bun.color('#FF5555', 'ansi') || '\x1b[91m', // bright red
51
+ },
52
+ warning: {
53
+ light: Bun.color('#B58900', 'ansi') || '\x1b[33m', // yellow
54
+ dark: Bun.color('#FFFF55', 'ansi') || '\x1b[93m', // bright yellow
55
+ },
56
+ info: {
57
+ light: Bun.color('#008B8B', 'ansi') || '\x1b[36m', // dark cyan
58
+ dark: Bun.color('#55FFFF', 'ansi') || '\x1b[96m', // bright cyan
59
+ },
60
+ muted: {
61
+ light: Bun.color('#808080', 'ansi') || '\x1b[90m', // gray
62
+ dark: Bun.color('#888888', 'ansi') || '\x1b[90m', // darker gray
63
+ },
64
+ bold: {
65
+ light: '\x1b[1m',
66
+ dark: '\x1b[1m',
67
+ },
68
+ link: {
69
+ light: '\x1b[34;4m', // blue underline (need ANSI for underline)
70
+ dark: '\x1b[94;4m', // bright blue underline
71
+ },
72
+ reset: '\x1b[0m',
73
+ };
74
+ }
75
+ let currentColorScheme = process.env.CI ? 'light' : 'dark';
76
+ export function setColorScheme(scheme) {
77
+ currentColorScheme = scheme;
78
+ process.env.COLOR_SCHEME = scheme;
79
+ }
80
+ export function isDarkMode() {
81
+ return currentColorScheme === 'dark';
82
+ }
83
+ function getColor(colorKey) {
84
+ const COLORS = getColors();
85
+ const color = COLORS[colorKey];
86
+ if (typeof color === 'string') {
87
+ return color;
88
+ }
89
+ return color[currentColorScheme];
90
+ }
91
+ /**
92
+ * Color helpers that return colored strings (for inline use, no icons)
93
+ */
94
+ export function colorSuccess(text) {
95
+ const color = getColor('success');
96
+ const reset = getColor('reset');
97
+ return `${color}${text}${reset}`;
98
+ }
99
+ export function colorError(text) {
100
+ const color = getColor('error');
101
+ const reset = getColor('reset');
102
+ return `${color}${text}${reset}`;
103
+ }
104
+ export function colorWarning(text) {
105
+ const color = getColor('warning');
106
+ const reset = getColor('reset');
107
+ return `${color}${text}${reset}`;
108
+ }
109
+ export function colorInfo(text) {
110
+ const color = getColor('info');
111
+ const reset = getColor('reset');
112
+ return `${color}${text}${reset}`;
113
+ }
114
+ export function colorMuted(text) {
115
+ const color = getColor('muted');
116
+ const reset = getColor('reset');
117
+ return `${color}${text}${reset}`;
118
+ }
119
+ /**
120
+ * Print a success message with a green checkmark
121
+ */
122
+ export function success(message) {
123
+ const color = getColor('success');
124
+ const reset = getColor('reset');
125
+ process.stderr.write(`${color}${ICONS.success} ${message}${reset}\n`);
126
+ }
127
+ /**
128
+ * Print an error message with a red X
129
+ */
130
+ export function error(message) {
131
+ const color = getColor('error');
132
+ const reset = getColor('reset');
133
+ process.stderr.write(`${color}${ICONS.error} ${message}${reset}\n`);
134
+ }
135
+ /**
136
+ * Print an error message with a red X and then exit
137
+ */
138
+ export function fatal(message, errorCode) {
139
+ const color = getColor('error');
140
+ const reset = getColor('reset');
141
+ process.stderr.write(`${color}${ICONS.error} ${message}${reset}\n`);
142
+ if (errorCode) {
143
+ const exitCode = getExitCode(errorCode);
144
+ process.exit(exitCode);
145
+ }
146
+ else {
147
+ process.exit(1);
148
+ }
149
+ }
150
+ /**
151
+ * Print a warning message with a yellow warning icon
152
+ */
153
+ export function warning(message, asError = false) {
154
+ const color = asError ? getColor('error') : getColor('warning');
155
+ const reset = getColor('reset');
156
+ process.stderr.write(`${color}${ICONS.warning} ${message}${reset}\n`);
157
+ }
158
+ /**
159
+ * Print an info message with a cyan info icon
160
+ */
161
+ export function info(message) {
162
+ const color = getColor('info');
163
+ const reset = getColor('reset');
164
+ process.stderr.write(`${color}${ICONS.info} ${message}${reset}\n`);
165
+ }
166
+ /**
167
+ * Format text in muted/gray color
168
+ */
169
+ export function muted(text) {
170
+ const color = getColor('muted');
171
+ const reset = getColor('reset');
172
+ return `${color}${text}${reset}`;
173
+ }
174
+ /**
175
+ * Format text in warn color
176
+ */
177
+ export function warn(text) {
178
+ const color = getColor('warning');
179
+ const reset = getColor('reset');
180
+ return `${color}${text}${reset}`;
181
+ }
182
+ /**
183
+ * Format text in bold
184
+ */
185
+ export function bold(text) {
186
+ const color = getColor('bold');
187
+ const reset = getColor('reset');
188
+ return `${color}${text}${reset}`;
189
+ }
190
+ /**
191
+ * Format text as a link (blue and underlined)
192
+ */
193
+ export function link(url, title) {
194
+ const color = getColor('link');
195
+ const reset = getColor('reset');
196
+ // Check if terminal supports hyperlinks (OSC 8) and colors are enabled
197
+ if (shouldUseColors() && supportsHyperlinks()) {
198
+ return `\x1b]8;;${url}\x07${color}${title ?? url}${reset}\x1b]8;;\x07`;
199
+ }
200
+ return `${color}${url}${reset}`;
201
+ }
202
+ /**
203
+ * Check if terminal supports OSC 8 hyperlinks
204
+ */
205
+ function supportsHyperlinks() {
206
+ const term = process.env.TERM || '';
207
+ const termProgram = process.env.TERM_PROGRAM || '';
208
+ const wtSession = process.env.WT_SESSION || '';
209
+ // Known terminal programs that support OSC 8
210
+ return (termProgram.includes('iTerm.app') ||
211
+ termProgram.includes('WezTerm') ||
212
+ termProgram.includes('ghostty') ||
213
+ termProgram.includes('Apple_Terminal') ||
214
+ termProgram.includes('Hyper') ||
215
+ term.includes('xterm-kitty') ||
216
+ term.includes('xterm-256color') ||
217
+ wtSession !== '' // Windows Terminal
218
+ );
219
+ }
220
+ /**
221
+ * Print a bulleted list item
222
+ */
223
+ export function bullet(message) {
224
+ process.stderr.write(`${ICONS.bullet} ${message}\n`);
225
+ }
226
+ /**
227
+ * Print an arrow item (for showing next steps)
228
+ */
229
+ export function arrow(message) {
230
+ process.stderr.write(`${ICONS.arrow} ${message}\n`);
231
+ }
232
+ /**
233
+ * Print a blank line
234
+ */
235
+ export function newline() {
236
+ process.stderr.write('\n');
237
+ }
238
+ /**
239
+ * Get the display width of a string, handling ANSI codes and OSC 8 hyperlinks
240
+ *
241
+ * Note: Bun.stringWidth() counts OSC 8 hyperlink escape sequences in the width,
242
+ * which causes incorrect alignment. We strip OSC 8 codes first, then use Bun.stringWidth()
243
+ * to handle regular ANSI codes and unicode characters correctly.
244
+ */
245
+ function getDisplayWidth(str) {
246
+ // Remove OSC-8 hyperlink sequences using Unicode escapes (\u001b = ESC, \u0007 = BEL) to satisfy linter
247
+ // eslint-disable-next-line no-control-regex
248
+ const withoutOSC8 = str.replace(/\u001b\]8;;[^\u0007]*\u0007/g, '');
249
+ return Bun.stringWidth(withoutOSC8);
250
+ }
251
+ /**
252
+ * Pad a string to a specific length on the right
253
+ */
254
+ export function padRight(str, length, pad = ' ') {
255
+ const displayWidth = getDisplayWidth(str);
256
+ if (displayWidth >= length) {
257
+ return str;
258
+ }
259
+ return str + pad.repeat(length - displayWidth);
260
+ }
261
+ /**
262
+ * Pad a string to a specific length on the left
263
+ */
264
+ export function padLeft(str, length, pad = ' ') {
265
+ const displayWidth = getDisplayWidth(str);
266
+ if (displayWidth >= length) {
267
+ return str;
268
+ }
269
+ return pad.repeat(length - displayWidth) + str;
270
+ }
271
+ /**
272
+ * Display a formatted banner with title and body content
273
+ * Creates a bordered box around the content
274
+ *
275
+ * Uses Bun.stringWidth() for accurate width calculation with ANSI codes and unicode
276
+ * Responsive to terminal width - adapts to narrow terminals
277
+ */
278
+ export function banner(title, body, options) {
279
+ // Get terminal width, default to 120 if not available
280
+ const termWidth = process.stdout.columns || 120;
281
+ const border = {
282
+ topLeft: '╭',
283
+ topRight: '╮',
284
+ bottomLeft: '╰',
285
+ bottomRight: '╯',
286
+ horizontal: '─',
287
+ vertical: '│',
288
+ };
289
+ // Calculate content width first (before wrapping)
290
+ const titleWidth = getDisplayWidth(title);
291
+ const bodyLines = body.split('\n');
292
+ const maxBodyWidth = Math.max(0, ...bodyLines.map((line) => getDisplayWidth(line)));
293
+ const requiredContentWidth = Math.max(titleWidth, maxBodyWidth);
294
+ // Box width = content + borders (2) + side spaces (2)
295
+ const boxWidth = Math.min(requiredContentWidth + 4, termWidth);
296
+ // If required content width exceeds terminal width, skip box and print plain text
297
+ if (requiredContentWidth + 4 > termWidth) {
298
+ console.log('\n' + bold(title));
299
+ console.log(body + '\n');
300
+ return;
301
+ }
302
+ // Inner width is box width minus borders (2) and side spaces (2)
303
+ const innerWidth = boxWidth - 4;
304
+ // Wrap text to fit box width
305
+ const wrappedBodyLines = wrapText(body, innerWidth);
306
+ // Colors
307
+ const borderColor = getColor('muted');
308
+ const titleColor = getColor('info');
309
+ const reset = getColor('reset');
310
+ // Build banner
311
+ const lines = [];
312
+ // Top border
313
+ lines.push(`${borderColor}${border.topLeft}${border.horizontal.repeat(boxWidth - 2)}${border.topRight}${reset}`);
314
+ if (options?.topSpacer === true || options?.topSpacer === undefined) {
315
+ // Empty line
316
+ lines.push(`${borderColor}${border.vertical}${' '.repeat(boxWidth - 2)}${border.vertical}${reset}`);
317
+ }
318
+ // Title (centered and bold)
319
+ const titleDisplayWidth = getDisplayWidth(title);
320
+ if (options?.centerTitle === true || options?.centerTitle === undefined) {
321
+ const titlePadding = Math.max(0, Math.floor((innerWidth - titleDisplayWidth) / 2));
322
+ const titleRightPadding = Math.max(0, innerWidth - titlePadding - titleDisplayWidth);
323
+ const titleLine = ' '.repeat(titlePadding) +
324
+ `${titleColor}${bold(title)}${reset}` +
325
+ ' '.repeat(titleRightPadding);
326
+ lines.push(`${borderColor}${border.vertical} ${reset}${titleLine}${borderColor} ${border.vertical}${reset}`);
327
+ }
328
+ else {
329
+ const titleRightPadding = Math.max(0, innerWidth - titleDisplayWidth);
330
+ const titleLine = `${titleColor}${bold(title)}${reset}` + ' '.repeat(titleRightPadding);
331
+ lines.push(`${borderColor}${border.vertical} ${reset}${titleLine}${borderColor} ${border.vertical}${reset}`);
332
+ }
333
+ if (options?.middleSpacer === true || options?.middleSpacer === undefined) {
334
+ // Empty line
335
+ lines.push(`${borderColor}${border.vertical}${' '.repeat(boxWidth - 2)}${border.vertical}${reset}`);
336
+ }
337
+ // Body lines
338
+ for (const line of wrappedBodyLines) {
339
+ const lineWidth = getDisplayWidth(line);
340
+ const linePadding = Math.max(0, innerWidth - lineWidth);
341
+ lines.push(`${borderColor}${border.vertical} ${reset}${line}${' '.repeat(linePadding)}${borderColor} ${border.vertical}${reset}`);
342
+ }
343
+ if (options?.bottomSpacer === true || options?.bottomSpacer === undefined) {
344
+ // Empty line
345
+ lines.push(`${borderColor}${border.vertical}${' '.repeat(boxWidth - 2)}${border.vertical}${reset}`);
346
+ }
347
+ // Bottom border
348
+ lines.push(`${borderColor}${border.bottomLeft}${border.horizontal.repeat(boxWidth - 2)}${border.bottomRight}${reset}`);
349
+ // Print the banner
350
+ console.log('\n' + lines.join('\n') + '\n');
351
+ }
352
+ /**
353
+ * Wait for any key press before continuing
354
+ * Displays a prompt message and waits for user input
355
+ * Exits with code 1 if CTRL+C is pressed
356
+ */
357
+ export async function waitForAnyKey(message = 'Press Enter to continue...') {
358
+ process.stdout.write(muted(message));
359
+ // Check if we're in a TTY environment
360
+ if (!process.stdin.isTTY) {
361
+ // Not a TTY (CI/piped), just write newline and exit
362
+ console.log('');
363
+ return Promise.resolve();
364
+ }
365
+ // Set stdin to raw mode to read a single keypress
366
+ process.stdin.setRawMode(true);
367
+ process.stdin.resume();
368
+ let rawModeSet = true;
369
+ return new Promise((resolve) => {
370
+ process.stdin.once('data', (data) => {
371
+ if (rawModeSet && process.stdin.isTTY) {
372
+ process.stdin.setRawMode(false);
373
+ rawModeSet = false;
374
+ }
375
+ process.stdin.pause();
376
+ // Check for CTRL+C (character code 3)
377
+ if (data.length === 1 && data[0] === 3) {
378
+ console.log('\n');
379
+ process.exit(1);
380
+ }
381
+ console.log('');
382
+ resolve();
383
+ });
384
+ });
385
+ }
386
+ /**
387
+ * Prompts user with a yes/no question
388
+ * Returns true for yes, false for no
389
+ * Exits with code 1 if CTRL+C is pressed
390
+ */
391
+ export async function confirm(message, defaultValue = true) {
392
+ const suffix = defaultValue ? '[Y/n]' : '[y/N]';
393
+ process.stdout.write(`${message} ${muted(suffix)} `);
394
+ // Check if we're in a TTY environment
395
+ if (!process.stdin.isTTY) {
396
+ console.log('');
397
+ return defaultValue;
398
+ }
399
+ // Set stdin to raw mode to read a single keypress
400
+ process.stdin.setRawMode(true);
401
+ process.stdin.resume();
402
+ let rawModeSet = true;
403
+ return new Promise((resolve) => {
404
+ process.stdin.once('data', (data) => {
405
+ if (rawModeSet && process.stdin.isTTY) {
406
+ process.stdin.setRawMode(false);
407
+ rawModeSet = false;
408
+ }
409
+ process.stdin.pause();
410
+ // Check for CTRL+C (character code 3)
411
+ if (data.length === 1 && data[0] === 3) {
412
+ console.log('\n');
413
+ process.exit(1);
414
+ }
415
+ const input = data.toString().trim().toLowerCase();
416
+ console.log('');
417
+ // Enter key (just newline) uses default
418
+ if (input === '') {
419
+ resolve(defaultValue);
420
+ return;
421
+ }
422
+ // Check first character for y/n
423
+ const char = input.charAt(0);
424
+ if (char === 'y') {
425
+ resolve(true);
426
+ }
427
+ else if (char === 'n') {
428
+ resolve(false);
429
+ }
430
+ else {
431
+ // Invalid input, use default
432
+ resolve(defaultValue);
433
+ }
434
+ });
435
+ });
436
+ }
437
+ /**
438
+ * Display a signup benefits box with cyan border
439
+ * Shows the value proposition for creating an Agentuity account
440
+ */
441
+ export function showSignupBenefits() {
442
+ const CYAN = Bun.color('cyan', 'ansi-16m');
443
+ const TEXT = currentColorScheme === 'dark' ? Bun.color('white', 'ansi') : Bun.color('black', 'ansi');
444
+ const RESET = '\x1b[0m';
445
+ const lines = [
446
+ '╔════════════════════════════════════════════╗',
447
+ `║ ⨺ Signup for Agentuity ${muted('free')}${CYAN} ║`,
448
+ '║ ║',
449
+ `║ ✓ ${TEXT}Cloud deployment, previews and CI/CD${CYAN} ║`,
450
+ `║ ✓ ${TEXT}AI Gateway, KV, Vector and more${CYAN} ║`,
451
+ `║ ✓ ${TEXT}Observability, Tracing and Logging${CYAN} ║`,
452
+ `║ ✓ ${TEXT}Organization and Team support${CYAN} ║`,
453
+ `║ ✓ ${TEXT}And much more!${CYAN} ║`,
454
+ '╚════════════════════════════════════════════╝',
455
+ ];
456
+ console.log('');
457
+ lines.map((line) => console.log(CYAN + line + RESET));
458
+ console.log('');
459
+ }
460
+ /**
461
+ * Display a message when unauthenticated to let the user know certain capabilities are disabled
462
+ */
463
+ export function showLoggedOutMessage() {
464
+ const YELLOW = Bun.color('yellow', 'ansi-16m');
465
+ const TEXT = currentColorScheme === 'dark' ? Bun.color('white', 'ansi') : Bun.color('black', 'ansi');
466
+ const RESET = '\x1b[0m';
467
+ const signupTitle = 'Sign up / Login';
468
+ const showInline = supportsHyperlinks();
469
+ const signupURL = 'https://app.agentuity.com/sign-up';
470
+ const signupLink = showInline
471
+ ? link(signupURL, signupTitle)
472
+ : ' '.repeat(stringWidth(signupTitle));
473
+ const showNewLine = showInline ? '' : `║ ${RESET}${link(signupURL)}${YELLOW} ║`;
474
+ const lines = [
475
+ '╔══════════════════════════════════════════════╗',
476
+ `║ ⨺ Unauthenticated (local mode) ║`,
477
+ '║ ║',
478
+ `║ ${TEXT}Certain capabilities such as the AI services${YELLOW} ║`,
479
+ `║ ${TEXT}and devmode remote are unavailable when${YELLOW} ║`,
480
+ `║ ${TEXT}unauthenticated.${YELLOW} ${signupLink}${YELLOW} ║`,
481
+ showNewLine,
482
+ '╚══════════════════════════════════════════════╝',
483
+ ];
484
+ console.log('');
485
+ lines.filter(Boolean).map((line) => console.log(YELLOW + line + RESET));
486
+ }
487
+ /**
488
+ * Copy text to clipboard
489
+ * Returns true if successful, false otherwise
490
+ */
491
+ export async function copyToClipboard(text) {
492
+ try {
493
+ const platform = process.platform;
494
+ if (platform === 'darwin') {
495
+ // macOS - use pbcopy
496
+ const proc = Bun.spawn(['pbcopy'], {
497
+ stdin: 'pipe',
498
+ });
499
+ proc.stdin.write(text);
500
+ proc.stdin.end();
501
+ await proc.exited;
502
+ return proc.exitCode === 0;
503
+ }
504
+ else if (platform === 'win32') {
505
+ // Windows - use clip
506
+ const proc = Bun.spawn(['clip'], {
507
+ stdin: 'pipe',
508
+ });
509
+ proc.stdin.write(text);
510
+ proc.stdin.end();
511
+ await proc.exited;
512
+ return proc.exitCode === 0;
513
+ }
514
+ else {
515
+ // Linux - try xclip first, then xsel
516
+ try {
517
+ const proc = Bun.spawn(['xclip', '-selection', 'clipboard'], {
518
+ stdin: 'pipe',
519
+ });
520
+ proc.stdin.write(text);
521
+ proc.stdin.end();
522
+ await proc.exited;
523
+ return proc.exitCode === 0;
524
+ }
525
+ catch {
526
+ // Try xsel as fallback
527
+ const proc = Bun.spawn(['xsel', '--clipboard', '--input'], {
528
+ stdin: 'pipe',
529
+ });
530
+ proc.stdin.write(text);
531
+ proc.stdin.end();
532
+ await proc.exited;
533
+ return proc.exitCode === 0;
534
+ }
535
+ }
536
+ }
537
+ catch {
538
+ return false;
539
+ }
540
+ }
541
+ /**
542
+ * Extract ANSI codes from the beginning of a string
543
+ */
544
+ function extractLeadingAnsiCodes(str) {
545
+ // Match ANSI escape sequences at the start of the string
546
+ // eslint-disable-next-line no-control-regex
547
+ const match = str.match(/^(\x1b\[[0-9;]*m)+/);
548
+ return match ? match[0] : '';
549
+ }
550
+ /**
551
+ * Strip ANSI codes from a string
552
+ */
553
+ function stripAnsiCodes(str) {
554
+ // Remove all ANSI escape sequences
555
+ // eslint-disable-next-line no-control-regex
556
+ return str.replace(/\x1b\[[0-9;]*m/g, '');
557
+ }
558
+ /**
559
+ * Check if a string ends with ANSI reset code
560
+ */
561
+ function endsWithReset(str) {
562
+ return str.endsWith('\x1b[0m') || str.endsWith(getColor('reset'));
563
+ }
564
+ /**
565
+ * Wrap text to a maximum width
566
+ * Handles explicit newlines and word wrapping
567
+ * Preserves ANSI color codes across wrapped lines
568
+ */
569
+ function wrapText(text, maxWidth) {
570
+ const allLines = [];
571
+ // First split by explicit newlines
572
+ const paragraphs = text.split('\n');
573
+ for (const paragraph of paragraphs) {
574
+ // Skip empty paragraphs (they become blank lines)
575
+ if (paragraph.trim() === '') {
576
+ allLines.push('');
577
+ continue;
578
+ }
579
+ // Record starting index for this paragraph's lines
580
+ const paragraphStart = allLines.length;
581
+ // Extract any leading ANSI codes from the paragraph
582
+ const leadingCodes = extractLeadingAnsiCodes(paragraph);
583
+ const hasReset = endsWithReset(paragraph);
584
+ // Wrap each paragraph
585
+ const words = paragraph.split(' ');
586
+ let currentLine = '';
587
+ for (const word of words) {
588
+ const testLine = currentLine ? `${currentLine} ${word}` : word;
589
+ const testLineWidth = getDisplayWidth(testLine);
590
+ if (testLineWidth <= maxWidth) {
591
+ currentLine = testLine;
592
+ }
593
+ else {
594
+ // If current line has content, save it
595
+ if (currentLine) {
596
+ allLines.push(currentLine);
597
+ }
598
+ // If the word itself is longer than maxWidth, just use it as is
599
+ // (better to have a long line than break in the middle)
600
+ // But if we have leading codes and this isn't the first line, apply them
601
+ if (leadingCodes && currentLine) {
602
+ // Strip any existing codes from the word to avoid duplication
603
+ const strippedWord = stripAnsiCodes(word);
604
+ currentLine = leadingCodes + strippedWord;
605
+ }
606
+ else {
607
+ currentLine = word;
608
+ }
609
+ }
610
+ }
611
+ if (currentLine) {
612
+ allLines.push(currentLine);
613
+ }
614
+ // If the original paragraph had ANSI codes and ended with reset,
615
+ // ensure each wrapped line ends with reset (only for this paragraph's lines)
616
+ if (leadingCodes && hasReset) {
617
+ for (let i = paragraphStart; i < allLines.length; i++) {
618
+ if (!endsWithReset(allLines[i])) {
619
+ allLines[i] += getColor('reset');
620
+ }
621
+ }
622
+ }
623
+ }
624
+ return allLines.length > 0 ? allLines : [''];
625
+ }
626
+ export async function spinner(messageOrOptions, callback) {
627
+ // Normalize to options format
628
+ let options;
629
+ if (typeof messageOrOptions === 'string') {
630
+ if (callback === undefined) {
631
+ throw new Error('callback is required when first argument is a string');
632
+ }
633
+ options = { type: 'simple', message: messageOrOptions, callback };
634
+ }
635
+ else {
636
+ options = messageOrOptions;
637
+ }
638
+ const message = options.message;
639
+ const reset = getColor('reset');
640
+ // Check if progress should be disabled (from global options)
641
+ const { getOutputOptions, shouldDisableProgress } = await import('./output');
642
+ const outputOptions = getOutputOptions();
643
+ const noProgress = outputOptions ? shouldDisableProgress(outputOptions) : false;
644
+ // If no TTY or progress disabled, just execute the callback without animation
645
+ if (!process.stderr.isTTY || noProgress) {
646
+ try {
647
+ const result = options.type === 'progress'
648
+ ? await options.callback(() => { })
649
+ : typeof options.callback === 'function'
650
+ ? await options.callback()
651
+ : await options.callback;
652
+ // If clearOnSuccess is true, don't show success message
653
+ if (!options.clearOnSuccess) {
654
+ const successColor = getColor('success');
655
+ console.error(`${successColor}${ICONS.success} ${message}${reset}`);
656
+ }
657
+ return result;
658
+ }
659
+ catch (err) {
660
+ const errorColor = getColor('error');
661
+ console.error(`${errorColor}${ICONS.error} ${message}${reset}`);
662
+ throw err;
663
+ }
664
+ }
665
+ const frames = ['◐', '◓', '◑', '◒'];
666
+ const spinnerColors = [
667
+ { light: '\x1b[36m', dark: '\x1b[96m' }, // cyan
668
+ { light: '\x1b[34m', dark: '\x1b[94m' }, // blue
669
+ { light: '\x1b[35m', dark: '\x1b[95m' }, // magenta
670
+ { light: '\x1b[36m', dark: '\x1b[96m' }, // cyan
671
+ ];
672
+ const bold = '\x1b[1m';
673
+ const cyanColor = { light: '\x1b[36m', dark: '\x1b[96m' }[currentColorScheme];
674
+ let frameIndex = 0;
675
+ let currentProgress;
676
+ // Hide cursor
677
+ process.stderr.write('\x1B[?25l');
678
+ // Start animation
679
+ const interval = setInterval(() => {
680
+ const colorDef = spinnerColors[frameIndex % spinnerColors.length];
681
+ const color = colorDef[currentColorScheme];
682
+ const frame = `${color}${bold}${frames[frameIndex % frames.length]}${reset}`;
683
+ // Add progress indicator if available
684
+ const progressIndicator = currentProgress !== undefined
685
+ ? ` ${cyanColor}${Math.floor(currentProgress)}%${reset}`
686
+ : '';
687
+ // Clear line and render
688
+ process.stderr.write('\r\x1B[K' + `${frame} ${message}${progressIndicator}`);
689
+ frameIndex++;
690
+ }, 120);
691
+ // Progress callback
692
+ const progressCallback = (progress) => {
693
+ currentProgress = Math.min(100, Math.max(0, progress));
694
+ };
695
+ try {
696
+ // Execute callback
697
+ const result = options.type === 'progress'
698
+ ? await options.callback(progressCallback)
699
+ : typeof options.callback === 'function'
700
+ ? await options.callback()
701
+ : await options.callback;
702
+ // Clear interval and line
703
+ clearInterval(interval);
704
+ process.stderr.write('\r\x1B[K');
705
+ // If clearOnSuccess is false, show success message
706
+ if (!options.clearOnSuccess) {
707
+ // Show success
708
+ const successColor = getColor('success');
709
+ console.error(`${successColor}${ICONS.success} ${message}${reset}`);
710
+ }
711
+ // Show cursor
712
+ process.stderr.write('\x1B[?25h');
713
+ return result;
714
+ }
715
+ catch (err) {
716
+ // Clear interval and line
717
+ clearInterval(interval);
718
+ process.stderr.write('\r\x1B[K');
719
+ // Show error
720
+ const errorColor = getColor('error');
721
+ console.error(`${errorColor}${ICONS.error} ${message}${reset}`);
722
+ // Show cursor
723
+ process.stderr.write('\x1B[?25h');
724
+ throw err;
725
+ }
726
+ }
727
+ /**
728
+ * Run an external command and stream its output with a live UI
729
+ *
730
+ * Displays the command with a colored $ prompt:
731
+ * - Blue while running
732
+ * - Green on successful exit (code 0)
733
+ * - Red on failed exit (code != 0)
734
+ *
735
+ * Shows the last 3 lines of output as it streams.
736
+ */
737
+ export async function runCommand(options) {
738
+ const { command, cmd, cwd, env, clearOnSuccess = false, truncate = true, maxLinesOutput = 3, maxLinesOnFailure = 10, } = options;
739
+ const isTTY = process.stdout.isTTY;
740
+ // If not a TTY, just run the command normally and log output
741
+ if (!isTTY) {
742
+ const proc = Bun.spawn(cmd, {
743
+ cwd,
744
+ env: { ...process.env, ...env },
745
+ stdout: 'inherit',
746
+ stderr: 'inherit',
747
+ });
748
+ return await proc.exited;
749
+ }
750
+ // Colors using Bun.color
751
+ const blue = currentColorScheme === 'light'
752
+ ? Bun.color('#0000FF', 'ansi') || '\x1b[34m'
753
+ : Bun.color('#5C9CFF', 'ansi') || '\x1b[94m';
754
+ const green = getColor('success');
755
+ const red = getColor('error');
756
+ const cmdColor = currentColorScheme === 'light'
757
+ ? '\x1b[1m' + (Bun.color('#00008B', 'ansi') || '\x1b[34m')
758
+ : Bun.color('#FFFFFF', 'ansi') || '\x1b[97m'; // bold dark blue / white
759
+ const mutedColor = Bun.color('#808080', 'ansi') || '\x1b[90m';
760
+ const reset = getColor('reset');
761
+ // Get terminal width
762
+ const termWidth = process.stdout.columns || 80;
763
+ const maxCmdWidth = Math.min(40, termWidth);
764
+ const maxLineWidth = Math.min(80, termWidth);
765
+ // Truncate command if needed
766
+ let displayCmd = command;
767
+ if (getDisplayWidth(displayCmd) > maxCmdWidth) {
768
+ // Simple truncation for now - could be smarter about this
769
+ displayCmd = displayCmd.slice(0, maxCmdWidth - 3) + '...';
770
+ }
771
+ // Store all output lines, display subset based on context
772
+ const allOutputLines = [];
773
+ let linesRendered = 0;
774
+ // Hide cursor
775
+ process.stdout.write('\x1B[?25l');
776
+ // Render the command and output lines in place
777
+ const renderOutput = (linesToShow) => {
778
+ // Move cursor up to start of our output area
779
+ if (linesRendered > 0) {
780
+ process.stdout.write(`\x1b[${linesRendered}A`);
781
+ }
782
+ // Render command line
783
+ process.stdout.write(`\r\x1b[K${blue}$${reset} ${cmdColor}${displayCmd}${reset}\n`);
784
+ // Get last N lines to display
785
+ const displayLines = allOutputLines.slice(-linesToShow);
786
+ // Render output lines
787
+ for (const line of displayLines) {
788
+ // Truncate line if needed
789
+ let displayLine = line;
790
+ if (getDisplayWidth(displayLine) > maxLineWidth) {
791
+ displayLine = displayLine.slice(0, maxLineWidth - 3) + '...';
792
+ }
793
+ process.stdout.write(`\r\x1b[K${mutedColor}${displayLine}${reset}\n`);
794
+ }
795
+ // Update count of lines we've rendered (command + output lines)
796
+ linesRendered = 1 + displayLines.length;
797
+ };
798
+ // Initial display
799
+ renderOutput(maxLinesOutput);
800
+ try {
801
+ // Spawn the command
802
+ const proc = Bun.spawn(cmd, {
803
+ cwd,
804
+ env: { ...process.env, ...env },
805
+ stdout: 'pipe',
806
+ stderr: 'pipe',
807
+ });
808
+ // Process output streams
809
+ const processStream = async (stream) => {
810
+ const reader = stream.getReader();
811
+ const decoder = new TextDecoder();
812
+ let buffer = '';
813
+ try {
814
+ while (true) {
815
+ const { done, value } = await reader.read();
816
+ if (done)
817
+ break;
818
+ buffer += decoder.decode(value, { stream: true });
819
+ const lines = buffer.split('\n');
820
+ buffer = lines.pop() || ''; // Keep incomplete line in buffer
821
+ for (const line of lines) {
822
+ if (line.trim()) {
823
+ allOutputLines.push(line);
824
+ renderOutput(maxLinesOutput); // Show last N lines while streaming
825
+ }
826
+ }
827
+ }
828
+ }
829
+ finally {
830
+ reader.releaseLock();
831
+ }
832
+ };
833
+ // Process both stdout and stderr
834
+ await Promise.all([processStream(proc.stdout), processStream(proc.stderr)]);
835
+ // Wait for process to exit
836
+ const exitCode = await proc.exited;
837
+ // If clearOnSuccess is true and command succeeded, clear everything
838
+ if (clearOnSuccess && exitCode === 0) {
839
+ if (linesRendered > 0) {
840
+ // Move up to the command line
841
+ process.stdout.write(`\x1b[${linesRendered}A`);
842
+ // Clear each line (entire line) and move cursor back up
843
+ for (let i = 0; i < linesRendered; i++) {
844
+ process.stdout.write('\x1b[2K'); // Clear entire line
845
+ if (i < linesRendered - 1) {
846
+ process.stdout.write('\x1b[B'); // Move down one line
847
+ }
848
+ }
849
+ // Move cursor back up to original position
850
+ process.stdout.write(`\x1b[${linesRendered}A\r`);
851
+ }
852
+ return exitCode;
853
+ }
854
+ // Clear all rendered lines completely
855
+ if (linesRendered > 0) {
856
+ // Move up to the command line (first line of our output)
857
+ process.stdout.write(`\x1b[${linesRendered}A`);
858
+ // Move to beginning of line and clear from cursor to end of screen
859
+ process.stdout.write('\r\x1b[J');
860
+ }
861
+ // Determine icon based on exit code
862
+ const icon = exitCode === 0 ? ICONS.success : ICONS.error;
863
+ const statusColor = exitCode === 0 ? green : red;
864
+ // Show final status: icon + command
865
+ process.stdout.write(`\r\x1b[K${statusColor}${icon}${reset} ${cmdColor}${displayCmd}${reset}\n`);
866
+ // Determine how many lines to show in final output
867
+ const finalLinesToShow = exitCode === 0 ? maxLinesOutput : maxLinesOnFailure;
868
+ // Show final output lines
869
+ const finalOutputLines = allOutputLines.slice(-finalLinesToShow);
870
+ for (const line of finalOutputLines) {
871
+ let displayLine = line;
872
+ if (truncate && getDisplayWidth(displayLine) > maxLineWidth) {
873
+ displayLine = displayLine.slice(0, maxLineWidth - 3) + '...';
874
+ }
875
+ process.stdout.write(`\r\x1b[K${mutedColor}${displayLine}${reset}\n`);
876
+ }
877
+ return exitCode;
878
+ }
879
+ catch (err) {
880
+ // Move cursor up to clear our UI
881
+ if (linesRendered > 0) {
882
+ process.stdout.write(`\x1b[${linesRendered}A`);
883
+ // Clear all our lines
884
+ for (let i = 0; i < linesRendered; i++) {
885
+ process.stdout.write('\r\x1b[K\n');
886
+ }
887
+ process.stdout.write(`\x1b[${linesRendered}A`);
888
+ }
889
+ // Show error status
890
+ process.stdout.write(`\r\x1b[K${red}$${reset} ${cmdColor}${displayCmd}${reset}\n`);
891
+ // Log the error
892
+ const errorMsg = err instanceof Error ? err.message : String(err);
893
+ console.error(`${red}${ICONS.error} Failed to spawn command: ${errorMsg}${reset}`);
894
+ if (cwd) {
895
+ console.error(`${mutedColor} cwd: ${cwd}${reset}`);
896
+ }
897
+ console.error(`${mutedColor} cmd: ${cmd.join(' ')}${reset}`);
898
+ return 1; // Return non-zero exit code
899
+ }
900
+ finally {
901
+ // Always restore cursor visibility
902
+ process.stdout.write('\x1B[?25h');
903
+ }
904
+ }
905
+ /**
906
+ * Prompt user for text input
907
+ * Returns the input string
908
+ */
909
+ export async function prompt(message) {
910
+ process.stdout.write(message);
911
+ // Check if we're in a TTY environment
912
+ if (!process.stdin.isTTY) {
913
+ console.log('');
914
+ return '';
915
+ }
916
+ // Use readline for full line input
917
+ const rl = readline.createInterface({
918
+ input: process.stdin,
919
+ output: process.stdout,
920
+ });
921
+ return new Promise((resolve) => {
922
+ rl.question('', (answer) => {
923
+ rl.close();
924
+ resolve(answer);
925
+ });
926
+ });
927
+ }
928
+ export async function selectOrganization(orgs, initial) {
929
+ if (orgs.length === 0) {
930
+ fatal('You do not belong to any organizations.\n' +
931
+ 'Please contact support or create an organization at https://agentuity.com');
932
+ }
933
+ if (process.env.AGENTUITY_CLOUD_ORG_ID) {
934
+ const org = orgs.find((o) => o.id === process.env.AGENTUITY_CLOUD_ORG_ID);
935
+ if (org) {
936
+ return org.id;
937
+ }
938
+ }
939
+ if (!process.stdin.isTTY) {
940
+ if (orgs.length === 1) {
941
+ return orgs[0].id;
942
+ }
943
+ if (initial) {
944
+ return initial;
945
+ }
946
+ fatal('Organization selection required but cannot prompt in non-interactive environment. Set AGENTUITY_CLOUD_ORG_ID or provide a default organization using --org-id');
947
+ }
948
+ const response = await enquirer.prompt({
949
+ type: 'select',
950
+ name: 'action',
951
+ message: 'Select an organization',
952
+ initial: initial || (orgs.length === 1 ? orgs[0].id : undefined),
953
+ choices: orgs.map((o) => ({ message: o.name, name: o.id })),
954
+ });
955
+ return response.action;
956
+ }
957
+ /**
958
+ * show a project list picker
959
+ *
960
+ * @param apiClient
961
+ * @param showDeployment
962
+ * @returns
963
+ */
964
+ export async function showProjectList(apiClient, showDeploymentId = false) {
965
+ const projects = await spinner({
966
+ message: 'Fetching projects',
967
+ clearOnSuccess: true,
968
+ callback: () => {
969
+ return projectList(apiClient, showDeploymentId);
970
+ },
971
+ });
972
+ if (projects.length === 0) {
973
+ return '';
974
+ }
975
+ // TODO: might want to sort by the last org_id we used
976
+ if (projects) {
977
+ projects.sort((a, b) => {
978
+ return a.name.localeCompare(b.name);
979
+ });
980
+ }
981
+ const response = await enquirer.prompt({
982
+ type: 'select',
983
+ name: 'id',
984
+ message: 'Select a project:',
985
+ choices: projects.map((p) => ({
986
+ name: p.id,
987
+ message: `${p.name.padEnd(25, ' ')} ${muted(p.id)} ${showDeploymentId ? muted(p.latestDeploymentId ?? 'no deployment') : ''}`,
988
+ })),
989
+ });
990
+ return response.id;
991
+ }
992
+ /**
993
+ * Show a profile list picker
994
+ *
995
+ * @param profiles List of profiles to choose from
996
+ * @param message Prompt message
997
+ * @returns The name of the selected profile
998
+ */
999
+ export async function showProfileList(profiles, message = 'Select a profile:') {
1000
+ if (profiles.length === 0) {
1001
+ warning('No profiles found');
1002
+ process.exit(0);
1003
+ }
1004
+ // If only one profile, just return it? No, let them confirm/see it if they asked to pick?
1005
+ // But for "use" it implies switching. If only one, you are already on it or it's the only choice.
1006
+ // But for delete, you might want to delete the only one.
1007
+ // So always show list.
1008
+ // Find currently selected profile for initial selection
1009
+ const selectedProfile = profiles.find((p) => p.selected);
1010
+ const initial = selectedProfile ? selectedProfile.name : undefined;
1011
+ // If non-interactive, return initial or first
1012
+ if (!process.stdin.isTTY) {
1013
+ if (initial)
1014
+ return initial;
1015
+ if (profiles.length === 1) {
1016
+ return profiles[0].name;
1017
+ }
1018
+ fatal('Profile selection required but cannot prompt in non-interactive environment. ' +
1019
+ 'Pass a profile name explicitly when running non-interactively.');
1020
+ }
1021
+ const response = await enquirer.prompt({
1022
+ type: 'select',
1023
+ name: 'name',
1024
+ message: message,
1025
+ initial: initial,
1026
+ choices: profiles.map((p) => ({
1027
+ name: p.name,
1028
+ message: p.selected ? `${p.name.padEnd(15, ' ')} ${muted('(current)')}` : p.name,
1029
+ })),
1030
+ });
1031
+ return response.name;
1032
+ }
1033
+ export function json(value) {
1034
+ const stringValue = typeof value === 'string' ? value : JSON.stringify(value, null, 2);
1035
+ if (shouldUseColors() && process.stdout.isTTY) {
1036
+ try {
1037
+ console.log(colorize(stringValue));
1038
+ return;
1039
+ }
1040
+ catch {
1041
+ /* */
1042
+ }
1043
+ }
1044
+ console.log(stringValue);
1045
+ }
1046
+ export function plural(count, singular, plural) {
1047
+ switch (count) {
1048
+ case 0:
1049
+ return plural;
1050
+ case 1:
1051
+ return singular;
1052
+ default:
1053
+ return plural;
1054
+ }
1055
+ }
1056
+ /**
1057
+ * Display data in a formatted table using console-table-printer
1058
+ *
1059
+ * Supports two modes:
1060
+ * 1. Simple mode: Pass data array and optional column names
1061
+ * 2. Advanced mode: Pass column configurations with custom names and alignment
1062
+ *
1063
+ * @param data - Array of data objects to display
1064
+ * @param columns - Column names or column configurations
1065
+ * @param options - Additional options
1066
+ * @returns If render=true, returns the table as a string, otherwise prints to stdout
1067
+ */
1068
+ export function table(data, columns, options) {
1069
+ // Dynamic import to avoid type errors (console-table-printer has poor typings)
1070
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
1071
+ const { Table } = require('console-table-printer');
1072
+ if (!data || data.length === 0) {
1073
+ return options?.render ? '' : undefined;
1074
+ }
1075
+ // Determine if we're using advanced column config or simple column names
1076
+ const isAdvancedMode = columns && columns.length > 0 && typeof columns[0] === 'object';
1077
+ let tableConfig;
1078
+ if (isAdvancedMode) {
1079
+ // Advanced mode: use provided column configurations
1080
+ tableConfig = {
1081
+ columns: columns.map((col) => ({
1082
+ name: col.name,
1083
+ alignment: col.alignment || 'left',
1084
+ })),
1085
+ };
1086
+ }
1087
+ else {
1088
+ // Simple mode: determine column names from data or columns parameter
1089
+ const columnNames = columns
1090
+ ? columns.map((c) => String(c))
1091
+ : data.length > 0
1092
+ ? Object.keys(data[0])
1093
+ : [];
1094
+ tableConfig = {
1095
+ columns: columnNames.map((name) => ({
1096
+ name,
1097
+ alignment: 'left',
1098
+ })),
1099
+ };
1100
+ }
1101
+ const t = new Table(tableConfig);
1102
+ // Add rows to table
1103
+ for (const row of data) {
1104
+ if (columns && !isAdvancedMode) {
1105
+ // Simple mode with column filtering
1106
+ const filtered = {};
1107
+ for (const col of columns) {
1108
+ filtered[String(col)] = row[col];
1109
+ }
1110
+ t.addRow(filtered);
1111
+ }
1112
+ else {
1113
+ // Advanced mode or no column filtering
1114
+ t.addRow(row);
1115
+ }
1116
+ }
1117
+ if (options?.render) {
1118
+ return t.render();
1119
+ }
1120
+ else {
1121
+ t.printTable();
1122
+ }
1123
+ }
1124
+ //# sourceMappingURL=tui.js.map