nikcli 0.0.6

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 (602) hide show
  1. package/.turbo/turbo-typecheck.log +1 -0
  2. package/AGENTS.md +27 -0
  3. package/Dockerfile +18 -0
  4. package/README.md +15 -0
  5. package/bin/nikcli +84 -0
  6. package/config.json +13 -0
  7. package/docs/tailscale-mobile/01-tailscale-setup.md +94 -0
  8. package/docs/tailscale-mobile/02-host-setup.md +115 -0
  9. package/docs/tailscale-mobile/03-phone-and-serve.md +134 -0
  10. package/docs/tailscale-mobile/README.md +59 -0
  11. package/examples/README.md +54 -0
  12. package/package.json +147 -0
  13. package/parsers-config.ts +253 -0
  14. package/script/build.ts +179 -0
  15. package/script/postinstall.mjs +125 -0
  16. package/script/publish-registries.ts +187 -0
  17. package/script/publish.ts +100 -0
  18. package/script/schema.ts +47 -0
  19. package/script/seed-e2e.ts +50 -0
  20. package/sequential-prancing-forest.md +373 -0
  21. package/src/acp/README.md +164 -0
  22. package/src/acp/agent.ts +1303 -0
  23. package/src/acp/session.ts +105 -0
  24. package/src/acp/types.ts +22 -0
  25. package/src/agent/agent.ts +528 -0
  26. package/src/agent/generate.txt +32 -0
  27. package/src/agent/prompt/compaction.txt +14 -0
  28. package/src/agent/prompt/explore.txt +18 -0
  29. package/src/agent/prompt/summary.txt +11 -0
  30. package/src/agent/prompt/title.txt +44 -0
  31. package/src/auth/index.ts +73 -0
  32. package/src/bun/index.ts +119 -0
  33. package/src/bun/registry.ts +54 -0
  34. package/src/bus/bus-event.ts +43 -0
  35. package/src/bus/global.ts +10 -0
  36. package/src/bus/index.ts +105 -0
  37. package/src/chatbot/handlers.ts +150 -0
  38. package/src/chatbot/index.ts +132 -0
  39. package/src/cli/bootstrap.ts +17 -0
  40. package/src/cli/cmd/acp.ts +69 -0
  41. package/src/cli/cmd/ads.ts +377 -0
  42. package/src/cli/cmd/agent.ts +259 -0
  43. package/src/cli/cmd/auth.ts +400 -0
  44. package/src/cli/cmd/chatbot.ts +420 -0
  45. package/src/cli/cmd/cmd.ts +7 -0
  46. package/src/cli/cmd/companion.ts +81 -0
  47. package/src/cli/cmd/connectors.ts +593 -0
  48. package/src/cli/cmd/debug/agent.ts +166 -0
  49. package/src/cli/cmd/debug/config.ts +16 -0
  50. package/src/cli/cmd/debug/file.ts +97 -0
  51. package/src/cli/cmd/debug/index.ts +48 -0
  52. package/src/cli/cmd/debug/lsp.ts +52 -0
  53. package/src/cli/cmd/debug/ripgrep.ts +87 -0
  54. package/src/cli/cmd/debug/scrap.ts +16 -0
  55. package/src/cli/cmd/debug/skill.ts +16 -0
  56. package/src/cli/cmd/debug/snapshot.ts +52 -0
  57. package/src/cli/cmd/export.ts +88 -0
  58. package/src/cli/cmd/generate.ts +38 -0
  59. package/src/cli/cmd/github.ts +412 -0
  60. package/src/cli/cmd/image-model.ts +128 -0
  61. package/src/cli/cmd/import.ts +201 -0
  62. package/src/cli/cmd/lovable.ts +128 -0
  63. package/src/cli/cmd/mcp.ts +738 -0
  64. package/src/cli/cmd/mobile.ts +223 -0
  65. package/src/cli/cmd/models.ts +77 -0
  66. package/src/cli/cmd/plug.ts +231 -0
  67. package/src/cli/cmd/pr.ts +104 -0
  68. package/src/cli/cmd/rag-model.ts +167 -0
  69. package/src/cli/cmd/remote.ts +416 -0
  70. package/src/cli/cmd/run.ts +589 -0
  71. package/src/cli/cmd/serve.ts +51 -0
  72. package/src/cli/cmd/session.ts +133 -0
  73. package/src/cli/cmd/speak-model.ts +204 -0
  74. package/src/cli/cmd/stats.ts +402 -0
  75. package/src/cli/cmd/tui/app.tsx +841 -0
  76. package/src/cli/cmd/tui/attach.ts +31 -0
  77. package/src/cli/cmd/tui/component/border.tsx +75 -0
  78. package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
  79. package/src/cli/cmd/tui/component/dialog-command.tsx +172 -0
  80. package/src/cli/cmd/tui/component/dialog-config.tsx +291 -0
  81. package/src/cli/cmd/tui/component/dialog-connectors.tsx +440 -0
  82. package/src/cli/cmd/tui/component/dialog-image-model.tsx +97 -0
  83. package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
  84. package/src/cli/cmd/tui/component/dialog-model.tsx +234 -0
  85. package/src/cli/cmd/tui/component/dialog-provider.tsx +260 -0
  86. package/src/cli/cmd/tui/component/dialog-rag-model.tsx +217 -0
  87. package/src/cli/cmd/tui/component/dialog-remote.tsx +489 -0
  88. package/src/cli/cmd/tui/component/dialog-session-list.tsx +170 -0
  89. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
  90. package/src/cli/cmd/tui/component/dialog-settings/index.tsx +59 -0
  91. package/src/cli/cmd/tui/component/dialog-settings/prompt.tsx +40 -0
  92. package/src/cli/cmd/tui/component/dialog-settings/sidebar.tsx +39 -0
  93. package/src/cli/cmd/tui/component/dialog-settings/spinner.tsx +62 -0
  94. package/src/cli/cmd/tui/component/dialog-settings/ui.tsx +58 -0
  95. package/src/cli/cmd/tui/component/dialog-skills.tsx +117 -0
  96. package/src/cli/cmd/tui/component/dialog-speak-model.tsx +304 -0
  97. package/src/cli/cmd/tui/component/dialog-stash.tsx +87 -0
  98. package/src/cli/cmd/tui/component/dialog-status.tsx +165 -0
  99. package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
  100. package/src/cli/cmd/tui/component/dialog-theme-create.tsx +717 -0
  101. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +52 -0
  102. package/src/cli/cmd/tui/component/dialog-workspace-list.tsx +350 -0
  103. package/src/cli/cmd/tui/component/error-component.tsx +91 -0
  104. package/src/cli/cmd/tui/component/logo.tsx +103 -0
  105. package/src/cli/cmd/tui/component/plugin-route-missing.tsx +14 -0
  106. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +669 -0
  107. package/src/cli/cmd/tui/component/prompt/frecency.tsx +89 -0
  108. package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
  109. package/src/cli/cmd/tui/component/prompt/index.tsx +2165 -0
  110. package/src/cli/cmd/tui/component/prompt/stash.tsx +63 -0
  111. package/src/cli/cmd/tui/component/spinner.tsx +24 -0
  112. package/src/cli/cmd/tui/component/startup-loading.tsx +63 -0
  113. package/src/cli/cmd/tui/component/table/markdown-table.tsx +267 -0
  114. package/src/cli/cmd/tui/component/table-db/db/connections.ts +75 -0
  115. package/src/cli/cmd/tui/component/table-db/db/db-connection.ts +223 -0
  116. package/src/cli/cmd/tui/component/table-db/db/db-preview.ts +202 -0
  117. package/src/cli/cmd/tui/component/table-db/db/factory.ts +77 -0
  118. package/src/cli/cmd/tui/component/table-db/db/index.ts +9 -0
  119. package/src/cli/cmd/tui/component/table-db/db/mysql-connection.ts +330 -0
  120. package/src/cli/cmd/tui/component/table-db/db/postgres-connection.ts +338 -0
  121. package/src/cli/cmd/tui/component/table-db/db/sqlite-connection.ts +302 -0
  122. package/src/cli/cmd/tui/component/table-db/db/types.ts +108 -0
  123. package/src/cli/cmd/tui/component/table-db/table/dbedit-hooks.ts +74 -0
  124. package/src/cli/cmd/tui/component/table-db/table/index.ts +15 -0
  125. package/src/cli/cmd/tui/component/table-db/table/table-events.ts +54 -0
  126. package/src/cli/cmd/tui/component/table-db/table/table-formatters.ts +191 -0
  127. package/src/cli/cmd/tui/component/table-db/table/table-hooks.ts +105 -0
  128. package/src/cli/cmd/tui/component/table-db/table/table-keyboard-handler.ts +255 -0
  129. package/src/cli/cmd/tui/component/table-db/table/table-layout-engine.ts +208 -0
  130. package/src/cli/cmd/tui/component/table-db/table/table-renderable.ts +486 -0
  131. package/src/cli/cmd/tui/component/table-db/table/table-selection-manager.ts +136 -0
  132. package/src/cli/cmd/tui/component/table-db/table/table-state.ts +198 -0
  133. package/src/cli/cmd/tui/component/table-db/table/types.ts +69 -0
  134. package/src/cli/cmd/tui/component/table-db/ui/db-visualizer.tsx +71 -0
  135. package/src/cli/cmd/tui/component/table-db/ui/index.ts +2 -0
  136. package/src/cli/cmd/tui/component/table-db/ui/table-renderer.ts +607 -0
  137. package/src/cli/cmd/tui/component/textarea-keybindings.ts +73 -0
  138. package/src/cli/cmd/tui/component/tips.tsx +195 -0
  139. package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
  140. package/src/cli/cmd/tui/context/args.tsx +14 -0
  141. package/src/cli/cmd/tui/context/directory.ts +13 -0
  142. package/src/cli/cmd/tui/context/exit.tsx +24 -0
  143. package/src/cli/cmd/tui/context/helper.tsx +25 -0
  144. package/src/cli/cmd/tui/context/keybind.tsx +102 -0
  145. package/src/cli/cmd/tui/context/kv.tsx +52 -0
  146. package/src/cli/cmd/tui/context/local.tsx +458 -0
  147. package/src/cli/cmd/tui/context/plugin-keybinds.ts +41 -0
  148. package/src/cli/cmd/tui/context/prompt.tsx +18 -0
  149. package/src/cli/cmd/tui/context/route.tsx +54 -0
  150. package/src/cli/cmd/tui/context/sdk.tsx +128 -0
  151. package/src/cli/cmd/tui/context/server.tsx +8 -0
  152. package/src/cli/cmd/tui/context/sync.tsx +510 -0
  153. package/src/cli/cmd/tui/context/theme/abyss.json +233 -0
  154. package/src/cli/cmd/tui/context/theme/apple.json +235 -0
  155. package/src/cli/cmd/tui/context/theme/arctic.json +232 -0
  156. package/src/cli/cmd/tui/context/theme/aura.json +69 -0
  157. package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
  158. package/src/cli/cmd/tui/context/theme/ayuai.json +229 -0
  159. package/src/cli/cmd/tui/context/theme/blood.json +229 -0
  160. package/src/cli/cmd/tui/context/theme/carbonfox.json +248 -0
  161. package/src/cli/cmd/tui/context/theme/catmoe.json +235 -0
  162. package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
  163. package/src/cli/cmd/tui/context/theme/catppuccin-latte.json +233 -0
  164. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
  165. package/src/cli/cmd/tui/context/theme/catppuccin.json +259 -0
  166. package/src/cli/cmd/tui/context/theme/charcoal.json +230 -0
  167. package/src/cli/cmd/tui/context/theme/chromatic.json +235 -0
  168. package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
  169. package/src/cli/cmd/tui/context/theme/cosmic.json +234 -0
  170. package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
  171. package/src/cli/cmd/tui/context/theme/cyber.json +235 -0
  172. package/src/cli/cmd/tui/context/theme/dawnfox.json +229 -0
  173. package/src/cli/cmd/tui/context/theme/dimension.json +235 -0
  174. package/src/cli/cmd/tui/context/theme/dracula-official.json +222 -0
  175. package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
  176. package/src/cli/cmd/tui/context/theme/dream.json +235 -0
  177. package/src/cli/cmd/tui/context/theme/duo.json +235 -0
  178. package/src/cli/cmd/tui/context/theme/dusk.json +235 -0
  179. package/src/cli/cmd/tui/context/theme/ebony.json +232 -0
  180. package/src/cli/cmd/tui/context/theme/equilibrium.json +232 -0
  181. package/src/cli/cmd/tui/context/theme/ethereal.json +235 -0
  182. package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
  183. package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
  184. package/src/cli/cmd/tui/context/theme/fusion.json +235 -0
  185. package/src/cli/cmd/tui/context/theme/ghost.json +235 -0
  186. package/src/cli/cmd/tui/context/theme/github-dark.json +229 -0
  187. package/src/cli/cmd/tui/context/theme/github-dimmed.json +231 -0
  188. package/src/cli/cmd/tui/context/theme/github-light.json +229 -0
  189. package/src/cli/cmd/tui/context/theme/github.json +233 -0
  190. package/src/cli/cmd/tui/context/theme/glass.json +235 -0
  191. package/src/cli/cmd/tui/context/theme/gold.json +235 -0
  192. package/src/cli/cmd/tui/context/theme/gone.json +234 -0
  193. package/src/cli/cmd/tui/context/theme/greyscale.json +229 -0
  194. package/src/cli/cmd/tui/context/theme/gruvbox.json +242 -0
  195. package/src/cli/cmd/tui/context/theme/hacker.json +229 -0
  196. package/src/cli/cmd/tui/context/theme/holo.json +235 -0
  197. package/src/cli/cmd/tui/context/theme/ink.json +235 -0
  198. package/src/cli/cmd/tui/context/theme/jet.json +233 -0
  199. package/src/cli/cmd/tui/context/theme/kanagawa.json +227 -0
  200. package/src/cli/cmd/tui/context/theme/lavender.json +236 -0
  201. package/src/cli/cmd/tui/context/theme/lightph.json +235 -0
  202. package/src/cli/cmd/tui/context/theme/lucent-orng.json +237 -0
  203. package/src/cli/cmd/tui/context/theme/material-ocean.json +230 -0
  204. package/src/cli/cmd/tui/context/theme/material.json +235 -0
  205. package/src/cli/cmd/tui/context/theme/matrix.json +227 -0
  206. package/src/cli/cmd/tui/context/theme/mercury.json +245 -0
  207. package/src/cli/cmd/tui/context/theme/midnight.json +235 -0
  208. package/src/cli/cmd/tui/context/theme/modern.json +235 -0
  209. package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
  210. package/src/cli/cmd/tui/context/theme/muted.json +229 -0
  211. package/src/cli/cmd/tui/context/theme/neon.json +229 -0
  212. package/src/cli/cmd/tui/context/theme/neonfusion.json +235 -0
  213. package/src/cli/cmd/tui/context/theme/neutral.json +235 -0
  214. package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
  215. package/src/cli/cmd/tui/context/theme/nikcli.json +245 -0
  216. package/src/cli/cmd/tui/context/theme/nord.json +223 -0
  217. package/src/cli/cmd/tui/context/theme/nordic.json +235 -0
  218. package/src/cli/cmd/tui/context/theme/nova.json +235 -0
  219. package/src/cli/cmd/tui/context/theme/obsidian.json +234 -0
  220. package/src/cli/cmd/tui/context/theme/one-dark.json +231 -0
  221. package/src/cli/cmd/tui/context/theme/one-pro.json +229 -0
  222. package/src/cli/cmd/tui/context/theme/onyx.json +233 -0
  223. package/src/cli/cmd/tui/context/theme/orng.json +249 -0
  224. package/src/cli/cmd/tui/context/theme/osaka-jade.json +240 -0
  225. package/src/cli/cmd/tui/context/theme/oxocarbon.json +229 -0
  226. package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
  227. package/src/cli/cmd/tui/context/theme/poimandres.json +230 -0
  228. package/src/cli/cmd/tui/context/theme/prism.json +235 -0
  229. package/src/cli/cmd/tui/context/theme/radiant.json +235 -0
  230. package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
  231. package/src/cli/cmd/tui/context/theme/shadow.json +235 -0
  232. package/src/cli/cmd/tui/context/theme/silicon.json +235 -0
  233. package/src/cli/cmd/tui/context/theme/slate.json +233 -0
  234. package/src/cli/cmd/tui/context/theme/soft.json +235 -0
  235. package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
  236. package/src/cli/cmd/tui/context/theme/spectrum.json +235 -0
  237. package/src/cli/cmd/tui/context/theme/starlight.json +233 -0
  238. package/src/cli/cmd/tui/context/theme/sunrise.json +235 -0
  239. package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
  240. package/src/cli/cmd/tui/context/theme/tech.json +235 -0
  241. package/src/cli/cmd/tui/context/theme/tokyonight-storm.json +245 -0
  242. package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
  243. package/src/cli/cmd/tui/context/theme/vapor.json +235 -0
  244. package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
  245. package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
  246. package/src/cli/cmd/tui/context/theme/vivid.json +232 -0
  247. package/src/cli/cmd/tui/context/theme/void.json +235 -0
  248. package/src/cli/cmd/tui/context/theme/vscode.json +235 -0
  249. package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
  250. package/src/cli/cmd/tui/context/theme/zinc.json +236 -0
  251. package/src/cli/cmd/tui/context/theme.tsx +1303 -0
  252. package/src/cli/cmd/tui/event.ts +48 -0
  253. package/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx +152 -0
  254. package/src/cli/cmd/tui/feature-plugins/home/tips.tsx +50 -0
  255. package/src/cli/cmd/tui/feature-plugins/sidebar/context.tsx +63 -0
  256. package/src/cli/cmd/tui/feature-plugins/sidebar/files.tsx +62 -0
  257. package/src/cli/cmd/tui/feature-plugins/sidebar/footer.tsx +93 -0
  258. package/src/cli/cmd/tui/feature-plugins/sidebar/lsp.tsx +66 -0
  259. package/src/cli/cmd/tui/feature-plugins/sidebar/mcp.tsx +96 -0
  260. package/src/cli/cmd/tui/feature-plugins/sidebar/todo.tsx +48 -0
  261. package/src/cli/cmd/tui/feature-plugins/system/plugins.tsx +288 -0
  262. package/src/cli/cmd/tui/plugin/api.tsx +407 -0
  263. package/src/cli/cmd/tui/plugin/index.ts +3 -0
  264. package/src/cli/cmd/tui/plugin/internal.ts +25 -0
  265. package/src/cli/cmd/tui/plugin/runtime.ts +1048 -0
  266. package/src/cli/cmd/tui/plugin/slots.tsx +61 -0
  267. package/src/cli/cmd/tui/routes/home.tsx +153 -0
  268. package/src/cli/cmd/tui/routes/session/dbedit.tsx +474 -0
  269. package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +65 -0
  270. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +110 -0
  271. package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +105 -0
  272. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
  273. package/src/cli/cmd/tui/routes/session/footer.tsx +75 -0
  274. package/src/cli/cmd/tui/routes/session/header.tsx +177 -0
  275. package/src/cli/cmd/tui/routes/session/index.tsx +2280 -0
  276. package/src/cli/cmd/tui/routes/session/permission.tsx +540 -0
  277. package/src/cli/cmd/tui/routes/session/question.tsx +435 -0
  278. package/src/cli/cmd/tui/routes/session/sidebar.tsx +313 -0
  279. package/src/cli/cmd/tui/thread.ts +174 -0
  280. package/src/cli/cmd/tui/ui/dialog-alert.tsx +57 -0
  281. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +83 -0
  282. package/src/cli/cmd/tui/ui/dialog-export-options.tsx +204 -0
  283. package/src/cli/cmd/tui/ui/dialog-help.tsx +38 -0
  284. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +102 -0
  285. package/src/cli/cmd/tui/ui/dialog-select.tsx +389 -0
  286. package/src/cli/cmd/tui/ui/dialog.tsx +180 -0
  287. package/src/cli/cmd/tui/ui/link.tsx +34 -0
  288. package/src/cli/cmd/tui/ui/spinner.ts +368 -0
  289. package/src/cli/cmd/tui/ui/toast.tsx +138 -0
  290. package/src/cli/cmd/tui/util/clipboard.ts +154 -0
  291. package/src/cli/cmd/tui/util/editor.ts +32 -0
  292. package/src/cli/cmd/tui/util/signal.ts +7 -0
  293. package/src/cli/cmd/tui/util/terminal.ts +114 -0
  294. package/src/cli/cmd/tui/util/transcript.ts +98 -0
  295. package/src/cli/cmd/tui/win32.ts +110 -0
  296. package/src/cli/cmd/tui/worker.ts +156 -0
  297. package/src/cli/cmd/uninstall.ts +357 -0
  298. package/src/cli/cmd/upgrade.ts +72 -0
  299. package/src/cli/cmd/web.ts +87 -0
  300. package/src/cli/cmd/workspace-serve.ts +16 -0
  301. package/src/cli/error.ts +57 -0
  302. package/src/cli/network.ts +55 -0
  303. package/src/cli/remote/index.ts +36 -0
  304. package/src/cli/remote/notifications.ts +104 -0
  305. package/src/cli/remote/qr-renderer.ts +86 -0
  306. package/src/cli/remote/remote-service.ts +757 -0
  307. package/src/cli/remote/session-manager.ts +284 -0
  308. package/src/cli/remote/subagent-hooks.ts +151 -0
  309. package/src/cli/remote/types.ts +121 -0
  310. package/src/cli/ui.ts +96 -0
  311. package/src/cli/upgrade.ts +25 -0
  312. package/src/command/index.ts +174 -0
  313. package/src/command/template/initialize.txt +10 -0
  314. package/src/command/template/review.txt +99 -0
  315. package/src/config/config.ts +1760 -0
  316. package/src/config/markdown.ts +88 -0
  317. package/src/config/migrate-tui-config.ts +155 -0
  318. package/src/config/paths.ts +174 -0
  319. package/src/config/tui-schema.ts +36 -0
  320. package/src/config/tui.ts +209 -0
  321. package/src/connectors/api/base.ts +75 -0
  322. package/src/connectors/api/figma.ts +103 -0
  323. package/src/connectors/api/github.ts +247 -0
  324. package/src/connectors/api/lovable.ts +126 -0
  325. package/src/connectors/api/slack.ts +137 -0
  326. package/src/connectors/auth.ts +68 -0
  327. package/src/connectors/cache.ts +119 -0
  328. package/src/connectors/credentials.ts +81 -0
  329. package/src/connectors/index.ts +202 -0
  330. package/src/connectors/registry.ts +358 -0
  331. package/src/docs/context.ts +120 -0
  332. package/src/docs/library.ts +189 -0
  333. package/src/env/index.ts +26 -0
  334. package/src/file/ignore.ts +83 -0
  335. package/src/file/index.ts +411 -0
  336. package/src/file/ripgrep.ts +402 -0
  337. package/src/file/time.ts +65 -0
  338. package/src/file/watcher.ts +127 -0
  339. package/src/flag/flag.ts +128 -0
  340. package/src/format/formatter.ts +356 -0
  341. package/src/format/index.ts +137 -0
  342. package/src/global/index.ts +57 -0
  343. package/src/id/id.ts +83 -0
  344. package/src/ide/index.ts +76 -0
  345. package/src/index.ts +184 -0
  346. package/src/installation/index.ts +246 -0
  347. package/src/lsp/client.ts +250 -0
  348. package/src/lsp/index.ts +483 -0
  349. package/src/lsp/language.ts +119 -0
  350. package/src/lsp/server.ts +2046 -0
  351. package/src/mcp/auth.ts +121 -0
  352. package/src/mcp/index.ts +860 -0
  353. package/src/mcp/oauth-callback.ts +198 -0
  354. package/src/mcp/oauth-provider.ts +148 -0
  355. package/src/mobile/auth.ts +97 -0
  356. package/src/mobile/github-repo.ts +185 -0
  357. package/src/patch/index.ts +631 -0
  358. package/src/permission/arity.ts +150 -0
  359. package/src/permission/dbedit.ts +236 -0
  360. package/src/permission/index.ts +210 -0
  361. package/src/permission/next.ts +287 -0
  362. package/src/plugin/codex.ts +493 -0
  363. package/src/plugin/copilot.ts +261 -0
  364. package/src/plugin/index.ts +714 -0
  365. package/src/plugin/install.ts +379 -0
  366. package/src/plugin/meta.ts +165 -0
  367. package/src/plugin/shared.ts +188 -0
  368. package/src/project/bootstrap.ts +35 -0
  369. package/src/project/instance.ts +84 -0
  370. package/src/project/project.ts +373 -0
  371. package/src/project/state.ts +66 -0
  372. package/src/project/vcs.ts +76 -0
  373. package/src/prompt/stash-store.ts +93 -0
  374. package/src/provider/auth.ts +147 -0
  375. package/src/provider/models-macro.ts +22 -0
  376. package/src/provider/models.ts +216 -0
  377. package/src/provider/provider.ts +1483 -0
  378. package/src/provider/sdk/openai-compatible/src/README.md +5 -0
  379. package/src/provider/sdk/openai-compatible/src/index.ts +2 -0
  380. package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +100 -0
  381. package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +303 -0
  382. package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +22 -0
  383. package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +18 -0
  384. package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +22 -0
  385. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +207 -0
  386. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +1732 -0
  387. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +177 -0
  388. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +1 -0
  389. package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +88 -0
  390. package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +128 -0
  391. package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +115 -0
  392. package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +65 -0
  393. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +104 -0
  394. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +103 -0
  395. package/src/provider/transform.ts +828 -0
  396. package/src/pty/index.ts +241 -0
  397. package/src/question/index.ts +171 -0
  398. package/src/rag/chunk.ts +43 -0
  399. package/src/rag/embed.ts +179 -0
  400. package/src/rag/index.ts +376 -0
  401. package/src/rag/storage.ts +76 -0
  402. package/src/scheduler/index.ts +61 -0
  403. package/src/server/error.ts +36 -0
  404. package/src/server/event.ts +7 -0
  405. package/src/server/mdns.ts +59 -0
  406. package/src/server/routes/chatbot.ts +205 -0
  407. package/src/server/routes/companion.ts +729 -0
  408. package/src/server/routes/config.ts +92 -0
  409. package/src/server/routes/connectors.ts +121 -0
  410. package/src/server/routes/dbedit.ts +76 -0
  411. package/src/server/routes/experimental.ts +210 -0
  412. package/src/server/routes/file.ts +197 -0
  413. package/src/server/routes/global.ts +135 -0
  414. package/src/server/routes/mcp.ts +225 -0
  415. package/src/server/routes/mobile.ts +2044 -0
  416. package/src/server/routes/permission.ts +68 -0
  417. package/src/server/routes/project.ts +82 -0
  418. package/src/server/routes/provider.ts +235 -0
  419. package/src/server/routes/pty.ts +169 -0
  420. package/src/server/routes/question.ts +98 -0
  421. package/src/server/routes/session.ts +968 -0
  422. package/src/server/routes/tui.ts +379 -0
  423. package/src/server/routes/workspace.ts +104 -0
  424. package/src/server/server.ts +761 -0
  425. package/src/server/ssh.ts +207 -0
  426. package/src/session/auth.ts +402 -0
  427. package/src/session/compaction.ts +253 -0
  428. package/src/session/generate.ts +38 -0
  429. package/src/session/index.ts +598 -0
  430. package/src/session/llm.ts +273 -0
  431. package/src/session/message-v2.ts +836 -0
  432. package/src/session/message.ts +189 -0
  433. package/src/session/processor.ts +408 -0
  434. package/src/session/prompt/anthropic-20250930.txt +165 -0
  435. package/src/session/prompt/anthropic.txt +105 -0
  436. package/src/session/prompt/anthropic_spoof.txt +1 -0
  437. package/src/session/prompt/beast.txt +147 -0
  438. package/src/session/prompt/build-switch.txt +5 -0
  439. package/src/session/prompt/codex_header.txt +79 -0
  440. package/src/session/prompt/copilot-gpt-5.txt +143 -0
  441. package/src/session/prompt/gemini.txt +155 -0
  442. package/src/session/prompt/max-steps.txt +16 -0
  443. package/src/session/prompt/plan-reminder-anthropic.txt +67 -0
  444. package/src/session/prompt/plan.txt +25 -0
  445. package/src/session/prompt/qwen.txt +108 -0
  446. package/src/session/prompt.ts +1942 -0
  447. package/src/session/retry.ts +90 -0
  448. package/src/session/revert.ts +120 -0
  449. package/src/session/stats.ts +404 -0
  450. package/src/session/status.ts +84 -0
  451. package/src/session/summary.ts +184 -0
  452. package/src/session/system.ts +195 -0
  453. package/src/session/toast.tsx +105 -0
  454. package/src/session/todo.ts +258 -0
  455. package/src/session/uninstall.ts +357 -0
  456. package/src/share/share-next.ts +421 -0
  457. package/src/share/share.ts +92 -0
  458. package/src/shell/shell.ts +65 -0
  459. package/src/skill/index.ts +1 -0
  460. package/src/skill/skill.ts +232 -0
  461. package/src/snapshot/index.ts +297 -0
  462. package/src/storage/storage.ts +227 -0
  463. package/src/tool/apply_patch.ts +288 -0
  464. package/src/tool/apply_patch.txt +33 -0
  465. package/src/tool/bash.ts +252 -0
  466. package/src/tool/bash.txt +115 -0
  467. package/src/tool/batch.ts +175 -0
  468. package/src/tool/batch.txt +24 -0
  469. package/src/tool/codesearch.ts +132 -0
  470. package/src/tool/codesearch.txt +12 -0
  471. package/src/tool/context_collect.ts +152 -0
  472. package/src/tool/context_collect.txt +9 -0
  473. package/src/tool/context_diagnostics.ts +81 -0
  474. package/src/tool/context_diagnostics.txt +5 -0
  475. package/src/tool/context_related.ts +117 -0
  476. package/src/tool/context_related.txt +5 -0
  477. package/src/tool/context_search.ts +108 -0
  478. package/src/tool/context_search.txt +8 -0
  479. package/src/tool/db-diff.ts +434 -0
  480. package/src/tool/db-table.txt +15 -0
  481. package/src/tool/docs_add.ts +50 -0
  482. package/src/tool/docs_add.txt +5 -0
  483. package/src/tool/docs_context.ts +56 -0
  484. package/src/tool/docs_context.txt +4 -0
  485. package/src/tool/docs_gap_report.ts +79 -0
  486. package/src/tool/docs_gap_report.txt +7 -0
  487. package/src/tool/docs_load.ts +41 -0
  488. package/src/tool/docs_load.txt +4 -0
  489. package/src/tool/docs_request.ts +129 -0
  490. package/src/tool/docs_request.txt +7 -0
  491. package/src/tool/docs_search.ts +51 -0
  492. package/src/tool/docs_search.txt +6 -0
  493. package/src/tool/docs_unload.ts +38 -0
  494. package/src/tool/docs_unload.txt +5 -0
  495. package/src/tool/edit.ts +614 -0
  496. package/src/tool/edit.txt +10 -0
  497. package/src/tool/external-directory.ts +32 -0
  498. package/src/tool/generate_image.ts +174 -0
  499. package/src/tool/generate_image.txt +12 -0
  500. package/src/tool/glob.ts +79 -0
  501. package/src/tool/glob.txt +6 -0
  502. package/src/tool/grep.ts +153 -0
  503. package/src/tool/grep.txt +8 -0
  504. package/src/tool/invalid.ts +17 -0
  505. package/src/tool/ls.ts +116 -0
  506. package/src/tool/ls.txt +1 -0
  507. package/src/tool/lsp.ts +96 -0
  508. package/src/tool/lsp.txt +19 -0
  509. package/src/tool/memory_search.ts +141 -0
  510. package/src/tool/memory_search.txt +8 -0
  511. package/src/tool/multiedit.ts +46 -0
  512. package/src/tool/multiedit.txt +41 -0
  513. package/src/tool/plan-enter.txt +14 -0
  514. package/src/tool/plan-exit.txt +13 -0
  515. package/src/tool/plan.ts +130 -0
  516. package/src/tool/question.ts +33 -0
  517. package/src/tool/question.txt +10 -0
  518. package/src/tool/rag_index.ts +77 -0
  519. package/src/tool/rag_index.txt +10 -0
  520. package/src/tool/rag_reset.ts +26 -0
  521. package/src/tool/rag_reset.txt +4 -0
  522. package/src/tool/rag_search.ts +62 -0
  523. package/src/tool/rag_search.txt +6 -0
  524. package/src/tool/rag_status.ts +45 -0
  525. package/src/tool/rag_status.txt +4 -0
  526. package/src/tool/read.ts +203 -0
  527. package/src/tool/read.txt +12 -0
  528. package/src/tool/registry.ts +214 -0
  529. package/src/tool/skill.ts +169 -0
  530. package/src/tool/skill.txt +3 -0
  531. package/src/tool/smart_docs.ts +74 -0
  532. package/src/tool/smart_docs.txt +7 -0
  533. package/src/tool/speak/elevenlabs.ts +201 -0
  534. package/src/tool/speak/openrouter.ts +240 -0
  535. package/src/tool/speak/provider.ts +83 -0
  536. package/src/tool/speak.ts +440 -0
  537. package/src/tool/task.ts +194 -0
  538. package/src/tool/task.txt +60 -0
  539. package/src/tool/todo.ts +53 -0
  540. package/src/tool/todoread.txt +14 -0
  541. package/src/tool/todowrite.txt +167 -0
  542. package/src/tool/tool.ts +87 -0
  543. package/src/tool/tree.ts +218 -0
  544. package/src/tool/tree.txt +8 -0
  545. package/src/tool/truncation.ts +106 -0
  546. package/src/tool/use-connector.ts +47 -0
  547. package/src/tool/voice.ts +188 -0
  548. package/src/tool/webfetch.ts +205 -0
  549. package/src/tool/webfetch.txt +13 -0
  550. package/src/tool/websearch.ts +150 -0
  551. package/src/tool/websearch.txt +14 -0
  552. package/src/tool/write.ts +80 -0
  553. package/src/tool/write.txt +8 -0
  554. package/src/util/archive.ts +16 -0
  555. package/src/util/color.ts +19 -0
  556. package/src/util/context.ts +25 -0
  557. package/src/util/defer.ts +12 -0
  558. package/src/util/error.ts +77 -0
  559. package/src/util/eventloop.ts +20 -0
  560. package/src/util/filesystem.ts +125 -0
  561. package/src/util/flock.ts +329 -0
  562. package/src/util/fn.ts +11 -0
  563. package/src/util/format.ts +20 -0
  564. package/src/util/hash.ts +7 -0
  565. package/src/util/iife.ts +3 -0
  566. package/src/util/keybind.ts +103 -0
  567. package/src/util/lazy.ts +18 -0
  568. package/src/util/locale.ts +81 -0
  569. package/src/util/lock.ts +98 -0
  570. package/src/util/log.ts +180 -0
  571. package/src/util/network.ts +9 -0
  572. package/src/util/process.ts +15 -0
  573. package/src/util/queue.ts +32 -0
  574. package/src/util/record.ts +3 -0
  575. package/src/util/rpc.ts +66 -0
  576. package/src/util/scrap.ts +10 -0
  577. package/src/util/signal.ts +12 -0
  578. package/src/util/timeout.ts +14 -0
  579. package/src/util/token.ts +7 -0
  580. package/src/util/wildcard.ts +56 -0
  581. package/src/workspace/adaptors/index.ts +271 -0
  582. package/src/workspace/adaptors/types.ts +14 -0
  583. package/src/workspace/adaptors/worktree.ts +31 -0
  584. package/src/workspace/config.ts +19 -0
  585. package/src/workspace/index.ts +223 -0
  586. package/src/workspace/session-proxy-middleware.ts +97 -0
  587. package/src/workspace/sse.ts +66 -0
  588. package/src/workspace/workspace-context.ts +23 -0
  589. package/src/workspace/workspace-server/routes.ts +33 -0
  590. package/src/workspace/workspace-server/server.ts +47 -0
  591. package/src/worktree/index.ts +487 -0
  592. package/sst-env.d.ts +10 -0
  593. package/test/benchmark.test.ts +121 -0
  594. package/test/build-optimizations.test.ts +124 -0
  595. package/test/id-benchmark.test.ts +132 -0
  596. package/test/optimizations.test.ts +302 -0
  597. package/test/preload.ts +1 -0
  598. package/test/solidjs-benchmark.test.ts +262 -0
  599. package/test/solidjs-optimizations.test.ts +259 -0
  600. package/test/tui-benchmark.test.ts +230 -0
  601. package/test/wildcard-benchmark.test.ts +180 -0
  602. package/tsconfig.json +26 -0
