bonecode 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 (605) hide show
  1. package/ARCHITECTURE.md +183 -0
  2. package/README.md +71 -0
  3. package/bin/bonecode +62 -0
  4. package/bone/migrations/rag_vectors.sql +258 -0
  5. package/bone/output/agent/.dockerignore +7 -0
  6. package/bone/output/agent/.env.example +36 -0
  7. package/bone/output/agent/.github/workflows/ci.yaml +58 -0
  8. package/bone/output/agent/AgentDomain.bone.map +350 -0
  9. package/bone/output/agent/AgentDomain.postman_collection.json +958 -0
  10. package/bone/output/agent/Dockerfile +22 -0
  11. package/bone/output/agent/README.md +47 -0
  12. package/bone/output/agent/admin/index.html +740 -0
  13. package/bone/output/agent/docker-compose.yaml +22 -0
  14. package/bone/output/agent/k8s/deployment.yaml +75 -0
  15. package/bone/output/agent/migrations/agent.sql +36 -0
  16. package/bone/output/agent/migrations/agent_instance.sql +36 -0
  17. package/bone/output/agent/migrations/audit_log.sql +18 -0
  18. package/bone/output/agent/migrations/build_step.sql +34 -0
  19. package/bone/output/agent/migrations/event_outbox.sql +31 -0
  20. package/bone/output/agent/migrations/plan.sql +30 -0
  21. package/bone/output/agent/migrations/task.sql +30 -0
  22. package/bone/output/agent/migrations/tool_call.sql +33 -0
  23. package/bone/output/agent/openapi.yaml +1116 -0
  24. package/bone/output/agent/package.json +36 -0
  25. package/bone/output/agent/schema.graphql +233 -0
  26. package/bone/output/agent/sdk/client.ts +231 -0
  27. package/bone/output/agent/src/algorithms.ts +2 -0
  28. package/bone/output/agent/src/audit.ts +44 -0
  29. package/bone/output/agent/src/auth.ts +57 -0
  30. package/bone/output/agent/src/cron.ts +12 -0
  31. package/bone/output/agent/src/db.ts +32 -0
  32. package/bone/output/agent/src/debug.ts +66 -0
  33. package/bone/output/agent/src/events.ts +243 -0
  34. package/bone/output/agent/src/extensions.ts +54 -0
  35. package/bone/output/agent/src/failure_rules.ts +323 -0
  36. package/bone/output/agent/src/flows.ts +168 -0
  37. package/bone/output/agent/src/health.ts +43 -0
  38. package/bone/output/agent/src/index.ts +100 -0
  39. package/bone/output/agent/src/logger.ts +66 -0
  40. package/bone/output/agent/src/metrics.ts +75 -0
  41. package/bone/output/agent/src/migrate.ts +352 -0
  42. package/bone/output/agent/src/migration_diff.ts +108 -0
  43. package/bone/output/agent/src/notify.ts +125 -0
  44. package/bone/output/agent/src/routes/agent_instance.ts +234 -0
  45. package/bone/output/agent/src/routes/build_step.ts +105 -0
  46. package/bone/output/agent/src/routes/plan.ts +91 -0
  47. package/bone/output/agent/src/routes/task.ts +105 -0
  48. package/bone/output/agent/src/routes/tool_call.ts +166 -0
  49. package/bone/output/agent/src/schemas.ts +384 -0
  50. package/bone/output/agent/src/state_machines/agent_instance.ts +24 -0
  51. package/bone/output/agent/src/state_machines/build_step.ts +22 -0
  52. package/bone/output/agent/src/state_machines/plan.ts +22 -0
  53. package/bone/output/agent/src/state_machines/task.ts +22 -0
  54. package/bone/output/agent/src/state_machines/tool_call.ts +22 -0
  55. package/bone/output/agent/src/tests.ts +362 -0
  56. package/bone/output/agent/src/websocket.ts +201 -0
  57. package/bone/output/agent/tsconfig.json +25 -0
  58. package/bone/output/rag/.dockerignore +7 -0
  59. package/bone/output/rag/.env.example +36 -0
  60. package/bone/output/rag/.github/workflows/ci.yaml +58 -0
  61. package/bone/output/rag/Dockerfile +22 -0
  62. package/bone/output/rag/RAGDomain.bone.map +287 -0
  63. package/bone/output/rag/RAGDomain.postman_collection.json +923 -0
  64. package/bone/output/rag/README.md +47 -0
  65. package/bone/output/rag/admin/index.html +818 -0
  66. package/bone/output/rag/docker-compose.yaml +22 -0
  67. package/bone/output/rag/k8s/deployment.yaml +75 -0
  68. package/bone/output/rag/migrations/audit_log.sql +18 -0
  69. package/bone/output/rag/migrations/code_chunk.sql +34 -0
  70. package/bone/output/rag/migrations/code_file.sql +33 -0
  71. package/bone/output/rag/migrations/event_outbox.sql +31 -0
  72. package/bone/output/rag/migrations/indexing_job.sql +33 -0
  73. package/bone/output/rag/migrations/knowledge_base.sql +35 -0
  74. package/bone/output/rag/migrations/memory_entry.sql +34 -0
  75. package/bone/output/rag/openapi.yaml +1097 -0
  76. package/bone/output/rag/package.json +36 -0
  77. package/bone/output/rag/schema.graphql +245 -0
  78. package/bone/output/rag/sdk/client.ts +234 -0
  79. package/bone/output/rag/src/algorithms.ts +2 -0
  80. package/bone/output/rag/src/audit.ts +37 -0
  81. package/bone/output/rag/src/auth.ts +57 -0
  82. package/bone/output/rag/src/cron.ts +12 -0
  83. package/bone/output/rag/src/db.ts +32 -0
  84. package/bone/output/rag/src/debug.ts +66 -0
  85. package/bone/output/rag/src/events.ts +243 -0
  86. package/bone/output/rag/src/extensions.ts +350 -0
  87. package/bone/output/rag/src/failure_rules.ts +315 -0
  88. package/bone/output/rag/src/flows.ts +239 -0
  89. package/bone/output/rag/src/health.ts +43 -0
  90. package/bone/output/rag/src/index.ts +95 -0
  91. package/bone/output/rag/src/logger.ts +66 -0
  92. package/bone/output/rag/src/metrics.ts +75 -0
  93. package/bone/output/rag/src/migrate.ts +364 -0
  94. package/bone/output/rag/src/migration_diff.ts +108 -0
  95. package/bone/output/rag/src/notify.ts +99 -0
  96. package/bone/output/rag/src/routes/code_chunk.ts +75 -0
  97. package/bone/output/rag/src/routes/code_file.ts +101 -0
  98. package/bone/output/rag/src/routes/indexing_job.ts +87 -0
  99. package/bone/output/rag/src/routes/knowledge_base.ts +230 -0
  100. package/bone/output/rag/src/routes/memory_entry.ts +87 -0
  101. package/bone/output/rag/src/schemas.ts +394 -0
  102. package/bone/output/rag/src/state_machines/code_file.ts +23 -0
  103. package/bone/output/rag/src/state_machines/indexing_job.ts +22 -0
  104. package/bone/output/rag/src/state_machines/knowledge_base.ts +23 -0
  105. package/bone/output/rag/src/state_machines/memory_entry.ts +20 -0
  106. package/bone/output/rag/src/tests.ts +340 -0
  107. package/bone/output/rag/tsconfig.json +25 -0
  108. package/bone/output/session/.dockerignore +7 -0
  109. package/bone/output/session/.env.example +36 -0
  110. package/bone/output/session/.github/workflows/ci.yaml +58 -0
  111. package/bone/output/session/Dockerfile +22 -0
  112. package/bone/output/session/README.md +47 -0
  113. package/bone/output/session/SessionDomain.bone.map +350 -0
  114. package/bone/output/session/SessionDomain.postman_collection.json +958 -0
  115. package/bone/output/session/admin/index.html +667 -0
  116. package/bone/output/session/docker-compose.yaml +22 -0
  117. package/bone/output/session/k8s/deployment.yaml +75 -0
  118. package/bone/output/session/migrations/audit_log.sql +18 -0
  119. package/bone/output/session/migrations/event_outbox.sql +31 -0
  120. package/bone/output/session/migrations/message.sql +31 -0
  121. package/bone/output/session/migrations/part.sql +28 -0
  122. package/bone/output/session/migrations/permission.sql +28 -0
  123. package/bone/output/session/migrations/project.sql +28 -0
  124. package/bone/output/session/migrations/session.sql +38 -0
  125. package/bone/output/session/openapi.yaml +1101 -0
  126. package/bone/output/session/package.json +36 -0
  127. package/bone/output/session/schema.graphql +222 -0
  128. package/bone/output/session/sdk/client.ts +225 -0
  129. package/bone/output/session/src/algorithms.ts +2 -0
  130. package/bone/output/session/src/audit.ts +44 -0
  131. package/bone/output/session/src/auth.ts +57 -0
  132. package/bone/output/session/src/cron.ts +12 -0
  133. package/bone/output/session/src/db.ts +32 -0
  134. package/bone/output/session/src/debug.ts +66 -0
  135. package/bone/output/session/src/events.ts +270 -0
  136. package/bone/output/session/src/extensions.ts +215 -0
  137. package/bone/output/session/src/failure_rules.ts +284 -0
  138. package/bone/output/session/src/flows.ts +168 -0
  139. package/bone/output/session/src/health.ts +43 -0
  140. package/bone/output/session/src/index.ts +100 -0
  141. package/bone/output/session/src/logger.ts +66 -0
  142. package/bone/output/session/src/metrics.ts +75 -0
  143. package/bone/output/session/src/migrate.ts +332 -0
  144. package/bone/output/session/src/migration_diff.ts +108 -0
  145. package/bone/output/session/src/notify.ts +112 -0
  146. package/bone/output/session/src/routes/message.ts +93 -0
  147. package/bone/output/session/src/routes/part.ts +79 -0
  148. package/bone/output/session/src/routes/permission.ts +79 -0
  149. package/bone/output/session/src/routes/project.ts +79 -0
  150. package/bone/output/session/src/routes/session.ts +294 -0
  151. package/bone/output/session/src/schemas.ts +357 -0
  152. package/bone/output/session/src/state_machines/session.ts +23 -0
  153. package/bone/output/session/src/tests.ts +326 -0
  154. package/bone/output/session/src/websocket.ts +201 -0
  155. package/bone/output/session/tsconfig.json +25 -0
  156. package/bone/output/workspace/.dockerignore +7 -0
  157. package/bone/output/workspace/.env.example +36 -0
  158. package/bone/output/workspace/.github/workflows/ci.yaml +58 -0
  159. package/bone/output/workspace/Dockerfile +22 -0
  160. package/bone/output/workspace/README.md +45 -0
  161. package/bone/output/workspace/WorkspaceDomain.bone.map +189 -0
  162. package/bone/output/workspace/WorkspaceDomain.postman_collection.json +621 -0
  163. package/bone/output/workspace/admin/index.html +485 -0
  164. package/bone/output/workspace/docker-compose.yaml +22 -0
  165. package/bone/output/workspace/k8s/deployment.yaml +75 -0
  166. package/bone/output/workspace/migrations/audit_log.sql +18 -0
  167. package/bone/output/workspace/migrations/codebase.sql +34 -0
  168. package/bone/output/workspace/migrations/event_outbox.sql +31 -0
  169. package/bone/output/workspace/migrations/snapshot.sql +32 -0
  170. package/bone/output/workspace/migrations/workspace.sql +33 -0
  171. package/bone/output/workspace/openapi.yaml +721 -0
  172. package/bone/output/workspace/package.json +36 -0
  173. package/bone/output/workspace/schema.graphql +153 -0
  174. package/bone/output/workspace/sdk/client.ts +155 -0
  175. package/bone/output/workspace/src/algorithms.ts +2 -0
  176. package/bone/output/workspace/src/audit.ts +37 -0
  177. package/bone/output/workspace/src/auth.ts +57 -0
  178. package/bone/output/workspace/src/cron.ts +12 -0
  179. package/bone/output/workspace/src/db.ts +32 -0
  180. package/bone/output/workspace/src/debug.ts +66 -0
  181. package/bone/output/workspace/src/events.ts +243 -0
  182. package/bone/output/workspace/src/extensions.ts +44 -0
  183. package/bone/output/workspace/src/failure_rules.ts +153 -0
  184. package/bone/output/workspace/src/health.ts +43 -0
  185. package/bone/output/workspace/src/index.ts +89 -0
  186. package/bone/output/workspace/src/logger.ts +66 -0
  187. package/bone/output/workspace/src/metrics.ts +75 -0
  188. package/bone/output/workspace/src/migrate.ts +220 -0
  189. package/bone/output/workspace/src/migration_diff.ts +108 -0
  190. package/bone/output/workspace/src/notify.ts +73 -0
  191. package/bone/output/workspace/src/routes/codebase.ts +87 -0
  192. package/bone/output/workspace/src/routes/snapshot.ts +127 -0
  193. package/bone/output/workspace/src/routes/workspace.ts +190 -0
  194. package/bone/output/workspace/src/schemas.ts +231 -0
  195. package/bone/output/workspace/src/state_machines/codebase.ts +21 -0
  196. package/bone/output/workspace/src/state_machines/snapshot.ts +20 -0
  197. package/bone/output/workspace/src/state_machines/workspace.ts +21 -0
  198. package/bone/output/workspace/src/tests.ts +249 -0
  199. package/bone/output/workspace/tsconfig.json +25 -0
  200. package/compat/opencode_adapter.ts +410 -0
  201. package/package.json +69 -0
  202. package/scripts/check_benchmark_session.js +34 -0
  203. package/scripts/check_finish_event.js +24 -0
  204. package/scripts/check_parts.js +15 -0
  205. package/scripts/compile.js +79 -0
  206. package/scripts/copy_opencode.ps1 +53 -0
  207. package/scripts/create_functions.sql +129 -0
  208. package/scripts/migrate.js +85 -0
  209. package/scripts/migrate_from_opencode.ts +218 -0
  210. package/scripts/test_agent_loop.js +101 -0
  211. package/scripts/test_api.ps1 +116 -0
  212. package/scripts/test_context_builder.js +136 -0
  213. package/scripts/test_context_builder.ts +97 -0
  214. package/scripts/test_rag.js +189 -0
  215. package/scripts/test_stream_events.js +36 -0
  216. package/scripts/test_websocket_and_saga.js +216 -0
  217. package/src/cli.ts +475 -0
  218. package/src/config.ts +162 -0
  219. package/src/context_builder.ts +598 -0
  220. package/src/engine/account/account.sql.ts +39 -0
  221. package/src/engine/account/account.ts +456 -0
  222. package/src/engine/account/repo.ts +166 -0
  223. package/src/engine/account/schema.ts +99 -0
  224. package/src/engine/account/url.ts +8 -0
  225. package/src/engine/acp/README.md +174 -0
  226. package/src/engine/acp/agent.ts +1968 -0
  227. package/src/engine/acp/runtime.ts +22 -0
  228. package/src/engine/acp/session.ts +122 -0
  229. package/src/engine/acp/types.ts +24 -0
  230. package/src/engine/agent/agent.ts +463 -0
  231. package/src/engine/agent/generate.txt +75 -0
  232. package/src/engine/agent/prompt/compaction.txt +9 -0
  233. package/src/engine/agent/prompt/explore.txt +18 -0
  234. package/src/engine/agent/prompt/scout.txt +36 -0
  235. package/src/engine/agent/prompt/summary.txt +11 -0
  236. package/src/engine/agent/prompt/title.txt +44 -0
  237. package/src/engine/agent/subagent-permissions.ts +34 -0
  238. package/src/engine/auth/index.ts +96 -0
  239. package/src/engine/background/background/job.ts +200 -0
  240. package/src/engine/background/job.ts +200 -0
  241. package/src/engine/bus/bus-event.ts +45 -0
  242. package/src/engine/bus/global.ts +22 -0
  243. package/src/engine/bus/index.ts +203 -0
  244. package/src/engine/command/command/index.ts +181 -0
  245. package/src/engine/command/command/template/initialize.txt +66 -0
  246. package/src/engine/command/command/template/review.txt +101 -0
  247. package/src/engine/command/index.ts +181 -0
  248. package/src/engine/command/template/initialize.txt +66 -0
  249. package/src/engine/command/template/review.txt +101 -0
  250. package/src/engine/config/agent.ts +172 -0
  251. package/src/engine/config/attachment.ts +25 -0
  252. package/src/engine/config/command.ts +62 -0
  253. package/src/engine/config/config.ts +833 -0
  254. package/src/engine/config/console-state.ts +14 -0
  255. package/src/engine/config/entry-name.ts +16 -0
  256. package/src/engine/config/error.ts +23 -0
  257. package/src/engine/config/formatter.ts +13 -0
  258. package/src/engine/config/layout.ts +6 -0
  259. package/src/engine/config/lsp.ts +43 -0
  260. package/src/engine/config/managed.ts +71 -0
  261. package/src/engine/config/markdown.ts +96 -0
  262. package/src/engine/config/mcp.ts +56 -0
  263. package/src/engine/config/model-id.ts +5 -0
  264. package/src/engine/config/parse.ts +79 -0
  265. package/src/engine/config/paths.ts +45 -0
  266. package/src/engine/config/permission.ts +58 -0
  267. package/src/engine/config/plugin.ts +84 -0
  268. package/src/engine/config/provider.ts +111 -0
  269. package/src/engine/config/reference.ts +23 -0
  270. package/src/engine/config/server.ts +19 -0
  271. package/src/engine/config/skills.ts +14 -0
  272. package/src/engine/config/variable.ts +90 -0
  273. package/src/engine/control-plane/adapters/index.ts +41 -0
  274. package/src/engine/control-plane/adapters/worktree.ts +96 -0
  275. package/src/engine/control-plane/dev/README.md +19 -0
  276. package/src/engine/control-plane/dev/debug-workspace-plugin.ts +73 -0
  277. package/src/engine/control-plane/schema.ts +14 -0
  278. package/src/engine/control-plane/types.ts +59 -0
  279. package/src/engine/control-plane/util.ts +39 -0
  280. package/src/engine/control-plane/workspace-adapter-runtime.ts +51 -0
  281. package/src/engine/control-plane/workspace-context.ts +26 -0
  282. package/src/engine/control-plane/workspace.sql.ts +20 -0
  283. package/src/engine/control-plane/workspace.ts +1072 -0
  284. package/src/engine/data-migration.ts +161 -0
  285. package/src/engine/effect/app-runtime.ts +143 -0
  286. package/src/engine/effect/bootstrap-runtime.ts +29 -0
  287. package/src/engine/effect/bridge.ts +84 -0
  288. package/src/engine/effect/config-service.ts +67 -0
  289. package/src/engine/effect/instance-ref.ts +11 -0
  290. package/src/engine/effect/instance-registry.ts +12 -0
  291. package/src/engine/effect/instance-state.ts +72 -0
  292. package/src/engine/effect/promise.ts +17 -0
  293. package/src/engine/effect/run-service.ts +47 -0
  294. package/src/engine/effect/runner.ts +217 -0
  295. package/src/engine/effect/runtime-flags.ts +74 -0
  296. package/src/engine/effect/service-use.ts +38 -0
  297. package/src/engine/env/index.ts +37 -0
  298. package/src/engine/event-v2-bridge.ts +89 -0
  299. package/src/engine/file/file/ignore.ts +81 -0
  300. package/src/engine/file/file/index.ts +651 -0
  301. package/src/engine/file/file/protected.ts +59 -0
  302. package/src/engine/file/file/ripgrep.ts +481 -0
  303. package/src/engine/file/file/watcher.ts +167 -0
  304. package/src/engine/file/ignore.ts +81 -0
  305. package/src/engine/file/index.ts +651 -0
  306. package/src/engine/file/protected.ts +59 -0
  307. package/src/engine/file/ripgrep.ts +481 -0
  308. package/src/engine/file/watcher.ts +167 -0
  309. package/src/engine/format/format/formatter.ts +404 -0
  310. package/src/engine/format/format/index.ts +209 -0
  311. package/src/engine/format/formatter.ts +404 -0
  312. package/src/engine/format/index.ts +209 -0
  313. package/src/engine/git/git/index.ts +347 -0
  314. package/src/engine/git/index.ts +347 -0
  315. package/src/engine/id/id.ts +80 -0
  316. package/src/engine/ide/index.ts +70 -0
  317. package/src/engine/image/image/image.ts +176 -0
  318. package/src/engine/image/image.ts +176 -0
  319. package/src/engine/index.ts +251 -0
  320. package/src/engine/installation/index.ts +327 -0
  321. package/src/engine/lsp/client.ts +707 -0
  322. package/src/engine/lsp/diagnostic.ts +29 -0
  323. package/src/engine/lsp/language.ts +121 -0
  324. package/src/engine/lsp/launch.ts +21 -0
  325. package/src/engine/lsp/lsp/client.ts +707 -0
  326. package/src/engine/lsp/lsp/diagnostic.ts +29 -0
  327. package/src/engine/lsp/lsp/language.ts +121 -0
  328. package/src/engine/lsp/lsp/launch.ts +21 -0
  329. package/src/engine/lsp/lsp/lsp.ts +507 -0
  330. package/src/engine/lsp/lsp/server.ts +2064 -0
  331. package/src/engine/lsp/lsp.ts +507 -0
  332. package/src/engine/lsp/server.ts +2064 -0
  333. package/src/engine/mcp/auth.ts +146 -0
  334. package/src/engine/mcp/index.ts +958 -0
  335. package/src/engine/mcp/mcp/auth.ts +146 -0
  336. package/src/engine/mcp/mcp/index.ts +958 -0
  337. package/src/engine/mcp/mcp/oauth-callback.ts +232 -0
  338. package/src/engine/mcp/mcp/oauth-provider.ts +214 -0
  339. package/src/engine/mcp/oauth-callback.ts +232 -0
  340. package/src/engine/mcp/oauth-provider.ts +214 -0
  341. package/src/engine/node.ts +6 -0
  342. package/src/engine/patch/index.ts +689 -0
  343. package/src/engine/patch/patch/index.ts +689 -0
  344. package/src/engine/permission/arity.ts +163 -0
  345. package/src/engine/permission/evaluate.ts +15 -0
  346. package/src/engine/permission/index.ts +306 -0
  347. package/src/engine/permission/permission/arity.ts +163 -0
  348. package/src/engine/permission/permission/evaluate.ts +15 -0
  349. package/src/engine/permission/permission/index.ts +306 -0
  350. package/src/engine/permission/permission/schema.ts +13 -0
  351. package/src/engine/permission/schema.ts +13 -0
  352. package/src/engine/plugin/azure.ts +26 -0
  353. package/src/engine/plugin/cloudflare.ts +76 -0
  354. package/src/engine/plugin/codex.ts +622 -0
  355. package/src/engine/plugin/digitalocean.ts +411 -0
  356. package/src/engine/plugin/github-copilot/copilot.ts +394 -0
  357. package/src/engine/plugin/github-copilot/models.ts +196 -0
  358. package/src/engine/plugin/index.ts +295 -0
  359. package/src/engine/plugin/install.ts +439 -0
  360. package/src/engine/plugin/loader.ts +216 -0
  361. package/src/engine/plugin/meta.ts +188 -0
  362. package/src/engine/plugin/shared.ts +323 -0
  363. package/src/engine/project/bootstrap-service.ts +9 -0
  364. package/src/engine/project/bootstrap.ts +75 -0
  365. package/src/engine/project/instance-context.ts +24 -0
  366. package/src/engine/project/instance-layer.ts +11 -0
  367. package/src/engine/project/instance-runtime.ts +16 -0
  368. package/src/engine/project/instance-store.ts +193 -0
  369. package/src/engine/project/project.sql.ts +17 -0
  370. package/src/engine/project/project.ts +537 -0
  371. package/src/engine/project/schema.ts +13 -0
  372. package/src/engine/project/vcs.ts +405 -0
  373. package/src/engine/provider/auth.ts +225 -0
  374. package/src/engine/provider/error.ts +204 -0
  375. package/src/engine/provider/model-status.ts +8 -0
  376. package/src/engine/provider/provider.ts +1843 -0
  377. package/src/engine/provider/schema.ts +30 -0
  378. package/src/engine/provider/sdk/copilot/AGENTS.md +1 -0
  379. package/src/engine/provider/transform.ts +1376 -0
  380. package/src/engine/pty/index.ts +365 -0
  381. package/src/engine/pty/input.ts +24 -0
  382. package/src/engine/pty/pty/index.ts +365 -0
  383. package/src/engine/pty/pty/input.ts +24 -0
  384. package/src/engine/pty/pty/pty.bun.ts +26 -0
  385. package/src/engine/pty/pty/pty.node.ts +27 -0
  386. package/src/engine/pty/pty/pty.ts +25 -0
  387. package/src/engine/pty/pty/schema.ts +14 -0
  388. package/src/engine/pty/pty/ticket.ts +68 -0
  389. package/src/engine/pty/pty.bun.ts +26 -0
  390. package/src/engine/pty/pty.node.ts +27 -0
  391. package/src/engine/pty/pty.ts +25 -0
  392. package/src/engine/pty/schema.ts +14 -0
  393. package/src/engine/pty/ticket.ts +68 -0
  394. package/src/engine/question/index.ts +213 -0
  395. package/src/engine/question/question/index.ts +213 -0
  396. package/src/engine/question/question/schema.ts +10 -0
  397. package/src/engine/question/schema.ts +10 -0
  398. package/src/engine/reference/reference/reference.ts +241 -0
  399. package/src/engine/reference/reference/repository-cache.ts +147 -0
  400. package/src/engine/reference/reference.ts +241 -0
  401. package/src/engine/reference/repository-cache.ts +147 -0
  402. package/src/engine/session/compaction.ts +651 -0
  403. package/src/engine/session/compaction_logic.ts +120 -0
  404. package/src/engine/session/instruction.ts +238 -0
  405. package/src/engine/session/instruction_loader.ts +54 -0
  406. package/src/engine/session/llm.ts +459 -0
  407. package/src/engine/session/message-error.ts +14 -0
  408. package/src/engine/session/message-v2.ts +1202 -0
  409. package/src/engine/session/message.ts +146 -0
  410. package/src/engine/session/overflow.ts +32 -0
  411. package/src/engine/session/overflow_check.ts +46 -0
  412. package/src/engine/session/processor.ts +823 -0
  413. package/src/engine/session/prompt/anthropic.txt +105 -0
  414. package/src/engine/session/prompt/beast.txt +147 -0
  415. package/src/engine/session/prompt/build-switch.txt +5 -0
  416. package/src/engine/session/prompt/codex.txt +79 -0
  417. package/src/engine/session/prompt/copilot-gpt-5.txt +143 -0
  418. package/src/engine/session/prompt/default.txt +105 -0
  419. package/src/engine/session/prompt/gemini.txt +155 -0
  420. package/src/engine/session/prompt/gpt.txt +107 -0
  421. package/src/engine/session/prompt/kimi.txt +95 -0
  422. package/src/engine/session/prompt/max-steps.txt +16 -0
  423. package/src/engine/session/prompt/plan-reminder-anthropic.txt +67 -0
  424. package/src/engine/session/prompt/plan.txt +26 -0
  425. package/src/engine/session/prompt/trinity.txt +97 -0
  426. package/src/engine/session/prompt.ts +671 -0
  427. package/src/engine/session/provider_transform.ts +187 -0
  428. package/src/engine/session/retry.ts +200 -0
  429. package/src/engine/session/retry_logic.ts +65 -0
  430. package/src/engine/session/revert.ts +162 -0
  431. package/src/engine/session/run-state.ts +153 -0
  432. package/src/engine/session/schema.ts +26 -0
  433. package/src/engine/session/session.sql.ts +137 -0
  434. package/src/engine/session/session.ts +1011 -0
  435. package/src/engine/session/status.ts +94 -0
  436. package/src/engine/session/summary.ts +164 -0
  437. package/src/engine/session/system.ts +84 -0
  438. package/src/engine/session/system_prompt.ts +65 -0
  439. package/src/engine/session/todo.ts +81 -0
  440. package/src/engine/session/tool_registry.ts +162 -0
  441. package/src/engine/share/session.ts +61 -0
  442. package/src/engine/share/share-next.ts +376 -0
  443. package/src/engine/share/share.sql.ts +13 -0
  444. package/src/engine/shell/shell/shell.ts +215 -0
  445. package/src/engine/shell/shell.ts +215 -0
  446. package/src/engine/skill/discovery.ts +116 -0
  447. package/src/engine/skill/index.ts +336 -0
  448. package/src/engine/skill/prompt/customize-opencode.md +377 -0
  449. package/src/engine/skill/skill/discovery.ts +116 -0
  450. package/src/engine/skill/skill/index.ts +336 -0
  451. package/src/engine/skill/skill/prompt/customize-opencode.md +377 -0
  452. package/src/engine/snapshot/index.ts +762 -0
  453. package/src/engine/snapshot/snapshot/index.ts +762 -0
  454. package/src/engine/sync/README.md +179 -0
  455. package/src/engine/sync/event.sql.ts +17 -0
  456. package/src/engine/sync/index.ts +410 -0
  457. package/src/engine/sync/schema.ts +11 -0
  458. package/src/engine/temporary.ts +33 -0
  459. package/src/engine/tool/apply_patch.ts +313 -0
  460. package/src/engine/tool/apply_patch.txt +33 -0
  461. package/src/engine/tool/edit.ts +711 -0
  462. package/src/engine/tool/edit.txt +10 -0
  463. package/src/engine/tool/external-directory.ts +49 -0
  464. package/src/engine/tool/glob.ts +103 -0
  465. package/src/engine/tool/glob.txt +6 -0
  466. package/src/engine/tool/grep.ts +156 -0
  467. package/src/engine/tool/grep.txt +8 -0
  468. package/src/engine/tool/invalid.ts +21 -0
  469. package/src/engine/tool/json-schema.ts +164 -0
  470. package/src/engine/tool/lsp.ts +113 -0
  471. package/src/engine/tool/lsp.txt +24 -0
  472. package/src/engine/tool/mcp-websearch.ts +96 -0
  473. package/src/engine/tool/plan-enter.txt +14 -0
  474. package/src/engine/tool/plan-exit.txt +13 -0
  475. package/src/engine/tool/plan.ts +78 -0
  476. package/src/engine/tool/question.ts +44 -0
  477. package/src/engine/tool/question.txt +10 -0
  478. package/src/engine/tool/read.ts +337 -0
  479. package/src/engine/tool/read.txt +14 -0
  480. package/src/engine/tool/registry.ts +472 -0
  481. package/src/engine/tool/repo_clone.ts +80 -0
  482. package/src/engine/tool/repo_clone.txt +5 -0
  483. package/src/engine/tool/repo_overview.ts +279 -0
  484. package/src/engine/tool/repo_overview.txt +4 -0
  485. package/src/engine/tool/schema.ts +14 -0
  486. package/src/engine/tool/shell/id.ts +19 -0
  487. package/src/engine/tool/shell/prompt.ts +295 -0
  488. package/src/engine/tool/shell/shell.txt +77 -0
  489. package/src/engine/tool/shell.ts +647 -0
  490. package/src/engine/tool/skill.ts +75 -0
  491. package/src/engine/tool/skill.txt +5 -0
  492. package/src/engine/tool/task.ts +337 -0
  493. package/src/engine/tool/task.txt +58 -0
  494. package/src/engine/tool/task_status.ts +179 -0
  495. package/src/engine/tool/task_status.txt +13 -0
  496. package/src/engine/tool/todo.ts +57 -0
  497. package/src/engine/tool/todowrite.txt +167 -0
  498. package/src/engine/tool/tool/apply_patch.ts +313 -0
  499. package/src/engine/tool/tool/apply_patch.txt +33 -0
  500. package/src/engine/tool/tool/edit.ts +711 -0
  501. package/src/engine/tool/tool/edit.txt +10 -0
  502. package/src/engine/tool/tool/external-directory.ts +49 -0
  503. package/src/engine/tool/tool/glob.ts +103 -0
  504. package/src/engine/tool/tool/glob.txt +6 -0
  505. package/src/engine/tool/tool/grep.ts +156 -0
  506. package/src/engine/tool/tool/grep.txt +8 -0
  507. package/src/engine/tool/tool/invalid.ts +21 -0
  508. package/src/engine/tool/tool/json-schema.ts +164 -0
  509. package/src/engine/tool/tool/lsp.ts +113 -0
  510. package/src/engine/tool/tool/lsp.txt +24 -0
  511. package/src/engine/tool/tool/mcp-websearch.ts +96 -0
  512. package/src/engine/tool/tool/plan-enter.txt +14 -0
  513. package/src/engine/tool/tool/plan-exit.txt +13 -0
  514. package/src/engine/tool/tool/plan.ts +78 -0
  515. package/src/engine/tool/tool/question.ts +44 -0
  516. package/src/engine/tool/tool/question.txt +10 -0
  517. package/src/engine/tool/tool/read.ts +337 -0
  518. package/src/engine/tool/tool/read.txt +14 -0
  519. package/src/engine/tool/tool/registry.ts +472 -0
  520. package/src/engine/tool/tool/repo_clone.ts +80 -0
  521. package/src/engine/tool/tool/repo_clone.txt +5 -0
  522. package/src/engine/tool/tool/repo_overview.ts +279 -0
  523. package/src/engine/tool/tool/repo_overview.txt +4 -0
  524. package/src/engine/tool/tool/schema.ts +14 -0
  525. package/src/engine/tool/tool/shell/id.ts +19 -0
  526. package/src/engine/tool/tool/shell/prompt.ts +295 -0
  527. package/src/engine/tool/tool/shell/shell.txt +77 -0
  528. package/src/engine/tool/tool/shell.ts +647 -0
  529. package/src/engine/tool/tool/skill.ts +75 -0
  530. package/src/engine/tool/tool/skill.txt +5 -0
  531. package/src/engine/tool/tool/task.ts +337 -0
  532. package/src/engine/tool/tool/task.txt +58 -0
  533. package/src/engine/tool/tool/task_status.ts +179 -0
  534. package/src/engine/tool/tool/task_status.txt +13 -0
  535. package/src/engine/tool/tool/todo.ts +57 -0
  536. package/src/engine/tool/tool/todowrite.txt +167 -0
  537. package/src/engine/tool/tool/tool.ts +164 -0
  538. package/src/engine/tool/tool/truncate.ts +160 -0
  539. package/src/engine/tool/tool/truncation-dir.ts +4 -0
  540. package/src/engine/tool/tool/webfetch.ts +192 -0
  541. package/src/engine/tool/tool/webfetch.txt +13 -0
  542. package/src/engine/tool/tool/websearch.ts +143 -0
  543. package/src/engine/tool/tool/websearch.txt +14 -0
  544. package/src/engine/tool/tool/write.ts +104 -0
  545. package/src/engine/tool/tool/write.txt +8 -0
  546. package/src/engine/tool/tool.ts +164 -0
  547. package/src/engine/tool/truncate.ts +160 -0
  548. package/src/engine/tool/truncation-dir.ts +4 -0
  549. package/src/engine/tool/webfetch.ts +192 -0
  550. package/src/engine/tool/webfetch.txt +13 -0
  551. package/src/engine/tool/websearch.ts +143 -0
  552. package/src/engine/tool/websearch.txt +14 -0
  553. package/src/engine/tool/write.ts +104 -0
  554. package/src/engine/tool/write.txt +8 -0
  555. package/src/engine/util/archive.ts +17 -0
  556. package/src/engine/util/bom.ts +31 -0
  557. package/src/engine/util/data-url.ts +9 -0
  558. package/src/engine/util/defer.ts +10 -0
  559. package/src/engine/util/effect-http-client.ts +11 -0
  560. package/src/engine/util/error.ts +88 -0
  561. package/src/engine/util/filesystem.ts +252 -0
  562. package/src/engine/util/format.ts +20 -0
  563. package/src/engine/util/iife.ts +3 -0
  564. package/src/engine/util/lazy.ts +20 -0
  565. package/src/engine/util/local-context.ts +25 -0
  566. package/src/engine/util/locale.ts +86 -0
  567. package/src/engine/util/media.ts +26 -0
  568. package/src/engine/util/process.ts +176 -0
  569. package/src/engine/util/queue.ts +32 -0
  570. package/src/engine/util/record.ts +3 -0
  571. package/src/engine/util/repository.ts +158 -0
  572. package/src/engine/util/rpc.ts +66 -0
  573. package/src/engine/util/signal.ts +12 -0
  574. package/src/engine/util/timeout.ts +13 -0
  575. package/src/engine/util/token.ts +7 -0
  576. package/src/engine/util/util/archive.ts +17 -0
  577. package/src/engine/util/util/bom.ts +31 -0
  578. package/src/engine/util/util/data-url.ts +9 -0
  579. package/src/engine/util/util/defer.ts +10 -0
  580. package/src/engine/util/util/effect-http-client.ts +11 -0
  581. package/src/engine/util/util/error.ts +88 -0
  582. package/src/engine/util/util/filesystem.ts +252 -0
  583. package/src/engine/util/util/format.ts +20 -0
  584. package/src/engine/util/util/iife.ts +3 -0
  585. package/src/engine/util/util/lazy.ts +20 -0
  586. package/src/engine/util/util/local-context.ts +25 -0
  587. package/src/engine/util/util/locale.ts +86 -0
  588. package/src/engine/util/util/media.ts +26 -0
  589. package/src/engine/util/util/process.ts +176 -0
  590. package/src/engine/util/util/queue.ts +32 -0
  591. package/src/engine/util/util/record.ts +3 -0
  592. package/src/engine/util/util/repository.ts +158 -0
  593. package/src/engine/util/util/rpc.ts +66 -0
  594. package/src/engine/util/util/signal.ts +12 -0
  595. package/src/engine/util/util/timeout.ts +13 -0
  596. package/src/engine/util/util/token.ts +7 -0
  597. package/src/engine/util/util/which.ts +14 -0
  598. package/src/engine/util/util/wildcard.ts +59 -0
  599. package/src/engine/util/which.ts +14 -0
  600. package/src/engine/util/wildcard.ts +59 -0
  601. package/src/engine/worktree/index.ts +621 -0
  602. package/src/rag_worker.ts +519 -0
  603. package/src/server.ts +201 -0
  604. package/src/tui.ts +637 -0
  605. package/tsconfig.json +24 -0
