@amodalai/amodal 0.1.2

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 (511) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE +21 -0
  3. package/README.md +42 -0
  4. package/dist/src/auth/index.d.ts +13 -0
  5. package/dist/src/auth/index.d.ts.map +1 -0
  6. package/dist/src/auth/index.js +10 -0
  7. package/dist/src/auth/index.js.map +1 -0
  8. package/dist/src/auth/oauth2.d.ts +51 -0
  9. package/dist/src/auth/oauth2.d.ts.map +1 -0
  10. package/dist/src/auth/oauth2.js +196 -0
  11. package/dist/src/auth/oauth2.js.map +1 -0
  12. package/dist/src/auth/prompt.d.ts +21 -0
  13. package/dist/src/auth/prompt.d.ts.map +1 -0
  14. package/dist/src/auth/prompt.js +81 -0
  15. package/dist/src/auth/prompt.js.map +1 -0
  16. package/dist/src/auth/test-connection.d.ts +27 -0
  17. package/dist/src/auth/test-connection.d.ts.map +1 -0
  18. package/dist/src/auth/test-connection.js +153 -0
  19. package/dist/src/auth/test-connection.js.map +1 -0
  20. package/dist/src/auth/types.d.ts +32 -0
  21. package/dist/src/auth/types.d.ts.map +1 -0
  22. package/dist/src/auth/types.js +7 -0
  23. package/dist/src/auth/types.js.map +1 -0
  24. package/dist/src/commands/audit.d.ts +18 -0
  25. package/dist/src/commands/audit.d.ts.map +1 -0
  26. package/dist/src/commands/audit.js +86 -0
  27. package/dist/src/commands/audit.js.map +1 -0
  28. package/dist/src/commands/automations.d.ts +28 -0
  29. package/dist/src/commands/automations.d.ts.map +1 -0
  30. package/dist/src/commands/automations.js +179 -0
  31. package/dist/src/commands/automations.js.map +1 -0
  32. package/dist/src/commands/build-manifest-types.d.ts +33 -0
  33. package/dist/src/commands/build-manifest-types.d.ts.map +1 -0
  34. package/dist/src/commands/build-manifest-types.js +30 -0
  35. package/dist/src/commands/build-manifest-types.js.map +1 -0
  36. package/dist/src/commands/build-tools.d.ts +33 -0
  37. package/dist/src/commands/build-tools.d.ts.map +1 -0
  38. package/dist/src/commands/build-tools.js +237 -0
  39. package/dist/src/commands/build-tools.js.map +1 -0
  40. package/dist/src/commands/build.d.ts +23 -0
  41. package/dist/src/commands/build.d.ts.map +1 -0
  42. package/dist/src/commands/build.js +120 -0
  43. package/dist/src/commands/build.js.map +1 -0
  44. package/dist/src/commands/chat.d.ts +26 -0
  45. package/dist/src/commands/chat.d.ts.map +1 -0
  46. package/dist/src/commands/chat.js +123 -0
  47. package/dist/src/commands/chat.js.map +1 -0
  48. package/dist/src/commands/connect.d.ts +18 -0
  49. package/dist/src/commands/connect.d.ts.map +1 -0
  50. package/dist/src/commands/connect.js +198 -0
  51. package/dist/src/commands/connect.js.map +1 -0
  52. package/dist/src/commands/deploy.d.ts +20 -0
  53. package/dist/src/commands/deploy.d.ts.map +1 -0
  54. package/dist/src/commands/deploy.js +137 -0
  55. package/dist/src/commands/deploy.js.map +1 -0
  56. package/dist/src/commands/deployments.d.ts +17 -0
  57. package/dist/src/commands/deployments.d.ts.map +1 -0
  58. package/dist/src/commands/deployments.js +77 -0
  59. package/dist/src/commands/deployments.js.map +1 -0
  60. package/dist/src/commands/dev.d.ts +17 -0
  61. package/dist/src/commands/dev.d.ts.map +1 -0
  62. package/dist/src/commands/dev.js +109 -0
  63. package/dist/src/commands/dev.js.map +1 -0
  64. package/dist/src/commands/diff.d.ts +19 -0
  65. package/dist/src/commands/diff.d.ts.map +1 -0
  66. package/dist/src/commands/diff.js +120 -0
  67. package/dist/src/commands/diff.js.map +1 -0
  68. package/dist/src/commands/docker.d.ts +21 -0
  69. package/dist/src/commands/docker.d.ts.map +1 -0
  70. package/dist/src/commands/docker.js +215 -0
  71. package/dist/src/commands/docker.js.map +1 -0
  72. package/dist/src/commands/eval.d.ts +20 -0
  73. package/dist/src/commands/eval.d.ts.map +1 -0
  74. package/dist/src/commands/eval.js +236 -0
  75. package/dist/src/commands/eval.js.map +1 -0
  76. package/dist/src/commands/experiment.d.ts +21 -0
  77. package/dist/src/commands/experiment.d.ts.map +1 -0
  78. package/dist/src/commands/experiment.js +133 -0
  79. package/dist/src/commands/experiment.js.map +1 -0
  80. package/dist/src/commands/index.d.ts +10 -0
  81. package/dist/src/commands/index.d.ts.map +1 -0
  82. package/dist/src/commands/index.js +75 -0
  83. package/dist/src/commands/index.js.map +1 -0
  84. package/dist/src/commands/init.d.ts +17 -0
  85. package/dist/src/commands/init.d.ts.map +1 -0
  86. package/dist/src/commands/init.js +73 -0
  87. package/dist/src/commands/init.js.map +1 -0
  88. package/dist/src/commands/inspect.d.ts +22 -0
  89. package/dist/src/commands/inspect.d.ts.map +1 -0
  90. package/dist/src/commands/inspect.js +131 -0
  91. package/dist/src/commands/inspect.js.map +1 -0
  92. package/dist/src/commands/install-pkg.d.ts +29 -0
  93. package/dist/src/commands/install-pkg.d.ts.map +1 -0
  94. package/dist/src/commands/install-pkg.js +202 -0
  95. package/dist/src/commands/install-pkg.js.map +1 -0
  96. package/dist/src/commands/link.d.ts +32 -0
  97. package/dist/src/commands/link.d.ts.map +1 -0
  98. package/dist/src/commands/link.js +227 -0
  99. package/dist/src/commands/link.js.map +1 -0
  100. package/dist/src/commands/list.d.ts +19 -0
  101. package/dist/src/commands/list.d.ts.map +1 -0
  102. package/dist/src/commands/list.js +78 -0
  103. package/dist/src/commands/list.js.map +1 -0
  104. package/dist/src/commands/login.d.ts +31 -0
  105. package/dist/src/commands/login.d.ts.map +1 -0
  106. package/dist/src/commands/login.js +205 -0
  107. package/dist/src/commands/login.js.map +1 -0
  108. package/dist/src/commands/promote.d.ts +16 -0
  109. package/dist/src/commands/promote.d.ts.map +1 -0
  110. package/dist/src/commands/promote.js +55 -0
  111. package/dist/src/commands/promote.js.map +1 -0
  112. package/dist/src/commands/publish.d.ts +18 -0
  113. package/dist/src/commands/publish.d.ts.map +1 -0
  114. package/dist/src/commands/publish.js +122 -0
  115. package/dist/src/commands/publish.js.map +1 -0
  116. package/dist/src/commands/rollback.d.ts +17 -0
  117. package/dist/src/commands/rollback.d.ts.map +1 -0
  118. package/dist/src/commands/rollback.js +62 -0
  119. package/dist/src/commands/rollback.js.map +1 -0
  120. package/dist/src/commands/search.d.ts +20 -0
  121. package/dist/src/commands/search.d.ts.map +1 -0
  122. package/dist/src/commands/search.js +133 -0
  123. package/dist/src/commands/search.js.map +1 -0
  124. package/dist/src/commands/secrets.d.ts +20 -0
  125. package/dist/src/commands/secrets.d.ts.map +1 -0
  126. package/dist/src/commands/secrets.js +137 -0
  127. package/dist/src/commands/secrets.js.map +1 -0
  128. package/dist/src/commands/serve.d.ts +23 -0
  129. package/dist/src/commands/serve.d.ts.map +1 -0
  130. package/dist/src/commands/serve.js +144 -0
  131. package/dist/src/commands/serve.js.map +1 -0
  132. package/dist/src/commands/status.d.ts +16 -0
  133. package/dist/src/commands/status.d.ts.map +1 -0
  134. package/dist/src/commands/status.js +83 -0
  135. package/dist/src/commands/status.js.map +1 -0
  136. package/dist/src/commands/sync.d.ts +21 -0
  137. package/dist/src/commands/sync.d.ts.map +1 -0
  138. package/dist/src/commands/sync.js +94 -0
  139. package/dist/src/commands/sync.js.map +1 -0
  140. package/dist/src/commands/test-query.d.ts +19 -0
  141. package/dist/src/commands/test-query.d.ts.map +1 -0
  142. package/dist/src/commands/test-query.js +116 -0
  143. package/dist/src/commands/test-query.js.map +1 -0
  144. package/dist/src/commands/uninstall.d.ts +19 -0
  145. package/dist/src/commands/uninstall.d.ts.map +1 -0
  146. package/dist/src/commands/uninstall.js +84 -0
  147. package/dist/src/commands/uninstall.js.map +1 -0
  148. package/dist/src/commands/update.d.ts +21 -0
  149. package/dist/src/commands/update.d.ts.map +1 -0
  150. package/dist/src/commands/update.js +145 -0
  151. package/dist/src/commands/update.js.map +1 -0
  152. package/dist/src/commands/validate.d.ts +19 -0
  153. package/dist/src/commands/validate.d.ts.map +1 -0
  154. package/dist/src/commands/validate.js +114 -0
  155. package/dist/src/commands/validate.js.map +1 -0
  156. package/dist/src/fixtures/incident-response.d.ts +91 -0
  157. package/dist/src/fixtures/incident-response.d.ts.map +1 -0
  158. package/dist/src/fixtures/incident-response.js +208 -0
  159. package/dist/src/fixtures/incident-response.js.map +1 -0
  160. package/dist/src/main.d.ts +8 -0
  161. package/dist/src/main.d.ts.map +1 -0
  162. package/dist/src/main.js +30 -0
  163. package/dist/src/main.js.map +1 -0
  164. package/dist/src/shared/platform-client.d.ts +92 -0
  165. package/dist/src/shared/platform-client.d.ts.map +1 -0
  166. package/dist/src/shared/platform-client.js +155 -0
  167. package/dist/src/shared/platform-client.js.map +1 -0
  168. package/dist/src/shared/repo-discovery.d.ts +11 -0
  169. package/dist/src/shared/repo-discovery.d.ts.map +1 -0
  170. package/dist/src/shared/repo-discovery.js +33 -0
  171. package/dist/src/shared/repo-discovery.js.map +1 -0
  172. package/dist/src/templates/compose-template.d.ts +10 -0
  173. package/dist/src/templates/compose-template.d.ts.map +1 -0
  174. package/dist/src/templates/compose-template.js +30 -0
  175. package/dist/src/templates/compose-template.js.map +1 -0
  176. package/dist/src/templates/config-template.d.ts +14 -0
  177. package/dist/src/templates/config-template.d.ts.map +1 -0
  178. package/dist/src/templates/config-template.js +35 -0
  179. package/dist/src/templates/config-template.js.map +1 -0
  180. package/dist/src/templates/dockerfile-template.d.ts +10 -0
  181. package/dist/src/templates/dockerfile-template.d.ts.map +1 -0
  182. package/dist/src/templates/dockerfile-template.js +30 -0
  183. package/dist/src/templates/dockerfile-template.js.map +1 -0
  184. package/dist/src/templates/env-template.d.ts +12 -0
  185. package/dist/src/templates/env-template.d.ts.map +1 -0
  186. package/dist/src/templates/env-template.js +24 -0
  187. package/dist/src/templates/env-template.js.map +1 -0
  188. package/dist/src/templates/knowledge-template.d.ts +10 -0
  189. package/dist/src/templates/knowledge-template.d.ts.map +1 -0
  190. package/dist/src/templates/knowledge-template.js +24 -0
  191. package/dist/src/templates/knowledge-template.js.map +1 -0
  192. package/dist/src/templates/skill-template.d.ts +10 -0
  193. package/dist/src/templates/skill-template.d.ts.map +1 -0
  194. package/dist/src/templates/skill-template.js +27 -0
  195. package/dist/src/templates/skill-template.js.map +1 -0
  196. package/dist/src/ui/AskUserPrompt.d.ts +14 -0
  197. package/dist/src/ui/AskUserPrompt.d.ts.map +1 -0
  198. package/dist/src/ui/AskUserPrompt.js +17 -0
  199. package/dist/src/ui/AskUserPrompt.js.map +1 -0
  200. package/dist/src/ui/AssistantMessage.d.ts +14 -0
  201. package/dist/src/ui/AssistantMessage.d.ts.map +1 -0
  202. package/dist/src/ui/AssistantMessage.js +8 -0
  203. package/dist/src/ui/AssistantMessage.js.map +1 -0
  204. package/dist/src/ui/ChatApp.d.ts +15 -0
  205. package/dist/src/ui/ChatApp.d.ts.map +1 -0
  206. package/dist/src/ui/ChatApp.js +144 -0
  207. package/dist/src/ui/ChatApp.js.map +1 -0
  208. package/dist/src/ui/ConfirmationPrompt.d.ts +17 -0
  209. package/dist/src/ui/ConfirmationPrompt.d.ts.map +1 -0
  210. package/dist/src/ui/ConfirmationPrompt.js +21 -0
  211. package/dist/src/ui/ConfirmationPrompt.js.map +1 -0
  212. package/dist/src/ui/DiffRenderer.d.ts +32 -0
  213. package/dist/src/ui/DiffRenderer.d.ts.map +1 -0
  214. package/dist/src/ui/DiffRenderer.js +118 -0
  215. package/dist/src/ui/DiffRenderer.js.map +1 -0
  216. package/dist/src/ui/ExpandableContent.d.ts +15 -0
  217. package/dist/src/ui/ExpandableContent.d.ts.map +1 -0
  218. package/dist/src/ui/ExpandableContent.js +22 -0
  219. package/dist/src/ui/ExpandableContent.js.map +1 -0
  220. package/dist/src/ui/ExploreIndicator.d.ts +13 -0
  221. package/dist/src/ui/ExploreIndicator.d.ts.map +1 -0
  222. package/dist/src/ui/ExploreIndicator.js +14 -0
  223. package/dist/src/ui/ExploreIndicator.js.map +1 -0
  224. package/dist/src/ui/Footer.d.ts +19 -0
  225. package/dist/src/ui/Footer.d.ts.map +1 -0
  226. package/dist/src/ui/Footer.js +57 -0
  227. package/dist/src/ui/Footer.js.map +1 -0
  228. package/dist/src/ui/FullScreenLayout.d.ts +18 -0
  229. package/dist/src/ui/FullScreenLayout.d.ts.map +1 -0
  230. package/dist/src/ui/FullScreenLayout.js +14 -0
  231. package/dist/src/ui/FullScreenLayout.js.map +1 -0
  232. package/dist/src/ui/Header.d.ts +14 -0
  233. package/dist/src/ui/Header.d.ts.map +1 -0
  234. package/dist/src/ui/Header.js +11 -0
  235. package/dist/src/ui/Header.js.map +1 -0
  236. package/dist/src/ui/InputBar.d.ts +17 -0
  237. package/dist/src/ui/InputBar.d.ts.map +1 -0
  238. package/dist/src/ui/InputBar.js +49 -0
  239. package/dist/src/ui/InputBar.js.map +1 -0
  240. package/dist/src/ui/MessageList.d.ts +18 -0
  241. package/dist/src/ui/MessageList.d.ts.map +1 -0
  242. package/dist/src/ui/MessageList.js +9 -0
  243. package/dist/src/ui/MessageList.js.map +1 -0
  244. package/dist/src/ui/NotificationBar.d.ts +14 -0
  245. package/dist/src/ui/NotificationBar.d.ts.map +1 -0
  246. package/dist/src/ui/NotificationBar.js +38 -0
  247. package/dist/src/ui/NotificationBar.js.map +1 -0
  248. package/dist/src/ui/ScrollableMessageList.d.ts +27 -0
  249. package/dist/src/ui/ScrollableMessageList.d.ts.map +1 -0
  250. package/dist/src/ui/ScrollableMessageList.js +16 -0
  251. package/dist/src/ui/ScrollableMessageList.js.map +1 -0
  252. package/dist/src/ui/SessionBrowser.d.ts +20 -0
  253. package/dist/src/ui/SessionBrowser.d.ts.map +1 -0
  254. package/dist/src/ui/SessionBrowser.js +93 -0
  255. package/dist/src/ui/SessionBrowser.js.map +1 -0
  256. package/dist/src/ui/StatusMessage.d.ts +13 -0
  257. package/dist/src/ui/StatusMessage.d.ts.map +1 -0
  258. package/dist/src/ui/StatusMessage.js +17 -0
  259. package/dist/src/ui/StatusMessage.js.map +1 -0
  260. package/dist/src/ui/StreamingView.d.ts +19 -0
  261. package/dist/src/ui/StreamingView.d.ts.map +1 -0
  262. package/dist/src/ui/StreamingView.js +18 -0
  263. package/dist/src/ui/StreamingView.js.map +1 -0
  264. package/dist/src/ui/SubagentDisplay.d.ts +13 -0
  265. package/dist/src/ui/SubagentDisplay.d.ts.map +1 -0
  266. package/dist/src/ui/SubagentDisplay.js +22 -0
  267. package/dist/src/ui/SubagentDisplay.js.map +1 -0
  268. package/dist/src/ui/ThinkingDisplay.d.ts +13 -0
  269. package/dist/src/ui/ThinkingDisplay.d.ts.map +1 -0
  270. package/dist/src/ui/ThinkingDisplay.js +15 -0
  271. package/dist/src/ui/ThinkingDisplay.js.map +1 -0
  272. package/dist/src/ui/ToolCallDisplay.d.ts +16 -0
  273. package/dist/src/ui/ToolCallDisplay.d.ts.map +1 -0
  274. package/dist/src/ui/ToolCallDisplay.js +136 -0
  275. package/dist/src/ui/ToolCallDisplay.js.map +1 -0
  276. package/dist/src/ui/UserMessage.d.ts +12 -0
  277. package/dist/src/ui/UserMessage.d.ts.map +1 -0
  278. package/dist/src/ui/UserMessage.js +5 -0
  279. package/dist/src/ui/UserMessage.js.map +1 -0
  280. package/dist/src/ui/commands/clear.d.ts +7 -0
  281. package/dist/src/ui/commands/clear.d.ts.map +1 -0
  282. package/dist/src/ui/commands/clear.js +13 -0
  283. package/dist/src/ui/commands/clear.js.map +1 -0
  284. package/dist/src/ui/commands/help.d.ts +7 -0
  285. package/dist/src/ui/commands/help.d.ts.map +1 -0
  286. package/dist/src/ui/commands/help.js +26 -0
  287. package/dist/src/ui/commands/help.js.map +1 -0
  288. package/dist/src/ui/commands/index.d.ts +14 -0
  289. package/dist/src/ui/commands/index.d.ts.map +1 -0
  290. package/dist/src/ui/commands/index.js +15 -0
  291. package/dist/src/ui/commands/index.js.map +1 -0
  292. package/dist/src/ui/commands/model.d.ts +7 -0
  293. package/dist/src/ui/commands/model.d.ts.map +1 -0
  294. package/dist/src/ui/commands/model.js +16 -0
  295. package/dist/src/ui/commands/model.js.map +1 -0
  296. package/dist/src/ui/commands/registry.d.ts +30 -0
  297. package/dist/src/ui/commands/registry.d.ts.map +1 -0
  298. package/dist/src/ui/commands/registry.js +45 -0
  299. package/dist/src/ui/commands/registry.js.map +1 -0
  300. package/dist/src/ui/commands/sessions.d.ts +7 -0
  301. package/dist/src/ui/commands/sessions.d.ts.map +1 -0
  302. package/dist/src/ui/commands/sessions.js +13 -0
  303. package/dist/src/ui/commands/sessions.js.map +1 -0
  304. package/dist/src/ui/commands/stats.d.ts +7 -0
  305. package/dist/src/ui/commands/stats.d.ts.map +1 -0
  306. package/dist/src/ui/commands/stats.js +33 -0
  307. package/dist/src/ui/commands/stats.js.map +1 -0
  308. package/dist/src/ui/commands/theme.d.ts +7 -0
  309. package/dist/src/ui/commands/theme.d.ts.map +1 -0
  310. package/dist/src/ui/commands/theme.js +35 -0
  311. package/dist/src/ui/commands/theme.js.map +1 -0
  312. package/dist/src/ui/markdown/CodeBlock.d.ts +14 -0
  313. package/dist/src/ui/markdown/CodeBlock.d.ts.map +1 -0
  314. package/dist/src/ui/markdown/CodeBlock.js +55 -0
  315. package/dist/src/ui/markdown/CodeBlock.js.map +1 -0
  316. package/dist/src/ui/markdown/InlineRenderer.d.ts +12 -0
  317. package/dist/src/ui/markdown/InlineRenderer.d.ts.map +1 -0
  318. package/dist/src/ui/markdown/InlineRenderer.js +70 -0
  319. package/dist/src/ui/markdown/InlineRenderer.js.map +1 -0
  320. package/dist/src/ui/markdown/MarkdownDisplay.d.ts +17 -0
  321. package/dist/src/ui/markdown/MarkdownDisplay.d.ts.map +1 -0
  322. package/dist/src/ui/markdown/MarkdownDisplay.js +142 -0
  323. package/dist/src/ui/markdown/MarkdownDisplay.js.map +1 -0
  324. package/dist/src/ui/markdown/Table.d.ts +14 -0
  325. package/dist/src/ui/markdown/Table.d.ts.map +1 -0
  326. package/dist/src/ui/markdown/Table.js +31 -0
  327. package/dist/src/ui/markdown/Table.js.map +1 -0
  328. package/dist/src/ui/theme.d.ts +39 -0
  329. package/dist/src/ui/theme.d.ts.map +1 -0
  330. package/dist/src/ui/theme.js +39 -0
  331. package/dist/src/ui/theme.js.map +1 -0
  332. package/dist/src/ui/themes/index.d.ts +45 -0
  333. package/dist/src/ui/themes/index.d.ts.map +1 -0
  334. package/dist/src/ui/themes/index.js +73 -0
  335. package/dist/src/ui/themes/index.js.map +1 -0
  336. package/dist/src/ui/themes/light.d.ts +8 -0
  337. package/dist/src/ui/themes/light.d.ts.map +1 -0
  338. package/dist/src/ui/themes/light.js +37 -0
  339. package/dist/src/ui/themes/light.js.map +1 -0
  340. package/dist/src/ui/themes/monochrome.d.ts +8 -0
  341. package/dist/src/ui/themes/monochrome.d.ts.map +1 -0
  342. package/dist/src/ui/themes/monochrome.js +37 -0
  343. package/dist/src/ui/themes/monochrome.js.map +1 -0
  344. package/dist/src/ui/types.d.ts +191 -0
  345. package/dist/src/ui/types.d.ts.map +1 -0
  346. package/dist/src/ui/types.js +7 -0
  347. package/dist/src/ui/types.js.map +1 -0
  348. package/dist/src/ui/useAlternateBuffer.d.ts +11 -0
  349. package/dist/src/ui/useAlternateBuffer.d.ts.map +1 -0
  350. package/dist/src/ui/useAlternateBuffer.js +25 -0
  351. package/dist/src/ui/useAlternateBuffer.js.map +1 -0
  352. package/dist/src/ui/useChat.d.ts +18 -0
  353. package/dist/src/ui/useChat.d.ts.map +1 -0
  354. package/dist/src/ui/useChat.js +599 -0
  355. package/dist/src/ui/useChat.js.map +1 -0
  356. package/dist/src/ui/useElapsedTime.d.ts +11 -0
  357. package/dist/src/ui/useElapsedTime.d.ts.map +1 -0
  358. package/dist/src/ui/useElapsedTime.js +39 -0
  359. package/dist/src/ui/useElapsedTime.js.map +1 -0
  360. package/dist/src/ui/useResponsiveLayout.d.ts +16 -0
  361. package/dist/src/ui/useResponsiveLayout.d.ts.map +1 -0
  362. package/dist/src/ui/useResponsiveLayout.js +24 -0
  363. package/dist/src/ui/useResponsiveLayout.js.map +1 -0
  364. package/dist/src/ui/useScroll.d.ts +20 -0
  365. package/dist/src/ui/useScroll.d.ts.map +1 -0
  366. package/dist/src/ui/useScroll.js +64 -0
  367. package/dist/src/ui/useScroll.js.map +1 -0
  368. package/dist/src/ui/useSessionResume.d.ts +20 -0
  369. package/dist/src/ui/useSessionResume.d.ts.map +1 -0
  370. package/dist/src/ui/useSessionResume.js +77 -0
  371. package/dist/src/ui/useSessionResume.js.map +1 -0
  372. package/dist/tsconfig.tsbuildinfo +1 -0
  373. package/package.json +60 -0
  374. package/src/auth/index.ts +13 -0
  375. package/src/auth/oauth2.test.ts +305 -0
  376. package/src/auth/oauth2.ts +269 -0
  377. package/src/auth/prompt.test.ts +205 -0
  378. package/src/auth/prompt.ts +111 -0
  379. package/src/auth/test-connection.test.ts +224 -0
  380. package/src/auth/test-connection.ts +196 -0
  381. package/src/auth/types.ts +34 -0
  382. package/src/commands/audit.test.ts +92 -0
  383. package/src/commands/audit.ts +113 -0
  384. package/src/commands/automations.test.ts +85 -0
  385. package/src/commands/automations.ts +205 -0
  386. package/src/commands/build-manifest-types.ts +35 -0
  387. package/src/commands/build-tools.ts +281 -0
  388. package/src/commands/build.test.ts +63 -0
  389. package/src/commands/build.ts +135 -0
  390. package/src/commands/chat.ts +147 -0
  391. package/src/commands/command-exports.test.ts +88 -0
  392. package/src/commands/connect.test.ts +343 -0
  393. package/src/commands/connect.ts +237 -0
  394. package/src/commands/deploy.test.ts +124 -0
  395. package/src/commands/deploy.ts +153 -0
  396. package/src/commands/deployments.ts +90 -0
  397. package/src/commands/dev.test.ts +63 -0
  398. package/src/commands/dev.ts +124 -0
  399. package/src/commands/diff.test.ts +183 -0
  400. package/src/commands/diff.ts +143 -0
  401. package/src/commands/docker.ts +232 -0
  402. package/src/commands/eval.test.ts +88 -0
  403. package/src/commands/eval.ts +268 -0
  404. package/src/commands/experiment.test.ts +125 -0
  405. package/src/commands/experiment.ts +153 -0
  406. package/src/commands/index.ts +76 -0
  407. package/src/commands/init.test.ts +109 -0
  408. package/src/commands/init.ts +98 -0
  409. package/src/commands/inspect.test.ts +234 -0
  410. package/src/commands/inspect.ts +157 -0
  411. package/src/commands/install-pkg.test.ts +265 -0
  412. package/src/commands/install-pkg.ts +234 -0
  413. package/src/commands/link.ts +270 -0
  414. package/src/commands/list.test.ts +152 -0
  415. package/src/commands/list.ts +95 -0
  416. package/src/commands/login.test.ts +195 -0
  417. package/src/commands/login.ts +258 -0
  418. package/src/commands/promote.ts +64 -0
  419. package/src/commands/publish.test.ts +203 -0
  420. package/src/commands/publish.ts +142 -0
  421. package/src/commands/rollback.ts +72 -0
  422. package/src/commands/search.test.ts +174 -0
  423. package/src/commands/search.ts +154 -0
  424. package/src/commands/secrets.test.ts +168 -0
  425. package/src/commands/secrets.ts +163 -0
  426. package/src/commands/serve.ts +166 -0
  427. package/src/commands/status.ts +94 -0
  428. package/src/commands/sync.test.ts +130 -0
  429. package/src/commands/sync.ts +119 -0
  430. package/src/commands/test-query.test.ts +42 -0
  431. package/src/commands/test-query.ts +129 -0
  432. package/src/commands/uninstall.test.ts +162 -0
  433. package/src/commands/uninstall.ts +107 -0
  434. package/src/commands/update.test.ts +281 -0
  435. package/src/commands/update.ts +180 -0
  436. package/src/commands/validate.test.ts +260 -0
  437. package/src/commands/validate.ts +139 -0
  438. package/src/e2e-automations.test.ts +305 -0
  439. package/src/e2e-commands.test.ts +587 -0
  440. package/src/e2e-incident-response.test.ts +345 -0
  441. package/src/e2e-plugin-connections.test.ts +415 -0
  442. package/src/e2e-plugins.test.ts +492 -0
  443. package/src/e2e.test.ts +602 -0
  444. package/src/fixtures/incident-response.ts +232 -0
  445. package/src/main.ts +35 -0
  446. package/src/shared/platform-client.test.ts +106 -0
  447. package/src/shared/platform-client.ts +193 -0
  448. package/src/shared/repo-discovery.test.ts +56 -0
  449. package/src/shared/repo-discovery.ts +40 -0
  450. package/src/templates/compose-template.ts +30 -0
  451. package/src/templates/config-template.ts +44 -0
  452. package/src/templates/deployment-templates.test.ts +99 -0
  453. package/src/templates/dockerfile-template.ts +30 -0
  454. package/src/templates/env-template.ts +27 -0
  455. package/src/templates/knowledge-template.ts +24 -0
  456. package/src/templates/skill-template.ts +27 -0
  457. package/src/ui/AskUserPrompt.tsx +58 -0
  458. package/src/ui/AssistantMessage.tsx +51 -0
  459. package/src/ui/ChatApp.tsx +283 -0
  460. package/src/ui/ConfirmationPrompt.tsx +75 -0
  461. package/src/ui/DiffRenderer.test.ts +104 -0
  462. package/src/ui/DiffRenderer.tsx +188 -0
  463. package/src/ui/ExpandableContent.tsx +68 -0
  464. package/src/ui/ExploreIndicator.tsx +44 -0
  465. package/src/ui/Footer.tsx +87 -0
  466. package/src/ui/FullScreenLayout.tsx +45 -0
  467. package/src/ui/Header.tsx +50 -0
  468. package/src/ui/InputBar.tsx +105 -0
  469. package/src/ui/MessageList.tsx +31 -0
  470. package/src/ui/NotificationBar.tsx +64 -0
  471. package/src/ui/ScrollableMessageList.tsx +82 -0
  472. package/src/ui/SessionBrowser.test.ts +49 -0
  473. package/src/ui/SessionBrowser.tsx +179 -0
  474. package/src/ui/StatusMessage.tsx +35 -0
  475. package/src/ui/StreamingView.tsx +87 -0
  476. package/src/ui/SubagentDisplay.tsx +57 -0
  477. package/src/ui/ThinkingDisplay.tsx +45 -0
  478. package/src/ui/ToolCallDisplay.tsx +268 -0
  479. package/src/ui/UserMessage.tsx +22 -0
  480. package/src/ui/chat-e2e.test.ts +581 -0
  481. package/src/ui/commands/clear.ts +15 -0
  482. package/src/ui/commands/help.ts +31 -0
  483. package/src/ui/commands/index.ts +22 -0
  484. package/src/ui/commands/model.ts +19 -0
  485. package/src/ui/commands/registry.test.ts +76 -0
  486. package/src/ui/commands/registry.ts +66 -0
  487. package/src/ui/commands/sessions.ts +16 -0
  488. package/src/ui/commands/stats.ts +35 -0
  489. package/src/ui/commands/theme.ts +41 -0
  490. package/src/ui/markdown/CodeBlock.tsx +118 -0
  491. package/src/ui/markdown/InlineRenderer.tsx +115 -0
  492. package/src/ui/markdown/MarkdownDisplay.tsx +223 -0
  493. package/src/ui/markdown/Table.tsx +75 -0
  494. package/src/ui/theme.ts +39 -0
  495. package/src/ui/themes/index.ts +113 -0
  496. package/src/ui/themes/light.ts +39 -0
  497. package/src/ui/themes/monochrome.ts +39 -0
  498. package/src/ui/types.ts +157 -0
  499. package/src/ui/useAlternateBuffer.ts +27 -0
  500. package/src/ui/useChat.test.ts +492 -0
  501. package/src/ui/useChat.ts +693 -0
  502. package/src/ui/useElapsedTime.test.ts +33 -0
  503. package/src/ui/useElapsedTime.ts +43 -0
  504. package/src/ui/useResponsiveLayout.test.ts +54 -0
  505. package/src/ui/useResponsiveLayout.ts +32 -0
  506. package/src/ui/useScroll.test.ts +99 -0
  507. package/src/ui/useScroll.ts +97 -0
  508. package/src/ui/useSessionResume.test.ts +80 -0
  509. package/src/ui/useSessionResume.ts +102 -0
  510. package/tsconfig.json +17 -0
  511. package/vitest.config.ts +20 -0
