@amoree-code/ocean 0.0.0-stage → 1.2.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 (300) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +284 -2
  3. package/dist/application/auth/auth-orchestrator.js +77 -0
  4. package/dist/application/clients/clients.js +646 -0
  5. package/dist/application/clients/editors.js +398 -0
  6. package/dist/application/clients/hooks.js +335 -0
  7. package/dist/application/clients/native.js +410 -0
  8. package/dist/application/clients/opencode-plugin.js +158 -0
  9. package/dist/application/clients/registry.js +188 -0
  10. package/dist/application/gateway/webhook-gateway.js +137 -0
  11. package/dist/application/install/provider-installer.js +184 -0
  12. package/dist/application/integrations/charter-copies.js +76 -0
  13. package/dist/application/integrations/claude-vscode.js +106 -0
  14. package/dist/application/integrations/engine-entry-repair.js +105 -0
  15. package/dist/application/integrations/skill-filter.js +254 -0
  16. package/dist/application/layout/layout-apply.js +443 -0
  17. package/dist/application/layout/layout-plan.js +236 -0
  18. package/dist/application/layout/workspace-template.js +34 -0
  19. package/dist/application/onboarding/state.js +23 -0
  20. package/dist/application/onboarding/steps.js +249 -0
  21. package/dist/application/profiles/profile-distribution.js +53 -0
  22. package/dist/application/profiles/profile-suggestion.js +39 -0
  23. package/dist/application/profiles/profile-upgrade-service.js +208 -0
  24. package/dist/application/pull/history-index.js +476 -0
  25. package/dist/application/pull/project-match.js +132 -0
  26. package/dist/application/pull/pull.js +202 -0
  27. package/dist/application/pull/server-capture.js +307 -0
  28. package/dist/application/scheduler/local-scheduler.js +210 -0
  29. package/dist/composition/runtime.js +64 -0
  30. package/dist/engine-root.js +9 -0
  31. package/dist/infrastructure/filesystem/profile-loader.js +46 -0
  32. package/dist/infrastructure/filesystem/runtime-config-loader.js +29 -0
  33. package/dist/infrastructure/filesystem/skill-loader.js +84 -0
  34. package/dist/infrastructure/filesystem/workspace-capability.js +37 -0
  35. package/dist/infrastructure/observability/runtime-logger.js +16 -0
  36. package/dist/infrastructure/process/agent-tool-executor.js +99 -0
  37. package/dist/infrastructure/process/cli-process.js +49 -0
  38. package/dist/infrastructure/process/interactive-process.js +145 -0
  39. package/dist/infrastructure/process/process-supervisor.js +194 -0
  40. package/dist/infrastructure/process/service.js +252 -0
  41. package/dist/infrastructure/providers/browser-provider.js +2 -0
  42. package/dist/infrastructure/providers/claude-sdk.js +137 -0
  43. package/dist/infrastructure/providers/client-home.js +14 -0
  44. package/dist/infrastructure/providers/fake-browser-provider.js +146 -0
  45. package/dist/infrastructure/providers/ollama-embedder.js +74 -0
  46. package/dist/infrastructure/providers/openai-compatible.js +268 -0
  47. package/dist/infrastructure/providers/playwright-browser-provider.js +380 -0
  48. package/dist/infrastructure/providers/provider-capabilities.js +50 -0
  49. package/dist/infrastructure/providers/provider-invocation.js +184 -0
  50. package/dist/infrastructure/providers/provider-registry.js +77 -0
  51. package/dist/infrastructure/providers/providers.js +134 -0
  52. package/dist/infrastructure/wrappers/wrapper-manager.js +374 -0
  53. package/dist/interfaces/cli/auth-command.js +54 -0
  54. package/dist/interfaces/cli/browser-bridge-command.js +93 -0
  55. package/dist/interfaces/cli/browser-command.js +119 -0
  56. package/dist/interfaces/cli/capture-command.js +125 -0
  57. package/dist/interfaces/cli/client-test-command.js +133 -0
  58. package/dist/interfaces/cli/clients-command.js +129 -0
  59. package/dist/interfaces/cli/command-registry.js +1211 -0
  60. package/dist/interfaces/cli/context-command.js +172 -0
  61. package/dist/interfaces/cli/daily-command.js +131 -0
  62. package/dist/interfaces/cli/governance-command.js +55 -0
  63. package/dist/interfaces/cli/handoff-command.js +35 -0
  64. package/dist/interfaces/cli/help-command.js +207 -0
  65. package/dist/interfaces/cli/idea-command.js +47 -0
  66. package/dist/interfaces/cli/intercept-command.js +317 -0
  67. package/dist/interfaces/cli/layout-command.js +27 -0
  68. package/dist/interfaces/cli/market-command.js +52 -0
  69. package/dist/interfaces/cli/mcp-gateway-command.js +30 -0
  70. package/dist/interfaces/cli/memory-command.js +317 -0
  71. package/dist/interfaces/cli/migrate-command.js +26 -0
  72. package/dist/interfaces/cli/observe-command.js +78 -0
  73. package/dist/interfaces/cli/onboard-command.js +123 -0
  74. package/dist/interfaces/cli/operate-command.js +59 -0
  75. package/dist/interfaces/cli/profiles-command.js +70 -0
  76. package/dist/interfaces/cli/pull-command.js +54 -0
  77. package/dist/interfaces/cli/setup-command.js +143 -0
  78. package/dist/interfaces/cli/tasks-command.js +249 -0
  79. package/dist/main.js +49 -0
  80. package/dist/node-warnings.js +17 -0
  81. package/dist/version.js +8 -0
  82. package/market/mcp/chrome-devtools/ocean.capability.json +28 -0
  83. package/market/mcp/gateway/ocean.capability.json +21 -0
  84. package/market/mcp/obsidian/ocean.capability.json +21 -0
  85. package/market/mcp/ocean/ocean.capability.json +21 -0
  86. package/market/mcp/playwright/ocean.capability.json +23 -0
  87. package/node_modules/@ocean/brain/dist/brain/brain-eval.js +29 -0
  88. package/node_modules/@ocean/brain/dist/brain/brain-markdown.js +226 -0
  89. package/node_modules/@ocean/brain/dist/brain/brain-reindex.js +247 -0
  90. package/node_modules/@ocean/brain/dist/brain/brain-service.js +320 -0
  91. package/node_modules/@ocean/brain/dist/capture/inbox-sync.js +22 -0
  92. package/node_modules/@ocean/brain/dist/evidence/verification-report.js +13 -0
  93. package/node_modules/@ocean/brain/dist/infrastructure/brain/brain-index.js +285 -0
  94. package/node_modules/@ocean/brain/dist/memory/brain-dump.js +145 -0
  95. package/node_modules/@ocean/brain/dist/memory/daily-narrative.js +142 -0
  96. package/node_modules/@ocean/brain/dist/memory/index-sync.js +124 -0
  97. package/node_modules/@ocean/brain/dist/memory/lesson-activation.js +93 -0
  98. package/node_modules/@ocean/brain/dist/memory/lesson-learning.js +188 -0
  99. package/node_modules/@ocean/brain/dist/memory/lessons.js +321 -0
  100. package/node_modules/@ocean/brain/dist/memory/memory-audit.js +100 -0
  101. package/node_modules/@ocean/brain/dist/memory/memory-digest.js +49 -0
  102. package/node_modules/@ocean/brain/dist/memory/memory-harvest.js +345 -0
  103. package/node_modules/@ocean/brain/dist/memory/memory-paths.js +174 -0
  104. package/node_modules/@ocean/brain/dist/memory/memory-review.js +278 -0
  105. package/node_modules/@ocean/brain/dist/memory/memory-trash.js +152 -0
  106. package/node_modules/@ocean/brain/dist/memory/memory-write.js +216 -0
  107. package/node_modules/@ocean/brain/dist/memory/model-narrative.js +129 -0
  108. package/node_modules/@ocean/brain/dist/memory/profile-facts.js +103 -0
  109. package/node_modules/@ocean/brain/dist/memory/session-promotion.js +61 -0
  110. package/node_modules/@ocean/brain/dist/memory/session-summary.js +113 -0
  111. package/node_modules/@ocean/brain/package.json +11 -0
  112. package/node_modules/@ocean/context/dist/context/context-compression.js +66 -0
  113. package/node_modules/@ocean/context/dist/context/context-cost.js +587 -0
  114. package/node_modules/@ocean/context/dist/context/context-ladder.js +363 -0
  115. package/node_modules/@ocean/context/dist/context/context-packet.js +213 -0
  116. package/node_modules/@ocean/context/dist/context/context-references.js +255 -0
  117. package/node_modules/@ocean/context/dist/context/intent-router.js +259 -0
  118. package/node_modules/@ocean/context/dist/context/resource-injection.js +36 -0
  119. package/node_modules/@ocean/context/package.json +11 -0
  120. package/node_modules/@ocean/core/dist/domain/brain/brain-record.js +22 -0
  121. package/node_modules/@ocean/core/dist/domain/brain/record-scope.js +22 -0
  122. package/node_modules/@ocean/core/dist/domain/capabilities/browser-contract.js +64 -0
  123. package/node_modules/@ocean/core/dist/domain/capabilities/capability-contract.js +16 -0
  124. package/node_modules/@ocean/core/dist/domain/capabilities/capability-manifest.js +72 -0
  125. package/node_modules/@ocean/core/dist/domain/context/context-validator.js +5 -0
  126. package/node_modules/@ocean/core/dist/domain/context/context.js +34 -0
  127. package/node_modules/@ocean/core/dist/domain/context/project-id.js +30 -0
  128. package/node_modules/@ocean/core/dist/domain/evidence/evidence.js +16 -0
  129. package/node_modules/@ocean/core/dist/domain/mcp/mcp-contract.js +36 -0
  130. package/node_modules/@ocean/core/dist/domain/mcp/memory-contract.js +41 -0
  131. package/node_modules/@ocean/core/dist/domain/ports/brain-index-port.js +7 -0
  132. package/node_modules/@ocean/core/dist/domain/ports/browser-port.js +8 -0
  133. package/node_modules/@ocean/core/dist/domain/ports/embedder-port.js +2 -0
  134. package/node_modules/@ocean/core/dist/domain/ports/platform-ports.js +2 -0
  135. package/node_modules/@ocean/core/dist/domain/ports/runtime-ports.js +2 -0
  136. package/node_modules/@ocean/core/dist/domain/ports/session-store-port.js +2 -0
  137. package/node_modules/@ocean/core/dist/domain/process/process-events.js +5 -0
  138. package/node_modules/@ocean/core/dist/domain/profiles/profile-distribution.js +19 -0
  139. package/node_modules/@ocean/core/dist/domain/profiles/profile-policy.js +34 -0
  140. package/node_modules/@ocean/core/dist/domain/profiles/profile-selection.js +73 -0
  141. package/node_modules/@ocean/core/dist/domain/profiles/profile-upgrade.js +123 -0
  142. package/node_modules/@ocean/core/dist/domain/profiles/profile-validator.js +5 -0
  143. package/node_modules/@ocean/core/dist/domain/profiles/profile.js +218 -0
  144. package/node_modules/@ocean/core/dist/domain/profiles/runtime-config.js +30 -0
  145. package/node_modules/@ocean/core/dist/domain/providers/provider-capability.js +28 -0
  146. package/node_modules/@ocean/core/dist/domain/providers/provider.js +2 -0
  147. package/node_modules/@ocean/core/dist/domain/redaction/redaction.js +86 -0
  148. package/node_modules/@ocean/core/dist/domain/runs/agent-tools.js +92 -0
  149. package/node_modules/@ocean/core/dist/domain/runs/execution-plan.js +71 -0
  150. package/node_modules/@ocean/core/dist/domain/runs/run-contract.js +38 -0
  151. package/node_modules/@ocean/core/dist/domain/sessions/entry-contract.js +31 -0
  152. package/node_modules/@ocean/core/dist/domain/sessions/handoff.js +78 -0
  153. package/node_modules/@ocean/core/dist/domain/sessions/session.js +58 -0
  154. package/node_modules/@ocean/core/dist/domain/skills/default-skills.js +29 -0
  155. package/node_modules/@ocean/core/dist/domain/skills/skill-validator.js +5 -0
  156. package/node_modules/@ocean/core/dist/domain/skills/skill.js +11 -0
  157. package/node_modules/@ocean/core/dist/domain/tasks/task-id.js +85 -0
  158. package/node_modules/@ocean/core/dist/shared/fs-utils.js +212 -0
  159. package/node_modules/@ocean/core/dist/shared/local-date.js +16 -0
  160. package/node_modules/@ocean/core/dist/shared/paths.js +324 -0
  161. package/node_modules/@ocean/core/dist/shared/project-resolution.js +1065 -0
  162. package/node_modules/@ocean/core/dist/shared/task-reference.js +133 -0
  163. package/node_modules/@ocean/core/package.json +14 -0
  164. package/node_modules/@ocean/loop/dist/handoff/handoff-service.js +133 -0
  165. package/node_modules/@ocean/loop/dist/hooks/lifecycle-hooks.js +15 -0
  166. package/node_modules/@ocean/loop/dist/hooks/session-end-hook.js +183 -0
  167. package/node_modules/@ocean/loop/dist/hooks/session-start-hook.js +237 -0
  168. package/node_modules/@ocean/loop/dist/infrastructure/persistence/session-store.js +690 -0
  169. package/node_modules/@ocean/loop/dist/infrastructure/persistence/session-validator.js +5 -0
  170. package/node_modules/@ocean/loop/dist/loops/iteration-graph.js +147 -0
  171. package/node_modules/@ocean/loop/dist/loops/task-loop-record.js +2 -0
  172. package/node_modules/@ocean/loop/dist/loops/task-loop.js +281 -0
  173. package/node_modules/@ocean/loop/dist/operations/memory-knowledge-operations.js +263 -0
  174. package/node_modules/@ocean/loop/dist/operations/operation-contract.js +227 -0
  175. package/node_modules/@ocean/loop/dist/operations/operation-scope.js +28 -0
  176. package/node_modules/@ocean/loop/dist/operations/project-operations.js +93 -0
  177. package/node_modules/@ocean/loop/dist/operations/record-io.js +65 -0
  178. package/node_modules/@ocean/loop/dist/operations/record-operations.js +45 -0
  179. package/node_modules/@ocean/loop/dist/operations/task-linker.js +40 -0
  180. package/node_modules/@ocean/loop/dist/operations/task-operations.js +279 -0
  181. package/node_modules/@ocean/loop/dist/operations/write-guard.js +157 -0
  182. package/node_modules/@ocean/loop/dist/runs/prompt-assembly.js +112 -0
  183. package/node_modules/@ocean/loop/dist/runs/run-agent.js +425 -0
  184. package/node_modules/@ocean/loop/dist/runs/run-authorization.js +24 -0
  185. package/node_modules/@ocean/loop/dist/runs/tool-loop.js +173 -0
  186. package/node_modules/@ocean/loop/dist/sessions/session-closeout.js +150 -0
  187. package/node_modules/@ocean/loop/dist/sessions/session-pointer.js +133 -0
  188. package/node_modules/@ocean/loop/dist/sessions/session-projects.js +26 -0
  189. package/node_modules/@ocean/loop/dist/sessions/session-retention.js +132 -0
  190. package/node_modules/@ocean/loop/dist/tasks/archive-tasks.js +210 -0
  191. package/node_modules/@ocean/loop/dist/tasks/checkpoint-task.js +31 -0
  192. package/node_modules/@ocean/loop/dist/tasks/claim-task.js +126 -0
  193. package/node_modules/@ocean/loop/dist/tasks/list-tasks.js +113 -0
  194. package/node_modules/@ocean/loop/dist/tasks/new-task.js +42 -0
  195. package/node_modules/@ocean/loop/dist/tasks/renumber-tasks.js +134 -0
  196. package/node_modules/@ocean/loop/dist/tasks/verification-readiness.js +78 -0
  197. package/node_modules/@ocean/loop/dist/tasks/verify-task.js +234 -0
  198. package/node_modules/@ocean/loop/package.json +11 -0
  199. package/node_modules/@ocean/market/dist/adapters/mcp-adapter.js +64 -0
  200. package/node_modules/@ocean/market/dist/adapters/owned-directory.js +89 -0
  201. package/node_modules/@ocean/market/dist/adapters/plugin-adapter.js +39 -0
  202. package/node_modules/@ocean/market/dist/adapters/skill-adapter.js +32 -0
  203. package/node_modules/@ocean/market/dist/capability/adapter.js +2 -0
  204. package/node_modules/@ocean/market/dist/capability/catalog.js +63 -0
  205. package/node_modules/@ocean/market/dist/capability/content-digest.js +40 -0
  206. package/node_modules/@ocean/market/dist/capability/registry.js +81 -0
  207. package/node_modules/@ocean/market/dist/install/default-marketplace.js +47 -0
  208. package/node_modules/@ocean/market/dist/install/lock.js +34 -0
  209. package/node_modules/@ocean/market/dist/install/marketplace-service.js +165 -0
  210. package/node_modules/@ocean/market/dist/mcp/browser-bridge.js +263 -0
  211. package/node_modules/@ocean/market/dist/mcp/mcp-connection.js +67 -0
  212. package/node_modules/@ocean/market/dist/mcp/mcp-gateway.js +317 -0
  213. package/node_modules/@ocean/market/dist/mcp/mcp-launch.js +102 -0
  214. package/node_modules/@ocean/market/dist/mcp/mcp-runner.js +32 -0
  215. package/node_modules/@ocean/market/dist/security/description-scan.js +45 -0
  216. package/node_modules/@ocean/market/dist/security/permissions.js +57 -0
  217. package/node_modules/@ocean/market/dist/skills/core-skill-sync.js +113 -0
  218. package/node_modules/@ocean/market/dist/skills/signal-detection.js +84 -0
  219. package/node_modules/@ocean/market/dist/skills/skill-hub.js +220 -0
  220. package/node_modules/@ocean/market/package.json +11 -0
  221. package/node_modules/@ocean/services/dist/browser/browser-service.js +124 -0
  222. package/node_modules/@ocean/services/dist/browser/browser-session.js +287 -0
  223. package/node_modules/@ocean/services/dist/browser/browser-task-runner.js +70 -0
  224. package/node_modules/@ocean/services/dist/doctor/workspace-doctor.js +547 -0
  225. package/node_modules/@ocean/services/dist/infrastructure/mcp/call-ledger.js +114 -0
  226. package/node_modules/@ocean/services/dist/infrastructure/mcp/mcp-client.js +162 -0
  227. package/node_modules/@ocean/services/dist/infrastructure/mcp/obsidian-server.js +251 -0
  228. package/node_modules/@ocean/services/dist/infrastructure/mcp/ocean-server.js +880 -0
  229. package/node_modules/@ocean/services/dist/infrastructure/mcp/server-version.js +20 -0
  230. package/node_modules/@ocean/services/dist/infrastructure/mcp/tool-registry.js +29 -0
  231. package/node_modules/@ocean/services/dist/obsidian/conflict-log.js +68 -0
  232. package/node_modules/@ocean/services/dist/obsidian/inbox-promotion.js +80 -0
  233. package/node_modules/@ocean/services/dist/obsidian/vault-discovery.js +113 -0
  234. package/node_modules/@ocean/services/dist/obsidian/vault-ingestion.js +77 -0
  235. package/node_modules/@ocean/services/dist/obsidian/vault-sync.js +73 -0
  236. package/node_modules/@ocean/services/dist/obsidian/vault-writer.js +75 -0
  237. package/node_modules/@ocean/services/package.json +11 -0
  238. package/ocean-marketplace.json +415 -0
  239. package/package.json +67 -4
  240. package/skills/core/business-logic/SKILL.md +22 -0
  241. package/skills/core/catch-up/SKILL.md +17 -0
  242. package/skills/core/core-thinking/SKILL.md +11 -0
  243. package/skills/core/design-thinking/SKILL.md +19 -0
  244. package/skills/core/graft/SKILL.md +156 -0
  245. package/skills/core/loop/SKILL.md +20 -0
  246. package/skills/core/session-handoff/SKILL.md +15 -0
  247. package/skills/core/use-browser/SKILL.md +69 -0
  248. package/skills/core/verification/SKILL.md +11 -0
  249. package/skills/index.json +56 -0
  250. package/templates/README.md +4 -0
  251. package/templates/profiles/default.json +51 -0
  252. package/templates/profiles/developer.json +51 -0
  253. package/templates/profiles/reviewer.json +51 -0
  254. package/templates/profiles/strategist.json +51 -0
  255. package/templates/workspace/00-inbox/INBOX.md +7 -0
  256. package/templates/workspace/00-inbox/candidates/.gitkeep +0 -0
  257. package/templates/workspace/00-inbox/native/.gitkeep +0 -0
  258. package/templates/workspace/01-daily/.gitkeep +0 -0
  259. package/templates/workspace/02-personal/CONFLICTS.md +7 -0
  260. package/templates/workspace/02-personal/MEMORY.md +15 -0
  261. package/templates/workspace/02-personal/goals/.gitkeep +0 -0
  262. package/templates/workspace/02-personal/identity/.gitkeep +0 -0
  263. package/templates/workspace/02-personal/preferences/.gitkeep +0 -0
  264. package/templates/workspace/03-professional/.gitkeep +0 -0
  265. package/templates/workspace/04-projects/PROJECTS.md +5 -0
  266. package/templates/workspace/04-projects/_template/docs/.gitkeep +0 -0
  267. package/templates/workspace/04-projects/_template/memory/.gitkeep +0 -0
  268. package/templates/workspace/04-projects/_template/tasks/.gitkeep +0 -0
  269. package/templates/workspace/05-knowledge/KNOWLEDGE.md +8 -0
  270. package/templates/workspace/05-knowledge/decisions/.gitkeep +0 -0
  271. package/templates/workspace/05-knowledge/lessons/.gitkeep +0 -0
  272. package/templates/workspace/06-templates/TEMPLATES.md +25 -0
  273. package/templates/workspace/06-templates/decision.md +8 -0
  274. package/templates/workspace/06-templates/intelligence.md +32 -0
  275. package/templates/workspace/06-templates/project-claude.md +48 -0
  276. package/templates/workspace/06-templates/session.md +38 -0
  277. package/templates/workspace/06-templates/task.md +42 -0
  278. package/templates/workspace/MANIFEST.json +51 -0
  279. package/templates/workspace/bridge/config/CONFIG.md +21 -0
  280. package/templates/workspace/bridge/config/runtime.json +4 -0
  281. package/templates/workspace/charter/core.md +104 -0
  282. package/templates/workspace/charter/policies/README.md +31 -0
  283. package/templates/workspace/charter/policies/claude-code-context.md +90 -0
  284. package/templates/workspace/charter/policies/concise-output.md +29 -0
  285. package/templates/workspace/charter/policies/context.md +91 -0
  286. package/templates/workspace/charter/policies/contracts/task.md +189 -0
  287. package/templates/workspace/charter/policies/git.md +60 -0
  288. package/templates/workspace/charter/policies/graph.md +47 -0
  289. package/templates/workspace/charter/policies/knowledge.md +33 -0
  290. package/templates/workspace/charter/policies/lifecycle.md +119 -0
  291. package/templates/workspace/charter/policies/memory.md +67 -0
  292. package/templates/workspace/charter/policies/minimal-change.md +43 -0
  293. package/templates/workspace/charter/policies/models.md +57 -0
  294. package/templates/workspace/charter/policies/privacy-terms.txt +3 -0
  295. package/templates/workspace/charter/policies/response.md +39 -0
  296. package/templates/workspace/charter/policies/skill-observer.md +44 -0
  297. package/templates/workspace/charter/policies/stack.md +23 -0
  298. package/templates/workspace/charter/policies/task.md +139 -0
  299. package/templates/workspace/charter/policies/verification.md +29 -0
  300. package/templates/workspace/sessions/.gitkeep +0 -0