@@ -0,0 +1,439 @@
1
+ import path from "path"
2
+ import {
3
+ type ParseError as JsoncParseError,
4
+ applyEdits,
5
+ modify,
6
+ parse as parseJsonc,
7
+ printParseErrorCode,
8
+ } from "jsonc-parser"
9
+
10
+ import * as ConfigPaths from "@/config/paths"
11
+ import { Global } from "@opencode-ai/core/global"
12
+ import { Filesystem } from "@/util/filesystem"
13
+ import { Flock } from "@opencode-ai/core/util/flock"
14
+ import { isRecord } from "@/util/record"
15
+
16
+ import { parsePluginSpecifier, readPackageThemes, readPluginPackage, resolvePluginTarget } from "./shared"
17
+
18
+ type Mode = "noop" | "add" | "replace"
19
+ type Kind = "server" | "tui"
20
+
21
+ export type Target = {
22
+ kind: Kind
23
+ opts?: Record<string, unknown>
24
+ }
25
+
26
+ export type InstallDeps = {
27
+ resolve: (spec: string) => Promise<string>
28
+ }
29
+
30
+ export type PatchDeps = {
31
+ readText: (file: string) => Promise<string>
32
+ write: (file: string, text: string) => Promise<void>
33
+ exists: (file: string) => Promise<boolean>
34
+ files: (dir: string, name: "opencode" | "tui") => string[]
35
+ }
36
+
37
+ export type PatchInput = {
38
+ spec: string
39
+ targets: Target[]
40
+ force?: boolean
41
+ global?: boolean
42
+ vcs?: string
43
+ worktree: string
44
+ directory: string
45
+ config?: string
46
+ }
47
+
48
+ type Ok<T> = {
49
+ ok: true
50
+ } & T
51
+
52
+ type Err<C extends string, T> = {
53
+ ok: false
54
+ code: C
55
+ } & T
56
+
57
+ export type InstallResult = Ok<{ target: string }> | Err<"install_failed", { error: unknown }>
58
+
59
+ export type ManifestResult =
60
+ | Ok<{ targets: Target[] }>
61
+ | Err<"manifest_read_failed", { file: string; error: unknown }>
62
+ | Err<"manifest_no_targets", { file: string }>
63
+
64
+ export type PatchItem = {
65
+ kind: Kind
66
+ mode: Mode
67
+ file: string
68
+ }
69
+
70
+ type PatchErr =
71
+ | Err<"invalid_json", { kind: Kind; file: string; line: number; col: number; parse: string }>
72
+ | Err<"patch_failed", { kind: Kind; error: unknown }>
73
+
74
+ type PatchOne = Ok<{ item: PatchItem }> | PatchErr
75
+
76
+ export type PatchResult = Ok<{ dir: string; items: PatchItem[] }> | (PatchErr & { dir: string })
77
+
78
+ const defaultInstallDeps: InstallDeps = {
79
+ resolve: (spec) => resolvePluginTarget(spec),
80
+ }
81
+
82
+ const defaultPatchDeps: PatchDeps = {
83
+ readText: (file) => Filesystem.readText(file),
84
+ write: async (file, text) => {
85
+ await Filesystem.write(file, text)
86
+ },
87
+ exists: (file) => Filesystem.exists(file),
88
+ files: (dir, name) => ConfigPaths.fileInDirectory(dir, name),
89
+ }
90
+
91
+ function pluginSpec(item: unknown) {
92
+ if (typeof item === "string") return item
93
+ if (!Array.isArray(item)) return
94
+ if (typeof item[0] !== "string") return
95
+ return item[0]
96
+ }
97
+
98
+ function pluginList(data: unknown) {
99
+ if (!data || typeof data !== "object" || Array.isArray(data)) return
100
+ const item = data as { plugin?: unknown }
101
+ if (!Array.isArray(item.plugin)) return
102
+ return item.plugin
103
+ }
104
+
105
+ function exportValue(value: unknown): string | undefined {
106
+ if (typeof value === "string") {
107
+ const next = value.trim()
108
+ if (next) return next
109
+ return
110
+ }
111
+ if (!isRecord(value)) return
112
+ for (const key of ["import", "default"]) {
113
+ const next = value[key]
114
+ if (typeof next !== "string") continue
115
+ const hit = next.trim()
116
+ if (!hit) continue
117
+ return hit
118
+ }
119
+ }
120
+
121
+ function exportOptions(value: unknown): Record<string, unknown> | undefined {
122
+ if (!isRecord(value)) return
123
+ const config = value.config
124
+ if (!isRecord(config)) return
125
+ return config
126
+ }
127
+
128
+ function exportTarget(pkg: Record<string, unknown>, kind: Kind) {
129
+ const exports = pkg.exports
130
+ if (!isRecord(exports)) return
131
+ const value = exports[`./${kind}`]
132
+ const entry = exportValue(value)
133
+ if (!entry) return
134
+ return {
135
+ opts: exportOptions(value),
136
+ }
137
+ }
138
+
139
+ function hasMainTarget(pkg: Record<string, unknown>) {
140
+ const main = pkg.main
141
+ if (typeof main !== "string") return false
142
+ return Boolean(main.trim())
143
+ }
144
+
145
+ function packageTargets(pkg: { json: Record<string, unknown>; dir: string; pkg: string }) {
146
+ const spec =
147
+ typeof pkg.json.name === "string" && pkg.json.name.trim().length > 0 ? pkg.json.name.trim() : path.basename(pkg.dir)
148
+ const targets: Target[] = []
149
+ const server = exportTarget(pkg.json, "server")
150
+ if (server) {
151
+ targets.push({ kind: "server", opts: server.opts })
152
+ } else if (hasMainTarget(pkg.json)) {
153
+ targets.push({ kind: "server" })
154
+ }
155
+
156
+ const tui = exportTarget(pkg.json, "tui")
157
+ if (tui) {
158
+ targets.push({ kind: "tui", opts: tui.opts })
159
+ }
160
+
161
+ if (!targets.some((item) => item.kind === "tui") && readPackageThemes(spec, pkg).length) {
162
+ targets.push({ kind: "tui" })
163
+ }
164
+
165
+ return targets
166
+ }
167
+
168
+ function patch(text: string, path: Array<string | number>, value: unknown, insert = false) {
169
+ return applyEdits(
170
+ text,
171
+ modify(text, path, value, {
172
+ formattingOptions: {
173
+ tabSize: 2,
174
+ insertSpaces: true,
175
+ },
176
+ isArrayInsertion: insert,
177
+ }),
178
+ )
179
+ }
180
+
181
+ function patchPluginList(
182
+ text: string,
183
+ list: unknown[] | undefined,
184
+ spec: string,
185
+ next: unknown,
186
+ force = false,
187
+ ): { mode: Mode; text: string } {
188
+ const pkg = parsePluginSpecifier(spec).pkg
189
+ const rows = (list ?? []).map((item, i) => ({
190
+ item,
191
+ i,
192
+ spec: pluginSpec(item),
193
+ }))
194
+ const dup = rows.filter((item) => {
195
+ if (!item.spec) return false
196
+ if (item.spec === spec) return true
197
+ if (item.spec.startsWith("file://")) return false
198
+ return parsePluginSpecifier(item.spec).pkg === pkg
199
+ })
200
+
201
+ if (!dup.length) {
202
+ if (!list) {
203
+ return {
204
+ mode: "add",
205
+ text: patch(text, ["plugin"], [next]),
206
+ }
207
+ }
208
+ return {
209
+ mode: "add",
210
+ text: patch(text, ["plugin", list.length], next, true),
211
+ }
212
+ }
213
+
214
+ if (!force) {
215
+ return {
216
+ mode: "noop",
217
+ text,
218
+ }
219
+ }
220
+
221
+ const keep = dup[0]
222
+ if (!keep) {
223
+ return {
224
+ mode: "noop",
225
+ text,
226
+ }
227
+ }
228
+
229
+ if (dup.length === 1 && keep.spec === spec) {
230
+ return {
231
+ mode: "noop",
232
+ text,
233
+ }
234
+ }
235
+
236
+ let out = text
237
+ if (typeof keep.item === "string") {
238
+ out = patch(out, ["plugin", keep.i], next)
239
+ }
240
+ if (Array.isArray(keep.item) && typeof keep.item[0] === "string") {
241
+ out = patch(out, ["plugin", keep.i, 0], spec)
242
+ }
243
+
244
+ const del = dup
245
+ .map((item) => item.i)
246
+ .filter((i) => i !== keep.i)
247
+ .sort((a, b) => b - a)
248
+
249
+ for (const i of del) {
250
+ out = patch(out, ["plugin", i], undefined)
251
+ }
252
+
253
+ return {
254
+ mode: "replace",
255
+ text: out,
256
+ }
257
+ }
258
+
259
+ export async function installPlugin(spec: string, dep: InstallDeps = defaultInstallDeps): Promise<InstallResult> {
260
+ const target = await dep.resolve(spec).then(
261
+ (item) => ({
262
+ ok: true as const,
263
+ item,
264
+ }),
265
+ (error: unknown) => ({
266
+ ok: false as const,
267
+ error,
268
+ }),
269
+ )
270
+ if (!target.ok) {
271
+ return {
272
+ ok: false,
273
+ code: "install_failed",
274
+ error: target.error,
275
+ }
276
+ }
277
+ return {
278
+ ok: true,
279
+ target: target.item,
280
+ }
281
+ }
282
+
283
+ export async function readPluginManifest(target: string): Promise<ManifestResult> {
284
+ const pkg = await readPluginPackage(target).then(
285
+ (item) => ({
286
+ ok: true as const,
287
+ item,
288
+ }),
289
+ (error: unknown) => ({
290
+ ok: false as const,
291
+ error,
292
+ }),
293
+ )
294
+ if (!pkg.ok) {
295
+ return {
296
+ ok: false,
297
+ code: "manifest_read_failed",
298
+ file: target,
299
+ error: pkg.error,
300
+ }
301
+ }
302
+
303
+ const targets = await Promise.resolve()
304
+ .then(() => packageTargets(pkg.item))
305
+ .then(
306
+ (item) => ({ ok: true as const, item }),
307
+ (error: unknown) => ({ ok: false as const, error }),
308
+ )
309
+
310
+ if (!targets.ok) {
311
+ return {
312
+ ok: false,
313
+ code: "manifest_read_failed",
314
+ file: pkg.item.pkg,
315
+ error: targets.error,
316
+ }
317
+ }
318
+
319
+ if (!targets.item.length) {
320
+ return {
321
+ ok: false,
322
+ code: "manifest_no_targets",
323
+ file: pkg.item.pkg,
324
+ }
325
+ }
326
+
327
+ return {
328
+ ok: true,
329
+ targets: targets.item,
330
+ }
331
+ }
332
+
333
+ function patchDir(input: PatchInput) {
334
+ if (input.global) return input.config ?? Global.Path.config
335
+ const git = input.vcs === "git" && input.worktree !== "/"
336
+ const root = git ? input.worktree : input.directory
337
+ return path.join(root, ".opencode")
338
+ }
339
+
340
+ function patchName(kind: Kind): "opencode" | "tui" {
341
+ if (kind === "server") return "opencode"
342
+ return "tui"
343
+ }
344
+
345
+ async function patchOne(dir: string, target: Target, spec: string, force: boolean, dep: PatchDeps): Promise<PatchOne> {
346
+ const name = patchName(target.kind)
347
+ await using _ = await Flock.acquire(`plug-config:${Filesystem.resolve(path.join(dir, name))}`)
348
+
349
+ const files = dep.files(dir, name)
350
+ let cfg = files[0]
351
+ for (const file of files) {
352
+ if (!(await dep.exists(file))) continue
353
+ cfg = file
354
+ break
355
+ }
356
+
357
+ const src = await dep.readText(cfg).catch((err: NodeJS.ErrnoException) => {
358
+ if (err.code === "ENOENT") return "{}"
359
+ return err
360
+ })
361
+ if (src instanceof Error) {
362
+ return {
363
+ ok: false,
364
+ code: "patch_failed",
365
+ kind: target.kind,
366
+ error: src,
367
+ }
368
+ }
369
+ const text = src.trim() ? src : "{}"
370
+
371
+ const errs: JsoncParseError[] = []
372
+ const data = parseJsonc(text, errs, { allowTrailingComma: true })
373
+ if (errs.length) {
374
+ const err = errs[0]
375
+ const lines = text.substring(0, err.offset).split("\n")
376
+ return {
377
+ ok: false,
378
+ code: "invalid_json",
379
+ kind: target.kind,
380
+ file: cfg,
381
+ line: lines.length,
382
+ col: lines[lines.length - 1].length + 1,
383
+ parse: printParseErrorCode(err.error),
384
+ }
385
+ }
386
+
387
+ const list = pluginList(data)
388
+ const item = target.opts ? ([spec, target.opts] as const) : spec
389
+ const out = patchPluginList(text, list, spec, item, force)
390
+ if (out.mode === "noop") {
391
+ return {
392
+ ok: true,
393
+ item: {
394
+ kind: target.kind,
395
+ mode: out.mode,
396
+ file: cfg,
397
+ },
398
+ }
399
+ }
400
+
401
+ const write = await dep.write(cfg, out.text).catch((error: unknown) => error)
402
+ if (write instanceof Error) {
403
+ return {
404
+ ok: false,
405
+ code: "patch_failed",
406
+ kind: target.kind,
407
+ error: write,
408
+ }
409
+ }
410
+
411
+ return {
412
+ ok: true,
413
+ item: {
414
+ kind: target.kind,
415
+ mode: out.mode,
416
+ file: cfg,
417
+ },
418
+ }
419
+ }
420
+
421
+ export async function patchPluginConfig(input: PatchInput, dep: PatchDeps = defaultPatchDeps): Promise<PatchResult> {
422
+ const dir = patchDir(input)
423
+ const items: PatchItem[] = []
424
+ for (const target of input.targets) {
425
+ const hit = await patchOne(dir, target, input.spec, Boolean(input.force), dep)
426
+ if (!hit.ok) {
427
+ return {
428
+ ...hit,
429
+ dir,
430
+ }
431
+ }
432
+ items.push(hit.item)
433
+ }
434
+ return {
435
+ ok: true,
436
+ dir,
437
+ items,
438
+ }
439
+ }
@@ -0,0 +1,216 @@
1
+ import {
2
+ checkPluginCompatibility,
3
+ createPluginEntry,
4
+ isDeprecatedPlugin,
5
+ pluginSource,
6
+ resolvePluginTarget,
7
+ type PluginKind,
8
+ type PluginPackage,
9
+ type PluginSource,
10
+ } from "./shared"
11
+ import { ConfigPlugin } from "@/config/plugin"
12
+ import { InstallationVersion } from "@opencode-ai/core/installation/version"
13
+
14
+ export namespace PluginLoader {
15
+ // A normalized plugin declaration derived from config before any filesystem or npm work happens.
16
+ export type Plan = {
17
+ spec: string
18
+ options: ConfigPlugin.Options | undefined
19
+ deprecated: boolean
20
+ }
21
+
22
+ // A plugin that has been resolved to a concrete target and entrypoint on disk.
23
+ export type Resolved = Plan & {
24
+ source: PluginSource
25
+ target: string
26
+ entry: string
27
+ pkg?: PluginPackage
28
+ }
29
+
30
+ // A plugin target we could inspect, but which does not expose the requested kind of entrypoint.
31
+ export type Missing = Plan & {
32
+ source: PluginSource
33
+ target: string
34
+ pkg?: PluginPackage
35
+ message: string
36
+ }
37
+
38
+ // A resolved plugin whose module has been imported successfully.
39
+ export type Loaded = Resolved & {
40
+ mod: Record<string, unknown>
41
+ }
42
+
43
+ type Candidate = { origin: ConfigPlugin.Origin; plan: Plan }
44
+ type Report = {
45
+ // Called before each attempt so callers can log initial load attempts and retries uniformly.
46
+ start?: (candidate: Candidate, retry: boolean) => void
47
+ // Called when the package exists but does not provide the requested entrypoint.
48
+ missing?: (candidate: Candidate, retry: boolean, message: string, resolved: Missing) => void
49
+ // Called for operational failures such as install, compatibility, or dynamic import errors.
50
+ error?: (
51
+ candidate: Candidate,
52
+ retry: boolean,
53
+ stage: "install" | "entry" | "compatibility" | "load",
54
+ error: unknown,
55
+ resolved?: Resolved,
56
+ ) => void
57
+ }
58
+
59
+ // Normalize a config item into the loader's internal representation.
60
+ function plan(item: ConfigPlugin.Spec): Plan {
61
+ const spec = ConfigPlugin.pluginSpecifier(item)
62
+ return { spec, options: ConfigPlugin.pluginOptions(item), deprecated: isDeprecatedPlugin(spec) }
63
+ }
64
+
65
+ // Resolve a configured plugin into a concrete entrypoint that can later be imported.
66
+ //
67
+ // The stages here intentionally separate install/target resolution, entrypoint detection,
68
+ // and compatibility checks so callers can report the exact reason a plugin was skipped.
69
+ export async function resolve(
70
+ plan: Plan,
71
+ kind: PluginKind,
72
+ ): Promise<
73
+ | { ok: true; value: Resolved }
74
+ | { ok: false; stage: "missing"; value: Missing }
75
+ | { ok: false; stage: "install" | "entry" | "compatibility"; error: unknown }
76
+ > {
77
+ // First make sure the plugin exists locally, installing npm plugins on demand.
78
+ let target = ""
79
+ try {
80
+ target = await resolvePluginTarget(plan.spec)
81
+ } catch (error) {
82
+ return { ok: false, stage: "install", error }
83
+ }
84
+ if (!target) return { ok: false, stage: "install", error: new Error(`Plugin ${plan.spec} target is empty`) }
85
+
86
+ // Then inspect the target for the requested server/tui entrypoint.
87
+ let base
88
+ try {
89
+ base = await createPluginEntry(plan.spec, target, kind)
90
+ } catch (error) {
91
+ return { ok: false, stage: "entry", error }
92
+ }
93
+ if (!base.entry)
94
+ return {
95
+ ok: false,
96
+ stage: "missing",
97
+ value: {
98
+ ...plan,
99
+ source: base.source,
100
+ target: base.target,
101
+ pkg: base.pkg,
102
+ message: `Plugin ${plan.spec} does not expose a ${kind} entrypoint`,
103
+ },
104
+ }
105
+
106
+ // npm plugins can declare which opencode versions they support; file plugins are treated
107
+ // as local development code and skip this compatibility gate.
108
+ if (base.source === "npm") {
109
+ try {
110
+ await checkPluginCompatibility(base.target, InstallationVersion, base.pkg)
111
+ } catch (error) {
112
+ return { ok: false, stage: "compatibility", error }
113
+ }
114
+ }
115
+ return { ok: true, value: { ...plan, source: base.source, target: base.target, entry: base.entry, pkg: base.pkg } }
116
+ }
117
+
118
+ // Import the resolved module only after all earlier validation has succeeded.
119
+ export async function load(row: Resolved): Promise<{ ok: true; value: Loaded } | { ok: false; error: unknown }> {
120
+ let mod
121
+ try {
122
+ mod = await import(row.entry)
123
+ } catch (error) {
124
+ return { ok: false, error }
125
+ }
126
+ if (!mod) return { ok: false, error: new Error(`Plugin ${row.spec} module is empty`) }
127
+ return { ok: true, value: { ...row, mod } }
128
+ }
129
+
130
+ // Run one candidate through the full pipeline: resolve, optionally surface a missing entry,
131
+ // import the module, and finally let the caller transform the loaded plugin into any result type.
132
+ async function attempt<R>(
133
+ candidate: Candidate,
134
+ kind: PluginKind,
135
+ retry: boolean,
136
+ finish: ((load: Loaded, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>) | undefined,
137
+ missing: ((value: Missing, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>) | undefined,
138
+ report: Report | undefined,
139
+ ): Promise<R | undefined> {
140
+ const plan = candidate.plan
141
+
142
+ // Deprecated plugin packages are silently ignored because they are now built in.
143
+ if (plan.deprecated) return
144
+
145
+ report?.start?.(candidate, retry)
146
+
147
+ const resolved = await resolve(plan, kind)
148
+ if (!resolved.ok) {
149
+ if (resolved.stage === "missing") {
150
+ // Missing entrypoints are handled separately so callers can still inspect package metadata,
151
+ // for example to load theme files from a tui plugin package that has no code entrypoint.
152
+ if (missing) {
153
+ const value = await missing(resolved.value, candidate.origin, retry)
154
+ if (value !== undefined) return value
155
+ }
156
+ report?.missing?.(candidate, retry, resolved.value.message, resolved.value)
157
+ return
158
+ }
159
+ report?.error?.(candidate, retry, resolved.stage, resolved.error)
160
+ return
161
+ }
162
+
163
+ const loaded = await load(resolved.value)
164
+ if (!loaded.ok) {
165
+ report?.error?.(candidate, retry, "load", loaded.error, resolved.value)
166
+ return
167
+ }
168
+
169
+ // The default behavior is to return the successfully loaded plugin as-is, but callers can
170
+ // provide a finisher to adapt the result into a more specific runtime shape.
171
+ if (!finish) return loaded.value as R
172
+ return finish(loaded.value, candidate.origin, retry)
173
+ }
174
+
175
+ type Input<R> = {
176
+ items: ConfigPlugin.Origin[]
177
+ kind: PluginKind
178
+ wait?: () => Promise<void>
179
+ finish?: (load: Loaded, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>
180
+ missing?: (value: Missing, origin: ConfigPlugin.Origin, retry: boolean) => Promise<R | undefined>
181
+ report?: Report
182
+ }
183
+
184
+ // Resolve and load all configured plugins in parallel.
185
+ //
186
+ // If `wait` is provided, file-based plugins that initially failed are retried once after the
187
+ // caller finishes preparing dependencies. This supports local plugins that depend on an install
188
+ // step happening elsewhere before their entrypoint becomes loadable.
189
+ export async function loadExternal<R = Loaded>(input: Input<R>): Promise<R[]> {
190
+ const candidates = input.items.map((origin) => ({ origin, plan: plan(origin.spec) }))
191
+ const list: Array<Promise<R | undefined>> = []
192
+ for (const candidate of candidates) {
193
+ list.push(attempt(candidate, input.kind, false, input.finish, input.missing, input.report))
194
+ }
195
+ const out = await Promise.all(list)
196
+ if (input.wait) {
197
+ let deps: Promise<void> | undefined
198
+ for (let i = 0; i < candidates.length; i++) {
199
+ if (out[i] !== undefined) continue
200
+
201
+ // Only local file plugins are retried. npm plugins already attempted installation during
202
+ // the first pass, while file plugins may need the caller's dependency preparation to finish.
203
+ const candidate = candidates[i]
204
+ if (!candidate || pluginSource(candidate.plan.spec) !== "file") continue
205
+ deps ??= input.wait()
206
+ await deps
207
+ out[i] = await attempt(candidate, input.kind, true, input.finish, input.missing, input.report)
208
+ }
209
+ }
210
+
211
+ // Drop skipped/failed entries while preserving the successful result order.
212
+ const ready: R[] = []
213
+ for (const item of out) if (item !== undefined) ready.push(item)
214
+ return ready
215
+ }
216
+ }