@@ -0,0 +1,260 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+
7
+ import {describe, it, expect, vi, beforeEach} from 'vitest';
8
+
9
+ const mockFindRepoRoot = vi.fn(() => '/test/repo');
10
+ const mockLoadRepo = vi.fn();
11
+ const mockReadLockFile = vi.fn();
12
+ const mockResolveAllPackages = vi.fn();
13
+
14
+ vi.mock('../shared/repo-discovery.js', () => ({
15
+ findRepoRoot: mockFindRepoRoot,
16
+ }));
17
+
18
+ vi.mock('@amodalai/core', () => ({
19
+ loadRepo: mockLoadRepo,
20
+ readLockFile: mockReadLockFile,
21
+ resolveAllPackages: mockResolveAllPackages,
22
+ }));
23
+
24
+ describe('runValidate', () => {
25
+ beforeEach(() => {
26
+ vi.clearAllMocks();
27
+ mockFindRepoRoot.mockReturnValue('/test/repo');
28
+ });
29
+
30
+ it('should return 0 when repo is valid', async () => {
31
+ mockLoadRepo.mockResolvedValue({
32
+ connections: new Map([['api', {
33
+ surface: [{method: 'GET', path: '/test'}],
34
+ access: {},
35
+ }]]),
36
+ skills: [{name: 'test', body: 'content'}],
37
+ automations: [{name: 'daily', schedule: '0 8 * * *'}],
38
+ });
39
+
40
+ const {runValidate} = await import('./validate.js');
41
+ const result = await runValidate();
42
+ expect(result).toBe(0);
43
+ });
44
+
45
+ it('should warn when no connections exist', async () => {
46
+ mockLoadRepo.mockResolvedValue({
47
+ connections: new Map(),
48
+ skills: [],
49
+ automations: [],
50
+ });
51
+
52
+ const {runValidate} = await import('./validate.js');
53
+ const result = await runValidate();
54
+ expect(result).toBe(0); // warnings don't cause failure
55
+ });
56
+
57
+ it('should warn when connection has no surface endpoints', async () => {
58
+ mockLoadRepo.mockResolvedValue({
59
+ connections: new Map([['api', {surface: [], access: {}}]]),
60
+ skills: [],
61
+ automations: [],
62
+ });
63
+
64
+ const {runValidate} = await import('./validate.js');
65
+ const result = await runValidate();
66
+ expect(result).toBe(0);
67
+ });
68
+
69
+ it('should error when skill has empty body', async () => {
70
+ mockLoadRepo.mockResolvedValue({
71
+ connections: new Map(),
72
+ skills: [{name: 'empty-skill', body: ''}],
73
+ automations: [],
74
+ });
75
+
76
+ const {runValidate} = await import('./validate.js');
77
+ const result = await runValidate();
78
+ expect(result).toBe(1);
79
+ });
80
+
81
+ it('should warn when automation has no schedule', async () => {
82
+ mockLoadRepo.mockResolvedValue({
83
+ connections: new Map(),
84
+ skills: [],
85
+ automations: [{name: 'webhook-only'}],
86
+ });
87
+
88
+ const {runValidate} = await import('./validate.js');
89
+ const result = await runValidate();
90
+ expect(result).toBe(0);
91
+ });
92
+
93
+ it('should return 1 when repo load fails', async () => {
94
+ mockLoadRepo.mockRejectedValue(new Error('Config parse failed'));
95
+
96
+ const {runValidate} = await import('./validate.js');
97
+ const result = await runValidate();
98
+ expect(result).toBe(1);
99
+ });
100
+
101
+ it('should return 1 when repo root not found', async () => {
102
+ mockFindRepoRoot.mockImplementation(() => {
103
+ throw new Error('Not found');
104
+ });
105
+
106
+ const {runValidate} = await import('./validate.js');
107
+ const result = await runValidate();
108
+ expect(result).toBe(1);
109
+ });
110
+
111
+ it('should handle multiple skill errors', async () => {
112
+ mockLoadRepo.mockResolvedValue({
113
+ connections: new Map(),
114
+ skills: [
115
+ {name: 'skill-1', body: ''},
116
+ {name: 'skill-2', body: ''},
117
+ ],
118
+ automations: [],
119
+ });
120
+
121
+ const {runValidate} = await import('./validate.js');
122
+ const result = await runValidate();
123
+ expect(result).toBe(2);
124
+ });
125
+
126
+ it('should report both errors and warnings', async () => {
127
+ mockLoadRepo.mockResolvedValue({
128
+ connections: new Map(),
129
+ skills: [{name: 'bad', body: ''}],
130
+ automations: [{name: 'no-schedule'}],
131
+ });
132
+
133
+ const {runValidate} = await import('./validate.js');
134
+ const result = await runValidate();
135
+ expect(result).toBe(1); // 1 error
136
+ });
137
+
138
+ // Package-aware validation tests
139
+ it('should run package validation when packages flag set', async () => {
140
+ mockLoadRepo.mockResolvedValue({
141
+ connections: new Map([['api', {surface: [{method: 'GET', path: '/test'}], access: {}}]]),
142
+ skills: [{name: 'test', body: 'content'}],
143
+ automations: [],
144
+ });
145
+ mockReadLockFile.mockResolvedValue({lockVersion: 1, packages: {}});
146
+ mockResolveAllPackages.mockResolvedValue({
147
+ connections: new Map(),
148
+ skills: [],
149
+ automations: [],
150
+ knowledge: [],
151
+ warnings: [],
152
+ });
153
+
154
+ const {runValidate} = await import('./validate.js');
155
+ const result = await runValidate({packages: true});
156
+ expect(result).toBe(0);
157
+ expect(mockResolveAllPackages).toHaveBeenCalled();
158
+ });
159
+
160
+ it('should report resolution warnings as validation warnings', async () => {
161
+ mockLoadRepo.mockResolvedValue({
162
+ connections: new Map(),
163
+ skills: [],
164
+ automations: [],
165
+ });
166
+ mockReadLockFile.mockResolvedValue({lockVersion: 1, packages: {'connection/salesforce': {version: '1.0.0', npm: '@amodalai/connection-salesforce', integrity: 'sha512-x'}}});
167
+ mockResolveAllPackages.mockResolvedValue({
168
+ connections: new Map(),
169
+ skills: [],
170
+ automations: [],
171
+ knowledge: [],
172
+ warnings: ['Package connection/salesforce is in lock file but not installed (broken symlink?)'],
173
+ });
174
+
175
+ const {runValidate} = await import('./validate.js');
176
+ const result = await runValidate({packages: true});
177
+ expect(result).toBe(0); // warnings only
178
+ });
179
+
180
+ it('should error on empty resolved skill', async () => {
181
+ mockLoadRepo.mockResolvedValue({
182
+ connections: new Map(),
183
+ skills: [],
184
+ automations: [],
185
+ });
186
+ mockReadLockFile.mockResolvedValue({lockVersion: 1, packages: {}});
187
+ mockResolveAllPackages.mockResolvedValue({
188
+ connections: new Map(),
189
+ skills: [{name: 'bad-skill', body: ' '}],
190
+ automations: [],
191
+ knowledge: [],
192
+ warnings: [],
193
+ });
194
+
195
+ const {runValidate} = await import('./validate.js');
196
+ const result = await runValidate({packages: true});
197
+ expect(result).toBe(1);
198
+ });
199
+
200
+ it('should skip package validation when no lock file', async () => {
201
+ mockLoadRepo.mockResolvedValue({
202
+ connections: new Map([['api', {surface: [{method: 'GET', path: '/test'}], access: {}}]]),
203
+ skills: [],
204
+ automations: [],
205
+ });
206
+ mockReadLockFile.mockResolvedValue(null);
207
+
208
+ const {runValidate} = await import('./validate.js');
209
+ const result = await runValidate({packages: true});
210
+ expect(result).toBe(0);
211
+ expect(mockResolveAllPackages).not.toHaveBeenCalled();
212
+ });
213
+
214
+ it('should not run package checks without packages flag', async () => {
215
+ mockLoadRepo.mockResolvedValue({
216
+ connections: new Map([['api', {surface: [{method: 'GET', path: '/test'}], access: {}}]]),
217
+ skills: [],
218
+ automations: [],
219
+ });
220
+
221
+ const {runValidate} = await import('./validate.js');
222
+ const result = await runValidate();
223
+ expect(result).toBe(0);
224
+ expect(mockReadLockFile).not.toHaveBeenCalled();
225
+ });
226
+
227
+ it('should handle package resolution failure', async () => {
228
+ mockLoadRepo.mockResolvedValue({
229
+ connections: new Map(),
230
+ skills: [],
231
+ automations: [],
232
+ });
233
+ mockReadLockFile.mockResolvedValue({lockVersion: 1, packages: {}});
234
+ mockResolveAllPackages.mockRejectedValue(new Error('Disk error'));
235
+
236
+ const {runValidate} = await import('./validate.js');
237
+ const result = await runValidate({packages: true});
238
+ expect(result).toBe(1);
239
+ });
240
+
241
+ it('should warn on resolved connection with no endpoints', async () => {
242
+ mockLoadRepo.mockResolvedValue({
243
+ connections: new Map(),
244
+ skills: [],
245
+ automations: [],
246
+ });
247
+ mockReadLockFile.mockResolvedValue({lockVersion: 1, packages: {}});
248
+ mockResolveAllPackages.mockResolvedValue({
249
+ connections: new Map([['empty-conn', {surface: [], spec: {auth: null}}]]),
250
+ skills: [],
251
+ automations: [],
252
+ knowledge: [],
253
+ warnings: [],
254
+ });
255
+
256
+ const {runValidate} = await import('./validate.js');
257
+ const result = await runValidate({packages: true});
258
+ expect(result).toBe(0); // warning only
259
+ });
260
+ });
@@ -0,0 +1,139 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+
7
+ import type {CommandModule} from 'yargs';
8
+ import {loadRepo, readLockFile, resolveAllPackages} from '@amodalai/core';
9
+ import {findRepoRoot} from '../shared/repo-discovery.js';
10
+
11
+ export interface ValidateOptions {
12
+ cwd?: string;
13
+ packages?: boolean;
14
+ }
15
+
16
+ interface ValidationIssue {
17
+ level: 'error' | 'warning';
18
+ message: string;
19
+ }
20
+
21
+ /**
22
+ * Validates the amodal project configuration by loading the full repo
23
+ * and running cross-reference checks.
24
+ *
25
+ * Returns the number of errors found (0 = valid).
26
+ */
27
+ export async function runValidate(options: ValidateOptions = {}): Promise<number> {
28
+ const issues: ValidationIssue[] = [];
29
+
30
+ let repoPath: string;
31
+ try {
32
+ repoPath = findRepoRoot(options.cwd);
33
+ } catch (err) {
34
+ const msg = err instanceof Error ? err.message : String(err);
35
+ process.stderr.write(`[validate] ${msg}\n`);
36
+ return 1;
37
+ }
38
+
39
+ process.stderr.write(`[validate] Loading repo from ${repoPath}\n`);
40
+
41
+ try {
42
+ const repo = await loadRepo({localPath: repoPath});
43
+
44
+ // Check: at least one connection
45
+ if (repo.connections.size === 0) {
46
+ issues.push({level: 'warning', message: 'No connections defined. The agent cannot access external systems.'});
47
+ }
48
+
49
+ // Check: surface endpoints reference valid access config
50
+ for (const [name, conn] of repo.connections) {
51
+ if (conn.surface.length === 0) {
52
+ issues.push({level: 'warning', message: `Connection "${name}" has no surface endpoints.`});
53
+ }
54
+ }
55
+
56
+ // Check: skills have non-empty bodies
57
+ for (const skill of repo.skills) {
58
+ if (!skill.body.trim()) {
59
+ issues.push({level: 'error', message: `Skill "${skill.name}" has an empty body.`});
60
+ }
61
+ }
62
+
63
+ // Check: automations have schedules
64
+ for (const auto of repo.automations) {
65
+ if (!auto.schedule) {
66
+ issues.push({level: 'warning', message: `Automation "${auto.name}" has no schedule. It will only run via webhook.`});
67
+ }
68
+ }
69
+
70
+ } catch (err) {
71
+ const msg = err instanceof Error ? err.message : String(err);
72
+ issues.push({level: 'error', message: `Failed to load repo: ${msg}`});
73
+ }
74
+
75
+ // Package-aware validation
76
+ if (options.packages) {
77
+ try {
78
+ const lockFile = await readLockFile(repoPath);
79
+ if (lockFile) {
80
+ const resolved = await resolveAllPackages({repoPath, lockFile});
81
+
82
+ // Report warnings from resolution (missing packages, broken symlinks)
83
+ for (const warning of resolved.warnings) {
84
+ issues.push({level: 'warning', message: warning});
85
+ }
86
+
87
+ // Check for empty resolved connections
88
+ for (const [name, conn] of resolved.connections) {
89
+ if (conn.surface.length === 0) {
90
+ issues.push({level: 'warning', message: `Resolved connection "${name}" has no surface endpoints.`});
91
+ }
92
+ }
93
+
94
+ // Check for empty resolved skills
95
+ for (const skill of resolved.skills) {
96
+ if (!skill.body.trim()) {
97
+ issues.push({level: 'error', message: `Resolved skill "${skill.name}" has an empty body.`});
98
+ }
99
+ }
100
+ } else {
101
+ process.stderr.write('[validate] No lock file found, skipping package validation.\n');
102
+ }
103
+ } catch (err) {
104
+ const msg = err instanceof Error ? err.message : String(err);
105
+ issues.push({level: 'error', message: `Package resolution failed: ${msg}`});
106
+ }
107
+ }
108
+
109
+ // Print results
110
+ const errors = issues.filter((i) => i.level === 'error');
111
+ const warnings = issues.filter((i) => i.level === 'warning');
112
+
113
+ for (const issue of errors) {
114
+ process.stderr.write(` ERROR: ${issue.message}\n`);
115
+ }
116
+ for (const issue of warnings) {
117
+ process.stderr.write(` WARN: ${issue.message}\n`);
118
+ }
119
+
120
+ if (errors.length === 0 && warnings.length === 0) {
121
+ process.stderr.write('[validate] All checks passed.\n');
122
+ } else {
123
+ process.stderr.write(`[validate] ${errors.length} error(s), ${warnings.length} warning(s)\n`);
124
+ }
125
+
126
+ return errors.length;
127
+ }
128
+
129
+ export const validateCommand: CommandModule = {
130
+ command: 'validate',
131
+ describe: 'Validate the project configuration',
132
+ builder: (yargs) =>
133
+ yargs.option('packages', {type: 'boolean', default: false, describe: 'Include package resolution validation'}),
134
+ handler: async (argv) => {
135
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
136
+ const code = await runValidate({packages: argv['packages'] as boolean});
137
+ process.exit(code);
138
+ },
139
+ };
@@ -0,0 +1,305 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+
7
+ /**
8
+ * End-to-end test: Automation lifecycle
9
+ *
10
+ * Tests the full automation control flow via the HTTP API:
11
+ * 1. Create repo with cron + webhook automations on disk
12
+ * 2. Boot `createLocalServer` (which integrates ProactiveRunner)
13
+ * 3. List automations — verify both appear, cron is stopped, webhook is running
14
+ * 4. Start a cron automation — verify it becomes running
15
+ * 5. Stop a cron automation — verify it becomes stopped
16
+ * 6. Reject starting a webhook automation (always active)
17
+ * 7. Manually trigger an automation (run)
18
+ * 8. Webhook endpoint accepts events
19
+ */
20
+
21
+ import {describe, it, expect, beforeAll, afterAll} from 'vitest';
22
+ import {mkdtempSync, mkdirSync, writeFileSync, rmSync} from 'node:fs';
23
+ import {join} from 'node:path';
24
+ import {tmpdir} from 'node:os';
25
+ import type {AddressInfo} from 'node:net';
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Fixture data
29
+ // ---------------------------------------------------------------------------
30
+
31
+ const CONFIG = {
32
+ name: 'automation-test-agent',
33
+ version: '1.0.0',
34
+ description: 'Agent with automations for e2e testing',
35
+ models: {
36
+ main: {provider: 'anthropic', model: 'claude-sonnet-4-20250514'},
37
+ },
38
+ };
39
+
40
+ const CRON_AUTOMATION = `# Automation: Daily Scan
41
+
42
+ Schedule: */5 * * * *
43
+
44
+ ## Check
45
+ Scan all zones for anomalies and report findings.
46
+
47
+ ## Output
48
+ Summary of anomalies found.
49
+
50
+ ## Delivery
51
+ stdout
52
+ `;
53
+
54
+ const WEBHOOK_AUTOMATION = `# Automation: Alert Handler
55
+
56
+ ## Check
57
+ Run on webhook when an alert fires. Triage the alert and determine severity.
58
+
59
+ ## Output
60
+ Triage assessment with severity level.
61
+
62
+ ## Delivery
63
+ stdout
64
+ `;
65
+
66
+ // ---------------------------------------------------------------------------
67
+ // Test suite
68
+ // ---------------------------------------------------------------------------
69
+
70
+ describe('E2E: Automation Lifecycle', () => {
71
+ let repoDir: string;
72
+ let server: {app: unknown; start: () => Promise<unknown>; stop: () => Promise<void>} | null = null;
73
+ let baseUrl: string;
74
+
75
+ beforeAll(async () => {
76
+ // 1. Create repo directory with automations
77
+ repoDir = mkdtempSync(join(tmpdir(), 'amodal-e2e-automations-'));
78
+
79
+ // Config
80
+ writeFileSync(join(repoDir, 'amodal.json'), JSON.stringify(CONFIG, null, 2));
81
+
82
+ // Automations
83
+ const autoDir = join(repoDir, 'automations');
84
+ mkdirSync(autoDir, {recursive: true});
85
+ writeFileSync(join(autoDir, 'daily-scan.md'), CRON_AUTOMATION);
86
+ writeFileSync(join(autoDir, 'alert-handler.md'), WEBHOOK_AUTOMATION);
87
+
88
+ // 2. Boot repo server
89
+ const {createLocalServer} = await import('@amodalai/runtime');
90
+ const srv = await createLocalServer({
91
+ repoPath: repoDir,
92
+ port: 0, // random port
93
+ host: '127.0.0.1',
94
+ hotReload: false,
95
+ corsOrigin: '*',
96
+ });
97
+
98
+ const httpServer = await srv.start();
99
+ const addr = httpServer.address() as AddressInfo;
100
+ baseUrl = `http://127.0.0.1:${addr.port}`;
101
+ server = srv;
102
+ }, 30000);
103
+
104
+ afterAll(async () => {
105
+ if (server) {
106
+ await server.stop();
107
+ server = null;
108
+ }
109
+ rmSync(repoDir, {recursive: true, force: true});
110
+ });
111
+
112
+ // =========================================================================
113
+ // Health check — server is up
114
+ // =========================================================================
115
+
116
+ it('should respond to health check', async () => {
117
+ const resp = await fetch(`${baseUrl}/health`);
118
+ expect(resp.status).toBe(200);
119
+ const data = (await resp.json()) as Record<string, unknown>;
120
+ expect(data['status']).toBe('ok');
121
+ expect(data['mode']).toBe('repo');
122
+ });
123
+
124
+ // =========================================================================
125
+ // List — both automations appear with correct initial state
126
+ // =========================================================================
127
+
128
+ it('should list automations with correct types and initial state', async () => {
129
+ const resp = await fetch(`${baseUrl}/automations`);
130
+ expect(resp.status).toBe(200);
131
+
132
+
133
+ const data = (await resp.json()) as {
134
+ automations: Array<{
135
+ name: string;
136
+ title: string;
137
+ schedule?: string;
138
+ webhookTriggered: boolean;
139
+ running: boolean;
140
+ }>;
141
+ };
142
+
143
+ expect(data.automations).toHaveLength(2);
144
+
145
+ const cronAuto = data.automations.find((a) => a.name === 'daily-scan');
146
+ expect(cronAuto).toBeDefined();
147
+ expect(cronAuto?.title).toBe('Daily Scan');
148
+ expect(cronAuto?.schedule).toBe('*/5 * * * *');
149
+ expect(cronAuto?.webhookTriggered).toBe(false);
150
+ expect(cronAuto?.running).toBe(false); // not started yet
151
+
152
+ const webhookAuto = data.automations.find((a) => a.name === 'alert-handler');
153
+ expect(webhookAuto).toBeDefined();
154
+ expect(webhookAuto?.title).toBe('Alert Handler');
155
+ expect(webhookAuto?.webhookTriggered).toBe(true);
156
+ expect(webhookAuto?.running).toBe(true); // webhooks always active
157
+ });
158
+
159
+ // =========================================================================
160
+ // Start — cron automation becomes running
161
+ // =========================================================================
162
+
163
+ it('should start a cron automation', async () => {
164
+ const resp = await fetch(`${baseUrl}/automations/daily-scan/start`, {method: 'POST'});
165
+ expect(resp.status).toBe(200);
166
+ const data = (await resp.json()) as Record<string, unknown>;
167
+ expect(data['status']).toBe('started');
168
+
169
+ // Verify it's now running
170
+ const listResp = await fetch(`${baseUrl}/automations`);
171
+
172
+ const listData = (await listResp.json()) as {
173
+ automations: Array<{name: string; running: boolean}>;
174
+ };
175
+ const cronAuto = listData.automations.find((a) => a.name === 'daily-scan');
176
+ expect(cronAuto?.running).toBe(true);
177
+ });
178
+
179
+ // =========================================================================
180
+ // Start again — should fail (already running)
181
+ // =========================================================================
182
+
183
+ it('should reject starting an already running automation', async () => {
184
+ const resp = await fetch(`${baseUrl}/automations/daily-scan/start`, {method: 'POST'});
185
+ expect(resp.status).toBe(400);
186
+ const data = (await resp.json()) as Record<string, unknown>;
187
+ expect(data['error']).toContain('already running');
188
+ });
189
+
190
+ // =========================================================================
191
+ // Stop — cron automation becomes stopped
192
+ // =========================================================================
193
+
194
+ it('should stop a running cron automation', async () => {
195
+ const resp = await fetch(`${baseUrl}/automations/daily-scan/stop`, {method: 'POST'});
196
+ expect(resp.status).toBe(200);
197
+ const data = (await resp.json()) as Record<string, unknown>;
198
+ expect(data['status']).toBe('stopped');
199
+
200
+ // Verify it's now stopped
201
+ const listResp = await fetch(`${baseUrl}/automations`);
202
+
203
+ const listData = (await listResp.json()) as {
204
+ automations: Array<{name: string; running: boolean}>;
205
+ };
206
+ const cronAuto = listData.automations.find((a) => a.name === 'daily-scan');
207
+ expect(cronAuto?.running).toBe(false);
208
+ });
209
+
210
+ // =========================================================================
211
+ // Stop again — should fail (not running)
212
+ // =========================================================================
213
+
214
+ it('should reject stopping a non-running automation', async () => {
215
+ const resp = await fetch(`${baseUrl}/automations/daily-scan/stop`, {method: 'POST'});
216
+ expect(resp.status).toBe(400);
217
+ const data = (await resp.json()) as Record<string, unknown>;
218
+ expect(data['error']).toContain('not running');
219
+ });
220
+
221
+ // =========================================================================
222
+ // Start webhook automation — should fail (always active)
223
+ // =========================================================================
224
+
225
+ it('should reject starting a webhook-triggered automation', async () => {
226
+ const resp = await fetch(`${baseUrl}/automations/alert-handler/start`, {method: 'POST'});
227
+ expect(resp.status).toBe(400);
228
+ const data = (await resp.json()) as Record<string, unknown>;
229
+ expect(data['error']).toContain('webhook-triggered');
230
+ });
231
+
232
+ // =========================================================================
233
+ // Start unknown — should fail
234
+ // =========================================================================
235
+
236
+ it('should reject starting unknown automation', async () => {
237
+ const resp = await fetch(`${baseUrl}/automations/nonexistent/start`, {method: 'POST'});
238
+ expect(resp.status).toBe(400);
239
+ const data = (await resp.json()) as Record<string, unknown>;
240
+ expect(data['error']).toContain('not found');
241
+ });
242
+
243
+ // =========================================================================
244
+ // Run — manually trigger an automation (fire and forget)
245
+ // =========================================================================
246
+
247
+ it('should reject triggering unknown automation', async () => {
248
+ const resp = await fetch(`${baseUrl}/automations/nonexistent/run`, {
249
+ method: 'POST',
250
+ headers: {'Content-Type': 'application/json'},
251
+ body: JSON.stringify({}),
252
+ });
253
+ expect(resp.status).toBe(404);
254
+ });
255
+
256
+ // =========================================================================
257
+ // Webhook endpoint — accepts events
258
+ // =========================================================================
259
+
260
+ it('should accept webhook events for webhook-triggered automations', async () => {
261
+ const resp = await fetch(`${baseUrl}/webhooks/alert-handler`, {
262
+ method: 'POST',
263
+ headers: {'Content-Type': 'application/json'},
264
+ body: JSON.stringify({alert: 'high-cpu', host: 'web-01'}),
265
+ });
266
+
267
+ // May succeed or fail (depends on LLM availability), but route exists
268
+ expect([200, 500]).toContain(resp.status);
269
+ const data = (await resp.json()) as Record<string, unknown>;
270
+ // If 200, it matched and ran
271
+ if (resp.status === 200) {
272
+ expect(data['status']).toBe('accepted');
273
+ }
274
+ // If 500, it matched but execution failed (no LLM configured) — still validates routing
275
+ if (resp.status === 500) {
276
+ expect(data['matched']).toBe(true);
277
+ }
278
+ });
279
+
280
+ // =========================================================================
281
+ // Webhook for non-webhook automation — should 404
282
+ // =========================================================================
283
+
284
+ it('should reject webhook for cron-only automation', async () => {
285
+ const resp = await fetch(`${baseUrl}/webhooks/daily-scan`, {
286
+ method: 'POST',
287
+ headers: {'Content-Type': 'application/json'},
288
+ body: JSON.stringify({}),
289
+ });
290
+ expect(resp.status).toBe(404);
291
+ });
292
+
293
+ // =========================================================================
294
+ // Webhook for unknown automation — should 404
295
+ // =========================================================================
296
+
297
+ it('should reject webhook for unknown automation', async () => {
298
+ const resp = await fetch(`${baseUrl}/webhooks/nonexistent`, {
299
+ method: 'POST',
300
+ headers: {'Content-Type': 'application/json'},
301
+ body: JSON.stringify({}),
302
+ });
303
+ expect(resp.status).toBe(404);
304
+ });
305
+ });