@@ -0,0 +1,134 @@
1
+ import path from "node:path";
2
+ import { runHeadless } from "../process/cli-process.js";
3
+ import { explicitModel, runClaudeSdk } from "./claude-sdk.js";
4
+ import { resolveOriginalExecutable } from "./provider-registry.js";
5
+ // One flag per directory; never variadic, so the next argument cannot be swallowed.
6
+ function repeatFlag(flag, values) {
7
+ return (values ?? []).flatMap((value) => [flag, value]);
8
+ }
9
+ // The model is enforced with each client's own flag, never only described in the prompt.
10
+ // Flags verified against each installed CLI's --help on 2026-10-13.
11
+ function modelFlag(flag, model) {
12
+ const chosen = explicitModel(model);
13
+ return chosen ? [flag, chosen] : [];
14
+ }
15
+ export const providerAdapterRegistry = {
16
+ // Claude runs through the Claude Agent SDK, which builds Claude Code's flags itself
17
+ // (claudeSdkOptions: resume, model, read-only, read grant).
18
+ claude: {
19
+ provider: "claude",
20
+ capabilities: ["headless", "resume", "read-only"],
21
+ run: runClaudeSdk,
22
+ },
23
+ codex: {
24
+ provider: "codex",
25
+ capabilities: ["headless", "read-only"],
26
+ readOnlyArgs: ["--sandbox", "read-only"],
27
+ build: (request) => [
28
+ "exec",
29
+ ...modelFlag("--model", request.model),
30
+ ...(request.readOnly ? ["--sandbox", "read-only"] : []),
31
+ "--json",
32
+ "--skip-git-repo-check",
33
+ request.prompt,
34
+ ],
35
+ },
36
+ gemini: {
37
+ provider: "gemini",
38
+ capabilities: ["headless", "read-only"],
39
+ readOnlyArgs: ["--approval-mode=plan"],
40
+ build: (request) => [
41
+ ...modelFlag("--model", request.model),
42
+ ...(request.readOnly ? ["--approval-mode=plan"] : []),
43
+ ...repeatFlag("--include-directories", request.readDirectories),
44
+ "--prompt",
45
+ request.prompt,
46
+ "--output-format",
47
+ "stream-json",
48
+ ],
49
+ },
50
+ antigravity: {
51
+ provider: "antigravity",
52
+ capabilities: ["headless", "read-only"],
53
+ readOnlyArgs: ["--mode", "plan", "--sandbox"],
54
+ build: (request) => [
55
+ ...modelFlag("--model", request.model),
56
+ ...(request.readOnly ? ["--mode", "plan", "--sandbox"] : []),
57
+ "--print",
58
+ request.prompt,
59
+ "--output-format",
60
+ "stream-json",
61
+ ],
62
+ },
63
+ hermes: {
64
+ provider: "hermes",
65
+ capabilities: ["headless"],
66
+ build: (request) => [
67
+ ...modelFlag("-m", request.model),
68
+ "-z",
69
+ request.prompt,
70
+ ],
71
+ },
72
+ kilo: {
73
+ provider: "kilo",
74
+ capabilities: ["headless"],
75
+ build: (request) => [
76
+ "run",
77
+ ...modelFlag("--model", request.model),
78
+ "--auto",
79
+ request.prompt,
80
+ ],
81
+ },
82
+ kimi: {
83
+ provider: "kimi",
84
+ capabilities: ["headless", "read-only"],
85
+ readOnlyArgs: ["--plan"],
86
+ build: (request) => [
87
+ ...modelFlag("--model", request.model),
88
+ ...(request.readOnly ? ["--plan"] : []),
89
+ "--prompt",
90
+ request.prompt,
91
+ "--print",
92
+ "--output-format",
93
+ "stream-json",
94
+ ],
95
+ },
96
+ };
97
+ export function buildProviderInvocation(request) {
98
+ const command = request.provider === "antigravity" ? "agy" : request.provider;
99
+ const adapter = providerAdapterRegistry[request.provider];
100
+ if (!adapter.build)
101
+ throw new Error(`${request.provider} runs through its SDK, not a CLI invocation`);
102
+ if (request.readOnly)
103
+ assertProviderSupportsReadOnly(request.provider);
104
+ const args = adapter.build(request);
105
+ return { command, args };
106
+ }
107
+ export function assertProviderSupportsReadOnly(provider) {
108
+ if (!providerAdapterRegistry[provider].capabilities.includes("read-only"))
109
+ throw new Error(`Provider cannot enforce read-only execution: ${provider}`);
110
+ }
111
+ export function runProvider(request) {
112
+ const sdkRun = providerAdapterRegistry[request.provider].run;
113
+ if (sdkRun)
114
+ return sdkRun(request);
115
+ const { command, args } = buildProviderInvocation(request);
116
+ return runHeadless({
117
+ command: resolveOriginalExecutable(command),
118
+ args,
119
+ cwd: path.resolve(request.cwd),
120
+ // Built fresh for every run: the plan's variables override any inherited value of the
121
+ // same name, and nothing is written back to this process's environment.
122
+ env: {
123
+ ...request.env,
124
+ ...(request.provider === "hermes" && request.clientHome
125
+ ? { HERMES_HOME: request.clientHome }
126
+ : {}),
127
+ },
128
+ timeoutMs: request.timeoutMs,
129
+ maxOutputBytes: request.maxOutputBytes,
130
+ onEvent: request.onEvent,
131
+ onSpawn: request.onSpawn,
132
+ });
133
+ }
134
+ //# sourceMappingURL=providers.js.map
@@ -0,0 +1,374 @@
1
+ import { execFile as execFileCallback } from "node:child_process";
2
+ import { access, chmod, mkdir, readdir, readFile, rm, writeFile, } from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { promisify } from "node:util";
6
+ import { atomicWrite } from "@ocean/core/shared/fs-utils.js";
7
+ import { liveEngineEntry, oceanEnv, oceanPath, SYSTEM_DIR, } from "@ocean/core/shared/paths.js";
8
+ import { builtInProviderRecords, loadProviderRegistry, resolveOriginalExecutable, saveProviderRegistry, } from "../providers/provider-registry.js";
9
+ const execFile = promisify(execFileCallback);
10
+ const CLI_WRAPPER_NAMES = ["ocean"];
11
+ const DESKTOP_WRAPPER_PROVIDERS = ["claude"];
12
+ export function shimDirectory() {
13
+ return oceanPath(SYSTEM_DIR, "runtime", "shims");
14
+ }
15
+ function shellQuote(value) {
16
+ return `'${value.replaceAll("'", "'\\''")}'`;
17
+ }
18
+ function wrapperPath(command) {
19
+ return process.platform === "win32"
20
+ ? path.join(shimDirectory(), `${command}.cmd`)
21
+ : path.join(shimDirectory(), command);
22
+ }
23
+ // A provider wrapper never takes the provider's own name: a same-name shim shadows the native
24
+ // binary and its behaviour then depends on PATH order and on which shell is running (T-266).
25
+ // Clients reach Ocean through their native hooks; `ocean-<command>` is the explicit way in.
26
+ export function providerWrapperName(command) {
27
+ return `ocean-${command}`;
28
+ }
29
+ export function providerWrapperPath(command) {
30
+ return wrapperPath(providerWrapperName(command));
31
+ }
32
+ // Names Ocean may own in the shim directory; anything else there can shadow a real command.
33
+ function isNamespacedShim(name) {
34
+ const base = name.replace(/\.cmd$/i, "");
35
+ return (CLI_WRAPPER_NAMES.includes(base) ||
36
+ base.startsWith("ocean-") ||
37
+ DESKTOP_WRAPPER_PROVIDERS.some((provider) => base === `${provider}-vscode`));
38
+ }
39
+ // Every provider wrapper Ocean has written (posix and .cmd) sets OCEAN_SHIM_DIR and calls
40
+ // intercept; a user's own script that merely calls `ocean intercept` does not set the variable.
41
+ function isOceanInterceptWrapper(contents) {
42
+ return (contents.includes("OCEAN_SHIM_DIR=") &&
43
+ contents.includes(" intercept --client "));
44
+ }
45
+ // The interpreter recorded at sync time, else `node` from PATH once that one is gone (an fnm/nvm
46
+ // upgrade removes the old version's directory), else the usual install locations — an app started
47
+ // from the Dock has a minimal PATH (T-307).
48
+ function posixNode() {
49
+ return `node=${shellQuote(process.execPath)}\n[ -x "$node" ] || node=$(command -v node 2>/dev/null)\nfor candidate in /opt/homebrew/bin/node /usr/local/bin/node; do [ -x "$node" ] || node=$candidate; done\n`;
50
+ }
51
+ function windowsNode() {
52
+ return `set "node=${process.execPath}"\r\nif not exist "%node%" set "node=node"\r\n`;
53
+ }
54
+ export function desktopWrapperPath(provider = "claude") {
55
+ return wrapperPath(`${provider}-vscode`);
56
+ }
57
+ function wrapperContents(provider) {
58
+ const entry = shellQuote(liveEngineEntry());
59
+ if (process.platform === "win32") {
60
+ return `@echo off\r\nset "OCEAN_SHIM_DIR=${shimDirectory()}"\r\n${windowsNode()}"%node%" "${liveEngineEntry()}" intercept --client "${provider.id}" -- %*\r\n`;
61
+ }
62
+ return `#!/bin/sh\nexport OCEAN_SHIM_DIR=${shellQuote(shimDirectory())}\n${posixNode()}exec "$node" ${entry} intercept --client ${shellQuote(provider.id)} -- "$@"\n`;
63
+ }
64
+ function cliWrapperContents() {
65
+ if (process.platform === "win32") {
66
+ return `@echo off\r\n${windowsNode()}"%node%" "${liveEngineEntry()}" %*\r\n`;
67
+ }
68
+ return `#!/bin/sh\n${posixNode()}exec "$node" ${shellQuote(liveEngineEntry())} "$@"\n`;
69
+ }
70
+ function desktopWrapperContents(provider) {
71
+ if (process.platform === "win32") {
72
+ return `@echo off\r\nif "%~1"=="" exit /b 64\r\nset "native=%~1"\r\nshift\r\n${windowsNode()}"%node%" "${liveEngineEntry()}" intercept --client "${provider}" --executable "%native%" -- %*\r\n`;
73
+ }
74
+ return `#!/bin/sh
75
+ if [ "$#" -lt 1 ]; then
76
+ echo "Ocean desktop wrapper requires the native ${provider} executable path" >&2
77
+ exit 64
78
+ fi
79
+ native="$1"
80
+ shift
81
+ ${posixNode()}exec "$node" ${shellQuote(liveEngineEntry())} intercept --client ${shellQuote(provider)} --executable "$native" -- "$@"
82
+ `;
83
+ }
84
+ export async function syncProviderWrappers() {
85
+ const providers = loadProviderRegistry();
86
+ await mkdir(shimDirectory(), { recursive: true });
87
+ for (const name of CLI_WRAPPER_NAMES) {
88
+ const cliWrapper = wrapperPath(name);
89
+ await writeFile(cliWrapper, cliWrapperContents());
90
+ if (process.platform !== "win32")
91
+ await chmod(cliWrapper, 0o755);
92
+ }
93
+ for (const provider of providers) {
94
+ const file = providerWrapperPath(provider.command);
95
+ await writeFile(file, wrapperContents(provider));
96
+ if (process.platform !== "win32")
97
+ await chmod(file, 0o755);
98
+ }
99
+ for (const provider of DESKTOP_WRAPPER_PROVIDERS) {
100
+ const desktopWrapper = desktopWrapperPath(provider);
101
+ await writeFile(desktopWrapper, desktopWrapperContents(provider));
102
+ if (process.platform !== "win32")
103
+ await chmod(desktopWrapper, 0o755);
104
+ }
105
+ await removeLegacyShims();
106
+ await saveProviderRegistry(providers);
107
+ return { providers, directory: shimDirectory() };
108
+ }
109
+ // Removes same-name shims left by earlier syncs, including ones for providers no longer
110
+ // registered, but only files Ocean wrote: anything else is the user's, and doctor reports it.
111
+ async function removeLegacyShims() {
112
+ for (const name of await readdir(shimDirectory())) {
113
+ if (name.startsWith(".") || isNamespacedShim(name))
114
+ continue;
115
+ await removeLegacyShim(path.join(shimDirectory(), name));
116
+ }
117
+ }
118
+ async function removeLegacyShim(file) {
119
+ let contents;
120
+ try {
121
+ contents = await readFile(file, "utf8");
122
+ }
123
+ catch (error) {
124
+ if (error.code === "ENOENT")
125
+ return;
126
+ throw error;
127
+ }
128
+ if (isOceanInterceptWrapper(contents))
129
+ await rm(file, { force: true });
130
+ }
131
+ export async function registerProvider(id, command = id) {
132
+ if (!/^[a-zA-Z0-9._-]+$/.test(id) || !/^[a-zA-Z0-9._-]+$/.test(command)) {
133
+ throw new Error("Provider id and command may contain only letters, numbers, dot, underscore, and hyphen");
134
+ }
135
+ const providers = loadProviderRegistry().filter((provider) => provider.id !== id);
136
+ const provider = {
137
+ id,
138
+ command,
139
+ interactive: true,
140
+ headless: true,
141
+ };
142
+ await saveProviderRegistry([...providers, provider]);
143
+ await syncProviderWrappers();
144
+ return provider;
145
+ }
146
+ export async function setProviderEnabled(id, enabled) {
147
+ const providers = loadProviderRegistry();
148
+ const provider = providers.find((candidate) => candidate.id === id || candidate.command === id);
149
+ if (!provider)
150
+ throw new Error(`Provider is not registered: ${id}`);
151
+ const updated = { ...provider, enabled };
152
+ await saveProviderRegistry(providers.map((candidate) => candidate.id === provider.id ? updated : candidate));
153
+ return updated;
154
+ }
155
+ export async function removeProvider(id) {
156
+ const providers = loadProviderRegistry();
157
+ const provider = providers.find((candidate) => candidate.id === id || candidate.command === id);
158
+ if (!provider)
159
+ throw new Error(`Provider is not registered: ${id}`);
160
+ if (builtInProviderRecords().some((candidate) => candidate.id === provider.id)) {
161
+ throw new Error(`Built-in provider remains registered; use its package manager to remove: ${provider.id}`);
162
+ }
163
+ await rm(providerWrapperPath(provider.command), { force: true });
164
+ await removeLegacyShim(wrapperPath(provider.command));
165
+ await saveProviderRegistry(providers.filter((candidate) => candidate.id !== provider.id));
166
+ return provider;
167
+ }
168
+ export function shellKind(parentCommand, loginShell) {
169
+ const parent = path.basename(parentCommand.trim().split(/\s+/, 1)[0] ?? "");
170
+ if (parent === "fish")
171
+ return "fish";
172
+ if (["ash", "bash", "dash", "ksh", "sh", "zsh"].includes(parent))
173
+ return "posix";
174
+ return path.basename(loginShell) === "fish" ? "fish" : "posix";
175
+ }
176
+ async function currentShellName() {
177
+ if (process.platform === "win32")
178
+ return "powershell";
179
+ try {
180
+ const { stdout } = await execFile("ps", [
181
+ "-p",
182
+ String(process.ppid),
183
+ "-o",
184
+ "comm=",
185
+ ]);
186
+ const parent = path.basename(stdout.trim().split(/\s+/, 1)[0] ?? "");
187
+ if (["ash", "bash", "dash", "fish", "ksh", "sh", "zsh"].includes(parent))
188
+ return parent;
189
+ }
190
+ catch {
191
+ // Fall through to the configured login shell.
192
+ }
193
+ return path.basename(process.env.SHELL ?? "");
194
+ }
195
+ async function currentShellKind() {
196
+ return (await currentShellName()) === "fish" ? "fish" : "posix";
197
+ }
198
+ // Appended, once: the shim directory holds only Ocean's own names, so its PATH position no
199
+ // longer decides anything, and re-sourcing a profile never grows PATH.
200
+ export function shellPathLine(kind, directory) {
201
+ if (kind === "powershell")
202
+ return `if (-not (($env:Path -split ';') -contains "${directory}")) { $env:Path = "$env:Path;${directory}" }`;
203
+ const quoted = shellQuote(directory);
204
+ // --path edits $PATH itself; without it fish puts fish_user_paths in front of the system dirs.
205
+ if (kind === "fish")
206
+ return `fish_add_path --append --path ${quoted}`;
207
+ return `case ":$PATH:" in *:${quoted}:*) ;; *) export PATH="$PATH":${quoted} ;; esac`;
208
+ }
209
+ export async function installShellPath() {
210
+ const kind = process.platform === "win32" ? "powershell" : await currentShellKind();
211
+ return shellPathLine(kind, shimDirectory());
212
+ }
213
+ async function shellProfilePath() {
214
+ const home = os.homedir();
215
+ if (process.platform === "win32")
216
+ return (oceanEnv("SHELL_PROFILE") ??
217
+ path.join(home, "Documents", "PowerShell", "Microsoft.PowerShell_profile.ps1"));
218
+ const profile = oceanEnv("SHELL_PROFILE");
219
+ if (profile)
220
+ return profile;
221
+ const shell = await currentShellName();
222
+ if (shell === "fish")
223
+ return path.join(home, ".config", "fish", "config.fish");
224
+ if (shell === "zsh")
225
+ return path.join(home, ".zshrc");
226
+ if (shell === "bash")
227
+ return path.join(home, ".bashrc");
228
+ return path.join(home, ".profile");
229
+ }
230
+ const BLOCK_BEGIN = /^# >>> (ocean) interception >>>$/;
231
+ function withoutEol(line) {
232
+ return line.replace(/\r?\n$/, "");
233
+ }
234
+ // Removes every managed interception block and nothing else:
235
+ // all other lines keep their exact bytes and line endings. Only the single blank separator the
236
+ // installer puts above its block goes with it. A begin marker without a matching end marker is
237
+ // left alone rather than guessed at.
238
+ function stripInterceptionBlocks(text) {
239
+ const lines = text.split(/(?<=\n)/);
240
+ const kept = [];
241
+ // Once a name has no end marker after some position it has none after any later one, so
242
+ // repeated unterminated begin markers cost one scan, not one scan each.
243
+ const noEndFor = new Set();
244
+ for (let index = 0; index < lines.length; index += 1) {
245
+ const begin = BLOCK_BEGIN.exec(withoutEol(lines[index]));
246
+ if (begin && !noEndFor.has(begin[1])) {
247
+ const end = `# <<< ${begin[1]} interception <<<`;
248
+ const close = lines.findIndex((line, at) => at > index && withoutEol(line) === end);
249
+ if (close < 0)
250
+ noEndFor.add(begin[1]);
251
+ if (close >= 0) {
252
+ if (kept.length && withoutEol(kept[kept.length - 1]).trim() === "")
253
+ kept.pop();
254
+ index = close;
255
+ continue;
256
+ }
257
+ }
258
+ kept.push(lines[index]);
259
+ }
260
+ return kept.join("");
261
+ }
262
+ export async function installShellIntegration() {
263
+ const file = await shellProfilePath();
264
+ let existing = "";
265
+ try {
266
+ existing = await readFile(file, "utf8");
267
+ }
268
+ catch {
269
+ /* new profile */
270
+ }
271
+ const line = await installShellPath();
272
+ const eol = existing.includes("\r\n") ? "\r\n" : "\n";
273
+ let rest = stripInterceptionBlocks(existing);
274
+ if (rest && !rest.endsWith("\n"))
275
+ rest += eol;
276
+ const block = `${["# >>> ocean interception >>>", line, "# <<< ocean interception <<<"].join(eol)}${eol}`;
277
+ await mkdir(path.dirname(file), { recursive: true });
278
+ await atomicWrite(file, rest ? `${rest}${eol}${block}` : block);
279
+ return file;
280
+ }
281
+ export async function wrapperDoctor(commandPath) {
282
+ const findings = [];
283
+ const providers = loadProviderRegistry();
284
+ for (const name of CLI_WRAPPER_NAMES) {
285
+ try {
286
+ const contents = await readFile(wrapperPath(name), "utf8");
287
+ if (!contents.includes(liveEngineEntry()))
288
+ findings.push(`${name}: CLI wrapper is stale or does not route through Ocean; run: ocean doctor --fix`);
289
+ }
290
+ catch {
291
+ findings.push(`${name}: CLI wrapper is missing at ${wrapperPath(name)}; run: ocean doctor --fix`);
292
+ }
293
+ }
294
+ for (const provider of providers) {
295
+ try {
296
+ const contents = await readFile(providerWrapperPath(provider.command), "utf8");
297
+ if (!contents.includes(`intercept --client '${provider.id}'`) &&
298
+ !contents.includes(`intercept --client "${provider.id}"`)) {
299
+ findings.push(`${provider.id}: wrapper is stale or does not route through Ocean`);
300
+ }
301
+ }
302
+ catch {
303
+ findings.push(`${provider.id}: wrapper is missing at ${providerWrapperPath(provider.command)}`);
304
+ }
305
+ try {
306
+ resolveOriginalExecutable(provider.command);
307
+ }
308
+ catch (error) {
309
+ findings.push(`${provider.id}: ${error instanceof Error ? error.message : String(error)}`);
310
+ }
311
+ }
312
+ const currentPath = (process.env.PATH ?? "")
313
+ .split(path.delimiter)
314
+ .map((entry) => path.resolve(entry));
315
+ if (!currentPath.includes(path.resolve(shimDirectory())))
316
+ findings.push(`Ocean shim directory is not on PATH: ${shimDirectory()}`);
317
+ let names = [];
318
+ try {
319
+ names = await readdir(shimDirectory());
320
+ }
321
+ catch {
322
+ /* reported above as missing wrappers */
323
+ }
324
+ const strays = names.filter((entry) => !entry.startsWith(".") && !isNamespacedShim(entry));
325
+ for (const name of strays.sort())
326
+ findings.push(`${name}: ${path.join(shimDirectory(), name)} can shadow the native ${name} command; run: ocean client sync, or remove it if it is not Ocean's`);
327
+ const bypass = commandPath ? absolutePathBypassFinding(commandPath) : null;
328
+ if (bypass)
329
+ findings.push(bypass);
330
+ return findings;
331
+ }
332
+ export function absolutePathBypassFinding(commandPath) {
333
+ if (!path.isAbsolute(commandPath))
334
+ return null;
335
+ const resolved = path.resolve(commandPath);
336
+ if (resolved.startsWith(`${path.resolve(shimDirectory())}${path.sep}`))
337
+ return null;
338
+ const command = path.basename(resolved).replace(/\.(cmd|exe|bat)$/i, "");
339
+ const provider = loadProviderRegistry().find((candidate) => candidate.command === command);
340
+ // Running a provider natively is the normal route since T-266, but it reaches Ocean only
341
+ // through a registered native hook; say so instead of calling it a misuse.
342
+ if (provider)
343
+ return `NATIVE_ENTRY: ${provider.id} at ${resolved} reaches Ocean only through its native hook; ${providerWrapperName(provider.command)} routes it through Ocean`;
344
+ return `BYPASS_DETECTED: absolute path is outside Ocean and is not a registered provider: ${resolved}`;
345
+ }
346
+ export async function wrapperStatus() {
347
+ const providers = loadProviderRegistry();
348
+ return Promise.all(providers.map(async (provider) => {
349
+ let realExecutable = null;
350
+ try {
351
+ realExecutable = resolveOriginalExecutable(provider.command);
352
+ }
353
+ catch {
354
+ /* reported by doctor */
355
+ }
356
+ let installed = true;
357
+ try {
358
+ await access(providerWrapperPath(provider.command));
359
+ }
360
+ catch {
361
+ installed = false;
362
+ }
363
+ return {
364
+ ...provider,
365
+ enabled: provider.enabled !== false,
366
+ wrapper: providerWrapperPath(provider.command),
367
+ installed,
368
+ realExecutable,
369
+ entryPoint: "terminal-shim",
370
+ controlLevel: "observed",
371
+ };
372
+ }));
373
+ }
374
+ //# sourceMappingURL=wrapper-manager.js.map
@@ -0,0 +1,54 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { validateSessionEntryContract } from "@ocean/core/domain/sessions/entry-contract.js";
3
+ import { openSessionStore } from "@ocean/loop/infrastructure/persistence/session-store.js";
4
+ import { sessionProjectId } from "@ocean/loop/sessions/session-projects.js";
5
+ import { authLogin, authStatus, } from "../../application/auth/auth-orchestrator.js";
6
+ import { defaultAuthDeps } from "../../composition/runtime.js";
7
+ export async function runAuthCommand(action, provider) {
8
+ if (!provider || !["status", "login"].includes(action)) {
9
+ console.error("Usage: ocean auth status|login <client>");
10
+ process.exitCode = 1;
11
+ return;
12
+ }
13
+ const store = await openSessionStore();
14
+ const sessionId = randomUUID();
15
+ store.create({
16
+ sessionId,
17
+ provider,
18
+ providerSessionId: null,
19
+ parentSessionId: null,
20
+ profile: `auth:${provider}`,
21
+ profileIdentity: "",
22
+ workingDirectory: process.cwd(),
23
+ projectId: await sessionProjectId(process.cwd()),
24
+ resumeData: null,
25
+ });
26
+ store.appendEvent(sessionId, "session_entry_contract", JSON.stringify(validateSessionEntryContract({
27
+ entryPoint: "ocean-run",
28
+ controlLevel: "full-head",
29
+ inputCapture: "none",
30
+ contextTransport: "provider-owned-auth",
31
+ policyEnforcement: "provider-owned-auth-boundary",
32
+ promotion: "explicit-review",
33
+ resume: "unsupported",
34
+ })));
35
+ store.updateStatus(sessionId, "running");
36
+ store.appendEvent(sessionId, "auth_started", JSON.stringify({ action, provider }));
37
+ const state = action === "status"
38
+ ? await authStatus(provider, defaultAuthDeps)
39
+ : await authLogin(provider, defaultAuthDeps);
40
+ store.appendEvent(sessionId, "auth_state", JSON.stringify({ state }));
41
+ store.appendEvent(sessionId, "evidence", JSON.stringify({
42
+ type: "auth",
43
+ result: state,
44
+ criterion: "provider-owned authentication state",
45
+ }));
46
+ store.updateStatus(sessionId, ["failed", "cancelled", "not_supported"].includes(state)
47
+ ? "failed"
48
+ : "completed");
49
+ store.close();
50
+ console.log(JSON.stringify({ provider, state, sessionId }));
51
+ if (state === "failed" || state === "not_supported")
52
+ process.exitCode = 1;
53
+ }
54
+ //# sourceMappingURL=auth-command.js.map
@@ -0,0 +1,93 @@
1
+ import { execFile } from "node:child_process";
2
+ import { mkdir, rm, writeFile } from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { promisify } from "node:util";
6
+ import { liveEngineEntry, oceanRoot } from "@ocean/core/shared/paths.js";
7
+ import { BrowserBridge, browserBridgeHealth, browserBridgePort, serveBrowserBridge, } from "@ocean/market/mcp/browser-bridge.js";
8
+ import { McpClient } from "@ocean/services/infrastructure/mcp/mcp-client.js";
9
+ import { oceanVersion } from "../../version.js";
10
+ const LABEL = "com.ocean.browser-bridge";
11
+ const run = promisify(execFile);
12
+ // `ocean mcp browser-bridge`: the one loopback MCP endpoint in front of the owner's Chrome. Logs
13
+ // go to stderr and never carry page content.
14
+ export async function runBrowserBridgeCommand() {
15
+ const log = (line) => console.error(`ocean browser bridge: ${line}`);
16
+ const bridge = new BrowserBridge({
17
+ client: (member) => new McpClient({
18
+ command: member.command,
19
+ args: member.args,
20
+ cwd: member.cwd,
21
+ env: member.env,
22
+ inheritEnv: member.inheritEnv,
23
+ }),
24
+ version: await oceanVersion(),
25
+ log,
26
+ });
27
+ const port = browserBridgePort();
28
+ const server = await serveBrowserBridge(bridge, port, log);
29
+ log(`listening on 127.0.0.1:${port}`);
30
+ const stop = () => {
31
+ bridge.close();
32
+ server.close();
33
+ process.exit(0);
34
+ };
35
+ process.once("SIGTERM", stop);
36
+ process.once("SIGINT", stop);
37
+ }
38
+ function plistPath() {
39
+ return path.join(os.homedir(), "Library", "LaunchAgents", `${LABEL}.plist`);
40
+ }
41
+ function launchdDomain() {
42
+ return `gui/${process.getuid?.() ?? ""}`;
43
+ }
44
+ async function installService() {
45
+ const logs = path.join(os.homedir(), "Library", "Logs");
46
+ const file = plistPath();
47
+ await mkdir(path.dirname(file), { recursive: true });
48
+ await writeFile(file, `<?xml version="1.0" encoding="UTF-8"?>
49
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
50
+ <plist version="1.0"><dict>
51
+ <key>Label</key><string>${LABEL}</string>
52
+ <key>ProgramArguments</key><array><string>${process.execPath}</string><string>${liveEngineEntry()}</string><string>mcp</string><string>browser-bridge</string></array>
53
+ <key>WorkingDirectory</key><string>${oceanRoot()}</string>
54
+ <key>RunAtLoad</key><true/>
55
+ <key>KeepAlive</key><true/>
56
+ <key>StandardErrorPath</key><string>${path.join(logs, `${LABEL}.log`)}</string>
57
+ </dict></plist>
58
+ `);
59
+ await run("launchctl", ["bootout", `${launchdDomain()}/${LABEL}`]).catch(() => { });
60
+ await run("launchctl", ["bootstrap", launchdDomain(), file]);
61
+ console.log(`installed ${LABEL}: ${file}`);
62
+ }
63
+ async function uninstallService() {
64
+ await run("launchctl", ["bootout", `${launchdDomain()}/${LABEL}`]).catch(() => { });
65
+ await rm(plistPath(), { force: true });
66
+ console.log(`removed ${LABEL}`);
67
+ }
68
+ export async function runBrowserServiceCommand(action) {
69
+ if (process.platform !== "darwin") {
70
+ console.error("ocean mcp browser-service supports macOS only; run `ocean mcp browser-bridge` under your own service manager.");
71
+ process.exitCode = 1;
72
+ }
73
+ else if (action === "install")
74
+ await installService();
75
+ else if (action === "uninstall")
76
+ await uninstallService();
77
+ else {
78
+ console.error("Usage: ocean mcp browser-service install|uninstall");
79
+ process.exitCode = 1;
80
+ }
81
+ }
82
+ export async function browserBridgeFindings() {
83
+ const health = await browserBridgeHealth();
84
+ const finding = (severity, message) => [
85
+ { code: "BROWSER_BRIDGE", severity, message, fixable: false },
86
+ ];
87
+ if (!health)
88
+ return finding("WARN", "browser bridge is not reachable; start it with `ocean mcp browser-service install` (a sandboxed shell cannot reach loopback)");
89
+ return health.chromeDebuggingListening
90
+ ? finding("OK", "browser bridge is up and Chrome debugging is listening")
91
+ : finding("WARN", "browser bridge is up but Chrome is closed or chrome://inspect/#remote-debugging is off");
92
+ }
93
+ //# sourceMappingURL=browser-bridge-command.js.map