@happy-creative/iroder 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (697) hide show
  1. package/.qwen/settings.json +8 -0
  2. package/.qwen/settings.json.orig +7 -0
  3. package/AGENTS.md +69 -0
  4. package/BUN_SHELL_MIGRATION_PLAN.md +136 -0
  5. package/Dockerfile +20 -0
  6. package/README.md +15 -0
  7. package/bin/iroder +182 -0
  8. package/docker-compose.iroder.yml +18 -0
  9. package/drizzle.config.ts +10 -0
  10. package/git +0 -0
  11. package/migration/20260127222353_familiar_lady_ursula/migration.sql +90 -0
  12. package/migration/20260127222353_familiar_lady_ursula/snapshot.json +796 -0
  13. package/migration/20260211171708_add_project_commands/migration.sql +1 -0
  14. package/migration/20260211171708_add_project_commands/snapshot.json +806 -0
  15. package/migration/20260213144116_wakeful_the_professor/migration.sql +11 -0
  16. package/migration/20260213144116_wakeful_the_professor/snapshot.json +897 -0
  17. package/migration/20260225215848_workspace/migration.sql +7 -0
  18. package/migration/20260225215848_workspace/snapshot.json +959 -0
  19. package/migration/20260227213759_add_session_workspace_id/migration.sql +2 -0
  20. package/migration/20260227213759_add_session_workspace_id/snapshot.json +983 -0
  21. package/migration/20260228203230_blue_harpoon/migration.sql +17 -0
  22. package/migration/20260228203230_blue_harpoon/snapshot.json +1102 -0
  23. package/migration/20260303231226_add_workspace_fields/migration.sql +5 -0
  24. package/migration/20260303231226_add_workspace_fields/snapshot.json +1013 -0
  25. package/migration/20260309230000_move_org_to_state/migration.sql +3 -0
  26. package/migration/20260309230000_move_org_to_state/snapshot.json +1156 -0
  27. package/migration/20260312043431_session_message_cursor/migration.sql +4 -0
  28. package/migration/20260312043431_session_message_cursor/snapshot.json +1168 -0
  29. package/migration/20260323234822_events/migration.sql +13 -0
  30. package/migration/20260323234822_events/snapshot.json +1271 -0
  31. package/package.json +180 -0
  32. package/parsers-config.ts +290 -0
  33. package/script/build-node.ts +60 -0
  34. package/script/build.ts +281 -0
  35. package/script/check-migrations.ts +16 -0
  36. package/script/e2e-local-real-key.ts +197 -0
  37. package/script/fix-node-pty.ts +28 -0
  38. package/script/postinstall.mjs +131 -0
  39. package/script/publish-all.sh +68 -0
  40. package/script/publish.ts +181 -0
  41. package/script/schema.ts +63 -0
  42. package/script/seed-e2e.ts +60 -0
  43. package/script/upgrade-opentui.ts +64 -0
  44. package/specs/effect-migration.md +310 -0
  45. package/specs/tui-plugins.md +436 -0
  46. package/specs/v2/keymappings.md +10 -0
  47. package/specs/v2/message-shape.md +136 -0
  48. package/src/account/account.sql.ts +39 -0
  49. package/src/account/index.ts +488 -0
  50. package/src/account/repo.ts +166 -0
  51. package/src/account/schema.ts +119 -0
  52. package/src/account/url.ts +8 -0
  53. package/src/acp/README.md +174 -0
  54. package/src/acp/agent.ts +1847 -0
  55. package/src/acp/session.ts +116 -0
  56. package/src/acp/types.ts +24 -0
  57. package/src/agent/agent.ts +422 -0
  58. package/src/agent/generate.txt +75 -0
  59. package/src/agent/prompt/compaction.txt +15 -0
  60. package/src/agent/prompt/explore.txt +18 -0
  61. package/src/agent/prompt/summary.txt +11 -0
  62. package/src/agent/prompt/title.txt +44 -0
  63. package/src/auth/index.ts +110 -0
  64. package/src/bus/bus-event.ts +40 -0
  65. package/src/bus/global.ts +10 -0
  66. package/src/bus/index.ts +185 -0
  67. package/src/cli/bootstrap.ts +17 -0
  68. package/src/cli/cmd/account.ts +257 -0
  69. package/src/cli/cmd/acp.ts +72 -0
  70. package/src/cli/cmd/agent.ts +245 -0
  71. package/src/cli/cmd/cmd.ts +7 -0
  72. package/src/cli/cmd/db.ts +120 -0
  73. package/src/cli/cmd/debug/agent.ts +170 -0
  74. package/src/cli/cmd/debug/config.ts +16 -0
  75. package/src/cli/cmd/debug/file.ts +97 -0
  76. package/src/cli/cmd/debug/index.ts +48 -0
  77. package/src/cli/cmd/debug/lsp.ts +53 -0
  78. package/src/cli/cmd/debug/ripgrep.ts +87 -0
  79. package/src/cli/cmd/debug/scrap.ts +16 -0
  80. package/src/cli/cmd/debug/skill.ts +16 -0
  81. package/src/cli/cmd/debug/snapshot.ts +52 -0
  82. package/src/cli/cmd/export.ts +89 -0
  83. package/src/cli/cmd/generate.ts +38 -0
  84. package/src/cli/cmd/github.ts +1647 -0
  85. package/src/cli/cmd/import.ts +207 -0
  86. package/src/cli/cmd/mcp.ts +754 -0
  87. package/src/cli/cmd/models.ts +78 -0
  88. package/src/cli/cmd/plug.ts +233 -0
  89. package/src/cli/cmd/pr.ts +127 -0
  90. package/src/cli/cmd/providers.ts +480 -0
  91. package/src/cli/cmd/run.ts +692 -0
  92. package/src/cli/cmd/serve.ts +23 -0
  93. package/src/cli/cmd/session.ts +159 -0
  94. package/src/cli/cmd/stats.ts +410 -0
  95. package/src/cli/cmd/tui/app.tsx +940 -0
  96. package/src/cli/cmd/tui/attach.ts +88 -0
  97. package/src/cli/cmd/tui/component/border.tsx +21 -0
  98. package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
  99. package/src/cli/cmd/tui/component/dialog-command.tsx +171 -0
  100. package/src/cli/cmd/tui/component/dialog-console-org.tsx +103 -0
  101. package/src/cli/cmd/tui/component/dialog-go-upsell.tsx +100 -0
  102. package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
  103. package/src/cli/cmd/tui/component/dialog-model.tsx +183 -0
  104. package/src/cli/cmd/tui/component/dialog-provider.tsx +360 -0
  105. package/src/cli/cmd/tui/component/dialog-session-list.tsx +108 -0
  106. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
  107. package/src/cli/cmd/tui/component/dialog-skill.tsx +36 -0
  108. package/src/cli/cmd/tui/component/dialog-stash.tsx +87 -0
  109. package/src/cli/cmd/tui/component/dialog-status.tsx +168 -0
  110. package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
  111. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
  112. package/src/cli/cmd/tui/component/dialog-variant.tsx +39 -0
  113. package/src/cli/cmd/tui/component/dialog-workspace-list.tsx +320 -0
  114. package/src/cli/cmd/tui/component/error-component.tsx +93 -0
  115. package/src/cli/cmd/tui/component/logo.tsx +85 -0
  116. package/src/cli/cmd/tui/component/plugin-route-missing.tsx +14 -0
  117. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +672 -0
  118. package/src/cli/cmd/tui/component/prompt/frecency.tsx +90 -0
  119. package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
  120. package/src/cli/cmd/tui/component/prompt/index.tsx +1261 -0
  121. package/src/cli/cmd/tui/component/prompt/part.ts +16 -0
  122. package/src/cli/cmd/tui/component/prompt/stash.tsx +101 -0
  123. package/src/cli/cmd/tui/component/spinner.tsx +24 -0
  124. package/src/cli/cmd/tui/component/startup-loading.tsx +63 -0
  125. package/src/cli/cmd/tui/component/textarea-keybindings.ts +73 -0
  126. package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
  127. package/src/cli/cmd/tui/component/workspace/dialog-session-list.tsx +151 -0
  128. package/src/cli/cmd/tui/context/args.tsx +15 -0
  129. package/src/cli/cmd/tui/context/directory.ts +13 -0
  130. package/src/cli/cmd/tui/context/exit.tsx +60 -0
  131. package/src/cli/cmd/tui/context/helper.tsx +25 -0
  132. package/src/cli/cmd/tui/context/keybind.tsx +105 -0
  133. package/src/cli/cmd/tui/context/kv.tsx +52 -0
  134. package/src/cli/cmd/tui/context/local.tsx +412 -0
  135. package/src/cli/cmd/tui/context/plugin-keybinds.ts +41 -0
  136. package/src/cli/cmd/tui/context/prompt.tsx +18 -0
  137. package/src/cli/cmd/tui/context/route.tsx +52 -0
  138. package/src/cli/cmd/tui/context/sdk.tsx +115 -0
  139. package/src/cli/cmd/tui/context/sync.tsx +516 -0
  140. package/src/cli/cmd/tui/context/theme/aura.json +69 -0
  141. package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
  142. package/src/cli/cmd/tui/context/theme/carbonfox.json +248 -0
  143. package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
  144. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
  145. package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
  146. package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
  147. package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
  148. package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
  149. package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
  150. package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
  151. package/src/cli/cmd/tui/context/theme/github.json +233 -0
  152. package/src/cli/cmd/tui/context/theme/gruvbox.json +242 -0
  153. package/src/cli/cmd/tui/context/theme/iroder.json +245 -0
  154. package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
  155. package/src/cli/cmd/tui/context/theme/lucent-orng.json +237 -0
  156. package/src/cli/cmd/tui/context/theme/material.json +235 -0
  157. package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
  158. package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
  159. package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
  160. package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
  161. package/src/cli/cmd/tui/context/theme/nord.json +223 -0
  162. package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
  163. package/src/cli/cmd/tui/context/theme/opencode.json +245 -0
  164. package/src/cli/cmd/tui/context/theme/orng.json +249 -0
  165. package/src/cli/cmd/tui/context/theme/osaka-jade.json +93 -0
  166. package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
  167. package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
  168. package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
  169. package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
  170. package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
  171. package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
  172. package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
  173. package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
  174. package/src/cli/cmd/tui/context/theme.tsx +1236 -0
  175. package/src/cli/cmd/tui/context/tui-config.tsx +9 -0
  176. package/src/cli/cmd/tui/event.ts +49 -0
  177. package/src/cli/cmd/tui/feature-plugins/home/footer.tsx +93 -0
  178. package/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx +155 -0
  179. package/src/cli/cmd/tui/feature-plugins/home/tips.tsx +50 -0
  180. package/src/cli/cmd/tui/feature-plugins/sidebar/context.tsx +63 -0
  181. package/src/cli/cmd/tui/feature-plugins/sidebar/files.tsx +62 -0
  182. package/src/cli/cmd/tui/feature-plugins/sidebar/footer.tsx +93 -0
  183. package/src/cli/cmd/tui/feature-plugins/sidebar/lsp.tsx +66 -0
  184. package/src/cli/cmd/tui/feature-plugins/sidebar/mcp.tsx +96 -0
  185. package/src/cli/cmd/tui/feature-plugins/sidebar/todo.tsx +48 -0
  186. package/src/cli/cmd/tui/feature-plugins/system/plugins.tsx +270 -0
  187. package/src/cli/cmd/tui/plugin/api.tsx +397 -0
  188. package/src/cli/cmd/tui/plugin/index.ts +3 -0
  189. package/src/cli/cmd/tui/plugin/internal.ts +27 -0
  190. package/src/cli/cmd/tui/plugin/runtime.ts +1031 -0
  191. package/src/cli/cmd/tui/plugin/slots.tsx +60 -0
  192. package/src/cli/cmd/tui/routes/home.tsx +84 -0
  193. package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +65 -0
  194. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +110 -0
  195. package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +26 -0
  196. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
  197. package/src/cli/cmd/tui/routes/session/footer.tsx +91 -0
  198. package/src/cli/cmd/tui/routes/session/index.tsx +2281 -0
  199. package/src/cli/cmd/tui/routes/session/permission.tsx +691 -0
  200. package/src/cli/cmd/tui/routes/session/question.tsx +468 -0
  201. package/src/cli/cmd/tui/routes/session/sidebar.tsx +74 -0
  202. package/src/cli/cmd/tui/routes/session/subagent-footer.tsx +131 -0
  203. package/src/cli/cmd/tui/thread.ts +241 -0
  204. package/src/cli/cmd/tui/ui/dialog-alert.tsx +59 -0
  205. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +89 -0
  206. package/src/cli/cmd/tui/ui/dialog-export-options.tsx +211 -0
  207. package/src/cli/cmd/tui/ui/dialog-help.tsx +40 -0
  208. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +115 -0
  209. package/src/cli/cmd/tui/ui/dialog-select.tsx +417 -0
  210. package/src/cli/cmd/tui/ui/dialog.tsx +192 -0
  211. package/src/cli/cmd/tui/ui/link.tsx +28 -0
  212. package/src/cli/cmd/tui/ui/spinner.ts +368 -0
  213. package/src/cli/cmd/tui/ui/toast.tsx +100 -0
  214. package/src/cli/cmd/tui/util/clipboard.ts +192 -0
  215. package/src/cli/cmd/tui/util/editor.ts +37 -0
  216. package/src/cli/cmd/tui/util/model.ts +23 -0
  217. package/src/cli/cmd/tui/util/provider-origin.ts +7 -0
  218. package/src/cli/cmd/tui/util/scroll.ts +23 -0
  219. package/src/cli/cmd/tui/util/selection.ts +25 -0
  220. package/src/cli/cmd/tui/util/signal.ts +7 -0
  221. package/src/cli/cmd/tui/util/terminal.ts +114 -0
  222. package/src/cli/cmd/tui/util/transcript.ts +112 -0
  223. package/src/cli/cmd/tui/win32.ts +129 -0
  224. package/src/cli/cmd/tui/worker.ts +195 -0
  225. package/src/cli/cmd/uninstall.ts +353 -0
  226. package/src/cli/cmd/upgrade.ts +73 -0
  227. package/src/cli/cmd/web.ts +83 -0
  228. package/src/cli/commands.ts +58 -0
  229. package/src/cli/effect/prompt.ts +25 -0
  230. package/src/cli/error.ts +50 -0
  231. package/src/cli/heap.ts +59 -0
  232. package/src/cli/llm-ready.ts +22 -0
  233. package/src/cli/logo.ts +8 -0
  234. package/src/cli/network.ts +60 -0
  235. package/src/cli/ui.ts +132 -0
  236. package/src/cli/upgrade.ts +31 -0
  237. package/src/command/index.ts +195 -0
  238. package/src/command/template/initialize.txt +66 -0
  239. package/src/command/template/review.txt +101 -0
  240. package/src/config/config.ts +1659 -0
  241. package/src/config/console-state.ts +15 -0
  242. package/src/config/markdown.ts +99 -0
  243. package/src/config/paths.ts +167 -0
  244. package/src/config/tui-migrate.ts +156 -0
  245. package/src/config/tui-schema.ts +37 -0
  246. package/src/config/tui.ts +179 -0
  247. package/src/control-plane/adaptors/index.ts +23 -0
  248. package/src/control-plane/adaptors/remote-acp.ts +35 -0
  249. package/src/control-plane/adaptors/remote-http.ts +35 -0
  250. package/src/control-plane/adaptors/worktree.ts +42 -0
  251. package/src/control-plane/schema.ts +17 -0
  252. package/src/control-plane/sse.ts +66 -0
  253. package/src/control-plane/types.ts +32 -0
  254. package/src/control-plane/workspace.sql.ts +17 -0
  255. package/src/control-plane/workspace.ts +169 -0
  256. package/src/effect/cross-spawn-spawner.ts +502 -0
  257. package/src/effect/instance-ref.ts +6 -0
  258. package/src/effect/instance-registry.ts +12 -0
  259. package/src/effect/instance-state.ts +82 -0
  260. package/src/effect/oltp.ts +34 -0
  261. package/src/effect/run-service.ts +34 -0
  262. package/src/effect/runner.ts +216 -0
  263. package/src/env/index.ts +28 -0
  264. package/src/file/ignore.ts +82 -0
  265. package/src/file/index.ts +686 -0
  266. package/src/file/protected.ts +59 -0
  267. package/src/file/ripgrep.ts +376 -0
  268. package/src/file/time.ts +133 -0
  269. package/src/file/watcher.ts +171 -0
  270. package/src/filesystem/index.ts +236 -0
  271. package/src/flag/flag.ts +171 -0
  272. package/src/format/formatter.ts +413 -0
  273. package/src/format/index.ts +203 -0
  274. package/src/git/index.ts +303 -0
  275. package/src/global/index.ts +54 -0
  276. package/src/id/id.ts +85 -0
  277. package/src/ide/index.ts +74 -0
  278. package/src/index.ts +202 -0
  279. package/src/installation/index.ts +356 -0
  280. package/src/installation/meta.ts +7 -0
  281. package/src/lsp/client.ts +252 -0
  282. package/src/lsp/index.ts +558 -0
  283. package/src/lsp/language.ts +120 -0
  284. package/src/lsp/launch.ts +21 -0
  285. package/src/lsp/server.ts +1968 -0
  286. package/src/mcp/auth.ts +173 -0
  287. package/src/mcp/index.ts +921 -0
  288. package/src/mcp/oauth-callback.ts +216 -0
  289. package/src/mcp/oauth-provider.ts +186 -0
  290. package/src/node.ts +6 -0
  291. package/src/npm/index.ts +188 -0
  292. package/src/patch/index.ts +680 -0
  293. package/src/permission/arity.ts +163 -0
  294. package/src/permission/evaluate.ts +15 -0
  295. package/src/permission/index.ts +325 -0
  296. package/src/permission/schema.ts +17 -0
  297. package/src/plugin/cloudflare.ts +67 -0
  298. package/src/plugin/codex.ts +608 -0
  299. package/src/plugin/github-copilot/copilot.ts +361 -0
  300. package/src/plugin/github-copilot/models.ts +144 -0
  301. package/src/plugin/index.ts +293 -0
  302. package/src/plugin/install.ts +439 -0
  303. package/src/plugin/loader.ts +174 -0
  304. package/src/plugin/meta.ts +188 -0
  305. package/src/plugin/shared.ts +323 -0
  306. package/src/project/bootstrap.ts +31 -0
  307. package/src/project/instance.ts +175 -0
  308. package/src/project/project.sql.ts +16 -0
  309. package/src/project/project.ts +519 -0
  310. package/src/project/schema.ts +16 -0
  311. package/src/project/state.ts +70 -0
  312. package/src/project/vcs.ts +254 -0
  313. package/src/provider/auth.ts +253 -0
  314. package/src/provider/error.ts +197 -0
  315. package/src/provider/models.ts +159 -0
  316. package/src/provider/provider.ts +1748 -0
  317. package/src/provider/schema.ts +38 -0
  318. package/src/provider/sdk/copilot/README.md +5 -0
  319. package/src/provider/sdk/copilot/chat/convert-to-openai-compatible-chat-messages.ts +170 -0
  320. package/src/provider/sdk/copilot/chat/get-response-metadata.ts +15 -0
  321. package/src/provider/sdk/copilot/chat/map-openai-compatible-finish-reason.ts +19 -0
  322. package/src/provider/sdk/copilot/chat/openai-compatible-api-types.ts +64 -0
  323. package/src/provider/sdk/copilot/chat/openai-compatible-chat-language-model.ts +815 -0
  324. package/src/provider/sdk/copilot/chat/openai-compatible-chat-options.ts +28 -0
  325. package/src/provider/sdk/copilot/chat/openai-compatible-metadata-extractor.ts +44 -0
  326. package/src/provider/sdk/copilot/chat/openai-compatible-prepare-tools.ts +83 -0
  327. package/src/provider/sdk/copilot/copilot-provider.ts +100 -0
  328. package/src/provider/sdk/copilot/index.ts +2 -0
  329. package/src/provider/sdk/copilot/openai-compatible-error.ts +27 -0
  330. package/src/provider/sdk/copilot/responses/convert-to-openai-responses-input.ts +335 -0
  331. package/src/provider/sdk/copilot/responses/map-openai-responses-finish-reason.ts +22 -0
  332. package/src/provider/sdk/copilot/responses/openai-config.ts +18 -0
  333. package/src/provider/sdk/copilot/responses/openai-error.ts +22 -0
  334. package/src/provider/sdk/copilot/responses/openai-responses-api-types.ts +214 -0
  335. package/src/provider/sdk/copilot/responses/openai-responses-language-model.ts +1769 -0
  336. package/src/provider/sdk/copilot/responses/openai-responses-prepare-tools.ts +173 -0
  337. package/src/provider/sdk/copilot/responses/openai-responses-settings.ts +1 -0
  338. package/src/provider/sdk/copilot/responses/tool/code-interpreter.ts +87 -0
  339. package/src/provider/sdk/copilot/responses/tool/file-search.ts +127 -0
  340. package/src/provider/sdk/copilot/responses/tool/image-generation.ts +114 -0
  341. package/src/provider/sdk/copilot/responses/tool/local-shell.ts +64 -0
  342. package/src/provider/sdk/copilot/responses/tool/web-search-preview.ts +103 -0
  343. package/src/provider/sdk/copilot/responses/tool/web-search.ts +102 -0
  344. package/src/provider/transform.ts +1051 -0
  345. package/src/pty/index.ts +397 -0
  346. package/src/pty/pty.bun.ts +26 -0
  347. package/src/pty/pty.node.ts +27 -0
  348. package/src/pty/pty.ts +25 -0
  349. package/src/pty/schema.ts +17 -0
  350. package/src/question/index.ts +224 -0
  351. package/src/question/schema.ts +17 -0
  352. package/src/runtime/adapters/acp/index.ts +127 -0
  353. package/src/runtime/adapters/local/index.ts +6 -0
  354. package/src/runtime/core/agent.ts +13 -0
  355. package/src/runtime/core/mcp.ts +10 -0
  356. package/src/runtime/core/ports.ts +16 -0
  357. package/src/runtime/core/service.ts +19 -0
  358. package/src/runtime/core/session-summarize.ts +52 -0
  359. package/src/runtime/core/session.ts +110 -0
  360. package/src/runtime/core/skill.ts +10 -0
  361. package/src/runtime/core/transport.ts +10 -0
  362. package/src/runtime/factory.ts +20 -0
  363. package/src/runtime/ports.ts +74 -0
  364. package/src/server/error.ts +36 -0
  365. package/src/server/event.ts +7 -0
  366. package/src/server/instance.ts +322 -0
  367. package/src/server/mdns.ts +60 -0
  368. package/src/server/middleware.ts +33 -0
  369. package/src/server/projectors.ts +28 -0
  370. package/src/server/proxy.ts +134 -0
  371. package/src/server/router.ts +161 -0
  372. package/src/server/routes/config.ts +92 -0
  373. package/src/server/routes/event.ts +83 -0
  374. package/src/server/routes/experimental.ts +379 -0
  375. package/src/server/routes/file.ts +197 -0
  376. package/src/server/routes/global.ts +312 -0
  377. package/src/server/routes/mcp.ts +226 -0
  378. package/src/server/routes/permission.ts +69 -0
  379. package/src/server/routes/project.ts +118 -0
  380. package/src/server/routes/provider.ts +171 -0
  381. package/src/server/routes/pty.ts +210 -0
  382. package/src/server/routes/question.ts +99 -0
  383. package/src/server/routes/session.ts +1011 -0
  384. package/src/server/routes/tui.ts +379 -0
  385. package/src/server/routes/workspace.ts +94 -0
  386. package/src/server/server.ts +367 -0
  387. package/src/session/compaction.ts +425 -0
  388. package/src/session/index.ts +887 -0
  389. package/src/session/instruction.ts +258 -0
  390. package/src/session/llm.ts +412 -0
  391. package/src/session/message-v2.ts +1038 -0
  392. package/src/session/message.ts +191 -0
  393. package/src/session/overflow.ts +22 -0
  394. package/src/session/processor.ts +515 -0
  395. package/src/session/projectors.ts +135 -0
  396. package/src/session/prompt/anthropic.txt +105 -0
  397. package/src/session/prompt/beast.txt +147 -0
  398. package/src/session/prompt/build-switch.txt +5 -0
  399. package/src/session/prompt/codex.txt +79 -0
  400. package/src/session/prompt/copilot-gpt-5.txt +143 -0
  401. package/src/session/prompt/default.txt +105 -0
  402. package/src/session/prompt/gemini.txt +155 -0
  403. package/src/session/prompt/gpt.txt +107 -0
  404. package/src/session/prompt/kimi.txt +95 -0
  405. package/src/session/prompt/max-steps.txt +16 -0
  406. package/src/session/prompt/plan-reminder-anthropic.txt +67 -0
  407. package/src/session/prompt/plan.txt +26 -0
  408. package/src/session/prompt/trinity.txt +97 -0
  409. package/src/session/prompt.ts +1908 -0
  410. package/src/session/retry.ts +123 -0
  411. package/src/session/revert.ts +176 -0
  412. package/src/session/schema.ts +38 -0
  413. package/src/session/session.sql.ts +103 -0
  414. package/src/session/status.ts +102 -0
  415. package/src/session/summary.ts +177 -0
  416. package/src/session/system.ts +76 -0
  417. package/src/session/todo.ts +95 -0
  418. package/src/share/share-next.ts +370 -0
  419. package/src/share/share.sql.ts +13 -0
  420. package/src/shell/shell.ts +110 -0
  421. package/src/skill/discovery.ts +116 -0
  422. package/src/skill/index.ts +289 -0
  423. package/src/snapshot/index.ts +723 -0
  424. package/src/sql.d.ts +4 -0
  425. package/src/storage/db.bun.ts +8 -0
  426. package/src/storage/db.node.ts +8 -0
  427. package/src/storage/db.ts +174 -0
  428. package/src/storage/json-migration.ts +425 -0
  429. package/src/storage/schema.sql.ts +10 -0
  430. package/src/storage/schema.ts +5 -0
  431. package/src/storage/storage.ts +353 -0
  432. package/src/sync/README.md +179 -0
  433. package/src/sync/event.sql.ts +16 -0
  434. package/src/sync/index.ts +263 -0
  435. package/src/sync/schema.ts +14 -0
  436. package/src/testing/llm-server.ts +2 -0
  437. package/src/tool/apply_patch.ts +279 -0
  438. package/src/tool/apply_patch.txt +33 -0
  439. package/src/tool/bash.ts +498 -0
  440. package/src/tool/bash.txt +117 -0
  441. package/src/tool/codesearch.ts +133 -0
  442. package/src/tool/codesearch.txt +12 -0
  443. package/src/tool/edit.ts +666 -0
  444. package/src/tool/edit.txt +10 -0
  445. package/src/tool/external-directory.ts +46 -0
  446. package/src/tool/glob.ts +78 -0
  447. package/src/tool/glob.txt +6 -0
  448. package/src/tool/grep.ts +156 -0
  449. package/src/tool/grep.txt +8 -0
  450. package/src/tool/invalid.ts +17 -0
  451. package/src/tool/ls.ts +121 -0
  452. package/src/tool/ls.txt +1 -0
  453. package/src/tool/lsp.ts +97 -0
  454. package/src/tool/lsp.txt +19 -0
  455. package/src/tool/multiedit.ts +46 -0
  456. package/src/tool/multiedit.txt +41 -0
  457. package/src/tool/plan-enter.txt +14 -0
  458. package/src/tool/plan-exit.txt +13 -0
  459. package/src/tool/plan.ts +131 -0
  460. package/src/tool/question.ts +46 -0
  461. package/src/tool/question.txt +10 -0
  462. package/src/tool/read.ts +330 -0
  463. package/src/tool/read.txt +14 -0
  464. package/src/tool/registry.ts +303 -0
  465. package/src/tool/schema.ts +17 -0
  466. package/src/tool/skill.ts +120 -0
  467. package/src/tool/task.ts +192 -0
  468. package/src/tool/task.txt +57 -0
  469. package/src/tool/todo.ts +48 -0
  470. package/src/tool/todowrite.txt +167 -0
  471. package/src/tool/tool.ts +137 -0
  472. package/src/tool/truncate.ts +144 -0
  473. package/src/tool/truncation-dir.ts +4 -0
  474. package/src/tool/webfetch.ts +210 -0
  475. package/src/tool/webfetch.txt +13 -0
  476. package/src/tool/websearch.ts +151 -0
  477. package/src/tool/websearch.txt +14 -0
  478. package/src/tool/write.ts +84 -0
  479. package/src/tool/write.txt +8 -0
  480. package/src/url/site.ts +118 -0
  481. package/src/util/abort.ts +35 -0
  482. package/src/util/archive.ts +17 -0
  483. package/src/util/color.ts +19 -0
  484. package/src/util/context.ts +25 -0
  485. package/src/util/data-url.ts +9 -0
  486. package/src/util/defer.ts +12 -0
  487. package/src/util/effect-http-client.ts +11 -0
  488. package/src/util/effect-zod.ts +98 -0
  489. package/src/util/error.ts +77 -0
  490. package/src/util/filesystem.ts +245 -0
  491. package/src/util/flock.ts +333 -0
  492. package/src/util/fn.ts +21 -0
  493. package/src/util/format.ts +20 -0
  494. package/src/util/glob.ts +34 -0
  495. package/src/util/hash.ts +7 -0
  496. package/src/util/iife.ts +3 -0
  497. package/src/util/keybind.ts +103 -0
  498. package/src/util/lazy.ts +23 -0
  499. package/src/util/locale.ts +81 -0
  500. package/src/util/lock.ts +98 -0
  501. package/src/util/log.ts +182 -0
  502. package/src/util/network.ts +9 -0
  503. package/src/util/process.ts +176 -0
  504. package/src/util/queue.ts +32 -0
  505. package/src/util/record.ts +3 -0
  506. package/src/util/rpc.ts +66 -0
  507. package/src/util/schema.ts +53 -0
  508. package/src/util/scrap.ts +10 -0
  509. package/src/util/signal.ts +12 -0
  510. package/src/util/timeout.ts +14 -0
  511. package/src/util/token.ts +7 -0
  512. package/src/util/update-schema.ts +13 -0
  513. package/src/util/which.ts +14 -0
  514. package/src/util/wildcard.ts +59 -0
  515. package/src/worktree/index.ts +612 -0
  516. package/sst-env.d.ts +10 -0
  517. package/test/AGENTS.md +81 -0
  518. package/test/account/repo.test.ts +352 -0
  519. package/test/account/service.test.ts +456 -0
  520. package/test/acp/agent-interface.test.ts +51 -0
  521. package/test/acp/event-subscription.test.ts +685 -0
  522. package/test/agent/agent.test.ts +717 -0
  523. package/test/auth/auth.test.ts +58 -0
  524. package/test/bus/bus-effect.test.ts +164 -0
  525. package/test/bus/bus-integration.test.ts +87 -0
  526. package/test/bus/bus.test.ts +219 -0
  527. package/test/cli/account.test.ts +26 -0
  528. package/test/cli/cmd/tui/prompt-part.test.ts +47 -0
  529. package/test/cli/commands.test.ts +49 -0
  530. package/test/cli/error.test.ts +18 -0
  531. package/test/cli/github-action.test.ts +198 -0
  532. package/test/cli/github-remote.test.ts +80 -0
  533. package/test/cli/import.test.ts +54 -0
  534. package/test/cli/llm-ready.test.ts +49 -0
  535. package/test/cli/plugin-auth-picker.test.ts +120 -0
  536. package/test/cli/tui/keybind-plugin.test.ts +90 -0
  537. package/test/cli/tui/plugin-add.test.ts +107 -0
  538. package/test/cli/tui/plugin-install.test.ts +89 -0
  539. package/test/cli/tui/plugin-lifecycle.test.ts +225 -0
  540. package/test/cli/tui/plugin-loader-entrypoint.test.ts +492 -0
  541. package/test/cli/tui/plugin-loader-pure.test.ts +72 -0
  542. package/test/cli/tui/plugin-loader.test.ts +752 -0
  543. package/test/cli/tui/plugin-toggle.test.ts +159 -0
  544. package/test/cli/tui/slot-replace.test.tsx +47 -0
  545. package/test/cli/tui/theme-store.test.ts +51 -0
  546. package/test/cli/tui/thread.test.ts +128 -0
  547. package/test/cli/tui/transcript.test.ts +426 -0
  548. package/test/config/agent-color.test.ts +71 -0
  549. package/test/config/config.test.ts +2364 -0
  550. package/test/config/fixtures/empty-frontmatter.md +4 -0
  551. package/test/config/fixtures/frontmatter.md +28 -0
  552. package/test/config/fixtures/markdown-header.md +11 -0
  553. package/test/config/fixtures/no-frontmatter.md +1 -0
  554. package/test/config/fixtures/weird-model-id.md +13 -0
  555. package/test/config/markdown.test.ts +228 -0
  556. package/test/config/tui.test.ts +800 -0
  557. package/test/control-plane/sse.test.ts +56 -0
  558. package/test/effect/cross-spawn-spawner.test.ts +412 -0
  559. package/test/effect/instance-state.test.ts +482 -0
  560. package/test/effect/run-service.test.ts +46 -0
  561. package/test/effect/runner.test.ts +523 -0
  562. package/test/fake/provider.ts +81 -0
  563. package/test/file/fsmonitor.test.ts +62 -0
  564. package/test/file/ignore.test.ts +10 -0
  565. package/test/file/index.test.ts +946 -0
  566. package/test/file/path-traversal.test.ts +198 -0
  567. package/test/file/ripgrep.test.ts +54 -0
  568. package/test/file/time.test.ts +445 -0
  569. package/test/file/watcher.test.ts +247 -0
  570. package/test/filesystem/filesystem.test.ts +319 -0
  571. package/test/fixture/db.ts +11 -0
  572. package/test/fixture/fixture.test.ts +26 -0
  573. package/test/fixture/fixture.ts +174 -0
  574. package/test/fixture/flock-worker.ts +72 -0
  575. package/test/fixture/lsp/fake-lsp-server.js +77 -0
  576. package/test/fixture/plug-worker.ts +93 -0
  577. package/test/fixture/plugin-meta-worker.ts +26 -0
  578. package/test/fixture/skills/agents-sdk/SKILL.md +152 -0
  579. package/test/fixture/skills/agents-sdk/references/callable.md +92 -0
  580. package/test/fixture/skills/cloudflare/SKILL.md +211 -0
  581. package/test/fixture/skills/index.json +6 -0
  582. package/test/fixture/tui-plugin.ts +328 -0
  583. package/test/fixture/tui-runtime.ts +27 -0
  584. package/test/format/format.test.ts +171 -0
  585. package/test/git/git.test.ts +128 -0
  586. package/test/ide/ide.test.ts +82 -0
  587. package/test/installation/installation.test.ts +151 -0
  588. package/test/keybind.test.ts +421 -0
  589. package/test/lib/effect.ts +53 -0
  590. package/test/lib/filesystem.ts +10 -0
  591. package/test/lib/llm-server.ts +795 -0
  592. package/test/lsp/client.test.ts +95 -0
  593. package/test/lsp/index.test.ts +138 -0
  594. package/test/lsp/launch.test.ts +22 -0
  595. package/test/lsp/lifecycle.test.ts +147 -0
  596. package/test/mcp/headers.test.ts +153 -0
  597. package/test/mcp/lifecycle.test.ts +750 -0
  598. package/test/mcp/oauth-auto-connect.test.ts +199 -0
  599. package/test/mcp/oauth-browser.test.ts +249 -0
  600. package/test/memory/abort-leak.test.ts +151 -0
  601. package/test/npm.test.ts +18 -0
  602. package/test/patch/patch.test.ts +348 -0
  603. package/test/permission/arity.test.ts +33 -0
  604. package/test/permission/next.test.ts +1148 -0
  605. package/test/permission-task.test.ts +323 -0
  606. package/test/plugin/auth-override.test.ts +74 -0
  607. package/test/plugin/codex.test.ts +123 -0
  608. package/test/plugin/github-copilot-models.test.ts +117 -0
  609. package/test/plugin/install-concurrency.test.ts +140 -0
  610. package/test/plugin/install.test.ts +570 -0
  611. package/test/plugin/loader-shared.test.ts +1136 -0
  612. package/test/plugin/meta.test.ts +137 -0
  613. package/test/plugin/shared.test.ts +88 -0
  614. package/test/plugin/trigger.test.ts +111 -0
  615. package/test/preload.ts +90 -0
  616. package/test/project/migrate-global.test.ts +141 -0
  617. package/test/project/project.test.ts +459 -0
  618. package/test/project/state.test.ts +115 -0
  619. package/test/project/vcs.test.ts +228 -0
  620. package/test/project/worktree-remove.test.ts +96 -0
  621. package/test/project/worktree.test.ts +173 -0
  622. package/test/provider/amazon-bedrock.test.ts +447 -0
  623. package/test/provider/copilot/convert-to-copilot-messages.test.ts +523 -0
  624. package/test/provider/copilot/copilot-chat-model.test.ts +592 -0
  625. package/test/provider/gitlab-duo.test.ts +412 -0
  626. package/test/provider/provider.test.ts +2494 -0
  627. package/test/provider/transform.test.ts +2839 -0
  628. package/test/pty/pty-output-isolation.test.ts +141 -0
  629. package/test/pty/pty-session.test.ts +92 -0
  630. package/test/pty/pty-shell.test.ts +59 -0
  631. package/test/question/question.test.ts +453 -0
  632. package/test/runtime/acp-adapter.test.ts +84 -0
  633. package/test/runtime/core-service.test.ts +16 -0
  634. package/test/runtime/session-summarize.test.ts +104 -0
  635. package/test/server/global-session-list.test.ts +89 -0
  636. package/test/server/project-init-git.test.ts +121 -0
  637. package/test/server/router.test.ts +52 -0
  638. package/test/server/session-actions.test.ts +83 -0
  639. package/test/server/session-list.test.ts +98 -0
  640. package/test/server/session-messages.test.ts +159 -0
  641. package/test/server/session-select.test.ts +84 -0
  642. package/test/session/compaction.test.ts +1239 -0
  643. package/test/session/instruction.test.ts +286 -0
  644. package/test/session/llm.test.ts +1093 -0
  645. package/test/session/message-v2.test.ts +957 -0
  646. package/test/session/messages-pagination.test.ts +885 -0
  647. package/test/session/processor-effect.test.ts +741 -0
  648. package/test/session/prompt-effect.test.ts +1339 -0
  649. package/test/session/prompt.test.ts +533 -0
  650. package/test/session/retry.test.ts +251 -0
  651. package/test/session/revert-compact.test.ts +621 -0
  652. package/test/session/session.test.ts +142 -0
  653. package/test/session/snapshot-tool-race.test.ts +242 -0
  654. package/test/session/structured-output-integration.test.ts +233 -0
  655. package/test/session/structured-output.test.ts +391 -0
  656. package/test/session/system.test.ts +59 -0
  657. package/test/share/share-next.test.ts +332 -0
  658. package/test/shell/shell.test.ts +73 -0
  659. package/test/skill/discovery.test.ts +116 -0
  660. package/test/skill/skill.test.ts +428 -0
  661. package/test/snapshot/snapshot.test.ts +1397 -0
  662. package/test/storage/db.test.ts +14 -0
  663. package/test/storage/json-migration.test.ts +832 -0
  664. package/test/storage/storage.test.ts +295 -0
  665. package/test/sync/index.test.ts +191 -0
  666. package/test/tool/apply_patch.test.ts +565 -0
  667. package/test/tool/bash.test.ts +1099 -0
  668. package/test/tool/edit.test.ts +681 -0
  669. package/test/tool/external-directory.test.ts +198 -0
  670. package/test/tool/fixtures/large-image.png +0 -0
  671. package/test/tool/fixtures/models-api.json +65179 -0
  672. package/test/tool/grep.test.ts +111 -0
  673. package/test/tool/question.test.ts +126 -0
  674. package/test/tool/read.test.ts +468 -0
  675. package/test/tool/registry.test.ts +157 -0
  676. package/test/tool/skill.test.ts +170 -0
  677. package/test/tool/task.test.ts +412 -0
  678. package/test/tool/tool-define.test.ts +49 -0
  679. package/test/tool/truncation.test.ts +161 -0
  680. package/test/tool/webfetch.test.ts +96 -0
  681. package/test/tool/write.test.ts +353 -0
  682. package/test/util/data-url.test.ts +14 -0
  683. package/test/util/effect-zod.test.ts +61 -0
  684. package/test/util/error.test.ts +38 -0
  685. package/test/util/filesystem.test.ts +656 -0
  686. package/test/util/flock.test.ts +383 -0
  687. package/test/util/format.test.ts +59 -0
  688. package/test/util/glob.test.ts +164 -0
  689. package/test/util/iife.test.ts +36 -0
  690. package/test/util/lazy.test.ts +50 -0
  691. package/test/util/lock.test.ts +72 -0
  692. package/test/util/module.test.ts +59 -0
  693. package/test/util/process.test.ts +128 -0
  694. package/test/util/timeout.test.ts +21 -0
  695. package/test/util/which.test.ts +100 -0
  696. package/test/util/wildcard.test.ts +90 -0
  697. package/tsconfig.json +23 -0
