@oar-sh/oar 0.9.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 (293) hide show
  1. package/.github/extensions/web-relay/README.md +190 -0
  2. package/.github/extensions/web-relay/extension.mjs +972 -0
  3. package/.github/extensions/web-relay/model-api/model-switching.mjs +338 -0
  4. package/.github/extensions/web-relay/polling/heartbeat.mjs +55 -0
  5. package/.github/extensions/web-relay/polling/polling-loop.mjs +1105 -0
  6. package/.github/extensions/web-relay/runtime/api-client.mjs +34 -0
  7. package/.github/extensions/web-relay/runtime/banner-state.mjs +126 -0
  8. package/.github/extensions/web-relay/runtime/config-loader.mjs +104 -0
  9. package/.github/extensions/web-relay/runtime/debug-log.mjs +27 -0
  10. package/.github/extensions/web-relay/runtime/send-and-wait-errors.mjs +170 -0
  11. package/.github/extensions/web-relay/runtime/session-binding.mjs +78 -0
  12. package/.github/extensions/web-relay/runtime/session-io.mjs +349 -0
  13. package/.github/extensions/web-relay/runtime/session-join-retry.mjs +44 -0
  14. package/.github/extensions/web-relay/runtime/session-registry.mjs +29 -0
  15. package/.github/extensions/web-relay/runtime/session-runtime-manager.mjs +156 -0
  16. package/.github/extensions/web-relay/runtime/session-sync-bridge.mjs +74 -0
  17. package/.github/extensions/web-relay/runtime/worker-websocket-link.mjs +325 -0
  18. package/.github/extensions/web-relay/runtime/workspace-root.mjs +55 -0
  19. package/.github/extensions/web-relay/server-lifecycle/managed-server.mjs +386 -0
  20. package/.github/extensions/web-relay/skills/prompt-context.mjs +160 -0
  21. package/.github/extensions/web-relay/skills/question-bridge.mjs +141 -0
  22. package/.github/extensions/web-relay/skills/question-routing-hooks.mjs +463 -0
  23. package/.github/extensions/web-relay/skills/reasoning-stream.mjs +285 -0
  24. package/.github/extensions/web-relay/skills/subagent-lifecycle.mjs +345 -0
  25. package/.github/extensions/web-relay/skills/tool-activity.mjs +243 -0
  26. package/.github/extensions/web-relay/utils/process-utils.mjs +25 -0
  27. package/.github/extensions/web-relay/utils/token-generation.mjs +58 -0
  28. package/CHANGELOG.md +25 -0
  29. package/LICENSE +21 -0
  30. package/README.md +717 -0
  31. package/bin/bat/oar.bat +46 -0
  32. package/bin/oar.js +665 -0
  33. package/package.json +68 -0
  34. package/server/README.md +1379 -0
  35. package/server/claude-worker/claude-attachments.mjs +134 -0
  36. package/server/claude-worker/claude-preview-tool.mjs +57 -0
  37. package/server/claude-worker/claude-sdk-adapter.mjs +420 -0
  38. package/server/claude-worker/claude-session-process.mjs +2391 -0
  39. package/server/claude-worker/claude-session-test-harness.mjs +236 -0
  40. package/server/claude-worker/claude-session-worker.mjs +186 -0
  41. package/server/claude-worker/claude-transcript-relocator.mjs +189 -0
  42. package/server/claude-worker/claude-turn-publisher.mjs +291 -0
  43. package/server/claude-worker/fixtures/workflow-run-record.json +1 -0
  44. package/server/claude-worker/sdk-message-normalizer.mjs +629 -0
  45. package/server/claude-worker/workflow-progress-digest.mjs +269 -0
  46. package/server/copilot-sdk-runtime.mjs +126 -0
  47. package/server/copilot-worker/copilot-attachments.mjs +97 -0
  48. package/server/copilot-worker/copilot-byok-provider.mjs +121 -0
  49. package/server/copilot-worker/copilot-continuation-signals.mjs +349 -0
  50. package/server/copilot-worker/copilot-plan-board.mjs +110 -0
  51. package/server/copilot-worker/copilot-prompt-context.mjs +86 -0
  52. package/server/copilot-worker/copilot-question-bridge.mjs +216 -0
  53. package/server/copilot-worker/copilot-sdk-adapter.mjs +494 -0
  54. package/server/copilot-worker/copilot-sdk-event-normalizer.mjs +800 -0
  55. package/server/copilot-worker/copilot-sdk-session-process.mjs +1729 -0
  56. package/server/copilot-worker/copilot-sdk-session-worker.mjs +167 -0
  57. package/server/copilot-worker/copilot-sdk-test-harness.mjs +329 -0
  58. package/server/copilot-worker/fixtures/abort-turn.json +588 -0
  59. package/server/copilot-worker/fixtures/ask-user-turn.json +1154 -0
  60. package/server/copilot-worker/fixtures/background-timer-continuation.json +445 -0
  61. package/server/copilot-worker/fixtures/background-timer-turn.json +497 -0
  62. package/server/copilot-worker/fixtures/happy-turn.json +566 -0
  63. package/server/copilot-worker/fixtures/quota-turn.json +647 -0
  64. package/server/copilot-worker/fixtures/reasoning-turn.json +625 -0
  65. package/server/copilot-worker/fixtures/resume-turn.json +700 -0
  66. package/server/copilot-worker/fixtures/subagent-turn.json +1378 -0
  67. package/server/copilot-worker/fixtures/tool-permission-turn.json +1293 -0
  68. package/server/cursor-worker/cursor-ask-user-tool.mjs +83 -0
  69. package/server/cursor-worker/cursor-attachments.mjs +88 -0
  70. package/server/cursor-worker/cursor-context-usage.mjs +85 -0
  71. package/server/cursor-worker/cursor-preview-tool.mjs +44 -0
  72. package/server/cursor-worker/cursor-sdk-adapter.mjs +554 -0
  73. package/server/cursor-worker/cursor-session-worker.mjs +145 -0
  74. package/server/cursor-worker/cursor-subagent-roster.mjs +81 -0
  75. package/server/cursor-worker/cursor-turn-runner.mjs +717 -0
  76. package/server/cursor-worker/sdk-message-normalizer.mjs +485 -0
  77. package/server/db-schema.mjs +932 -0
  78. package/server/features.mjs +106 -0
  79. package/server/grok-worker/acp-client.mjs +434 -0
  80. package/server/grok-worker/acp-host-services.mjs +277 -0
  81. package/server/grok-worker/grok-context-usage.mjs +77 -0
  82. package/server/grok-worker/grok-sdk-adapter.mjs +334 -0
  83. package/server/grok-worker/grok-session-worker.mjs +145 -0
  84. package/server/grok-worker/grok-turn-runner.mjs +523 -0
  85. package/server/grok-worker/sdk-message-normalizer.mjs +311 -0
  86. package/server/migrations/0001-add-structured-answer.mjs +80 -0
  87. package/server/migrations/0002-recent-workspace-roots-path-key.mjs +121 -0
  88. package/server/migrations/0003-push-subscriptions.mjs +87 -0
  89. package/server/public/app/action-confirmations.js +267 -0
  90. package/server/public/app/activity-replay-state.mjs +149 -0
  91. package/server/public/app/api-client.js +1082 -0
  92. package/server/public/app/ask-user-view.js +545 -0
  93. package/server/public/app/attachments-view.js +2112 -0
  94. package/server/public/app/auto-compact-window-options.mjs +59 -0
  95. package/server/public/app/background-tasks-view.mjs +494 -0
  96. package/server/public/app/bootstrap.js +4347 -0
  97. package/server/public/app/claude-auth-ui.js +394 -0
  98. package/server/public/app/claude-thinking-options.mjs +38 -0
  99. package/server/public/app/cli-install-ui.js +455 -0
  100. package/server/public/app/code-copy.mjs +85 -0
  101. package/server/public/app/composer-attachment-cache.mjs +151 -0
  102. package/server/public/app/composer-control-state.mjs +107 -0
  103. package/server/public/app/composer-paste.mjs +132 -0
  104. package/server/public/app/context-tier-options.mjs +68 -0
  105. package/server/public/app/context-usage-view.mjs +338 -0
  106. package/server/public/app/conversation-draft-timestamp-utils.mjs +11 -0
  107. package/server/public/app/conversation-preferences.mjs +91 -0
  108. package/server/public/app/conversation-provider-indicator.mjs +88 -0
  109. package/server/public/app/conversation-view.js +2898 -0
  110. package/server/public/app/cwd-menu-placement.mjs +85 -0
  111. package/server/public/app/cwd-picker.js +605 -0
  112. package/server/public/app/emoji-view.js +101 -0
  113. package/server/public/app/external-link-policy.mjs +80 -0
  114. package/server/public/app/file-preview-navigation.mjs +51 -0
  115. package/server/public/app/font-scaling.js +262 -0
  116. package/server/public/app/git-changes-view.js +339 -0
  117. package/server/public/app/git-diff-model.mjs +109 -0
  118. package/server/public/app/grok-auth-ui.js +387 -0
  119. package/server/public/app/image-downscale.mjs +174 -0
  120. package/server/public/app/in-flight-snapshot.mjs +40 -0
  121. package/server/public/app/infinite-loader.js +164 -0
  122. package/server/public/app/journal-view.js +1141 -0
  123. package/server/public/app/known-cwd-options.mjs +41 -0
  124. package/server/public/app/launch-cwd-path.mjs +23 -0
  125. package/server/public/app/live-message-dedupe.mjs +57 -0
  126. package/server/public/app/message-search-view.js +242 -0
  127. package/server/public/app/model-selector-options.mjs +31 -0
  128. package/server/public/app/model-vision-support.mjs +56 -0
  129. package/server/public/app/new-conversation-model-choice.mjs +67 -0
  130. package/server/public/app/plan-usage-view.mjs +307 -0
  131. package/server/public/app/preview-cards.mjs +273 -0
  132. package/server/public/app/preview-command.mjs +103 -0
  133. package/server/public/app/push-settings.js +370 -0
  134. package/server/public/app/pwa-install.js +456 -0
  135. package/server/public/app/question-schema-view.mjs +71 -0
  136. package/server/public/app/reasoning-effort-labels.mjs +42 -0
  137. package/server/public/app/relay-board-view.js +117 -0
  138. package/server/public/app/relay-dot-state.mjs +69 -0
  139. package/server/public/app/relay-error-ctas.mjs +52 -0
  140. package/server/public/app/relay-prompt-sanitizer.mjs +114 -0
  141. package/server/public/app/relay-thoughts.mjs +98 -0
  142. package/server/public/app/relay-watchdog-policy.mjs +29 -0
  143. package/server/public/app/repo-browser-preferences.mjs +79 -0
  144. package/server/public/app/repo-browser-tree-state.mjs +97 -0
  145. package/server/public/app/router.js +657 -0
  146. package/server/public/app/selection-guard.mjs +93 -0
  147. package/server/public/app/settings-modal.js +1484 -0
  148. package/server/public/app/settings-tabs.js +129 -0
  149. package/server/public/app/slash-autocomplete.mjs +188 -0
  150. package/server/public/app/slash-commands.mjs +183 -0
  151. package/server/public/app/socket-handlers.js +703 -0
  152. package/server/public/app/status-store.mjs +242 -0
  153. package/server/public/app/status-view.mjs +315 -0
  154. package/server/public/app/store.js +1454 -0
  155. package/server/public/app/stream-state.mjs +73 -0
  156. package/server/public/app/streaming-dom-patch.mjs +23 -0
  157. package/server/public/app/sync-outbox.mjs +208 -0
  158. package/server/public/app/tap-actions.js +62 -0
  159. package/server/public/app/thread-order.mjs +54 -0
  160. package/server/public/app/tmux-inspector-view.js +422 -0
  161. package/server/public/app/transcript-separators.mjs +282 -0
  162. package/server/public/app-icon-192.png +0 -0
  163. package/server/public/app-icon-512.png +0 -0
  164. package/server/public/app-icon.svg +4 -0
  165. package/server/public/favicon.ico +0 -0
  166. package/server/public/index.html +5743 -0
  167. package/server/public/manifest.webmanifest +32 -0
  168. package/server/public/sw.js +307 -0
  169. package/server/relay-exit-codes.mjs +4 -0
  170. package/server/relay-self-restart.mjs +170 -0
  171. package/server/relay-tools.md +27 -0
  172. package/server/relay.mjs +1026 -0
  173. package/server/repositories/image-conversation-repository.mjs +317 -0
  174. package/server/repositories/message-repository.mjs +434 -0
  175. package/server/repositories/question-repository.mjs +161 -0
  176. package/server/repositories/session-repository.mjs +320 -0
  177. package/server/repositories/share-visibility-statements.mjs +24 -0
  178. package/server/routes/ask-user-routes.mjs +292 -0
  179. package/server/routes/cache-routes.mjs +44 -0
  180. package/server/routes/git-routes.mjs +92 -0
  181. package/server/routes/messages-routes-test-harness.mjs +111 -0
  182. package/server/routes/messages-routes.mjs +7107 -0
  183. package/server/routes/preview-routes.mjs +108 -0
  184. package/server/routes/push-routes.mjs +59 -0
  185. package/server/routes/relay-board-routes.mjs +268 -0
  186. package/server/routes/sessions-routes.mjs +6150 -0
  187. package/server/server-runtime.mjs +7463 -0
  188. package/server/server.js +24 -0
  189. package/server/services/ask-user-routing-service.mjs +117 -0
  190. package/server/services/cache-rebuild-service.mjs +236 -0
  191. package/server/services/claude-auth-service.mjs +578 -0
  192. package/server/services/claude-context-usage.mjs +240 -0
  193. package/server/services/claude-session-root-service.mjs +228 -0
  194. package/server/services/cli-install-service.mjs +1152 -0
  195. package/server/services/cli-process-runner.mjs +181 -0
  196. package/server/services/cloudflared-tunnel-service.mjs +358 -0
  197. package/server/services/context-snapshot-service.mjs +499 -0
  198. package/server/services/context-usage-view.mjs +160 -0
  199. package/server/services/conversation-preferences-service.mjs +208 -0
  200. package/server/services/cursor-dashboard-usage.mjs +217 -0
  201. package/server/services/cursor-session-root-service.mjs +62 -0
  202. package/server/services/delete-archive-service.mjs +154 -0
  203. package/server/services/drives-path-helpers.mjs +66 -0
  204. package/server/services/fixtures/claude-auth-stub.sh +62 -0
  205. package/server/services/fixtures/cli-install-stub.sh +75 -0
  206. package/server/services/fixtures/grok-stub.sh +86 -0
  207. package/server/services/generated-image-cleanup-service.mjs +114 -0
  208. package/server/services/git-changes-service.mjs +195 -0
  209. package/server/services/github-billing-usage.mjs +133 -0
  210. package/server/services/grok-auth-service.mjs +593 -0
  211. package/server/services/grok-billing-usage.mjs +120 -0
  212. package/server/services/image-context-service.mjs +106 -0
  213. package/server/services/image-operation-service.mjs +135 -0
  214. package/server/services/keyed-mutex-service.mjs +82 -0
  215. package/server/services/mime-sniffer.mjs +118 -0
  216. package/server/services/oar-cli-helpers.mjs +109 -0
  217. package/server/services/oar-state-migration-service.mjs +243 -0
  218. package/server/services/plan-usage-claude.mjs +387 -0
  219. package/server/services/plan-usage-contract.mjs +271 -0
  220. package/server/services/plan-usage-copilot.mjs +421 -0
  221. package/server/services/plan-usage-cursor.mjs +566 -0
  222. package/server/services/plan-usage-grok.mjs +436 -0
  223. package/server/services/plan-usage-service.mjs +487 -0
  224. package/server/services/preview-health-probe.mjs +82 -0
  225. package/server/services/preview-proxy-server.mjs +498 -0
  226. package/server/services/preview-registry-service.mjs +457 -0
  227. package/server/services/preview-static-handler.mjs +237 -0
  228. package/server/services/provider-model-selection.mjs +91 -0
  229. package/server/services/provider-reasoning-effort.mjs +94 -0
  230. package/server/services/push-active-device-service.mjs +57 -0
  231. package/server/services/push-dispatch-service.mjs +459 -0
  232. package/server/services/rate-limit-service.mjs +63 -0
  233. package/server/services/relay-bridge-owner-service.mjs +84 -0
  234. package/server/services/relay-cli-launcher-service.mjs +209 -0
  235. package/server/services/relay-prompt-sanitizer.mjs +137 -0
  236. package/server/services/relay-queue-gate-service.mjs +38 -0
  237. package/server/services/relay-restart-orchestrator-service.mjs +935 -0
  238. package/server/services/relay-singleton-guard.mjs +124 -0
  239. package/server/services/safe-served-content.mjs +57 -0
  240. package/server/services/sdk-session-import-service.mjs +271 -0
  241. package/server/services/sdk-session-sync-service.mjs +207 -0
  242. package/server/services/session-history-refresh-service.mjs +497 -0
  243. package/server/services/session-transcript-service.mjs +419 -0
  244. package/server/services/session-worker-launch-service.mjs +786 -0
  245. package/server/services/session-worker-process-service.mjs +319 -0
  246. package/server/services/session-worker-registry-service.mjs +194 -0
  247. package/server/services/session-worker-stop-service.mjs +141 -0
  248. package/server/services/session-worker-supervisor-service.mjs +898 -0
  249. package/server/services/session-worker-websocket-service.mjs +483 -0
  250. package/server/services/ssh-tunnel-manager-service.mjs +423 -0
  251. package/server/services/status-event-service.mjs +158 -0
  252. package/server/services/tmux-inspector-access-policy.mjs +77 -0
  253. package/server/services/tmux-inspector-socket-service.mjs +191 -0
  254. package/server/services/tmux-inspector-stream-service.mjs +552 -0
  255. package/server/services/tunnel-worker-path-guard.mjs +107 -0
  256. package/server/services/upload-sweep.mjs +60 -0
  257. package/server/services/usage-snapshot-helpers.mjs +209 -0
  258. package/server/services/windows-autostart-service.mjs +115 -0
  259. package/server/services/workspace-root-defaults-service.mjs +34 -0
  260. package/server/services/workspace-root-path-policy.mjs +196 -0
  261. package/server/services/workspace-root-relaunch-service.mjs +132 -0
  262. package/server/services/workspace-symlink-guard.mjs +54 -0
  263. package/server/tools/capture-proxy.mjs +173 -0
  264. package/server/tty-console-bootstrap.mjs +75 -0
  265. package/server/uploads/README.md +36 -0
  266. package/server/uploads/image_input.md +540 -0
  267. package/server/windows-terminal-launcher.mjs +29 -0
  268. package/server/workspace-root.mjs +87 -0
  269. package/shared/ask-user-bridge.mjs +131 -0
  270. package/shared/auto-compact-window.mjs +116 -0
  271. package/shared/background-task-timeout.mjs +52 -0
  272. package/shared/claude-thinking.mjs +101 -0
  273. package/shared/context-window-fallbacks.mjs +148 -0
  274. package/shared/control-poller.mjs +115 -0
  275. package/shared/empty-turn-completion.mjs +20 -0
  276. package/shared/model-catalog-freshness.mjs +11 -0
  277. package/shared/model-descriptors.mjs +118 -0
  278. package/shared/model-id.mjs +147 -0
  279. package/shared/model-refresh.mjs +24 -0
  280. package/shared/openai-reasoning.mjs +13 -0
  281. package/shared/plan-lines.mjs +13 -0
  282. package/shared/preview-instructions.mjs +77 -0
  283. package/shared/preview-tool-core.mjs +191 -0
  284. package/shared/provider-routing.mjs +29 -0
  285. package/shared/question-schema.mjs +315 -0
  286. package/shared/question-timeout.mjs +18 -0
  287. package/shared/stream-emit-gating.mjs +28 -0
  288. package/shared/subagent-run-id.mjs +17 -0
  289. package/shared/thought-cap.mjs +13 -0
  290. package/shared/tool-activity.mjs +98 -0
  291. package/shared/turn-ceiling.mjs +53 -0
  292. package/shared/worker-bootstrap.mjs +44 -0
  293. package/shared/worker-crash-guard.mjs +60 -0
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ import { createCacheRebuildService } from '../services/cache-rebuild-service.mjs';
4
+
5
+ export function registerCacheRoutes(app, deps) {
6
+ const {
7
+ auth,
8
+ io,
9
+ db,
10
+ stmts,
11
+ fs,
12
+ path,
13
+ uploadsDir,
14
+ bootstrapRuntimeSessionBindings,
15
+ collectOrphanedUploadsFromConversation,
16
+ deleteOrphanedUploads,
17
+ } = deps;
18
+
19
+ const cacheRebuildService = createCacheRebuildService({
20
+ db,
21
+ stmts,
22
+ io,
23
+ fs,
24
+ path,
25
+ uploadsDir,
26
+ bootstrapRuntimeSessionBindings,
27
+ collectOrphanedUploadsFromConversation,
28
+ deleteOrphanedUploads,
29
+ });
30
+
31
+ app.post('/api/cache/rebuild', auth, (req, res) => {
32
+ const mode = String(req.body?.mode || 'reconcile').trim().toLowerCase();
33
+ try {
34
+ const result = cacheRebuildService.rebuildCache({ mode });
35
+ io.emit('cache_rebuilt', result);
36
+ return res.json({ ok: true, mode: result.mode, summary: result.summary });
37
+ } catch (error) {
38
+ const statusCode = Number(error?.statusCode || 500);
39
+ return res.status(Number.isInteger(statusCode) ? statusCode : 500).json({
40
+ error: error?.message || 'Failed to rebuild cache',
41
+ });
42
+ }
43
+ });
44
+ }
@@ -0,0 +1,92 @@
1
+ 'use strict';
2
+
3
+ // Routes backing the "Git changes" modal. The repository root is always
4
+ // resolved server-side from the conversation scope (mirroring the file
5
+ // preview routes) — the client never sends absolute paths.
6
+
7
+ export function resolveGitScopedRootPath(req, {
8
+ resolveConversationWorkspaceState,
9
+ currentWorkspaceRootPath,
10
+ } = {}) {
11
+ const conversationId = String(
12
+ req?.query?.conversationId
13
+ || req?.query?.conversation_id
14
+ || req?.headers?.['x-conversation-id']
15
+ || '',
16
+ ).trim();
17
+ const sdkSessionId = String(
18
+ req?.query?.sdkSessionId
19
+ || req?.query?.sdk_session_id
20
+ || req?.headers?.['x-sdk-session-id']
21
+ || '',
22
+ ).trim();
23
+ if ((conversationId || sdkSessionId) && typeof resolveConversationWorkspaceState === 'function') {
24
+ const state = resolveConversationWorkspaceState({ conversationId, sdkSessionId });
25
+ const scoped = String(state?.currentWorkspaceRootPath || '').trim();
26
+ if (scoped) return scoped;
27
+ }
28
+ if (typeof currentWorkspaceRootPath === 'function') {
29
+ const fallback = String(currentWorkspaceRootPath() || '').trim();
30
+ if (fallback) return fallback;
31
+ }
32
+ return null;
33
+ }
34
+
35
+ export function registerGitRoutes(app, deps) {
36
+ const {
37
+ auth,
38
+ gitChangesService,
39
+ resolveConversationWorkspaceState,
40
+ currentWorkspaceRootPath,
41
+ normalizeWorkspaceRelativePath,
42
+ resolveWorkspaceFilePath,
43
+ } = deps;
44
+
45
+ if (!gitChangesService) return;
46
+
47
+ function requireRootPath(req, res) {
48
+ const rootPath = resolveGitScopedRootPath(req, {
49
+ resolveConversationWorkspaceState,
50
+ currentWorkspaceRootPath,
51
+ });
52
+ if (!rootPath) {
53
+ res.status(400).json({ error: 'No workspace root available' });
54
+ return null;
55
+ }
56
+ return rootPath;
57
+ }
58
+
59
+ app.get('/api/git/status', auth, async (req, res) => {
60
+ const rootPath = requireRootPath(req, res);
61
+ if (!rootPath) return;
62
+ const payload = await gitChangesService.getStatus(rootPath);
63
+ res.setHeader('Cache-Control', 'no-store');
64
+ if (!payload.ok) return res.status(500).json({ error: payload.error || 'Failed to read git status' });
65
+ res.json({ ...payload, rootPath });
66
+ });
67
+
68
+ app.get('/api/git/diff', auth, async (req, res) => {
69
+ const rootPath = requireRootPath(req, res);
70
+ if (!rootPath) return;
71
+ const requestedPath = String(req.query?.path || '').trim();
72
+ const normalizedPath = normalizeWorkspaceRelativePath(requestedPath);
73
+ const absolutePath = resolveWorkspaceFilePath(requestedPath, rootPath);
74
+ if (!normalizedPath || !absolutePath) {
75
+ return res.status(400).json({ error: 'Invalid file path' });
76
+ }
77
+ const untracked = String(req.query?.untracked || '') === '1';
78
+ const payload = await gitChangesService.getDiff(rootPath, normalizedPath.replace(/\\/g, '/'), { untracked });
79
+ res.setHeader('Cache-Control', 'no-store');
80
+ if (!payload.ok) return res.status(500).json({ error: payload.error || 'Failed to read git diff' });
81
+ res.json(payload);
82
+ });
83
+
84
+ app.post('/api/git/pull', auth, async (req, res) => {
85
+ const rootPath = requireRootPath(req, res);
86
+ if (!rootPath) return;
87
+ const payload = await gitChangesService.pull(rootPath);
88
+ res.setHeader('Cache-Control', 'no-store');
89
+ if (!payload.ok) return res.status(500).json({ error: payload.error || 'git pull failed' });
90
+ res.json(payload);
91
+ });
92
+ }
@@ -0,0 +1,111 @@
1
+ // Shared scaffolding for the messages-routes test suites and the claude-worker
2
+ // routes-integration test: the route-capturing fake express app, the res stub,
3
+ // and the baseline deps stand-ins.
4
+ //
5
+ // server-runtime.mjs boots a live server on import, so the deps it injects are
6
+ // reproduced here rather than imported. makeRouteDeps carries only the minimal
7
+ // working defaults every suite shares; each suite supplies its own db/stmts —
8
+ // the stub suites keep their hand-rolled fakes and prepared-statement mirrors,
9
+ // the integration test passes a real better-sqlite3 database and the real
10
+ // repositories. The harness owns the shape, the suite owns the fakes.
11
+ import assert from 'node:assert/strict';
12
+
13
+ import { registerMessagesRoutes } from './messages-routes.mjs';
14
+
15
+ // Baseline deps: enough for registerMessagesRoutes to register every route
16
+ // without touching a real server. Registration eagerly prepares statements for
17
+ // many routes the suites never exercise, against tables they do not create —
18
+ // the db stub absorbs those; suites that need real statement/transaction
19
+ // semantics override db/stmts.
20
+ export function makeRouteDeps(overrides = {}) {
21
+ return {
22
+ auth: (_req, _res, next) => next(),
23
+ db: { prepare: () => ({ run() {}, get: () => null, all: () => [] }) },
24
+ stmts: {},
25
+ MAX_UPLOAD_BYTES: 1024 * 1024,
26
+ touchCli: () => {},
27
+ ensureSessionId: () => 'session-1',
28
+ normalizeRelayMode: (value) => String(value || '').trim().toLowerCase() || null,
29
+ DEFAULT_RELAY_MODE: 'default',
30
+ DEFAULT_MODEL: 'gpt-5',
31
+ normalizeAttachments: () => [],
32
+ collectReferenceAttachmentsFromText: () => ({ attachments: [] }),
33
+ mergeMessageAttachments: (left, right) => [...(left || []), ...(right || [])],
34
+ resolveRequestedModel: (model) => ({
35
+ ok: false,
36
+ error: `Model "${String(model || '')}" is not supported`,
37
+ available: [],
38
+ }),
39
+ getOpenAIProviderSettings: () => ({ configured: false, enabled: false, model: '', models: [] }),
40
+ getCursorProviderSettings: () => ({ enabled: false, model: '', models: [] }),
41
+ featureFlags: {},
42
+ ...overrides,
43
+ };
44
+ }
45
+
46
+ // Registers the routes on a fake express app and returns the captured handlers
47
+ // as a Map keyed 'METHOD /path' (last handler wins, matching how the suites
48
+ // always grabbed the terminal handler after the auth middleware).
49
+ export function captureRoutes(deps, registerFn = registerMessagesRoutes) {
50
+ const routes = new Map();
51
+ const register = (method) => (routePath, ...handlers) => {
52
+ routes.set(`${method} ${routePath}`, handlers[handlers.length - 1]);
53
+ };
54
+ const app = {
55
+ post: register('POST'),
56
+ get: register('GET'),
57
+ patch: register('PATCH'),
58
+ put: register('PUT'),
59
+ delete: register('DELETE'),
60
+ use() {},
61
+ };
62
+ registerFn(app, deps);
63
+ return routes;
64
+ }
65
+
66
+ // Express res stub capturing status/body.
67
+ export function makeRes() {
68
+ const captured = { status: 200, body: null };
69
+ const res = {
70
+ setHeader() {},
71
+ status(code) { captured.status = code; return res; },
72
+ json(payload) { captured.body = payload; return res; },
73
+ };
74
+ return { res, captured };
75
+ }
76
+
77
+ // Dispatches into a captured handler and returns { status, body }.
78
+ export async function invokeRoute(routes, method, routePath, { body = {}, headers = {}, query = {}, params = {}, socket = {} } = {}) {
79
+ const handler = routes.get(`${String(method).toUpperCase()} ${routePath}`);
80
+ assert.ok(handler, `${routePath} should be registered`);
81
+ const { res, captured } = makeRes();
82
+ await handler({ body, headers, query, params, socket }, res);
83
+ return captured;
84
+ }
85
+
86
+ // Registers once and returns an invoker for one POST route — for suites that
87
+ // drive the same handler repeatedly against one database.
88
+ export function postHandler(routePath, deps) {
89
+ const routes = captureRoutes(deps);
90
+ assert.ok(routes.has(`POST ${routePath}`), `${routePath} should be registered`);
91
+ return (body) => invokeRoute(routes, 'POST', routePath, { body });
92
+ }
93
+
94
+ // One-shot register + POST, the common form in the stub suites.
95
+ export function invokePost(routePath, deps, body) {
96
+ return postHandler(routePath, deps)(body);
97
+ }
98
+
99
+ // The worker runner's `api(method, path, body)` against the captured handlers:
100
+ // dispatch with a fixed header set (e.g. the bridge identity header the live
101
+ // worker sends) and surface non-2xx as a rejection like the worker's HTTP
102
+ // client does.
103
+ export function makeApi(routes, { headers = {} } = {}) {
104
+ return async (method, routePath, body) => {
105
+ const { status, body: responseBody } = await invokeRoute(routes, method, routePath, { body: body || {}, headers });
106
+ if (status >= 400) {
107
+ throw new Error(`${method} ${routePath} -> ${status}: ${JSON.stringify(responseBody)}`);
108
+ }
109
+ return responseBody;
110
+ };
111
+ }