soraacode 0.1.7 → 0.2.1

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 (352) hide show
  1. package/LICENSE +21 -0
  2. package/bin/soraacode.exe +10 -0
  3. package/package.json +17 -114
  4. package/{script/postinstall.mjs → postinstall.mjs} +5 -5
  5. package/AGENTS.md +0 -131
  6. package/Dockerfile +0 -18
  7. package/README.md +0 -15
  8. package/bin/opencode +0 -199
  9. package/bin/soraacode +0 -199
  10. package/bunfig.toml +0 -7
  11. package/git +0 -0
  12. package/migration/20260511173437_session-metadata/migration.sql +0 -1
  13. package/migration/20260511173437_session-metadata/snapshot.json +0 -1500
  14. package/parsers-config.ts +0 -1
  15. package/script/bench-search.ts +0 -94
  16. package/script/bench-test-suite.ts +0 -52
  17. package/script/build-headless.ts +0 -51
  18. package/script/build.ts +0 -245
  19. package/script/generate.ts +0 -14
  20. package/script/httpapi-exercise.ts +0 -1
  21. package/script/profile-test-files.ts +0 -42
  22. package/script/publish.ts +0 -213
  23. package/script/run-workspace-server +0 -106
  24. package/script/schema.ts +0 -77
  25. package/script/time.ts +0 -6
  26. package/script/trace-imports.ts +0 -153
  27. package/specs/effect/error-boundaries-plan.md +0 -235
  28. package/specs/effect/errors.md +0 -207
  29. package/specs/effect/facades.md +0 -218
  30. package/specs/effect/guide.md +0 -247
  31. package/specs/effect/instance-context.md +0 -13
  32. package/specs/effect/loose-ends.md +0 -30
  33. package/specs/effect/migration.md +0 -62
  34. package/specs/effect/routes.md +0 -61
  35. package/specs/effect/schema.md +0 -88
  36. package/specs/effect/server-package.md +0 -58
  37. package/specs/effect/todo.md +0 -241
  38. package/specs/effect/tools.md +0 -88
  39. package/specs/openapi-translation-cleanup.md +0 -204
  40. package/specs/tui-plugins.md +0 -544
  41. package/specs/v2/api.ts +0 -67
  42. package/specs/v2/message-shape.md +0 -136
  43. package/specs/v2/notifications.md +0 -13
  44. package/specs/v2/tui-command-shim.md +0 -67
  45. package/src/account/account.ts +0 -481
  46. package/src/account/repo.ts +0 -171
  47. package/src/account/schema.ts +0 -99
  48. package/src/account/url.ts +0 -8
  49. package/src/acp/agent.ts +0 -95
  50. package/src/acp/config-option.ts +0 -206
  51. package/src/acp/content.ts +0 -269
  52. package/src/acp/directory.ts +0 -212
  53. package/src/acp/error.ts +0 -97
  54. package/src/acp/event.ts +0 -421
  55. package/src/acp/permission.ts +0 -254
  56. package/src/acp/profile.ts +0 -42
  57. package/src/acp/service.ts +0 -1226
  58. package/src/acp/session.ts +0 -232
  59. package/src/acp/tool.ts +0 -364
  60. package/src/acp/usage.ts +0 -243
  61. package/src/agent/agent.ts +0 -450
  62. package/src/agent/generate.txt +0 -75
  63. package/src/agent/prompt/compaction.txt +0 -5
  64. package/src/agent/prompt/explore.txt +0 -18
  65. package/src/agent/prompt/summary.txt +0 -11
  66. package/src/agent/prompt/title.txt +0 -44
  67. package/src/agent/subagent-permissions.ts +0 -27
  68. package/src/audio.d.ts +0 -14
  69. package/src/auth/index.ts +0 -97
  70. package/src/background/job.ts +0 -37
  71. package/src/bus/global.ts +0 -22
  72. package/src/cli/bootstrap.ts +0 -11
  73. package/src/cli/cmd/cmd.ts +0 -7
  74. package/src/cli/cmd/serve.ts +0 -24
  75. package/src/cli/effect/prompt.ts +0 -37
  76. package/src/cli/effect-cmd.ts +0 -96
  77. package/src/cli/error.ts +0 -130
  78. package/src/cli/heap.ts +0 -45
  79. package/src/cli/logo.ts +0 -2
  80. package/src/cli/network.ts +0 -80
  81. package/src/cli/ui.ts +0 -132
  82. package/src/command/index.ts +0 -177
  83. package/src/command/template/initialize.txt +0 -66
  84. package/src/command/template/review.txt +0 -101
  85. package/src/config/agent.ts +0 -59
  86. package/src/config/command.ts +0 -39
  87. package/src/config/config.ts +0 -707
  88. package/src/config/entry-name.ts +0 -19
  89. package/src/config/managed.ts +0 -69
  90. package/src/config/markdown.ts +0 -36
  91. package/src/config/parse.ts +0 -61
  92. package/src/config/paths.ts +0 -45
  93. package/src/config/plugin.ts +0 -79
  94. package/src/config/v2-compat.ts +0 -449
  95. package/src/config/variable.ts +0 -91
  96. package/src/control-plane/adapters/index.ts +0 -41
  97. package/src/control-plane/adapters/worktree.ts +0 -96
  98. package/src/control-plane/dev/README.md +0 -19
  99. package/src/control-plane/dev/debug-workspace-plugin.ts +0 -73
  100. package/src/control-plane/types.ts +0 -59
  101. package/src/control-plane/util.ts +0 -39
  102. package/src/control-plane/workspace-adapter-runtime.ts +0 -51
  103. package/src/control-plane/workspace-context.ts +0 -26
  104. package/src/control-plane/workspace.ts +0 -966
  105. package/src/effect/app-node-builder-v1.ts +0 -12
  106. package/src/effect/app-runtime.ts +0 -135
  107. package/src/effect/bootstrap-runtime.ts +0 -19
  108. package/src/effect/bridge.ts +0 -84
  109. package/src/effect/config-service.ts +0 -67
  110. package/src/effect/instance-ref.ts +0 -11
  111. package/src/effect/instance-registry.ts +0 -12
  112. package/src/effect/instance-state.ts +0 -69
  113. package/src/effect/promise.ts +0 -17
  114. package/src/effect/run-service.ts +0 -47
  115. package/src/effect/runner.ts +0 -217
  116. package/src/effect/runtime-flags.ts +0 -78
  117. package/src/env/index.ts +0 -41
  118. package/src/event-manifest.ts +0 -3
  119. package/src/event-v2-bridge.ts +0 -71
  120. package/src/format/formatter.ts +0 -404
  121. package/src/format/index.ts +0 -203
  122. package/src/git/index.ts +0 -348
  123. package/src/id/id.ts +0 -80
  124. package/src/ide/index.ts +0 -54
  125. package/src/image/image.ts +0 -172
  126. package/src/index-headless.ts +0 -89
  127. package/src/index.ts +0 -93
  128. package/src/installation/index.ts +0 -336
  129. package/src/lsp/client.ts +0 -650
  130. package/src/lsp/diagnostic.ts +0 -29
  131. package/src/lsp/language.ts +0 -121
  132. package/src/lsp/launch.ts +0 -21
  133. package/src/lsp/lsp.ts +0 -507
  134. package/src/lsp/server.ts +0 -1983
  135. package/src/markdown.d.ts +0 -4
  136. package/src/mcp/auth.ts +0 -163
  137. package/src/mcp/browser.ts +0 -37
  138. package/src/mcp/catalog.ts +0 -170
  139. package/src/mcp/index.ts +0 -1004
  140. package/src/mcp/oauth-callback.ts +0 -194
  141. package/src/mcp/oauth-provider.ts +0 -259
  142. package/src/node.ts +0 -4
  143. package/src/patch/index.ts +0 -686
  144. package/src/permission/arity.ts +0 -163
  145. package/src/permission/evaluate.ts +0 -1
  146. package/src/permission/index.ts +0 -223
  147. package/src/plugin/github-copilot/copilot.ts +0 -415
  148. package/src/plugin/github-copilot/models.ts +0 -261
  149. package/src/plugin/index.ts +0 -298
  150. package/src/plugin/install.ts +0 -439
  151. package/src/plugin/loader.ts +0 -237
  152. package/src/plugin/meta.ts +0 -188
  153. package/src/plugin/openai/README.md +0 -31
  154. package/src/plugin/openai/codex.ts +0 -575
  155. package/src/plugin/openai/ws-pool.ts +0 -271
  156. package/src/plugin/openai/ws.ts +0 -383
  157. package/src/plugin/pty-environment.ts +0 -24
  158. package/src/plugin/shared.ts +0 -323
  159. package/src/project/bootstrap-service.ts +0 -9
  160. package/src/project/bootstrap.ts +0 -58
  161. package/src/project/instance-context.ts +0 -24
  162. package/src/project/instance-runtime.ts +0 -16
  163. package/src/project/instance-store.ts +0 -213
  164. package/src/project/project.ts +0 -483
  165. package/src/project/vcs.ts +0 -423
  166. package/src/provider/auth.ts +0 -229
  167. package/src/provider/error.ts +0 -195
  168. package/src/provider/model-status.ts +0 -8
  169. package/src/provider/provider.ts +0 -1955
  170. package/src/provider/transform.ts +0 -1923
  171. package/src/question/index.ts +0 -161
  172. package/src/question/schema.ts +0 -4
  173. package/src/server/auth.ts +0 -48
  174. package/src/server/event.ts +0 -10
  175. package/src/server/global-lifecycle.ts +0 -28
  176. package/src/server/mdns.ts +0 -47
  177. package/src/server/proxy-util.ts +0 -48
  178. package/src/server/routes/instance/httpapi/AGENTS.md +0 -39
  179. package/src/server/routes/instance/httpapi/api.ts +0 -97
  180. package/src/server/routes/instance/httpapi/errors.ts +0 -193
  181. package/src/server/routes/instance/httpapi/groups/config.ts +0 -65
  182. package/src/server/routes/instance/httpapi/groups/control-plane.ts +0 -35
  183. package/src/server/routes/instance/httpapi/groups/control.ts +0 -76
  184. package/src/server/routes/instance/httpapi/groups/event.ts +0 -29
  185. package/src/server/routes/instance/httpapi/groups/experimental.ts +0 -275
  186. package/src/server/routes/instance/httpapi/groups/file.ts +0 -185
  187. package/src/server/routes/instance/httpapi/groups/global.ts +0 -139
  188. package/src/server/routes/instance/httpapi/groups/instance.ts +0 -206
  189. package/src/server/routes/instance/httpapi/groups/mcp.ts +0 -156
  190. package/src/server/routes/instance/httpapi/groups/metadata.ts +0 -18
  191. package/src/server/routes/instance/httpapi/groups/permission.ts +0 -61
  192. package/src/server/routes/instance/httpapi/groups/project-copy.ts +0 -32
  193. package/src/server/routes/instance/httpapi/groups/project.ts +0 -93
  194. package/src/server/routes/instance/httpapi/groups/provider.ts +0 -101
  195. package/src/server/routes/instance/httpapi/groups/pty.ts +0 -172
  196. package/src/server/routes/instance/httpapi/groups/query.ts +0 -12
  197. package/src/server/routes/instance/httpapi/groups/question.ts +0 -74
  198. package/src/server/routes/instance/httpapi/groups/session.ts +0 -462
  199. package/src/server/routes/instance/httpapi/groups/sync.ts +0 -113
  200. package/src/server/routes/instance/httpapi/groups/tui.ts +0 -208
  201. package/src/server/routes/instance/httpapi/groups/workspace.ts +0 -141
  202. package/src/server/routes/instance/httpapi/handlers/config.ts +0 -34
  203. package/src/server/routes/instance/httpapi/handlers/control-plane.ts +0 -37
  204. package/src/server/routes/instance/httpapi/handlers/control.ts +0 -43
  205. package/src/server/routes/instance/httpapi/handlers/event.ts +0 -99
  206. package/src/server/routes/instance/httpapi/handlers/experimental.ts +0 -193
  207. package/src/server/routes/instance/httpapi/handlers/file.ts +0 -139
  208. package/src/server/routes/instance/httpapi/handlers/global.ts +0 -126
  209. package/src/server/routes/instance/httpapi/handlers/instance.ts +0 -110
  210. package/src/server/routes/instance/httpapi/handlers/mcp.ts +0 -111
  211. package/src/server/routes/instance/httpapi/handlers/permission.ts +0 -41
  212. package/src/server/routes/instance/httpapi/handlers/project-copy.ts +0 -83
  213. package/src/server/routes/instance/httpapi/handlers/project.ts +0 -63
  214. package/src/server/routes/instance/httpapi/handlers/provider.ts +0 -116
  215. package/src/server/routes/instance/httpapi/handlers/pty.ts +0 -273
  216. package/src/server/routes/instance/httpapi/handlers/question.ts +0 -54
  217. package/src/server/routes/instance/httpapi/handlers/session-errors.ts +0 -21
  218. package/src/server/routes/instance/httpapi/handlers/session.ts +0 -442
  219. package/src/server/routes/instance/httpapi/handlers/sync.ts +0 -89
  220. package/src/server/routes/instance/httpapi/handlers/tui.ts +0 -131
  221. package/src/server/routes/instance/httpapi/handlers/workspace.ts +0 -102
  222. package/src/server/routes/instance/httpapi/lifecycle.ts +0 -54
  223. package/src/server/routes/instance/httpapi/middleware/authorization.ts +0 -150
  224. package/src/server/routes/instance/httpapi/middleware/compression.ts +0 -64
  225. package/src/server/routes/instance/httpapi/middleware/cors-vary.ts +0 -29
  226. package/src/server/routes/instance/httpapi/middleware/error.ts +0 -44
  227. package/src/server/routes/instance/httpapi/middleware/fence.ts +0 -25
  228. package/src/server/routes/instance/httpapi/middleware/instance-context.ts +0 -43
  229. package/src/server/routes/instance/httpapi/middleware/proxy.ts +0 -131
  230. package/src/server/routes/instance/httpapi/middleware/schema-error.ts +0 -41
  231. package/src/server/routes/instance/httpapi/middleware/workspace-routing.ts +0 -250
  232. package/src/server/routes/instance/httpapi/public.ts +0 -537
  233. package/src/server/routes/instance/httpapi/server.ts +0 -325
  234. package/src/server/routes/instance/httpapi/websocket-tracker.ts +0 -60
  235. package/src/server/server.ts +0 -224
  236. package/src/server/shared/fence.ts +0 -60
  237. package/src/server/shared/pty-ticket.ts +0 -15
  238. package/src/server/shared/public-ui.ts +0 -12
  239. package/src/server/shared/tui-control.ts +0 -28
  240. package/src/server/shared/ui.ts +0 -108
  241. package/src/server/shared/workspace-routing.ts +0 -45
  242. package/src/server/tui-event.ts +0 -1
  243. package/src/session/compaction.ts +0 -608
  244. package/src/session/instruction.ts +0 -237
  245. package/src/session/llm/AGENTS.md +0 -90
  246. package/src/session/llm/ai-sdk.ts +0 -291
  247. package/src/session/llm/native-request.ts +0 -196
  248. package/src/session/llm/native-runtime.ts +0 -195
  249. package/src/session/llm/request.ts +0 -226
  250. package/src/session/llm.ts +0 -390
  251. package/src/session/message-error.ts +0 -14
  252. package/src/session/message-v2.ts +0 -742
  253. package/src/session/message.ts +0 -148
  254. package/src/session/overflow.ts +0 -34
  255. package/src/session/processor.ts +0 -732
  256. package/src/session/prompt/build-switch.txt +0 -5
  257. package/src/session/prompt/default.txt +0 -35
  258. package/src/session/prompt/plan-mode.txt +0 -70
  259. package/src/session/prompt/plan.txt +0 -26
  260. package/src/session/prompt.ts +0 -1631
  261. package/src/session/reminders.ts +0 -92
  262. package/src/session/retry.ts +0 -209
  263. package/src/session/revert.ts +0 -136
  264. package/src/session/run-state.ts +0 -151
  265. package/src/session/schema.ts +0 -26
  266. package/src/session/session.ts +0 -1016
  267. package/src/session/status.ts +0 -56
  268. package/src/session/summary.ts +0 -160
  269. package/src/session/system.ts +0 -123
  270. package/src/session/todo.ts +0 -74
  271. package/src/session/tools.ts +0 -590
  272. package/src/share/session.ts +0 -58
  273. package/src/share/share-next.ts +0 -371
  274. package/src/skill/discovery.ts +0 -140
  275. package/src/skill/index.ts +0 -354
  276. package/src/snapshot/index.ts +0 -807
  277. package/src/sql.d.ts +0 -4
  278. package/src/storage/schema.ts +0 -5
  279. package/src/storage/storage.ts +0 -327
  280. package/src/sync/README.md +0 -179
  281. package/src/sync/schema.ts +0 -11
  282. package/src/temporary.ts +0 -31
  283. package/src/tool/apply_patch.ts +0 -313
  284. package/src/tool/apply_patch.txt +0 -14
  285. package/src/tool/code-mode.ts +0 -310
  286. package/src/tool/edit.ts +0 -737
  287. package/src/tool/edit.txt +0 -13
  288. package/src/tool/external-directory.ts +0 -49
  289. package/src/tool/glob.ts +0 -76
  290. package/src/tool/glob.txt +0 -7
  291. package/src/tool/grep.ts +0 -115
  292. package/src/tool/grep.txt +0 -7
  293. package/src/tool/invalid.ts +0 -21
  294. package/src/tool/json-schema.ts +0 -164
  295. package/src/tool/lsp.ts +0 -113
  296. package/src/tool/lsp.txt +0 -1
  297. package/src/tool/mcp-websearch.ts +0 -96
  298. package/src/tool/plan-enter.txt +0 -1
  299. package/src/tool/plan-exit.txt +0 -1
  300. package/src/tool/plan.ts +0 -79
  301. package/src/tool/question.ts +0 -44
  302. package/src/tool/question.txt +0 -1
  303. package/src/tool/read.ts +0 -386
  304. package/src/tool/read.txt +0 -13
  305. package/src/tool/registry.ts +0 -455
  306. package/src/tool/schema.ts +0 -14
  307. package/src/tool/shell/id.ts +0 -19
  308. package/src/tool/shell/prompt.ts +0 -293
  309. package/src/tool/shell/shell.txt +0 -21
  310. package/src/tool/shell.ts +0 -645
  311. package/src/tool/skill.ts +0 -70
  312. package/src/tool/skill.txt +0 -1
  313. package/src/tool/task.ts +0 -371
  314. package/src/tool/task.txt +0 -11
  315. package/src/tool/todo.ts +0 -46
  316. package/src/tool/todowrite.txt +0 -11
  317. package/src/tool/tool.ts +0 -183
  318. package/src/tool/truncate.ts +0 -156
  319. package/src/tool/truncation-dir.ts +0 -4
  320. package/src/tool/webfetch.ts +0 -192
  321. package/src/tool/webfetch.txt +0 -9
  322. package/src/tool/websearch.ts +0 -143
  323. package/src/tool/websearch.txt +0 -9
  324. package/src/tool/write.ts +0 -104
  325. package/src/tool/write.txt +0 -11
  326. package/src/util/archive.ts +0 -17
  327. package/src/util/bom.ts +0 -27
  328. package/src/util/data-url.ts +0 -9
  329. package/src/util/defer.ts +0 -10
  330. package/src/util/effect-http-client.ts +0 -11
  331. package/src/util/error.ts +0 -16
  332. package/src/util/filesystem.ts +0 -251
  333. package/src/util/html.ts +0 -8
  334. package/src/util/iife.ts +0 -3
  335. package/src/util/lazy.ts +0 -20
  336. package/src/util/local-context.ts +0 -25
  337. package/src/util/locale.ts +0 -4
  338. package/src/util/media.ts +0 -26
  339. package/src/util/process.ts +0 -177
  340. package/src/util/proxy-env.ts +0 -72
  341. package/src/util/queue.ts +0 -32
  342. package/src/util/record.ts +0 -3
  343. package/src/util/repository.ts +0 -232
  344. package/src/util/rpc.ts +0 -66
  345. package/src/util/signal.ts +0 -12
  346. package/src/util/timeout.ts +0 -13
  347. package/src/util/token.ts +0 -1
  348. package/src/util/wildcard.ts +0 -59
  349. package/src/worktree/index.ts +0 -623
  350. package/sst-env.d.ts +0 -10
  351. package/tsconfig.headless.json +0 -13
  352. package/tsconfig.json +0 -16