@@ -0,0 +1,44 @@
1
+ You are a title generator. You output ONLY a thread title. Nothing else.
2
+
3
+ <task>
4
+ Generate a brief title that would help the user find this conversation later.
5
+
6
+ Follow all rules in <rules>
7
+ Use the <examples> so you know what a good title looks like.
8
+ Your output must be:
9
+ - A single line
10
+ - ≤50 characters
11
+ - No explanations
12
+ </task>
13
+
14
+ <rules>
15
+ - you MUST use the same language as the user message you are summarizing
16
+ - Title must be grammatically correct and read naturally - no word salad
17
+ - Never include tool names in the title (e.g. "read tool", "bash tool", "edit tool")
18
+ - Focus on the main topic or question the user needs to retrieve
19
+ - Vary your phrasing - avoid repetitive patterns like always starting with "Analyzing"
20
+ - When a file is mentioned, focus on WHAT the user wants to do WITH the file, not just that they shared it
21
+ - Keep exact: technical terms, numbers, filenames, HTTP codes
22
+ - Remove: the, this, my, a, an
23
+ - Never assume tech stack
24
+ - Never use tools
25
+ - NEVER respond to questions, just generate a title for the conversation
26
+ - The title should NEVER include "summarizing" or "generating" when generating a title
27
+ - DO NOT SAY YOU CANNOT GENERATE A TITLE OR COMPLAIN ABOUT THE INPUT
28
+ - Always output something meaningful, even if the input is minimal.
29
+ - If the user message is short or conversational (e.g. "hello", "lol", "what's up", "hey"):
30
+ → create a title that reflects the user's tone or intent (such as Greeting, Quick check-in, Light chat, Intro message, etc.)
31
+ </rules>
32
+
33
+ <examples>
34
+ "debug 500 errors in production" → Debugging production 500 errors
35
+ "refactor user service" → Refactoring user service
36
+ "why is app.js failing" → app.js failure investigation
37
+ "implement rate limiting" → Rate limiting implementation
38
+ "how do I connect postgres to my API" → Postgres API connection
39
+ "best practices for React hooks" → React hooks best practices
40
+ "@src/auth.ts can you add refresh token support" → Auth refresh token support
41
+ "@utils/parser.ts this is broken" → Parser bug fix
42
+ "look at @config.json" → Config review
43
+ "@App.tsx add dark mode toggle" → Dark mode toggle in App
44
+ </examples>
@@ -0,0 +1,110 @@
1
+ import path from "path"
2
+ import { Effect, Layer, Record, Result, Schema, ServiceMap } from "effect"
3
+ import { makeRuntime } from "@/effect/run-service"
4
+ import { zod } from "@/util/effect-zod"
5
+ import { Global } from "../global"
6
+ import { AppFileSystem } from "../filesystem"
7
+
8
+ export const OAUTH_DUMMY_KEY = "iroder-oauth-dummy-key"
9
+
10
+ const file = path.join(Global.Path.data, "auth.json")
11
+
12
+ const fail = (message: string) => (cause: unknown) => new Auth.AuthError({ message, cause })
13
+
14
+ export namespace Auth {
15
+ export class Oauth extends Schema.Class<Oauth>("OAuth")({
16
+ type: Schema.Literal("oauth"),
17
+ refresh: Schema.String,
18
+ access: Schema.String,
19
+ expires: Schema.Number,
20
+ accountId: Schema.optional(Schema.String),
21
+ enterpriseUrl: Schema.optional(Schema.String),
22
+ }) {}
23
+
24
+ export class Api extends Schema.Class<Api>("ApiAuth")({
25
+ type: Schema.Literal("api"),
26
+ key: Schema.String,
27
+ metadata: Schema.optional(Schema.Record(Schema.String, Schema.String)),
28
+ }) {}
29
+
30
+ export class WellKnown extends Schema.Class<WellKnown>("WellKnownAuth")({
31
+ type: Schema.Literal("wellknown"),
32
+ key: Schema.String,
33
+ token: Schema.String,
34
+ }) {}
35
+
36
+ const _Info = Schema.Union([Oauth, Api, WellKnown]).annotate({ discriminator: "type", identifier: "Auth" })
37
+ export const Info = Object.assign(_Info, { zod: zod(_Info) })
38
+ export type Info = Schema.Schema.Type<typeof _Info>
39
+
40
+ export class AuthError extends Schema.TaggedErrorClass<AuthError>()("AuthError", {
41
+ message: Schema.String,
42
+ cause: Schema.optional(Schema.Defect),
43
+ }) {}
44
+
45
+ export interface Interface {
46
+ readonly get: (providerID: string) => Effect.Effect<Info | undefined, AuthError>
47
+ readonly all: () => Effect.Effect<Record<string, Info>, AuthError>
48
+ readonly set: (key: string, info: Info) => Effect.Effect<void, AuthError>
49
+ readonly remove: (key: string) => Effect.Effect<void, AuthError>
50
+ }
51
+
52
+ export class Service extends ServiceMap.Service<Service, Interface>()("@iroder/Auth") {}
53
+
54
+ export const layer = Layer.effect(
55
+ Service,
56
+ Effect.gen(function* () {
57
+ const fsys = yield* AppFileSystem.Service
58
+ const decode = Schema.decodeUnknownOption(Info)
59
+
60
+ const all = Effect.fn("Auth.all")(function* () {
61
+ const data = (yield* fsys.readJson(file).pipe(Effect.orElseSucceed(() => ({})))) as Record<string, unknown>
62
+ return Record.filterMap(data, (value) => Result.fromOption(decode(value), () => undefined))
63
+ })
64
+
65
+ const get = Effect.fn("Auth.get")(function* (providerID: string) {
66
+ return (yield* all())[providerID]
67
+ })
68
+
69
+ const set = Effect.fn("Auth.set")(function* (key: string, info: Info) {
70
+ const norm = key.replace(/\/+$/, "")
71
+ const data = yield* all()
72
+ if (norm !== key) delete data[key]
73
+ delete data[norm + "/"]
74
+ yield* fsys
75
+ .writeJson(file, { ...data, [norm]: info }, 0o600)
76
+ .pipe(Effect.mapError(fail("Failed to write auth data")))
77
+ })
78
+
79
+ const remove = Effect.fn("Auth.remove")(function* (key: string) {
80
+ const norm = key.replace(/\/+$/, "")
81
+ const data = yield* all()
82
+ delete data[key]
83
+ delete data[norm]
84
+ yield* fsys.writeJson(file, data, 0o600).pipe(Effect.mapError(fail("Failed to write auth data")))
85
+ })
86
+
87
+ return Service.of({ get, all, set, remove })
88
+ }),
89
+ )
90
+
91
+ export const defaultLayer = layer.pipe(Layer.provide(AppFileSystem.defaultLayer))
92
+
93
+ const { runPromise } = makeRuntime(Service, defaultLayer)
94
+
95
+ export async function get(providerID: string) {
96
+ return runPromise((service) => service.get(providerID))
97
+ }
98
+
99
+ export async function all(): Promise<Record<string, Info>> {
100
+ return runPromise((service) => service.all())
101
+ }
102
+
103
+ export async function set(key: string, info: Info) {
104
+ return runPromise((service) => service.set(key, info))
105
+ }
106
+
107
+ export async function remove(key: string) {
108
+ return runPromise((service) => service.remove(key))
109
+ }
110
+ }
@@ -0,0 +1,40 @@
1
+ import z from "zod"
2
+ import type { ZodType } from "zod"
3
+
4
+ export namespace BusEvent {
5
+ export type Definition = ReturnType<typeof define>
6
+
7
+ const registry = new Map<string, Definition>()
8
+
9
+ export function define<Type extends string, Properties extends ZodType>(type: Type, properties: Properties) {
10
+ const result = {
11
+ type,
12
+ properties,
13
+ }
14
+ registry.set(type, result)
15
+ return result
16
+ }
17
+
18
+ export function payloads() {
19
+ return z
20
+ .discriminatedUnion(
21
+ "type",
22
+ registry
23
+ .entries()
24
+ .map(([type, def]) => {
25
+ return z
26
+ .object({
27
+ type: z.literal(type),
28
+ properties: def.properties,
29
+ })
30
+ .meta({
31
+ ref: "Event" + "." + def.type,
32
+ })
33
+ })
34
+ .toArray() as any,
35
+ )
36
+ .meta({
37
+ ref: "Event",
38
+ })
39
+ }
40
+ }
@@ -0,0 +1,10 @@
1
+ import { EventEmitter } from "events"
2
+
3
+ export const GlobalBus = new EventEmitter<{
4
+ event: [
5
+ {
6
+ directory?: string
7
+ payload: any
8
+ },
9
+ ]
10
+ }>()
@@ -0,0 +1,185 @@
1
+ import z from "zod"
2
+ import { Effect, Exit, Layer, PubSub, Scope, ServiceMap, Stream } from "effect"
3
+ import { Log } from "../util/log"
4
+ import { Instance } from "../project/instance"
5
+ import { BusEvent } from "./bus-event"
6
+ import { GlobalBus } from "./global"
7
+ import { InstanceState } from "@/effect/instance-state"
8
+ import { makeRuntime } from "@/effect/run-service"
9
+
10
+ export namespace Bus {
11
+ const log = Log.create({ service: "bus" })
12
+
13
+ export const InstanceDisposed = BusEvent.define(
14
+ "server.instance.disposed",
15
+ z.object({
16
+ directory: z.string(),
17
+ }),
18
+ )
19
+
20
+ type Payload<D extends BusEvent.Definition = BusEvent.Definition> = {
21
+ type: D["type"]
22
+ properties: z.infer<D["properties"]>
23
+ }
24
+
25
+ type State = {
26
+ wildcard: PubSub.PubSub<Payload>
27
+ typed: Map<string, PubSub.PubSub<Payload>>
28
+ }
29
+
30
+ export interface Interface {
31
+ readonly publish: <D extends BusEvent.Definition>(
32
+ def: D,
33
+ properties: z.output<D["properties"]>,
34
+ ) => Effect.Effect<void>
35
+ readonly subscribe: <D extends BusEvent.Definition>(def: D) => Stream.Stream<Payload<D>>
36
+ readonly subscribeAll: () => Stream.Stream<Payload>
37
+ readonly subscribeCallback: <D extends BusEvent.Definition>(
38
+ def: D,
39
+ callback: (event: Payload<D>) => unknown,
40
+ ) => Effect.Effect<() => void>
41
+ readonly subscribeAllCallback: (callback: (event: any) => unknown) => Effect.Effect<() => void>
42
+ }
43
+
44
+ export class Service extends ServiceMap.Service<Service, Interface>()("@iroder/Bus") {}
45
+
46
+ export const layer = Layer.effect(
47
+ Service,
48
+ Effect.gen(function* () {
49
+ const state = yield* InstanceState.make<State>(
50
+ Effect.fn("Bus.state")(function* (ctx) {
51
+ const wildcard = yield* PubSub.unbounded<Payload>()
52
+ const typed = new Map<string, PubSub.PubSub<Payload>>()
53
+
54
+ yield* Effect.addFinalizer(() =>
55
+ Effect.gen(function* () {
56
+ // Publish InstanceDisposed before shutting down so subscribers see it
57
+ yield* PubSub.publish(wildcard, {
58
+ type: InstanceDisposed.type,
59
+ properties: { directory: ctx.directory },
60
+ })
61
+ yield* PubSub.shutdown(wildcard)
62
+ for (const ps of typed.values()) {
63
+ yield* PubSub.shutdown(ps)
64
+ }
65
+ }),
66
+ )
67
+
68
+ return { wildcard, typed }
69
+ }),
70
+ )
71
+
72
+ function getOrCreate<D extends BusEvent.Definition>(state: State, def: D) {
73
+ return Effect.gen(function* () {
74
+ let ps = state.typed.get(def.type)
75
+ if (!ps) {
76
+ ps = yield* PubSub.unbounded<Payload>()
77
+ state.typed.set(def.type, ps)
78
+ }
79
+ return ps as unknown as PubSub.PubSub<Payload<D>>
80
+ })
81
+ }
82
+
83
+ function publish<D extends BusEvent.Definition>(def: D, properties: z.output<D["properties"]>) {
84
+ return Effect.gen(function* () {
85
+ const s = yield* InstanceState.get(state)
86
+ const payload: Payload = { type: def.type, properties }
87
+ log.info("publishing", { type: def.type })
88
+
89
+ const ps = s.typed.get(def.type)
90
+ if (ps) yield* PubSub.publish(ps, payload)
91
+ yield* PubSub.publish(s.wildcard, payload)
92
+
93
+ const dir = yield* InstanceState.directory
94
+ GlobalBus.emit("event", {
95
+ directory: dir,
96
+ payload,
97
+ })
98
+ })
99
+ }
100
+
101
+ function subscribe<D extends BusEvent.Definition>(def: D): Stream.Stream<Payload<D>> {
102
+ log.info("subscribing", { type: def.type })
103
+ return Stream.unwrap(
104
+ Effect.gen(function* () {
105
+ const s = yield* InstanceState.get(state)
106
+ const ps = yield* getOrCreate(s, def)
107
+ return Stream.fromPubSub(ps)
108
+ }),
109
+ ).pipe(Stream.ensuring(Effect.sync(() => log.info("unsubscribing", { type: def.type }))))
110
+ }
111
+
112
+ function subscribeAll(): Stream.Stream<Payload> {
113
+ log.info("subscribing", { type: "*" })
114
+ return Stream.unwrap(
115
+ Effect.gen(function* () {
116
+ const s = yield* InstanceState.get(state)
117
+ return Stream.fromPubSub(s.wildcard)
118
+ }),
119
+ ).pipe(Stream.ensuring(Effect.sync(() => log.info("unsubscribing", { type: "*" }))))
120
+ }
121
+
122
+ function on<T>(pubsub: PubSub.PubSub<T>, type: string, callback: (event: T) => unknown) {
123
+ return Effect.gen(function* () {
124
+ log.info("subscribing", { type })
125
+ const scope = yield* Scope.make()
126
+ const subscription = yield* Scope.provide(scope)(PubSub.subscribe(pubsub))
127
+
128
+ yield* Scope.provide(scope)(
129
+ Stream.fromSubscription(subscription).pipe(
130
+ Stream.runForEach((msg) =>
131
+ Effect.tryPromise({
132
+ try: () => Promise.resolve().then(() => callback(msg)),
133
+ catch: (cause) => {
134
+ log.error("subscriber failed", { type, cause })
135
+ },
136
+ }).pipe(Effect.ignore),
137
+ ),
138
+ Effect.forkScoped,
139
+ ),
140
+ )
141
+
142
+ return () => {
143
+ log.info("unsubscribing", { type })
144
+ Effect.runFork(Scope.close(scope, Exit.void))
145
+ }
146
+ })
147
+ }
148
+
149
+ const subscribeCallback = Effect.fn("Bus.subscribeCallback")(function* <D extends BusEvent.Definition>(
150
+ def: D,
151
+ callback: (event: Payload<D>) => unknown,
152
+ ) {
153
+ const s = yield* InstanceState.get(state)
154
+ const ps = yield* getOrCreate(s, def)
155
+ return yield* on(ps, def.type, callback)
156
+ })
157
+
158
+ const subscribeAllCallback = Effect.fn("Bus.subscribeAllCallback")(function* (callback: (event: any) => unknown) {
159
+ const s = yield* InstanceState.get(state)
160
+ return yield* on(s.wildcard, "*", callback)
161
+ })
162
+
163
+ return Service.of({ publish, subscribe, subscribeAll, subscribeCallback, subscribeAllCallback })
164
+ }),
165
+ )
166
+
167
+ const { runPromise, runSync } = makeRuntime(Service, layer)
168
+
169
+ // runSync is safe here because the subscribe chain (InstanceState.get, PubSub.subscribe,
170
+ // Scope.make, Effect.forkScoped) is entirely synchronous. If any step becomes async, this will throw.
171
+ export async function publish<D extends BusEvent.Definition>(def: D, properties: z.output<D["properties"]>) {
172
+ return runPromise((svc) => svc.publish(def, properties))
173
+ }
174
+
175
+ export function subscribe<D extends BusEvent.Definition>(
176
+ def: D,
177
+ callback: (event: { type: D["type"]; properties: z.infer<D["properties"]> }) => unknown,
178
+ ) {
179
+ return runSync((svc) => svc.subscribeCallback(def, callback))
180
+ }
181
+
182
+ export function subscribeAll(callback: (event: any) => unknown) {
183
+ return runSync((svc) => svc.subscribeAllCallback(callback))
184
+ }
185
+ }
@@ -0,0 +1,17 @@
1
+ import { InstanceBootstrap } from "../project/bootstrap"
2
+ import { Instance } from "../project/instance"
3
+
4
+ export async function bootstrap<T>(directory: string, cb: () => Promise<T>) {
5
+ return Instance.provide({
6
+ directory,
7
+ init: InstanceBootstrap,
8
+ fn: async () => {
9
+ try {
10
+ const result = await cb()
11
+ return result
12
+ } finally {
13
+ await Instance.dispose()
14
+ }
15
+ },
16
+ })
17
+ }
@@ -0,0 +1,257 @@
1
+ import { cmd } from "./cmd"
2
+ import { Duration, Effect, Match, Option } from "effect"
3
+ import { UI } from "../ui"
4
+ import { AccountID, Account, OrgID, PollExpired, type PollResult } from "@/account"
5
+ import { type AccountError } from "@/account/schema"
6
+ import * as Prompt from "../effect/prompt"
7
+ import open from "open"
8
+
9
+ const openBrowser = (url: string) => Effect.promise(() => open(url).catch(() => undefined))
10
+
11
+ const println = (msg: string) => Effect.sync(() => UI.println(msg))
12
+
13
+ const dim = (value: string) => UI.Style.TEXT_DIM + value + UI.Style.TEXT_NORMAL
14
+
15
+ const activeSuffix = (isActive: boolean) => (isActive ? dim(" (active)") : "")
16
+
17
+ export const formatAccountLabel = (account: { email: string; url: string }, isActive: boolean) =>
18
+ `${account.email} ${dim(account.url)}${activeSuffix(isActive)}`
19
+
20
+ const formatOrgChoiceLabel = (account: { email: string }, org: { name: string }, isActive: boolean) =>
21
+ `${org.name} (${account.email})${activeSuffix(isActive)}`
22
+
23
+ export const formatOrgLine = (
24
+ account: { email: string; url: string },
25
+ org: { id: string; name: string },
26
+ isActive: boolean,
27
+ ) => {
28
+ const dot = isActive ? UI.Style.TEXT_SUCCESS + "●" + UI.Style.TEXT_NORMAL : " "
29
+ const name = isActive ? UI.Style.TEXT_HIGHLIGHT_BOLD + org.name + UI.Style.TEXT_NORMAL : org.name
30
+ return ` ${dot} ${name} ${dim(account.email)} ${dim(account.url)} ${dim(org.id)}`
31
+ }
32
+
33
+ const isActiveOrgChoice = (
34
+ active: Option.Option<{ id: AccountID; active_org_id: OrgID | null }>,
35
+ choice: { accountID: AccountID; orgID: OrgID },
36
+ ) => Option.isSome(active) && active.value.id === choice.accountID && active.value.active_org_id === choice.orgID
37
+
38
+ const loginEffect = Effect.fn("login")(function* (url: string) {
39
+ const service = yield* Account.Service
40
+
41
+ yield* Prompt.intro("Log in")
42
+ const login = yield* service.login(url)
43
+
44
+ yield* Prompt.log.info("Go to: " + login.url)
45
+ yield* Prompt.log.info("Enter code: " + login.user)
46
+ yield* openBrowser(login.url)
47
+
48
+ const s = Prompt.spinner()
49
+ yield* s.start("Waiting for authorization...")
50
+
51
+ const poll = (wait: Duration.Duration): Effect.Effect<PollResult, AccountError> =>
52
+ Effect.gen(function* () {
53
+ yield* Effect.sleep(wait)
54
+ const result = yield* service.poll(login)
55
+ if (result._tag === "PollPending") return yield* poll(wait)
56
+ if (result._tag === "PollSlow") return yield* poll(Duration.sum(wait, Duration.seconds(5)))
57
+ return result
58
+ })
59
+
60
+ const result = yield* poll(login.interval).pipe(
61
+ Effect.timeout(login.expiry),
62
+ Effect.catchTag("TimeoutError", () => Effect.succeed(new PollExpired())),
63
+ )
64
+
65
+ yield* Match.valueTags(result, {
66
+ PollSuccess: (r) =>
67
+ Effect.gen(function* () {
68
+ yield* s.stop("Logged in as " + r.email)
69
+ yield* Prompt.outro("Done")
70
+ }),
71
+ PollExpired: () => s.stop("Device code expired", 1),
72
+ PollDenied: () => s.stop("Authorization denied", 1),
73
+ PollError: (r) => s.stop("Error: " + String(r.cause), 1),
74
+ PollPending: () => s.stop("Unexpected state", 1),
75
+ PollSlow: () => s.stop("Unexpected state", 1),
76
+ })
77
+ })
78
+
79
+ const logoutEffect = Effect.fn("logout")(function* (email?: string) {
80
+ const service = yield* Account.Service
81
+ const accounts = yield* service.list()
82
+ if (accounts.length === 0) return yield* println("Not logged in")
83
+
84
+ if (email) {
85
+ const match = accounts.find((a) => a.email === email)
86
+ if (!match) return yield* println("Account not found: " + email)
87
+ yield* service.remove(match.id)
88
+ yield* Prompt.outro("Logged out from " + email)
89
+ return
90
+ }
91
+
92
+ const active = yield* service.active()
93
+ const activeID = Option.map(active, (a) => a.id)
94
+
95
+ yield* Prompt.intro("Log out")
96
+
97
+ const opts = accounts.map((a) => {
98
+ const isActive = Option.isSome(activeID) && activeID.value === a.id
99
+ return {
100
+ value: a,
101
+ label: formatAccountLabel(a, isActive),
102
+ }
103
+ })
104
+
105
+ const selected = yield* Prompt.select({ message: "Select account to log out", options: opts })
106
+ if (Option.isNone(selected)) return
107
+
108
+ yield* service.remove(selected.value.id)
109
+ yield* Prompt.outro("Logged out from " + selected.value.email)
110
+ })
111
+
112
+ interface OrgChoice {
113
+ orgID: OrgID
114
+ accountID: AccountID
115
+ label: string
116
+ }
117
+
118
+ const switchEffect = Effect.fn("switch")(function* () {
119
+ const service = yield* Account.Service
120
+
121
+ const groups = yield* service.orgsByAccount()
122
+ if (groups.length === 0) return yield* println("Not logged in")
123
+
124
+ const active = yield* service.active()
125
+
126
+ const opts = groups.flatMap((group) =>
127
+ group.orgs.map((org) => {
128
+ const isActive = isActiveOrgChoice(active, { accountID: group.account.id, orgID: org.id })
129
+ return {
130
+ value: { orgID: org.id, accountID: group.account.id, label: org.name },
131
+ label: formatOrgChoiceLabel(group.account, org, isActive),
132
+ }
133
+ }),
134
+ )
135
+ if (opts.length === 0) return yield* println("No orgs found")
136
+
137
+ yield* Prompt.intro("Switch org")
138
+
139
+ const selected = yield* Prompt.select<OrgChoice>({ message: "Select org", options: opts })
140
+ if (Option.isNone(selected)) return
141
+
142
+ const choice = selected.value
143
+ yield* service.use(choice.accountID, Option.some(choice.orgID))
144
+ yield* Prompt.outro("Switched to " + choice.label)
145
+ })
146
+
147
+ const orgsEffect = Effect.fn("orgs")(function* () {
148
+ const service = yield* Account.Service
149
+
150
+ const groups = yield* service.orgsByAccount()
151
+ if (groups.length === 0) return yield* println("No accounts found")
152
+ if (!groups.some((group) => group.orgs.length > 0)) return yield* println("No orgs found")
153
+
154
+ const active = yield* service.active()
155
+
156
+ for (const group of groups) {
157
+ for (const org of group.orgs) {
158
+ const isActive = isActiveOrgChoice(active, { accountID: group.account.id, orgID: org.id })
159
+ yield* println(formatOrgLine(group.account, org, isActive))
160
+ }
161
+ }
162
+ })
163
+
164
+ const openEffect = Effect.fn("open")(function* () {
165
+ const service = yield* Account.Service
166
+ const active = yield* service.active()
167
+ if (Option.isNone(active)) return yield* println("No active account")
168
+
169
+ const url = active.value.url
170
+ yield* openBrowser(url)
171
+ yield* Prompt.outro("Opened " + url)
172
+ })
173
+
174
+ export const LoginCommand = cmd({
175
+ command: "login <url>",
176
+ describe: false,
177
+ builder: (yargs) =>
178
+ yargs.positional("url", {
179
+ describe: "server URL",
180
+ type: "string",
181
+ demandOption: true,
182
+ }),
183
+ async handler(args) {
184
+ UI.empty()
185
+ await Account.runPromise((_svc) => loginEffect(args.url))
186
+ },
187
+ })
188
+
189
+ export const LogoutCommand = cmd({
190
+ command: "logout [email]",
191
+ describe: false,
192
+ builder: (yargs) =>
193
+ yargs.positional("email", {
194
+ describe: "account email to log out from",
195
+ type: "string",
196
+ }),
197
+ async handler(args) {
198
+ UI.empty()
199
+ await Account.runPromise((_svc) => logoutEffect(args.email))
200
+ },
201
+ })
202
+
203
+ export const SwitchCommand = cmd({
204
+ command: "switch",
205
+ describe: false,
206
+ async handler() {
207
+ UI.empty()
208
+ await Account.runPromise((_svc) => switchEffect())
209
+ },
210
+ })
211
+
212
+ export const OrgsCommand = cmd({
213
+ command: "orgs",
214
+ describe: false,
215
+ async handler() {
216
+ UI.empty()
217
+ await Account.runPromise((_svc) => orgsEffect())
218
+ },
219
+ })
220
+
221
+ export const OpenCommand = cmd({
222
+ command: "open",
223
+ describe: false,
224
+ async handler() {
225
+ UI.empty()
226
+ await Account.runPromise((_svc) => openEffect())
227
+ },
228
+ })
229
+
230
+ export const ConsoleCommand = cmd({
231
+ command: "console",
232
+ describe: false,
233
+ builder: (yargs) =>
234
+ yargs
235
+ .command({
236
+ ...LoginCommand,
237
+ describe: "log in to console",
238
+ })
239
+ .command({
240
+ ...LogoutCommand,
241
+ describe: "log out from console",
242
+ })
243
+ .command({
244
+ ...SwitchCommand,
245
+ describe: "switch active org",
246
+ })
247
+ .command({
248
+ ...OrgsCommand,
249
+ describe: "list orgs",
250
+ })
251
+ .command({
252
+ ...OpenCommand,
253
+ describe: "open active console account",
254
+ })
255
+ .demandCommand(),
256
+ async handler() {},
257
+ })