@@ -0,0 +1,8 @@
1
+ Writes a file to the local filesystem.
2
+
3
+ Usage:
4
+ - This tool will overwrite the existing file if there is one at the provided path.
5
+ - If this is an existing file, you MUST use the Read tool first to read the file's contents. This tool will fail if you did not read the file first.
6
+ - ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.
7
+ - NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.
8
+ - Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.
@@ -0,0 +1,16 @@
1
+ import { $ } from "bun"
2
+ import path from "path"
3
+
4
+ export namespace Archive {
5
+ export async function extractZip(zipPath: string, destDir: string) {
6
+ if (process.platform === "win32") {
7
+ const winZipPath = path.resolve(zipPath)
8
+ const winDestDir = path.resolve(destDir)
9
+ // $global:ProgressPreference suppresses PowerShell's blue progress bar popup
10
+ const cmd = `$global:ProgressPreference = 'SilentlyContinue'; Expand-Archive -Path '${winZipPath}' -DestinationPath '${winDestDir}' -Force`
11
+ await $`powershell -NoProfile -NonInteractive -Command ${cmd}`.quiet()
12
+ } else {
13
+ await $`unzip -o -q ${zipPath} -d ${destDir}`.quiet()
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,19 @@
1
+ export namespace Color {
2
+ export function isValidHex(hex?: string): hex is string {
3
+ if (!hex) return false
4
+ return /^#[0-9a-fA-F]{6}$/.test(hex)
5
+ }
6
+
7
+ export function hexToRgb(hex: string): { r: number; g: number; b: number } {
8
+ const r = parseInt(hex.slice(1, 3), 16)
9
+ const g = parseInt(hex.slice(3, 5), 16)
10
+ const b = parseInt(hex.slice(5, 7), 16)
11
+ return { r, g, b }
12
+ }
13
+
14
+ export function hexToAnsiBold(hex?: string): string | undefined {
15
+ if (!isValidHex(hex)) return undefined
16
+ const { r, g, b } = hexToRgb(hex)
17
+ return `\x1b[38;2;${r};${g};${b}m\x1b[1m`
18
+ }
19
+ }
@@ -0,0 +1,25 @@
1
+ import { AsyncLocalStorage } from "async_hooks"
2
+
3
+ export namespace Context {
4
+ export class NotFound extends Error {
5
+ constructor(public override readonly name: string) {
6
+ super(`No context found for ${name}`)
7
+ }
8
+ }
9
+
10
+ export function create<T>(name: string) {
11
+ const storage = new AsyncLocalStorage<T>()
12
+ return {
13
+ use() {
14
+ const result = storage.getStore()
15
+ if (!result) {
16
+ throw new NotFound(name)
17
+ }
18
+ return result
19
+ },
20
+ provide<R>(value: T, fn: () => R) {
21
+ return storage.run(value, fn)
22
+ },
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,12 @@
1
+ export function defer<T extends () => void | Promise<void>>(
2
+ fn: T,
3
+ ): T extends () => Promise<void> ? { [Symbol.asyncDispose]: () => Promise<void> } : { [Symbol.dispose]: () => void } {
4
+ return {
5
+ [Symbol.dispose]() {
6
+ fn()
7
+ },
8
+ [Symbol.asyncDispose]() {
9
+ return Promise.resolve(fn())
10
+ },
11
+ } as any
12
+ }
@@ -0,0 +1,77 @@
1
+ import { isRecord } from "./record"
2
+
3
+ export function errorFormat(error: unknown): string {
4
+ if (error instanceof Error) {
5
+ return error.stack ?? `${error.name}: ${error.message}`
6
+ }
7
+
8
+ if (typeof error === "object" && error !== null) {
9
+ try {
10
+ return JSON.stringify(error, null, 2)
11
+ } catch {
12
+ return "Unexpected error (unserializable)"
13
+ }
14
+ }
15
+
16
+ return String(error)
17
+ }
18
+
19
+ export function errorMessage(error: unknown): string {
20
+ if (error instanceof Error) {
21
+ if (error.message) return error.message
22
+ if (error.name) return error.name
23
+ }
24
+
25
+ if (isRecord(error) && typeof error.message === "string" && error.message) {
26
+ return error.message
27
+ }
28
+
29
+ const text = String(error)
30
+ if (text && text !== "[object Object]") return text
31
+
32
+ const formatted = errorFormat(error)
33
+ if (formatted && formatted !== "{}") return formatted
34
+ return "unknown error"
35
+ }
36
+
37
+ export function errorData(error: unknown) {
38
+ if (error instanceof Error) {
39
+ return {
40
+ type: error.name,
41
+ message: errorMessage(error),
42
+ stack: error.stack,
43
+ cause: error.cause === undefined ? undefined : errorFormat(error.cause),
44
+ formatted: errorFormatted(error),
45
+ }
46
+ }
47
+
48
+ if (!isRecord(error)) {
49
+ return {
50
+ type: typeof error,
51
+ message: errorMessage(error),
52
+ formatted: errorFormatted(error),
53
+ }
54
+ }
55
+
56
+ const data = Object.getOwnPropertyNames(error).reduce<Record<string, unknown>>((acc, key) => {
57
+ const value = error[key]
58
+ if (value === undefined) return acc
59
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
60
+ acc[key] = value
61
+ return acc
62
+ }
63
+ acc[key] = value instanceof Error ? value.message : String(value)
64
+ return acc
65
+ }, {})
66
+
67
+ if (typeof data.message !== "string") data.message = errorMessage(error)
68
+ if (typeof data.type !== "string") data.type = (error as any).constructor?.name
69
+ data.formatted = errorFormatted(error)
70
+ return data
71
+ }
72
+
73
+ function errorFormatted(error: unknown) {
74
+ const formatted = errorFormat(error)
75
+ if (formatted !== "{}") return formatted
76
+ return String(error)
77
+ }
@@ -0,0 +1,20 @@
1
+ import { Log } from "./log"
2
+
3
+ export namespace EventLoop {
4
+ export async function wait() {
5
+ return new Promise<void>((resolve) => {
6
+ const check = () => {
7
+ const active = [...(process as any)._getActiveHandles(), ...(process as any)._getActiveRequests()]
8
+ Log.Default.info("eventloop", {
9
+ active,
10
+ })
11
+ if ((process as any)._getActiveHandles().length === 0 && (process as any)._getActiveRequests().length === 0) {
12
+ resolve()
13
+ } else {
14
+ setImmediate(check)
15
+ }
16
+ }
17
+ check()
18
+ })
19
+ }
20
+ }
@@ -0,0 +1,125 @@
1
+ import { realpathSync, statSync } from "fs"
2
+ import { dirname, join, relative, resolve as pathResolve } from "path"
3
+ import { mkdir } from "fs/promises"
4
+
5
+ export namespace Filesystem {
6
+ export function stat(p: string): import("fs").Stats | undefined {
7
+ try {
8
+ return statSync(p)
9
+ } catch {
10
+ return undefined
11
+ }
12
+ }
13
+
14
+ export async function readJson<T>(p: string): Promise<T> {
15
+ return Bun.file(p).json() as Promise<T>
16
+ }
17
+
18
+ export async function writeJson(p: string, data: unknown): Promise<void> {
19
+ await mkdir(dirname(p), { recursive: true })
20
+ await Bun.write(p, JSON.stringify(data, null, 2))
21
+ }
22
+
23
+ export async function readText(p: string): Promise<string> {
24
+ return Bun.file(p).text()
25
+ }
26
+
27
+ export async function write(p: string, text: string | Buffer): Promise<void> {
28
+ await mkdir(dirname(p), { recursive: true })
29
+ await Bun.write(p, text)
30
+ }
31
+
32
+ export function resolve(p: string): string {
33
+ return pathResolve(p)
34
+ }
35
+
36
+
37
+ export const exists = (p: string) =>
38
+ Bun.file(p)
39
+ .stat()
40
+ .then(() => true)
41
+ .catch(() => false)
42
+
43
+ export const isDir = (p: string) =>
44
+ Bun.file(p)
45
+ .stat()
46
+ .then((s) => s.isDirectory())
47
+ .catch(() => false)
48
+ /**
49
+ * On Windows, normalize a path to its canonical casing using the filesystem.
50
+ * This is needed because Windows paths are case-insensitive but LSP servers
51
+ * may return paths with different casing than what we send them.
52
+ */
53
+ export function normalizePath(p: string): string {
54
+ if (process.platform !== "win32") return p
55
+ try {
56
+ return realpathSync.native(p)
57
+ } catch {
58
+ return p
59
+ }
60
+ }
61
+ export function overlaps(a: string, b: string) {
62
+ const relA = relative(a, b)
63
+ const relB = relative(b, a)
64
+ return !relA || !relA.startsWith("..") || !relB || !relB.startsWith("..")
65
+ }
66
+
67
+ export function contains(parent: string, child: string) {
68
+ return !relative(parent, child).startsWith("..")
69
+ }
70
+
71
+ export async function findUp(target: string, start: string, stop?: string) {
72
+ let current = start
73
+ const result = []
74
+ while (true) {
75
+ const search = join(current, target)
76
+ if (await exists(search)) result.push(search)
77
+ if (stop === current) break
78
+ const parent = dirname(current)
79
+ if (parent === current) break
80
+ current = parent
81
+ }
82
+ return result
83
+ }
84
+
85
+ export async function* up(options: { targets: string[]; start: string; stop?: string }) {
86
+ const { targets, start, stop } = options
87
+ let current = start
88
+ while (true) {
89
+ for (const target of targets) {
90
+ const search = join(current, target)
91
+ if (await exists(search)) yield search
92
+ }
93
+ if (stop === current) break
94
+ const parent = dirname(current)
95
+ if (parent === current) break
96
+ current = parent
97
+ }
98
+ }
99
+
100
+ export async function globUp(pattern: string, start: string, stop?: string) {
101
+ let current = start
102
+ const result = []
103
+ while (true) {
104
+ try {
105
+ const glob = new Bun.Glob(pattern)
106
+ for await (const match of glob.scan({
107
+ cwd: current,
108
+ absolute: true,
109
+ onlyFiles: true,
110
+ followSymlinks: true,
111
+ dot: true,
112
+ })) {
113
+ result.push(match)
114
+ }
115
+ } catch {
116
+ // Skip invalid glob patterns
117
+ }
118
+ if (stop === current) break
119
+ const parent = dirname(current)
120
+ if (parent === current) break
121
+ current = parent
122
+ }
123
+ return result
124
+ }
125
+ }
@@ -0,0 +1,329 @@
1
+ import path from "path"
2
+ import os from "os"
3
+ import { randomBytes, randomUUID } from "crypto"
4
+ import { mkdir, readFile, rm, stat, utimes, writeFile } from "fs/promises"
5
+ import { Global } from "@/global"
6
+ import { Hash } from "@/util/hash"
7
+
8
+ export namespace Flock {
9
+ const root = path.join(Global.Path.state, "locks")
10
+ // Defaults for callers that do not provide timing options.
11
+ const defaultOpts = {
12
+ staleMs: 60_000,
13
+ timeoutMs: 5 * 60_000,
14
+ baseDelayMs: 100,
15
+ maxDelayMs: 2_000,
16
+ }
17
+
18
+ export interface WaitEvent {
19
+ key: string
20
+ attempt: number
21
+ delay: number
22
+ waited: number
23
+ }
24
+
25
+ export type Wait = (input: WaitEvent) => void | Promise<void>
26
+
27
+ export interface Options {
28
+ dir?: string
29
+ signal?: AbortSignal
30
+ staleMs?: number
31
+ timeoutMs?: number
32
+ baseDelayMs?: number
33
+ maxDelayMs?: number
34
+ onWait?: Wait
35
+ }
36
+
37
+ type Opts = {
38
+ staleMs: number
39
+ timeoutMs: number
40
+ baseDelayMs: number
41
+ maxDelayMs: number
42
+ }
43
+
44
+ type Owned = {
45
+ acquired: true
46
+ startHeartbeat: (intervalMs?: number) => void
47
+ release: () => Promise<void>
48
+ }
49
+
50
+ export interface Lease {
51
+ release: () => Promise<void>
52
+ [Symbol.asyncDispose]: () => Promise<void>
53
+ }
54
+
55
+ function code(err: unknown) {
56
+ if (typeof err !== "object" || err === null || !("code" in err)) return
57
+ const value = err.code
58
+ if (typeof value !== "string") return
59
+ return value
60
+ }
61
+
62
+ function sleep(ms: number, signal?: AbortSignal) {
63
+ return new Promise<void>((resolve, reject) => {
64
+ if (signal?.aborted) {
65
+ reject(signal.reason ?? new Error("Aborted"))
66
+ return
67
+ }
68
+
69
+ let timer: NodeJS.Timeout | undefined
70
+
71
+ const done = () => {
72
+ signal?.removeEventListener("abort", abort)
73
+ resolve()
74
+ }
75
+
76
+ const abort = () => {
77
+ if (timer) {
78
+ clearTimeout(timer)
79
+ }
80
+ signal?.removeEventListener("abort", abort)
81
+ reject(signal?.reason ?? new Error("Aborted"))
82
+ }
83
+
84
+ signal?.addEventListener("abort", abort, { once: true })
85
+ timer = setTimeout(done, ms)
86
+ })
87
+ }
88
+
89
+ function jitter(ms: number) {
90
+ const j = Math.floor(ms * 0.3)
91
+ const d = Math.floor(Math.random() * (2 * j + 1)) - j
92
+ return Math.max(0, ms + d)
93
+ }
94
+
95
+ function mono() {
96
+ return performance.now()
97
+ }
98
+
99
+ function wall() {
100
+ return performance.timeOrigin + mono()
101
+ }
102
+
103
+ async function stats(file: string) {
104
+ try {
105
+ return await stat(file)
106
+ } catch (err) {
107
+ const errCode = code(err)
108
+ if (errCode === "ENOENT" || errCode === "ENOTDIR") return
109
+ throw err
110
+ }
111
+ }
112
+
113
+ async function stale(lockDir: string, heartbeatPath: string, metaPath: string, staleMs: number) {
114
+ const now = wall()
115
+ const heartbeat = await stats(heartbeatPath)
116
+ if (heartbeat) {
117
+ return now - heartbeat.mtimeMs > staleMs
118
+ }
119
+
120
+ const meta = await stats(metaPath)
121
+ if (meta) {
122
+ return now - meta.mtimeMs > staleMs
123
+ }
124
+
125
+ const dir = await stats(lockDir)
126
+ if (!dir) {
127
+ return false
128
+ }
129
+
130
+ return now - dir.mtimeMs > staleMs
131
+ }
132
+
133
+ async function tryAcquireLockDir(lockDir: string, opts: Opts): Promise<Owned | { acquired: false }> {
134
+ const token = randomUUID?.() ?? randomBytes(16).toString("hex")
135
+ const metaPath = path.join(lockDir, "meta.json")
136
+ const heartbeatPath = path.join(lockDir, "heartbeat")
137
+
138
+ try {
139
+ await mkdir(lockDir, { mode: 0o700 })
140
+ } catch (err) {
141
+ if (code(err) !== "EEXIST") {
142
+ throw err
143
+ }
144
+
145
+ if (!(await stale(lockDir, heartbeatPath, metaPath, opts.staleMs))) {
146
+ return { acquired: false }
147
+ }
148
+
149
+ const breakerPath = lockDir + ".breaker"
150
+ try {
151
+ await mkdir(breakerPath, { mode: 0o700 })
152
+ } catch (claimErr) {
153
+ const errCode = code(claimErr)
154
+ if (errCode === "EEXIST") {
155
+ const breaker = await stats(breakerPath)
156
+ if (breaker && wall() - breaker.mtimeMs > opts.staleMs) {
157
+ await rm(breakerPath, { recursive: true, force: true }).catch(() => undefined)
158
+ }
159
+ return { acquired: false }
160
+ }
161
+
162
+ if (errCode === "ENOENT" || errCode === "ENOTDIR") {
163
+ return { acquired: false }
164
+ }
165
+
166
+ throw claimErr
167
+ }
168
+
169
+ try {
170
+ if (!(await stale(lockDir, heartbeatPath, metaPath, opts.staleMs))) {
171
+ return { acquired: false }
172
+ }
173
+
174
+ await rm(lockDir, { recursive: true, force: true })
175
+
176
+ try {
177
+ await mkdir(lockDir, { mode: 0o700 })
178
+ } catch (retryErr) {
179
+ const errCode = code(retryErr)
180
+ if (errCode === "EEXIST" || errCode === "ENOTEMPTY") {
181
+ return { acquired: false }
182
+ }
183
+ throw retryErr
184
+ }
185
+ } finally {
186
+ await rm(breakerPath, { recursive: true, force: true }).catch(() => undefined)
187
+ }
188
+ }
189
+
190
+ const meta = {
191
+ token,
192
+ pid: process.pid,
193
+ hostname: os.hostname(),
194
+ createdAt: new Date().toISOString(),
195
+ }
196
+
197
+ await writeFile(heartbeatPath, "", { flag: "wx" }).catch(async () => {
198
+ await rm(lockDir, { recursive: true, force: true })
199
+ throw new Error("Lock acquired but heartbeat already existed (possible compromise).")
200
+ })
201
+
202
+ await writeFile(metaPath, JSON.stringify(meta, null, 2), { flag: "wx" }).catch(async () => {
203
+ await rm(lockDir, { recursive: true, force: true })
204
+ throw new Error("Lock acquired but meta.json already existed (possible compromise).")
205
+ })
206
+
207
+ let timer: NodeJS.Timeout | undefined
208
+
209
+ const startHeartbeat = (intervalMs = Math.max(100, Math.floor(opts.staleMs / 3))) => {
210
+ if (timer) return
211
+ timer = setInterval(() => {
212
+ const t = new Date()
213
+ void utimes(heartbeatPath, t, t).catch(() => undefined)
214
+ }, intervalMs)
215
+ timer.unref?.()
216
+ }
217
+
218
+ const release = async () => {
219
+ if (timer) {
220
+ clearInterval(timer)
221
+ timer = undefined
222
+ }
223
+
224
+ const current = await readFile(metaPath, "utf8")
225
+ .then((raw) => {
226
+ const parsed = JSON.parse(raw)
227
+ if (!parsed || typeof parsed !== "object") return {}
228
+ return {
229
+ token: "token" in parsed && typeof parsed.token === "string" ? parsed.token : undefined,
230
+ }
231
+ })
232
+ .catch((err) => {
233
+ const errCode = code(err)
234
+ if (errCode === "ENOENT" || errCode === "ENOTDIR") {
235
+ throw new Error("Refusing to release: lock is compromised (metadata missing).")
236
+ }
237
+ if (err instanceof SyntaxError) {
238
+ throw new Error("Refusing to release: lock is compromised (metadata invalid).")
239
+ }
240
+ throw err
241
+ })
242
+ if (current.token !== token) {
243
+ throw new Error("Refusing to release: lock token mismatch (not the owner).")
244
+ }
245
+
246
+ await rm(lockDir, { recursive: true, force: true })
247
+ }
248
+
249
+ return {
250
+ acquired: true,
251
+ startHeartbeat,
252
+ release,
253
+ }
254
+ }
255
+
256
+ async function acquireLockDir(
257
+ lockDir: string,
258
+ input: { key: string; onWait?: Wait; signal?: AbortSignal },
259
+ opts: Opts,
260
+ ) {
261
+ const stop = mono() + opts.timeoutMs
262
+ let attempt = 0
263
+ let waited = 0
264
+ let delay = opts.baseDelayMs
265
+
266
+ while (true) {
267
+ input.signal?.throwIfAborted()
268
+
269
+ const res = await tryAcquireLockDir(lockDir, opts)
270
+ if (res.acquired) {
271
+ return res
272
+ }
273
+
274
+ if (mono() > stop) {
275
+ throw new Error(`Timed out waiting for lock: ${input.key}`)
276
+ }
277
+
278
+ attempt += 1
279
+ const ms = jitter(delay)
280
+ await input.onWait?.({
281
+ key: input.key,
282
+ attempt,
283
+ delay: ms,
284
+ waited,
285
+ })
286
+ await sleep(ms, input.signal)
287
+ waited += ms
288
+ delay = Math.min(opts.maxDelayMs, Math.floor(delay * 1.7))
289
+ }
290
+ }
291
+
292
+ export async function acquire(key: string, input: Options = {}): Promise<Lease> {
293
+ input.signal?.throwIfAborted()
294
+ const cfg: Opts = {
295
+ staleMs: input.staleMs ?? defaultOpts.staleMs,
296
+ timeoutMs: input.timeoutMs ?? defaultOpts.timeoutMs,
297
+ baseDelayMs: input.baseDelayMs ?? defaultOpts.baseDelayMs,
298
+ maxDelayMs: input.maxDelayMs ?? defaultOpts.maxDelayMs,
299
+ }
300
+ const dir = input.dir ?? root
301
+
302
+ await mkdir(dir, { recursive: true })
303
+ const lockfile = path.join(dir, Hash.fast(key) + ".lock")
304
+ const lock = await acquireLockDir(
305
+ lockfile,
306
+ {
307
+ key,
308
+ onWait: input.onWait,
309
+ signal: input.signal,
310
+ },
311
+ cfg,
312
+ )
313
+ lock.startHeartbeat()
314
+
315
+ const release = () => lock.release()
316
+ return {
317
+ release,
318
+ [Symbol.asyncDispose]() {
319
+ return release()
320
+ },
321
+ }
322
+ }
323
+
324
+ export async function withLock<T>(key: string, fn: () => Promise<T>, input: Options = {}) {
325
+ await using _ = await acquire(key, input)
326
+ input.signal?.throwIfAborted()
327
+ return await fn()
328
+ }
329
+ }
package/src/util/fn.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { z } from "zod"
2
+
3
+ export function fn<T extends z.ZodType, Result>(schema: T, cb: (input: z.infer<T>) => Result) {
4
+ const result = (input: z.infer<T>) => {
5
+ const parsed = schema.parse(input)
6
+ return cb(parsed)
7
+ }
8
+ result.force = (input: z.infer<T>) => cb(input)
9
+ result.schema = schema
10
+ return result
11
+ }
@@ -0,0 +1,20 @@
1
+ export function formatDuration(secs: number) {
2
+ if (secs <= 0) return ""
3
+ if (secs < 60) return `${secs}s`
4
+ if (secs < 3600) {
5
+ const mins = Math.floor(secs / 60)
6
+ const remaining = secs % 60
7
+ return remaining > 0 ? `${mins}m ${remaining}s` : `${mins}m`
8
+ }
9
+ if (secs < 86400) {
10
+ const hours = Math.floor(secs / 3600)
11
+ const remaining = Math.floor((secs % 3600) / 60)
12
+ return remaining > 0 ? `${hours}h ${remaining}m` : `${hours}h`
13
+ }
14
+ if (secs < 604800) {
15
+ const days = Math.floor(secs / 86400)
16
+ return days === 1 ? "~1 day" : `~${days} days`
17
+ }
18
+ const weeks = Math.floor(secs / 604800)
19
+ return weeks === 1 ? "~1 week" : `~${weeks} weeks`
20
+ }
@@ -0,0 +1,7 @@
1
+ import { createHash } from "crypto"
2
+
3
+ export namespace Hash {
4
+ export function fast(input: string | Buffer): string {
5
+ return createHash("sha1").update(input).digest("hex")
6
+ }
7
+ }
@@ -0,0 +1,3 @@
1
+ export function iife<T>(fn: () => T) {
2
+ return fn()
3
+ }