package/src/sql.d.ts DELETED
@@ -1,4 +0,0 @@
1
- declare module "*.sql" {
2
- const content: string
3
- export default content
4
- }
@@ -1,5 +0,0 @@
1
- export { AccountTable, AccountStateTable, ControlAccountTable } from "@soraacode/core/account/sql"
2
- export { ProjectTable } from "@soraacode/core/project/sql"
3
- export { SessionTable, MessageTable, PartTable, TodoTable } from "@soraacode/core/session/sql"
4
- export { SessionShareTable } from "@soraacode/core/share/sql"
5
- export { WorkspaceTable } from "@soraacode/core/control-plane/workspace.sql"
@@ -1,327 +0,0 @@
1
- import { LayerNode } from "@soraacode/core/effect/layer-node"
2
- import path from "path"
3
- import { Global } from "@soraacode/core/global"
4
- import { FSUtil } from "@soraacode/core/fs-util"
5
- import { Effect, Exit, Layer, Option, RcMap, Schema, Context, TxReentrantLock } from "effect"
6
- import { NonNegativeInt } from "@soraacode/core/schema"
7
- import { Git } from "@/git"
8
-
9
- type Migration = (dir: string, fs: FSUtil.Interface, git: Git.Interface) => Effect.Effect<void, FSUtil.Error>
10
-
11
- export class NotFoundError extends Schema.TaggedErrorClass<NotFoundError>()("NotFoundError", {
12
- message: Schema.String,
13
- }) {
14
- static isInstance(input: unknown): input is NotFoundError {
15
- return input instanceof NotFoundError
16
- }
17
- }
18
-
19
- export type Error = FSUtil.Error | NotFoundError
20
-
21
- const RootFile = Schema.Struct({
22
- path: Schema.optional(
23
- Schema.Struct({
24
- root: Schema.optional(Schema.String),
25
- }),
26
- ),
27
- })
28
-
29
- const SessionFile = Schema.Struct({
30
- id: Schema.String,
31
- })
32
-
33
- const MessageFile = Schema.Struct({
34
- id: Schema.String,
35
- })
36
-
37
- const DiffFile = Schema.Struct({
38
- additions: NonNegativeInt,
39
- deletions: NonNegativeInt,
40
- })
41
-
42
- const SummaryFile = Schema.Struct({
43
- id: Schema.String,
44
- projectID: Schema.String,
45
- summary: Schema.Struct({ diffs: Schema.Array(DiffFile) }),
46
- })
47
-
48
- const decodeRoot = Schema.decodeUnknownOption(RootFile)
49
- const decodeSession = Schema.decodeUnknownOption(SessionFile)
50
- const decodeMessage = Schema.decodeUnknownOption(MessageFile)
51
- const decodeSummary = Schema.decodeUnknownOption(SummaryFile)
52
-
53
- export interface Interface {
54
- readonly remove: (key: string[]) => Effect.Effect<void, FSUtil.Error>
55
- readonly read: <T>(key: string[]) => Effect.Effect<T, Error>
56
- readonly update: <T>(key: string[], fn: (draft: T) => void) => Effect.Effect<T, Error>
57
- readonly write: <T>(key: string[], content: T) => Effect.Effect<void, FSUtil.Error>
58
- readonly list: (prefix: string[]) => Effect.Effect<string[][], FSUtil.Error>
59
- }
60
-
61
- export class Service extends Context.Service<Service, Interface>()("@opencode/Storage") {}
62
-
63
- function file(dir: string, key: string[]) {
64
- return path.join(dir, ...key) + ".json"
65
- }
66
-
67
- function missing(err: unknown) {
68
- if (!err || typeof err !== "object") return false
69
- if ("code" in err && err.code === "ENOENT") return true
70
- if ("reason" in err && err.reason && typeof err.reason === "object" && "_tag" in err.reason) {
71
- return err.reason._tag === "NotFound"
72
- }
73
- return false
74
- }
75
-
76
- function parseMigration(text: string) {
77
- const value = Number.parseInt(text, 10)
78
- return Number.isNaN(value) ? 0 : value
79
- }
80
-
81
- const MIGRATIONS: Migration[] = [
82
- Effect.fn("Storage.migration.1")(function* (dir: string, fs: FSUtil.Interface, git: Git.Interface) {
83
- const project = path.resolve(dir, "../project")
84
- if (!(yield* fs.isDir(project))) return
85
- const projectDirs = yield* fs.glob("*", {
86
- cwd: project,
87
- include: "all",
88
- })
89
- for (const projectDir of projectDirs) {
90
- const full = path.join(project, projectDir)
91
- if (!(yield* fs.isDir(full))) continue
92
- yield* Effect.logInfo(`migrating project ${projectDir}`)
93
- let projectID = projectDir
94
- let worktree = "/"
95
-
96
- if (projectID !== "global") {
97
- for (const msgFile of yield* fs.glob("storage/session/message/*/*.json", {
98
- cwd: full,
99
- absolute: true,
100
- })) {
101
- const json = decodeRoot(yield* fs.readJson(msgFile), { onExcessProperty: "preserve" })
102
- const root = Option.isSome(json) ? json.value.path?.root : undefined
103
- if (!root) continue
104
- worktree = root
105
- break
106
- }
107
- if (!worktree) continue
108
- if (!(yield* fs.isDir(worktree))) continue
109
- const result = yield* git.run(["rev-list", "--max-parents=0", "--all"], {
110
- cwd: worktree,
111
- })
112
- const [id] = result
113
- .text()
114
- .split("\n")
115
- .filter(Boolean)
116
- .map((x) => x.trim())
117
- .toSorted()
118
- if (!id) continue
119
- projectID = id
120
-
121
- yield* fs.writeWithDirs(
122
- path.join(dir, "project", projectID + ".json"),
123
- JSON.stringify(
124
- {
125
- id,
126
- vcs: "git",
127
- worktree,
128
- time: {
129
- created: Date.now(),
130
- initialized: Date.now(),
131
- },
132
- },
133
- null,
134
- 2,
135
- ),
136
- )
137
-
138
- yield* Effect.logInfo(`migrating sessions for project ${projectID}`)
139
- for (const sessionFile of yield* fs.glob("storage/session/info/*.json", {
140
- cwd: full,
141
- absolute: true,
142
- })) {
143
- const dest = path.join(dir, "session", projectID, path.basename(sessionFile))
144
- yield* Effect.logInfo("copying", { sessionFile, dest })
145
- const session = yield* fs.readJson(sessionFile)
146
- const info = decodeSession(session, { onExcessProperty: "preserve" })
147
- yield* fs.writeWithDirs(dest, JSON.stringify(session, null, 2))
148
- if (Option.isNone(info)) continue
149
- yield* Effect.logInfo(`migrating messages for session ${info.value.id}`)
150
- for (const msgFile of yield* fs.glob(`storage/session/message/${info.value.id}/*.json`, {
151
- cwd: full,
152
- absolute: true,
153
- })) {
154
- const next = path.join(dir, "message", info.value.id, path.basename(msgFile))
155
- yield* Effect.logInfo("copying", {
156
- msgFile,
157
- dest: next,
158
- })
159
- const message = yield* fs.readJson(msgFile)
160
- const item = decodeMessage(message, { onExcessProperty: "preserve" })
161
- yield* fs.writeWithDirs(next, JSON.stringify(message, null, 2))
162
- if (Option.isNone(item)) continue
163
-
164
- yield* Effect.logInfo(`migrating parts for message ${item.value.id}`)
165
- for (const partFile of yield* fs.glob(`storage/session/part/${info.value.id}/${item.value.id}/*.json`, {
166
- cwd: full,
167
- absolute: true,
168
- })) {
169
- const out = path.join(dir, "part", item.value.id, path.basename(partFile))
170
- const part = yield* fs.readJson(partFile)
171
- yield* Effect.logInfo("copying", {
172
- partFile,
173
- dest: out,
174
- })
175
- yield* fs.writeWithDirs(out, JSON.stringify(part, null, 2))
176
- }
177
- }
178
- }
179
- }
180
- }
181
- }),
182
- Effect.fn("Storage.migration.2")(function* (dir: string, fs: FSUtil.Interface) {
183
- for (const item of yield* fs.glob("session/*/*.json", {
184
- cwd: dir,
185
- absolute: true,
186
- })) {
187
- const raw = yield* fs.readJson(item)
188
- const session = decodeSummary(raw, { onExcessProperty: "preserve" })
189
- if (Option.isNone(session)) continue
190
- const diffs = session.value.summary.diffs
191
- yield* fs.writeWithDirs(
192
- path.join(dir, "session_diff", session.value.id + ".json"),
193
- JSON.stringify(diffs, null, 2),
194
- )
195
- yield* fs.writeWithDirs(
196
- path.join(dir, "session", session.value.projectID, session.value.id + ".json"),
197
- JSON.stringify(
198
- {
199
- ...(raw as Record<string, unknown>),
200
- summary: {
201
- additions: diffs.reduce((sum, x) => sum + x.additions, 0),
202
- deletions: diffs.reduce((sum, x) => sum + x.deletions, 0),
203
- },
204
- },
205
- null,
206
- 2,
207
- ),
208
- )
209
- }
210
- }),
211
- ]
212
-
213
- const layer = Layer.effect(
214
- Service,
215
- Effect.gen(function* () {
216
- const fs = yield* FSUtil.Service
217
- const git = yield* Git.Service
218
- const locks = yield* RcMap.make({
219
- lookup: () => TxReentrantLock.make(),
220
- idleTimeToLive: 0,
221
- })
222
- const state = yield* Effect.cached(
223
- Effect.gen(function* () {
224
- const dir = path.join(Global.Path.data, "storage")
225
- const marker = path.join(dir, "migration")
226
- const migration = yield* fs.readFileString(marker).pipe(
227
- Effect.map(parseMigration),
228
- Effect.catchIf(missing, () => Effect.succeed(0)),
229
- Effect.orElseSucceed(() => 0),
230
- )
231
- for (let i = migration; i < MIGRATIONS.length; i++) {
232
- yield* Effect.logInfo("running migration", { index: i })
233
- const step = MIGRATIONS[i]!
234
- const exit = yield* Effect.exit(step(dir, fs, git))
235
- if (Exit.isFailure(exit)) {
236
- yield* Effect.logError("failed to run migration", { index: i, cause: exit.cause })
237
- break
238
- }
239
- yield* fs.writeWithDirs(marker, String(i + 1))
240
- }
241
- return { dir }
242
- }),
243
- )
244
-
245
- const fail = (target: string): Effect.Effect<never, NotFoundError> =>
246
- Effect.fail(new NotFoundError({ message: `Resource not found: ${target}` }))
247
-
248
- const wrap = <A>(target: string, body: Effect.Effect<A, FSUtil.Error>) =>
249
- body.pipe(Effect.catchIf(missing, () => fail(target)))
250
-
251
- const writeJson = Effect.fnUntraced(function* (target: string, content: unknown) {
252
- yield* fs.writeWithDirs(target, JSON.stringify(content, null, 2))
253
- })
254
-
255
- const withResolved = <A, E>(
256
- key: string[],
257
- fn: (target: string, rw: TxReentrantLock.TxReentrantLock) => Effect.Effect<A, E>,
258
- ): Effect.Effect<A, E | FSUtil.Error> =>
259
- Effect.scoped(
260
- Effect.gen(function* () {
261
- const target = file((yield* state).dir, key)
262
- return yield* fn(target, yield* RcMap.get(locks, target))
263
- }),
264
- )
265
-
266
- const remove: Interface["remove"] = Effect.fn("Storage.remove")(function* (key: string[]) {
267
- yield* withResolved(key, (target, rw) =>
268
- TxReentrantLock.withWriteLock(rw, fs.remove(target).pipe(Effect.catchIf(missing, () => Effect.void))),
269
- )
270
- })
271
-
272
- const read: Interface["read"] = <T>(key: string[]) =>
273
- Effect.gen(function* () {
274
- const value = yield* withResolved(key, (target, rw) =>
275
- TxReentrantLock.withReadLock(rw, wrap(target, fs.readJson(target))),
276
- )
277
- return value as T
278
- })
279
-
280
- const update: Interface["update"] = <T>(key: string[], fn: (draft: T) => void) =>
281
- Effect.gen(function* () {
282
- const value = yield* withResolved(key, (target, rw) =>
283
- TxReentrantLock.withWriteLock(
284
- rw,
285
- Effect.gen(function* () {
286
- const content = yield* wrap(target, fs.readJson(target))
287
- fn(content as T)
288
- yield* writeJson(target, content)
289
- return content
290
- }),
291
- ),
292
- )
293
- return value as T
294
- })
295
-
296
- const write: Interface["write"] = (key: string[], content: unknown) =>
297
- Effect.gen(function* () {
298
- yield* withResolved(key, (target, rw) => TxReentrantLock.withWriteLock(rw, writeJson(target, content)))
299
- })
300
-
301
- const list: Interface["list"] = Effect.fn("Storage.list")(function* (prefix: string[]) {
302
- const dir = (yield* state).dir
303
- const cwd = path.join(dir, ...prefix)
304
- const result = yield* fs
305
- .glob("**/*", {
306
- cwd,
307
- include: "file",
308
- })
309
- .pipe(Effect.catch(() => Effect.succeed<string[]>([])))
310
- return result
311
- .map((x) => [...prefix, ...x.slice(0, -5).split(path.sep)])
312
- .toSorted((a, b) => a.join("/").localeCompare(b.join("/")))
313
- })
314
-
315
- return Service.of({
316
- remove,
317
- read,
318
- update,
319
- write,
320
- list,
321
- })
322
- }),
323
- )
324
-
325
- export const node = LayerNode.make({ service: Service, layer: layer, deps: [FSUtil.node, Git.node] })
326
-
327
- export * as Storage from "./storage"
@@ -1,179 +0,0 @@
1
- tl;dr All of these APIs work, are properly type-checked, and are sync events are backwards compatible with `Bus`:
2
-
3
- ```ts
4
- // The schema from `Updated` typechecks the object correctly
5
- SyncEvent.run(Updated, { sessionID: id, info: { title: "foo" } })
6
-
7
- // `subscribeAll` passes a generic sync event
8
- SyncEvent.subscribeAll((event) => {
9
- // These will be type-checked correctly
10
- event.id
11
- event.seq
12
- // This will be unknown because we are listening for all events,
13
- // and this API is only used to record them
14
- event.data
15
- })
16
-
17
- // This works, but you shouldn't publish sync event like this (should fail in the future)
18
- Bus.publish(Updated, { sessionID: id, info: { title: "foo" } })
19
-
20
- // Update event is fully type-checked
21
- Bus.subscribe(Updated, (event) => event.properties.info.title)
22
-
23
- // Update event is fully type-checked
24
- client.subscribe("session.updated", (evt) => evt.properties.info.title)
25
- ```
26
-
27
- # Goal
28
-
29
- ## Syncing with only one writer
30
-
31
- This system defines a basic event sourcing system for session replayability. The goal is to allow for one device to control and modify the session, and allow multiple other devices to "sync" session data. The sync works by getting a log of events to replay and replaying them locally.
32
-
33
- Because only one device is allowed to write, we don't need any kind of sophisticated distributed system clocks or causal ordering. We implement total ordering with a simple sequence id (a number) and increment it by one every time we generate an event.
34
-
35
- ## Bus event integration and backwards compatibility
36
-
37
- This initial implementation aims to be fully backwards compatible. We should be able to land this without any visible changes to the user.
38
-
39
- An existing `Bus` abstraction to send events already exists. We already send events like `session.created` through the system. We should not duplicate this.
40
-
41
- The difference in event sourcing is events are sent _before_ the mutation happens, and "projectors" handle the effects and perform the mutations. This difference is subtle, and a necessary change for syncing to work.
42
-
43
- So the goal is:
44
-
45
- - Introduce a new syncing abstraction to handle event sourcing and projectors
46
- - Seamlessly integrate these new events into the same existing `Bus` abstraction
47
- - Maintain full backwards compatibility to reduce risk
48
-
49
- ## My approach
50
-
51
- This directory introduces a new abstraction: `SyncEvent`. This handles all of the event sourcing.
52
-
53
- There are now "sync events" which are different than "bus events". Bus events are defined like this:
54
-
55
- ```ts
56
- const Diff = BusEvent.define(
57
- "session.diff",
58
- Schema.Struct({
59
- sessionID: SessionID,
60
- diff: Schema.Array(Snapshot.FileDiff),
61
- }),
62
- )
63
- ```
64
-
65
- You can do `Bus.publish(Diff, { ... })` to push these events, and `Bus.subscribe(Diff, handler)` to listen to them.
66
-
67
- Sync events are a lower-level abstraction which are similar, but also handle the requirements for recording and replaying. Defining them looks like this:
68
-
69
- ```ts
70
- const Created = SyncEvent.define({
71
- type: "session.created",
72
- version: 1,
73
- aggregate: "sessionID",
74
- schema: Schema.Struct({
75
- sessionID: SessionID,
76
- info: Info,
77
- }),
78
- })
79
- ```
80
-
81
- Not too different, except they track a version and an "aggregate" field (will explain that later).
82
-
83
- You do this to run an event, which is kind of like `Bus.publish` except that it runs through the event sourcing system:
84
-
85
- ```
86
- SyncEvent.run(Created, { ... })
87
- ```
88
-
89
- The data passed as the second argument is properly type-checked based on the schema defined in `Created`.
90
-
91
- Importantly, **sync events automatically re-publish as bus events**. This makes them backwards compatible, and allows the `Bus` to still be the single abstraction that the system uses to listen for individual events.
92
-
93
- **We have upgraded many of the session events to be sync events** (all of the ones that mutate the db). Sync and bus events are largely compatible. Here are the differences:
94
-
95
- ### Event shape
96
-
97
- - The shape of the events are slightly different. A sync event has the `type`, `id`, `seq`, `aggregateID`, and `data` fields. A bus event has the `type` and `properties` fields. `data` and `properties` are largely the same thing. This conversion is automatically handled when the sync system re-published the event through the bus.
98
-
99
- The reason for this is because sync events need to track more information. I chose not to copy the `properties` naming to more clearly disambiguate the event types.
100
-
101
- ### Event flow
102
-
103
- There is no way to subscribe to individual sync events in `SyncEvent`. You can use `subscribeAll` to receive _all_ of the events, which is needed for clients that want to record them.
104
-
105
- To listen for individual events, use `Bus.subscribe`. You can pass in a sync event definition to it: `Bus.subscribe(Created, handler)`. This is fully supported.
106
-
107
- You should never "publish" a sync event however: `Bus.publish(Created, ...)`. I would like to force this to be a type error in the future. You should never be touching the db directly, and should not be manually handling these events.
108
-
109
- ### Backwards compatibility
110
-
111
- The system install projectors in `server/projectors.js`. It calls `SyncEvent.init` to do this. It also installs a hook for dynamically converting an event at runtime (`convertEvent`).
112
-
113
- This allows you to "reshape" an event from the sync system before it's published to the bus. This should be avoided, but might be necessary for temporary backwards compat.
114
-
115
- The only time we use this is the `session.updated` event. Previously this event contained the entire session object. The sync event only contains the fields updated. We convert the event to contain the full object for backwards compatibility (but ideally we'd remove this).
116
-
117
- It's very important that types are correct when working with events. Event definitions have a `schema` which carries the definition of the event shape. Examples:
118
-
119
- ```ts
120
- // The schema from `Updated` typechecks the object correctly
121
- SyncEvent.run(Updated, { sessionID: id, info: { title: "foo" } })
122
-
123
- // `subscribeAll` passes a generic sync event
124
- SyncEvent.subscribeAll((event) => {
125
- // These will be type-checked correctly
126
- event.id
127
- event.seq
128
- // This will be unknown because we are listening for all events,
129
- // and this API is only used to record them
130
- event.data
131
- })
132
-
133
- // This works, but you shouldn't publish sync event like this (should fail in the future)
134
- Bus.publish(Updated, { sessionID: id, info: { title: "foo" } })
135
-
136
- // Update event is fully type-checked
137
- Bus.subscribe(Updated, (event) => event.properties.info.title)
138
-
139
- // Update event is fully type-checked
140
- client.subscribe("session.updated", (evt) => evt.properties.info.title)
141
- ```
142
-
143
- The last two examples look similar to `SyncEvent.run`, but they were the cause of a lot of grief. Those are existing APIs that we can't break, but we are passing in the new sync event definitions to these APIs, which sometimes have a different event shape.
144
-
145
- I previously mentioned the runtime conversion of events, but we still need to the types to work! To do that, the `define` API supports an optional `busSchema` prop to give it the schema for backwards compatibility. For example this is the full definition of `Session.Update`:
146
-
147
- ```ts
148
- const Update = SyncEvent.define({
149
- type: "session.updated",
150
- version: 1,
151
- aggregate: "sessionID",
152
- schema: Schema.Struct({
153
- sessionID: SessionID,
154
- info: partialSchema(Info),
155
- }),
156
- busSchema: Schema.Struct({
157
- sessionID: SessionID,
158
- info: Info,
159
- }),
160
- })
161
- ```
162
-
163
- _Important_: the conversion done in `convertEvent` is not automatically type-checked with `busSchema`. It's very important they match, but because we need this at type-checking time this needs to live here.
164
-
165
- Internally, the way this works is `busSchema` is stored on a `properties` field which is what the bus system expects. Doing this made everything with `Bus` "just work". This is why you can pass a sync event to the bus APIs.
166
-
167
- _Alternatives_
168
-
169
- These are some other paths I explored:
170
-
171
- - Providing a way to subscribe to individual sync events, and change all the instances of `Bus.subscribe` in our code to it. Then you are directly only working with sync events always.
172
- - Two big problems. First, `Bus` is instance-scoped, and we'd need to make the sync event system instance-scoped too for backwards compat. If we didn't, those listeners would get calls for events they weren't expecting.
173
- - Second, we can't change consumers of our SDK. So they still have to use the old events, and we might as well stick with them for consistency
174
- - Directly add sync event support to bus system
175
- - I explored adding sync events to the bus, but due to backwards compat, it only made it more complicated (still need to support both shapes)
176
- - I explored a `convertSchema` function to convert the event schema at runtime so we didn't need `busSchema`
177
- - Fatal flaw: we need type-checking done earlier. We can't do this at run-time. This worked for consumers of our SDK (because it gets generated TS types from the converted schema) but breaks for our internal usage of `Bus.subscribe` calls
178
-
179
- I explored many other permutations of the above solutions. What we have today I think is the best balance of backwards compatibility while opening a path forward for the new events.
@@ -1,11 +0,0 @@
1
- import { Schema } from "effect"
2
-
3
- import { Identifier } from "@/id/id"
4
- import { statics } from "@soraacode/core/schema"
5
-
6
- export const EventID = Schema.String.check(Schema.isStartsWith("evt")).pipe(
7
- Schema.brand("EventID"),
8
- statics((s) => ({
9
- ascending: (id?: string) => s.make(Identifier.ascending("event", id)),
10
- })),
11
- )
package/src/temporary.ts DELETED
@@ -1,31 +0,0 @@
1
- import yargs from "yargs"
2
- import { TuiThreadCommand } from "./cli/cmd/tui"
3
- import { InstallationVersion } from "@soraacode/core/installation/version"
4
- import { hideBin } from "yargs/helpers"
5
- const cli = yargs(hideBin(process.argv))
6
- .parserConfiguration({ "populate--": true })
7
- .scriptName("opencode")
8
- .wrap(100)
9
- .help("help", "show help")
10
- .alias("help", "h")
11
- .version("version", "show version number", InstallationVersion)
12
- .alias("version", "v")
13
- .option("print-logs", {
14
- describe: "print logs to stderr",
15
- type: "boolean",
16
- })
17
- .option("log-level", {
18
- describe: "log level",
19
- type: "string",
20
- choices: ["DEBUG", "INFO", "WARN", "ERROR"],
21
- })
22
- .option("pure", {
23
- describe: "run without external plugins",
24
- type: "boolean",
25
- })
26
- .middleware((opts) => {
27
- if (opts.printLogs) process.env.OPENCODE_PRINT_LOGS = "1"
28
- if (opts.logLevel) process.env.OPENCODE_LOG_LEVEL = opts.logLevel
29
- })
30
- .command(TuiThreadCommand)
31
- .parse()