@amoree-code/ocean 0.0.0-stage → 1.1.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.
- package/LICENSE +21 -0
- package/README.md +284 -2
- package/dist/application/auth/auth-orchestrator.js +77 -0
- package/dist/application/clients/clients.js +646 -0
- package/dist/application/clients/editors.js +398 -0
- package/dist/application/clients/hooks.js +335 -0
- package/dist/application/clients/native.js +410 -0
- package/dist/application/clients/opencode-plugin.js +158 -0
- package/dist/application/clients/registry.js +188 -0
- package/dist/application/gateway/webhook-gateway.js +137 -0
- package/dist/application/install/provider-installer.js +184 -0
- package/dist/application/integrations/charter-copies.js +76 -0
- package/dist/application/integrations/claude-vscode.js +106 -0
- package/dist/application/integrations/engine-entry-repair.js +105 -0
- package/dist/application/integrations/skill-filter.js +254 -0
- package/dist/application/layout/layout-apply.js +443 -0
- package/dist/application/layout/layout-plan.js +236 -0
- package/dist/application/layout/workspace-template.js +34 -0
- package/dist/application/onboarding/state.js +23 -0
- package/dist/application/onboarding/steps.js +249 -0
- package/dist/application/profiles/profile-distribution.js +53 -0
- package/dist/application/profiles/profile-suggestion.js +39 -0
- package/dist/application/profiles/profile-upgrade-service.js +208 -0
- package/dist/application/pull/history-index.js +476 -0
- package/dist/application/pull/project-match.js +132 -0
- package/dist/application/pull/pull.js +202 -0
- package/dist/application/pull/server-capture.js +307 -0
- package/dist/application/scheduler/local-scheduler.js +210 -0
- package/dist/composition/runtime.js +64 -0
- package/dist/engine-root.js +9 -0
- package/dist/infrastructure/filesystem/profile-loader.js +46 -0
- package/dist/infrastructure/filesystem/runtime-config-loader.js +29 -0
- package/dist/infrastructure/filesystem/skill-loader.js +84 -0
- package/dist/infrastructure/filesystem/workspace-capability.js +37 -0
- package/dist/infrastructure/observability/runtime-logger.js +16 -0
- package/dist/infrastructure/process/agent-tool-executor.js +99 -0
- package/dist/infrastructure/process/cli-process.js +49 -0
- package/dist/infrastructure/process/interactive-process.js +145 -0
- package/dist/infrastructure/process/process-supervisor.js +194 -0
- package/dist/infrastructure/process/service.js +252 -0
- package/dist/infrastructure/providers/browser-provider.js +2 -0
- package/dist/infrastructure/providers/claude-sdk.js +137 -0
- package/dist/infrastructure/providers/client-home.js +14 -0
- package/dist/infrastructure/providers/fake-browser-provider.js +146 -0
- package/dist/infrastructure/providers/ollama-embedder.js +74 -0
- package/dist/infrastructure/providers/openai-compatible.js +268 -0
- package/dist/infrastructure/providers/playwright-browser-provider.js +380 -0
- package/dist/infrastructure/providers/provider-capabilities.js +50 -0
- package/dist/infrastructure/providers/provider-invocation.js +184 -0
- package/dist/infrastructure/providers/provider-registry.js +77 -0
- package/dist/infrastructure/providers/providers.js +134 -0
- package/dist/infrastructure/wrappers/wrapper-manager.js +374 -0
- package/dist/interfaces/cli/auth-command.js +54 -0
- package/dist/interfaces/cli/browser-bridge-command.js +93 -0
- package/dist/interfaces/cli/browser-command.js +119 -0
- package/dist/interfaces/cli/capture-command.js +125 -0
- package/dist/interfaces/cli/client-test-command.js +133 -0
- package/dist/interfaces/cli/clients-command.js +129 -0
- package/dist/interfaces/cli/command-registry.js +1211 -0
- package/dist/interfaces/cli/context-command.js +172 -0
- package/dist/interfaces/cli/daily-command.js +131 -0
- package/dist/interfaces/cli/governance-command.js +55 -0
- package/dist/interfaces/cli/handoff-command.js +35 -0
- package/dist/interfaces/cli/help-command.js +207 -0
- package/dist/interfaces/cli/idea-command.js +47 -0
- package/dist/interfaces/cli/intercept-command.js +317 -0
- package/dist/interfaces/cli/layout-command.js +27 -0
- package/dist/interfaces/cli/market-command.js +52 -0
- package/dist/interfaces/cli/mcp-gateway-command.js +30 -0
- package/dist/interfaces/cli/memory-command.js +317 -0
- package/dist/interfaces/cli/migrate-command.js +26 -0
- package/dist/interfaces/cli/observe-command.js +78 -0
- package/dist/interfaces/cli/onboard-command.js +123 -0
- package/dist/interfaces/cli/operate-command.js +59 -0
- package/dist/interfaces/cli/profiles-command.js +70 -0
- package/dist/interfaces/cli/pull-command.js +54 -0
- package/dist/interfaces/cli/setup-command.js +143 -0
- package/dist/interfaces/cli/tasks-command.js +249 -0
- package/dist/main.js +49 -0
- package/dist/node-warnings.js +17 -0
- package/dist/version.js +8 -0
- package/market/mcp/chrome-devtools/ocean.capability.json +28 -0
- package/market/mcp/gateway/ocean.capability.json +21 -0
- package/market/mcp/obsidian/ocean.capability.json +21 -0
- package/market/mcp/ocean/ocean.capability.json +21 -0
- package/market/mcp/playwright/ocean.capability.json +23 -0
- package/node_modules/@ocean/brain/dist/brain/brain-eval.js +29 -0
- package/node_modules/@ocean/brain/dist/brain/brain-markdown.js +226 -0
- package/node_modules/@ocean/brain/dist/brain/brain-reindex.js +247 -0
- package/node_modules/@ocean/brain/dist/brain/brain-service.js +320 -0
- package/node_modules/@ocean/brain/dist/capture/inbox-sync.js +22 -0
- package/node_modules/@ocean/brain/dist/evidence/verification-report.js +13 -0
- package/node_modules/@ocean/brain/dist/infrastructure/brain/brain-index.js +285 -0
- package/node_modules/@ocean/brain/dist/memory/brain-dump.js +145 -0
- package/node_modules/@ocean/brain/dist/memory/daily-narrative.js +142 -0
- package/node_modules/@ocean/brain/dist/memory/index-sync.js +124 -0
- package/node_modules/@ocean/brain/dist/memory/lesson-activation.js +93 -0
- package/node_modules/@ocean/brain/dist/memory/lesson-learning.js +188 -0
- package/node_modules/@ocean/brain/dist/memory/lessons.js +321 -0
- package/node_modules/@ocean/brain/dist/memory/memory-audit.js +100 -0
- package/node_modules/@ocean/brain/dist/memory/memory-digest.js +49 -0
- package/node_modules/@ocean/brain/dist/memory/memory-harvest.js +345 -0
- package/node_modules/@ocean/brain/dist/memory/memory-paths.js +174 -0
- package/node_modules/@ocean/brain/dist/memory/memory-review.js +278 -0
- package/node_modules/@ocean/brain/dist/memory/memory-trash.js +152 -0
- package/node_modules/@ocean/brain/dist/memory/memory-write.js +216 -0
- package/node_modules/@ocean/brain/dist/memory/model-narrative.js +129 -0
- package/node_modules/@ocean/brain/dist/memory/profile-facts.js +103 -0
- package/node_modules/@ocean/brain/dist/memory/session-promotion.js +61 -0
- package/node_modules/@ocean/brain/dist/memory/session-summary.js +113 -0
- package/node_modules/@ocean/brain/dist/memory/signal-detection.js +66 -0
- package/node_modules/@ocean/brain/package.json +11 -0
- package/node_modules/@ocean/context/dist/context/context-compression.js +66 -0
- package/node_modules/@ocean/context/dist/context/context-cost.js +587 -0
- package/node_modules/@ocean/context/dist/context/context-ladder.js +363 -0
- package/node_modules/@ocean/context/dist/context/context-packet.js +213 -0
- package/node_modules/@ocean/context/dist/context/context-references.js +255 -0
- package/node_modules/@ocean/context/dist/context/intent-router.js +259 -0
- package/node_modules/@ocean/context/dist/context/resource-injection.js +36 -0
- package/node_modules/@ocean/context/package.json +11 -0
- package/node_modules/@ocean/core/dist/domain/brain/brain-record.js +22 -0
- package/node_modules/@ocean/core/dist/domain/brain/record-scope.js +22 -0
- package/node_modules/@ocean/core/dist/domain/capabilities/browser-contract.js +64 -0
- package/node_modules/@ocean/core/dist/domain/capabilities/capability-contract.js +16 -0
- package/node_modules/@ocean/core/dist/domain/capabilities/capability-manifest.js +72 -0
- package/node_modules/@ocean/core/dist/domain/context/context-validator.js +5 -0
- package/node_modules/@ocean/core/dist/domain/context/context.js +34 -0
- package/node_modules/@ocean/core/dist/domain/context/project-id.js +30 -0
- package/node_modules/@ocean/core/dist/domain/evidence/evidence.js +16 -0
- package/node_modules/@ocean/core/dist/domain/mcp/mcp-contract.js +36 -0
- package/node_modules/@ocean/core/dist/domain/mcp/memory-contract.js +41 -0
- package/node_modules/@ocean/core/dist/domain/ports/brain-index-port.js +7 -0
- package/node_modules/@ocean/core/dist/domain/ports/browser-port.js +8 -0
- package/node_modules/@ocean/core/dist/domain/ports/embedder-port.js +2 -0
- package/node_modules/@ocean/core/dist/domain/ports/platform-ports.js +2 -0
- package/node_modules/@ocean/core/dist/domain/ports/runtime-ports.js +2 -0
- package/node_modules/@ocean/core/dist/domain/ports/session-store-port.js +2 -0
- package/node_modules/@ocean/core/dist/domain/process/process-events.js +5 -0
- package/node_modules/@ocean/core/dist/domain/profiles/profile-distribution.js +19 -0
- package/node_modules/@ocean/core/dist/domain/profiles/profile-policy.js +34 -0
- package/node_modules/@ocean/core/dist/domain/profiles/profile-selection.js +73 -0
- package/node_modules/@ocean/core/dist/domain/profiles/profile-upgrade.js +123 -0
- package/node_modules/@ocean/core/dist/domain/profiles/profile-validator.js +5 -0
- package/node_modules/@ocean/core/dist/domain/profiles/profile.js +218 -0
- package/node_modules/@ocean/core/dist/domain/profiles/runtime-config.js +30 -0
- package/node_modules/@ocean/core/dist/domain/providers/provider-capability.js +28 -0
- package/node_modules/@ocean/core/dist/domain/providers/provider.js +2 -0
- package/node_modules/@ocean/core/dist/domain/redaction/redaction.js +86 -0
- package/node_modules/@ocean/core/dist/domain/runs/agent-tools.js +92 -0
- package/node_modules/@ocean/core/dist/domain/runs/execution-plan.js +71 -0
- package/node_modules/@ocean/core/dist/domain/runs/run-contract.js +38 -0
- package/node_modules/@ocean/core/dist/domain/sessions/entry-contract.js +31 -0
- package/node_modules/@ocean/core/dist/domain/sessions/handoff.js +78 -0
- package/node_modules/@ocean/core/dist/domain/sessions/session.js +58 -0
- package/node_modules/@ocean/core/dist/domain/skills/default-skills.js +29 -0
- package/node_modules/@ocean/core/dist/domain/skills/skill-validator.js +5 -0
- package/node_modules/@ocean/core/dist/domain/skills/skill.js +11 -0
- package/node_modules/@ocean/core/dist/domain/tasks/task-id.js +85 -0
- package/node_modules/@ocean/core/dist/shared/fs-utils.js +212 -0
- package/node_modules/@ocean/core/dist/shared/local-date.js +16 -0
- package/node_modules/@ocean/core/dist/shared/paths.js +324 -0
- package/node_modules/@ocean/core/dist/shared/project-resolution.js +1065 -0
- package/node_modules/@ocean/core/dist/shared/task-reference.js +133 -0
- package/node_modules/@ocean/core/package.json +14 -0
- package/node_modules/@ocean/loop/dist/handoff/handoff-service.js +133 -0
- package/node_modules/@ocean/loop/dist/hooks/lifecycle-hooks.js +15 -0
- package/node_modules/@ocean/loop/dist/hooks/session-end-hook.js +183 -0
- package/node_modules/@ocean/loop/dist/hooks/session-start-hook.js +237 -0
- package/node_modules/@ocean/loop/dist/infrastructure/persistence/session-store.js +690 -0
- package/node_modules/@ocean/loop/dist/infrastructure/persistence/session-validator.js +5 -0
- package/node_modules/@ocean/loop/dist/loops/iteration-graph.js +147 -0
- package/node_modules/@ocean/loop/dist/loops/task-loop-record.js +2 -0
- package/node_modules/@ocean/loop/dist/loops/task-loop.js +281 -0
- package/node_modules/@ocean/loop/dist/operations/memory-knowledge-operations.js +263 -0
- package/node_modules/@ocean/loop/dist/operations/operation-contract.js +227 -0
- package/node_modules/@ocean/loop/dist/operations/operation-scope.js +28 -0
- package/node_modules/@ocean/loop/dist/operations/project-operations.js +93 -0
- package/node_modules/@ocean/loop/dist/operations/record-io.js +65 -0
- package/node_modules/@ocean/loop/dist/operations/record-operations.js +45 -0
- package/node_modules/@ocean/loop/dist/operations/task-linker.js +40 -0
- package/node_modules/@ocean/loop/dist/operations/task-operations.js +279 -0
- package/node_modules/@ocean/loop/dist/operations/write-guard.js +157 -0
- package/node_modules/@ocean/loop/dist/runs/prompt-assembly.js +112 -0
- package/node_modules/@ocean/loop/dist/runs/run-agent.js +425 -0
- package/node_modules/@ocean/loop/dist/runs/run-authorization.js +24 -0
- package/node_modules/@ocean/loop/dist/runs/tool-loop.js +173 -0
- package/node_modules/@ocean/loop/dist/sessions/session-closeout.js +150 -0
- package/node_modules/@ocean/loop/dist/sessions/session-pointer.js +133 -0
- package/node_modules/@ocean/loop/dist/sessions/session-projects.js +26 -0
- package/node_modules/@ocean/loop/dist/sessions/session-retention.js +132 -0
- package/node_modules/@ocean/loop/dist/tasks/archive-tasks.js +210 -0
- package/node_modules/@ocean/loop/dist/tasks/checkpoint-task.js +31 -0
- package/node_modules/@ocean/loop/dist/tasks/claim-task.js +126 -0
- package/node_modules/@ocean/loop/dist/tasks/list-tasks.js +113 -0
- package/node_modules/@ocean/loop/dist/tasks/new-task.js +42 -0
- package/node_modules/@ocean/loop/dist/tasks/renumber-tasks.js +134 -0
- package/node_modules/@ocean/loop/dist/tasks/verification-readiness.js +78 -0
- package/node_modules/@ocean/loop/dist/tasks/verify-task.js +234 -0
- package/node_modules/@ocean/loop/package.json +11 -0
- package/node_modules/@ocean/market/dist/adapters/mcp-adapter.js +64 -0
- package/node_modules/@ocean/market/dist/adapters/owned-directory.js +89 -0
- package/node_modules/@ocean/market/dist/adapters/plugin-adapter.js +39 -0
- package/node_modules/@ocean/market/dist/adapters/skill-adapter.js +32 -0
- package/node_modules/@ocean/market/dist/capability/adapter.js +2 -0
- package/node_modules/@ocean/market/dist/capability/catalog.js +63 -0
- package/node_modules/@ocean/market/dist/capability/content-digest.js +40 -0
- package/node_modules/@ocean/market/dist/capability/registry.js +81 -0
- package/node_modules/@ocean/market/dist/install/default-marketplace.js +47 -0
- package/node_modules/@ocean/market/dist/install/lock.js +34 -0
- package/node_modules/@ocean/market/dist/install/marketplace-service.js +165 -0
- package/node_modules/@ocean/market/dist/mcp/browser-bridge.js +263 -0
- package/node_modules/@ocean/market/dist/mcp/mcp-connection.js +67 -0
- package/node_modules/@ocean/market/dist/mcp/mcp-gateway.js +317 -0
- package/node_modules/@ocean/market/dist/mcp/mcp-launch.js +102 -0
- package/node_modules/@ocean/market/dist/mcp/mcp-runner.js +32 -0
- package/node_modules/@ocean/market/dist/security/description-scan.js +45 -0
- package/node_modules/@ocean/market/dist/security/permissions.js +57 -0
- package/node_modules/@ocean/market/dist/security/sandbox.js +74 -0
- package/node_modules/@ocean/market/dist/skills/core-skill-sync.js +113 -0
- package/node_modules/@ocean/market/dist/skills/signal-detection.js +84 -0
- package/node_modules/@ocean/market/dist/skills/skill-curation.js +123 -0
- package/node_modules/@ocean/market/dist/skills/skill-hub.js +220 -0
- package/node_modules/@ocean/market/dist/skills/task-observer.js +165 -0
- package/node_modules/@ocean/market/package.json +11 -0
- package/node_modules/@ocean/services/dist/browser/browser-service.js +124 -0
- package/node_modules/@ocean/services/dist/browser/browser-session.js +287 -0
- package/node_modules/@ocean/services/dist/browser/browser-task-runner.js +70 -0
- package/node_modules/@ocean/services/dist/doctor/workspace-doctor.js +547 -0
- package/node_modules/@ocean/services/dist/infrastructure/mcp/call-ledger.js +114 -0
- package/node_modules/@ocean/services/dist/infrastructure/mcp/mcp-client.js +162 -0
- package/node_modules/@ocean/services/dist/infrastructure/mcp/obsidian-server.js +251 -0
- package/node_modules/@ocean/services/dist/infrastructure/mcp/ocean-server.js +880 -0
- package/node_modules/@ocean/services/dist/infrastructure/mcp/server-version.js +20 -0
- package/node_modules/@ocean/services/dist/infrastructure/mcp/tool-registry.js +29 -0
- package/node_modules/@ocean/services/dist/obsidian/conflict-log.js +68 -0
- package/node_modules/@ocean/services/dist/obsidian/inbox-promotion.js +80 -0
- package/node_modules/@ocean/services/dist/obsidian/vault-discovery.js +113 -0
- package/node_modules/@ocean/services/dist/obsidian/vault-ingestion.js +77 -0
- package/node_modules/@ocean/services/dist/obsidian/vault-sync.js +73 -0
- package/node_modules/@ocean/services/dist/obsidian/vault-writer.js +75 -0
- package/node_modules/@ocean/services/package.json +11 -0
- package/ocean-marketplace.json +415 -0
- package/package.json +67 -4
- package/skills/core/business-logic/SKILL.md +22 -0
- package/skills/core/catch-up/SKILL.md +17 -0
- package/skills/core/core-thinking/SKILL.md +11 -0
- package/skills/core/design-thinking/SKILL.md +19 -0
- package/skills/core/graft/SKILL.md +156 -0
- package/skills/core/loop/SKILL.md +20 -0
- package/skills/core/session-handoff/SKILL.md +15 -0
- package/skills/core/use-browser/SKILL.md +69 -0
- package/skills/core/verification/SKILL.md +11 -0
- package/skills/index.json +56 -0
- package/templates/README.md +4 -0
- package/templates/profiles/default.json +51 -0
- package/templates/profiles/developer.json +51 -0
- package/templates/profiles/reviewer.json +51 -0
- package/templates/profiles/strategist.json +51 -0
- package/templates/workspace/00-inbox/INBOX.md +7 -0
- package/templates/workspace/00-inbox/candidates/.gitkeep +0 -0
- package/templates/workspace/00-inbox/native/.gitkeep +0 -0
- package/templates/workspace/01-daily/.gitkeep +0 -0
- package/templates/workspace/02-personal/CONFLICTS.md +7 -0
- package/templates/workspace/02-personal/MEMORY.md +15 -0
- package/templates/workspace/02-personal/goals/.gitkeep +0 -0
- package/templates/workspace/02-personal/identity/.gitkeep +0 -0
- package/templates/workspace/02-personal/preferences/.gitkeep +0 -0
- package/templates/workspace/03-professional/.gitkeep +0 -0
- package/templates/workspace/04-projects/PROJECTS.md +5 -0
- package/templates/workspace/04-projects/_template/docs/.gitkeep +0 -0
- package/templates/workspace/04-projects/_template/memory/.gitkeep +0 -0
- package/templates/workspace/04-projects/_template/tasks/.gitkeep +0 -0
- package/templates/workspace/05-knowledge/KNOWLEDGE.md +8 -0
- package/templates/workspace/05-knowledge/decisions/.gitkeep +0 -0
- package/templates/workspace/05-knowledge/lessons/.gitkeep +0 -0
- package/templates/workspace/06-templates/TEMPLATES.md +25 -0
- package/templates/workspace/06-templates/decision.md +8 -0
- package/templates/workspace/06-templates/intelligence.md +32 -0
- package/templates/workspace/06-templates/project-claude.md +48 -0
- package/templates/workspace/06-templates/session.md +38 -0
- package/templates/workspace/06-templates/task.md +42 -0
- package/templates/workspace/MANIFEST.json +51 -0
- package/templates/workspace/bridge/config/CONFIG.md +21 -0
- package/templates/workspace/bridge/config/runtime.json +4 -0
- package/templates/workspace/charter/core.md +104 -0
- package/templates/workspace/charter/policies/README.md +31 -0
- package/templates/workspace/charter/policies/claude-code-context.md +90 -0
- package/templates/workspace/charter/policies/concise-output.md +29 -0
- package/templates/workspace/charter/policies/context.md +91 -0
- package/templates/workspace/charter/policies/contracts/task.md +189 -0
- package/templates/workspace/charter/policies/git.md +60 -0
- package/templates/workspace/charter/policies/graph.md +47 -0
- package/templates/workspace/charter/policies/knowledge.md +33 -0
- package/templates/workspace/charter/policies/lifecycle.md +119 -0
- package/templates/workspace/charter/policies/memory.md +67 -0
- package/templates/workspace/charter/policies/minimal-change.md +43 -0
- package/templates/workspace/charter/policies/models.md +57 -0
- package/templates/workspace/charter/policies/privacy-terms.txt +3 -0
- package/templates/workspace/charter/policies/response.md +39 -0
- package/templates/workspace/charter/policies/skill-observer.md +44 -0
- package/templates/workspace/charter/policies/stack.md +23 -0
- package/templates/workspace/charter/policies/task.md +139 -0
- package/templates/workspace/charter/policies/verification.md +29 -0
- package/templates/workspace/sessions/.gitkeep +0 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { jsonEditor, tomlEditor, yamlEditor, yamlListEditor, } from "./editors.js";
|
|
2
|
+
// OpenCode stores `{ type: "local", command: [cmd, ...args], enabled }`.
|
|
3
|
+
const opencodeEditor = jsonEditor(["mcp"], (value) => ({
|
|
4
|
+
type: "local",
|
|
5
|
+
command: [value.command, ...(value.args ?? [])],
|
|
6
|
+
enabled: true,
|
|
7
|
+
}), (node) => {
|
|
8
|
+
const raw = (node ?? {});
|
|
9
|
+
const command = Array.isArray(raw.command) ? raw.command : [];
|
|
10
|
+
const rest = Object.fromEntries(Object.entries(raw).filter(([key]) => !["type", "command", "enabled"].includes(key)));
|
|
11
|
+
return {
|
|
12
|
+
...rest,
|
|
13
|
+
command: command[0],
|
|
14
|
+
args: command.slice(1),
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
export const CLIENT_ADAPTERS = [
|
|
18
|
+
{
|
|
19
|
+
id: "claude-code",
|
|
20
|
+
label: "Claude Code (CLI and IDE extensions)",
|
|
21
|
+
tier: "supported",
|
|
22
|
+
detect: { binaries: ["claude"], paths: [".claude.json"] },
|
|
23
|
+
config: { file: ".claude.json", editor: jsonEditor(["mcpServers"]) },
|
|
24
|
+
nativeMemory: "redirect-staging",
|
|
25
|
+
mcpClients: [{ name: "claude-code" }],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: "claude-desktop",
|
|
29
|
+
label: "Claude Desktop",
|
|
30
|
+
tier: "supported",
|
|
31
|
+
detect: {
|
|
32
|
+
apps: ["Claude.app"],
|
|
33
|
+
paths: ["Library/Application Support/Claude"],
|
|
34
|
+
},
|
|
35
|
+
config: {
|
|
36
|
+
file: "Library/Application Support/Claude/claude_desktop_config.json",
|
|
37
|
+
editor: jsonEditor(["mcpServers"]),
|
|
38
|
+
},
|
|
39
|
+
nativeMemory: "capture",
|
|
40
|
+
// Normal chat uses the claude.ai name although it runs locally; agent mode names its own
|
|
41
|
+
// `local-agent-mode-<server>` client.
|
|
42
|
+
mcpClients: [
|
|
43
|
+
{ name: "claude-ai", mode: "chat" },
|
|
44
|
+
{ name: "local-agent-mode-ocean", mode: "agent" },
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: "codex",
|
|
49
|
+
label: "Codex CLI",
|
|
50
|
+
tier: "supported",
|
|
51
|
+
detect: { binaries: ["codex"], paths: [".codex/config.toml"] },
|
|
52
|
+
config: { file: ".codex/config.toml", editor: tomlEditor("mcp_servers") },
|
|
53
|
+
nativeMemory: "disable",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: "gemini",
|
|
57
|
+
label: "Gemini CLI",
|
|
58
|
+
tier: "supported",
|
|
59
|
+
detect: { binaries: ["gemini"], paths: [".gemini/settings.json"] },
|
|
60
|
+
config: {
|
|
61
|
+
file: ".gemini/settings.json",
|
|
62
|
+
editor: jsonEditor(["mcpServers"]),
|
|
63
|
+
},
|
|
64
|
+
nativeMemory: "disable",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "antigravity",
|
|
68
|
+
label: "Antigravity",
|
|
69
|
+
tier: "supported",
|
|
70
|
+
detect: {
|
|
71
|
+
binaries: ["agy", "antigravity"],
|
|
72
|
+
apps: ["Antigravity.app"],
|
|
73
|
+
paths: [".gemini/antigravity/mcp_config.json"],
|
|
74
|
+
},
|
|
75
|
+
config: {
|
|
76
|
+
file: ".gemini/antigravity/mcp_config.json",
|
|
77
|
+
editor: jsonEditor(["mcpServers"]),
|
|
78
|
+
},
|
|
79
|
+
nativeMemory: "harvest",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: "cursor",
|
|
83
|
+
label: "Cursor",
|
|
84
|
+
tier: "supported",
|
|
85
|
+
detect: {
|
|
86
|
+
binaries: ["cursor-agent", "cursor"],
|
|
87
|
+
apps: ["Cursor.app"],
|
|
88
|
+
paths: [".cursor/mcp.json"],
|
|
89
|
+
},
|
|
90
|
+
config: { file: ".cursor/mcp.json", editor: jsonEditor(["mcpServers"]) },
|
|
91
|
+
nativeMemory: "uncaptured",
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: "hermes",
|
|
95
|
+
label: "Hermes",
|
|
96
|
+
tier: "supported",
|
|
97
|
+
detect: { binaries: ["hermes"], paths: [".hermes/config.yaml"] },
|
|
98
|
+
config: { file: ".hermes/config.yaml", editor: yamlEditor("mcp_servers") },
|
|
99
|
+
nativeMemory: "disable",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "opencode",
|
|
103
|
+
label: "OpenCode",
|
|
104
|
+
tier: "supported",
|
|
105
|
+
detect: {
|
|
106
|
+
binaries: ["opencode"],
|
|
107
|
+
paths: [".config/opencode/opencode.json"],
|
|
108
|
+
},
|
|
109
|
+
config: { file: ".config/opencode/opencode.json", editor: opencodeEditor },
|
|
110
|
+
nativeMemory: "none",
|
|
111
|
+
mcpClients: [{ name: "opencode" }],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: "qwen",
|
|
115
|
+
label: "Qwen Code",
|
|
116
|
+
tier: "supported",
|
|
117
|
+
detect: { binaries: ["qwen"], paths: [".qwen/settings.json"] },
|
|
118
|
+
config: { file: ".qwen/settings.json", editor: jsonEditor(["mcpServers"]) },
|
|
119
|
+
nativeMemory: "disable",
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: "continue",
|
|
123
|
+
label: "Continue (VS Code)",
|
|
124
|
+
tier: "supported",
|
|
125
|
+
detect: { paths: [".continue/config.yaml"] },
|
|
126
|
+
config: {
|
|
127
|
+
file: ".continue/config.yaml",
|
|
128
|
+
editor: yamlListEditor("mcpServers"),
|
|
129
|
+
},
|
|
130
|
+
nativeMemory: "none",
|
|
131
|
+
reason: "its mcpServers is a YAML list; Ocean does not rewrite it",
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: "aider",
|
|
135
|
+
label: "Aider",
|
|
136
|
+
tier: "context-file",
|
|
137
|
+
detect: { binaries: ["aider"] },
|
|
138
|
+
nativeMemory: "none",
|
|
139
|
+
reason: "no MCP support",
|
|
140
|
+
fallback: "pass an Ocean context file with --read; use `ocean memory …` for memory",
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: "chatgpt",
|
|
144
|
+
label: "ChatGPT desktop",
|
|
145
|
+
tier: "unsupported",
|
|
146
|
+
detect: { apps: ["ChatGPT.app"] },
|
|
147
|
+
nativeMemory: "capture",
|
|
148
|
+
reason: "cannot launch a local MCP server",
|
|
149
|
+
fallback: "`ocean pull` captures its conversations and memory from the browsers' caches",
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: "warp",
|
|
153
|
+
label: "Warp",
|
|
154
|
+
// Unsupported until its MCP support is verified (T-307), not left as an open question.
|
|
155
|
+
tier: "unsupported",
|
|
156
|
+
detect: { apps: ["Warp.app"], paths: [".warp"] },
|
|
157
|
+
nativeMemory: "unknown",
|
|
158
|
+
reason: "MCP support not verified",
|
|
159
|
+
fallback: "use Ocean from a supported client or the `ocean` CLI",
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: "xcode",
|
|
163
|
+
label: "Xcode",
|
|
164
|
+
tier: "unsupported",
|
|
165
|
+
detect: { apps: ["Xcode.app"] },
|
|
166
|
+
nativeMemory: "unknown",
|
|
167
|
+
reason: "AI/MCP support not verified",
|
|
168
|
+
fallback: "use Ocean from a supported client or the `ocean` CLI",
|
|
169
|
+
},
|
|
170
|
+
];
|
|
171
|
+
// Which client, and which of its modes, a call-ledger name belongs to; undefined when unknown.
|
|
172
|
+
export function mcpClientOf(name) {
|
|
173
|
+
for (const adapter of CLIENT_ADAPTERS) {
|
|
174
|
+
const known = adapter.mcpClients?.find((item) => item.name === name);
|
|
175
|
+
if (known)
|
|
176
|
+
return known.mode
|
|
177
|
+
? { id: adapter.id, mode: known.mode }
|
|
178
|
+
: { id: adapter.id };
|
|
179
|
+
}
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
export function clientAdapter(id) {
|
|
183
|
+
const adapter = CLIENT_ADAPTERS.find((item) => item.id === id);
|
|
184
|
+
if (!adapter)
|
|
185
|
+
throw new Error(`Unknown client: ${id}`);
|
|
186
|
+
return adapter;
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { createServer, } from "node:http";
|
|
3
|
+
import { actionFingerprint } from "@ocean/core/domain/mcp/mcp-contract.js";
|
|
4
|
+
import { runAgent } from "@ocean/loop/runs/run-agent.js";
|
|
5
|
+
function sameSecret(expected, actual) {
|
|
6
|
+
if (actual === undefined)
|
|
7
|
+
return false;
|
|
8
|
+
const expectedBytes = Buffer.from(expected);
|
|
9
|
+
const actualBytes = Buffer.from(actual);
|
|
10
|
+
return (expectedBytes.length === actualBytes.length &&
|
|
11
|
+
timingSafeEqual(expectedBytes, actualBytes));
|
|
12
|
+
}
|
|
13
|
+
export const telegramAdapter = {
|
|
14
|
+
id: "telegram",
|
|
15
|
+
normalize(input) {
|
|
16
|
+
if (!input || typeof input !== "object")
|
|
17
|
+
throw new Error("Invalid Telegram payload");
|
|
18
|
+
const message = input.message;
|
|
19
|
+
if (!message?.message_id ||
|
|
20
|
+
!message.chat?.id ||
|
|
21
|
+
typeof message.text !== "string" ||
|
|
22
|
+
!message.text.trim())
|
|
23
|
+
throw new Error("Invalid Telegram message");
|
|
24
|
+
const [profile = "default", ...prompt] = message.text
|
|
25
|
+
.trim()
|
|
26
|
+
.replace(/^\/run\s+/i, "")
|
|
27
|
+
.split(/\s+/);
|
|
28
|
+
return {
|
|
29
|
+
platform: "telegram",
|
|
30
|
+
externalId: `${message.chat.id}:${message.message_id}`,
|
|
31
|
+
profile,
|
|
32
|
+
prompt: prompt.join(" ") || profile,
|
|
33
|
+
approved: false,
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
function createGatewayHandler(expectedToken, cwd, runtime) {
|
|
38
|
+
const requestTimes = [];
|
|
39
|
+
return async (input, token) => {
|
|
40
|
+
const credentials = expectedToken.split(",").map((entry) => {
|
|
41
|
+
const separator = entry.indexOf("=");
|
|
42
|
+
if (separator < 1)
|
|
43
|
+
return { id: "default", token: entry, profiles: undefined };
|
|
44
|
+
const label = entry.slice(0, separator);
|
|
45
|
+
const scopeIndex = label.indexOf("@");
|
|
46
|
+
return {
|
|
47
|
+
id: scopeIndex < 1 ? label : label.slice(0, scopeIndex),
|
|
48
|
+
token: entry.slice(separator + 1),
|
|
49
|
+
profiles: scopeIndex < 1
|
|
50
|
+
? undefined
|
|
51
|
+
: new Set(label
|
|
52
|
+
.slice(scopeIndex + 1)
|
|
53
|
+
.split("|")
|
|
54
|
+
.filter(Boolean)),
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
const identity = credentials.find((credential) => sameSecret(credential.token, token) &&
|
|
58
|
+
(!credential.profiles ||
|
|
59
|
+
(typeof input === "object" &&
|
|
60
|
+
input !== null &&
|
|
61
|
+
credential.profiles.has(input.profile))))?.id;
|
|
62
|
+
if (!expectedToken || !identity)
|
|
63
|
+
return { status: 401, body: "Unauthorized" };
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
while (requestTimes[0] !== undefined && requestTimes[0] < now - 60_000)
|
|
66
|
+
requestTimes.shift();
|
|
67
|
+
if (requestTimes.length >= 10)
|
|
68
|
+
return { status: 429, body: "Rate limit exceeded" };
|
|
69
|
+
requestTimes.push(now);
|
|
70
|
+
if (!input ||
|
|
71
|
+
typeof input !== "object" ||
|
|
72
|
+
typeof input.profile !== "string" ||
|
|
73
|
+
typeof input.prompt !== "string" ||
|
|
74
|
+
input.approval?.approved !== true ||
|
|
75
|
+
typeof input.approval?.fingerprint !== "string" ||
|
|
76
|
+
input.approval.fingerprint !==
|
|
77
|
+
actionFingerprint("gateway.run", {
|
|
78
|
+
profile: input.profile,
|
|
79
|
+
prompt: input.prompt,
|
|
80
|
+
}))
|
|
81
|
+
return { status: 400, body: "Invalid or unapproved request" };
|
|
82
|
+
if (input.prompt.length > 8_000)
|
|
83
|
+
return { status: 413, body: "Prompt too large" };
|
|
84
|
+
try {
|
|
85
|
+
await runtime.loadProfile(input.profile);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return { status: 400, body: "Invalid profile" };
|
|
89
|
+
}
|
|
90
|
+
const session = await runAgent({
|
|
91
|
+
profileName: input.profile,
|
|
92
|
+
prompt: input.prompt,
|
|
93
|
+
cwd,
|
|
94
|
+
actor: `gateway:${identity}`,
|
|
95
|
+
}, runtime);
|
|
96
|
+
return {
|
|
97
|
+
status: 200,
|
|
98
|
+
body: JSON.stringify({
|
|
99
|
+
sessionId: session.sessionId,
|
|
100
|
+
status: session.status,
|
|
101
|
+
}),
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
export async function handleGatewayRequest(input, token, expectedToken, cwd, runtime) {
|
|
106
|
+
return createGatewayHandler(expectedToken, cwd, runtime)(input, token);
|
|
107
|
+
}
|
|
108
|
+
export function createWebhookGateway(cwd, expectedToken, runtime) {
|
|
109
|
+
const handleRequest = createGatewayHandler(expectedToken, cwd, runtime);
|
|
110
|
+
return createServer(async (request, response) => {
|
|
111
|
+
if (request.method !== "POST") {
|
|
112
|
+
response.writeHead(405);
|
|
113
|
+
response.end("Method not allowed");
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
let body = "";
|
|
117
|
+
request.on("data", (chunk) => {
|
|
118
|
+
body += chunk.toString();
|
|
119
|
+
if (body.length > 16_000)
|
|
120
|
+
request.destroy();
|
|
121
|
+
});
|
|
122
|
+
request.on("end", async () => {
|
|
123
|
+
try {
|
|
124
|
+
const result = await handleRequest(JSON.parse(body), request.headers.authorization?.replace(/^Bearer\s+/i, ""));
|
|
125
|
+
response.writeHead(result.status, {
|
|
126
|
+
"content-type": "application/json",
|
|
127
|
+
});
|
|
128
|
+
response.end(result.body);
|
|
129
|
+
}
|
|
130
|
+
catch (_error) {
|
|
131
|
+
response.writeHead(500);
|
|
132
|
+
response.end("Gateway request failed");
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=webhook-gateway.js.map
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { execFile, spawn } from "node:child_process";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { registryFile } from "@ocean/core/shared/fs-utils.js";
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
const catalog = [
|
|
8
|
+
{
|
|
9
|
+
provider: {
|
|
10
|
+
id: "claude",
|
|
11
|
+
command: "claude",
|
|
12
|
+
interactive: true,
|
|
13
|
+
headless: true,
|
|
14
|
+
},
|
|
15
|
+
platforms: ["darwin", "linux", "win32"],
|
|
16
|
+
installer: {
|
|
17
|
+
command: "npm",
|
|
18
|
+
args: ["install", "-g", "@anthropic-ai/claude-code"],
|
|
19
|
+
},
|
|
20
|
+
verify: { command: "claude", args: ["--version"] },
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
provider: {
|
|
24
|
+
id: "codex",
|
|
25
|
+
command: "codex",
|
|
26
|
+
interactive: true,
|
|
27
|
+
headless: true,
|
|
28
|
+
},
|
|
29
|
+
platforms: ["darwin", "linux", "win32"],
|
|
30
|
+
installer: { command: "npm", args: ["install", "-g", "@openai/codex"] },
|
|
31
|
+
verify: { command: "codex", args: ["--version"] },
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
provider: {
|
|
35
|
+
id: "gemini",
|
|
36
|
+
command: "gemini",
|
|
37
|
+
interactive: true,
|
|
38
|
+
headless: true,
|
|
39
|
+
},
|
|
40
|
+
platforms: ["darwin", "linux", "win32"],
|
|
41
|
+
installer: {
|
|
42
|
+
command: "npm",
|
|
43
|
+
args: ["install", "-g", "@google/gemini-cli"],
|
|
44
|
+
},
|
|
45
|
+
verify: { command: "gemini", args: ["--version"] },
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
provider: {
|
|
49
|
+
id: "kilo",
|
|
50
|
+
command: "kilo",
|
|
51
|
+
interactive: true,
|
|
52
|
+
headless: true,
|
|
53
|
+
},
|
|
54
|
+
platforms: ["darwin", "linux", "win32"],
|
|
55
|
+
installer: { command: "npm", args: ["install", "-g", "@kilocode/cli"] },
|
|
56
|
+
verify: { command: "kilo", args: ["--version"] },
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
provider: {
|
|
60
|
+
id: "kimi",
|
|
61
|
+
command: "kimi",
|
|
62
|
+
interactive: true,
|
|
63
|
+
headless: true,
|
|
64
|
+
},
|
|
65
|
+
platforms: ["darwin", "linux", "win32"],
|
|
66
|
+
installer: {
|
|
67
|
+
command: "uv",
|
|
68
|
+
args: ["tool", "install", "--force", "--python", "3.13", "kimi-cli"],
|
|
69
|
+
},
|
|
70
|
+
verify: { command: "kimi", args: ["--version"] },
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
provider: {
|
|
74
|
+
id: "hermes",
|
|
75
|
+
command: "hermes",
|
|
76
|
+
interactive: true,
|
|
77
|
+
headless: true,
|
|
78
|
+
},
|
|
79
|
+
platforms: ["darwin", "linux", "win32"],
|
|
80
|
+
installer: {
|
|
81
|
+
command: "uv",
|
|
82
|
+
args: ["tool", "install", "--force", "hermes-agent"],
|
|
83
|
+
},
|
|
84
|
+
verify: { command: "hermes", args: ["--version"] },
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
const installationReceiptPath = () => registryFile("installations.json");
|
|
88
|
+
export function listInstallSpecs() {
|
|
89
|
+
return catalog.map((spec) => ({
|
|
90
|
+
...spec,
|
|
91
|
+
provider: { ...spec.provider },
|
|
92
|
+
installer: { ...spec.installer },
|
|
93
|
+
verify: { ...spec.verify },
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
export function findInstallSpec(id) {
|
|
97
|
+
const spec = catalog.find((candidate) => candidate.provider.id === id || candidate.provider.command === id);
|
|
98
|
+
if (!spec)
|
|
99
|
+
throw new Error(`No approved installer is registered for provider: ${id}`);
|
|
100
|
+
return spec;
|
|
101
|
+
}
|
|
102
|
+
export function installPlan(id) {
|
|
103
|
+
const spec = findInstallSpec(id);
|
|
104
|
+
return {
|
|
105
|
+
provider: spec.provider.id,
|
|
106
|
+
installer: [spec.installer.command, ...spec.installer.args].join(" "),
|
|
107
|
+
verify: [spec.verify.command, ...spec.verify.args].join(" "),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
export function assertSupportedPlatform(spec, platform) {
|
|
111
|
+
if (!spec.platforms.includes(platform)) {
|
|
112
|
+
throw new Error(`Provider ${spec.provider.id} is not supported on platform: ${platform}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export async function installProvider(id, approved, wrapperManager, registry) {
|
|
116
|
+
const spec = findInstallSpec(id);
|
|
117
|
+
if (!approved)
|
|
118
|
+
throw new Error(`Installation approval required. Re-run with: ocean install ${spec.provider.id} --yes`);
|
|
119
|
+
assertSupportedPlatform(spec, process.platform);
|
|
120
|
+
await runInstaller(spec.installer.command, spec.installer.args);
|
|
121
|
+
const executable = registry.resolveOriginalExecutable(spec.verify.command);
|
|
122
|
+
await execFileAsync(spec.verify.command, spec.verify.args, {
|
|
123
|
+
env: process.env,
|
|
124
|
+
maxBuffer: 16_000,
|
|
125
|
+
timeout: 60_000,
|
|
126
|
+
});
|
|
127
|
+
const provider = await wrapperManager.registerProvider(spec.provider.id, spec.provider.command);
|
|
128
|
+
await recordInstallation({
|
|
129
|
+
provider: provider.id,
|
|
130
|
+
command: provider.command,
|
|
131
|
+
executable,
|
|
132
|
+
installer: JSON.stringify(installPlan(provider.id)),
|
|
133
|
+
installedAt: new Date().toISOString(),
|
|
134
|
+
});
|
|
135
|
+
return { provider, executable };
|
|
136
|
+
}
|
|
137
|
+
export async function updateProvider(id, approved, wrapperManager, registry) {
|
|
138
|
+
return installProvider(id, approved, wrapperManager, registry);
|
|
139
|
+
}
|
|
140
|
+
export async function removeInstalledProvider(id, approved, wrapperManager) {
|
|
141
|
+
if (!approved)
|
|
142
|
+
throw new Error(`Removal approval required. Re-run with: ocean remove ${id} --yes`);
|
|
143
|
+
const spec = findInstallSpec(id);
|
|
144
|
+
const provider = await wrapperManager.removeProvider(spec.provider.id);
|
|
145
|
+
await removeInstallationReceipt(provider.id);
|
|
146
|
+
return provider;
|
|
147
|
+
}
|
|
148
|
+
function runInstaller(command, args) {
|
|
149
|
+
return new Promise((resolve, reject) => {
|
|
150
|
+
const child = spawn(command, args, { env: process.env, stdio: "inherit" });
|
|
151
|
+
child.once("error", reject);
|
|
152
|
+
child.once("close", (code, signal) => {
|
|
153
|
+
if (code === 0)
|
|
154
|
+
resolve();
|
|
155
|
+
else
|
|
156
|
+
reject(new Error(`Installer failed: ${command} (${signal ?? `exit ${code ?? "unknown"}`})`));
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
async function recordInstallation(record) {
|
|
161
|
+
let records = [];
|
|
162
|
+
try {
|
|
163
|
+
records = JSON.parse(await readFile(installationReceiptPath(), "utf8"));
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
// First installation or an absent optional receipt.
|
|
167
|
+
}
|
|
168
|
+
const next = records.filter((entry) => entry.provider !== record.provider);
|
|
169
|
+
next.push(record);
|
|
170
|
+
await mkdir(path.dirname(installationReceiptPath()), { recursive: true });
|
|
171
|
+
await writeFile(installationReceiptPath(), `${JSON.stringify(next, null, 2)}\n`);
|
|
172
|
+
}
|
|
173
|
+
async function removeInstallationReceipt(provider) {
|
|
174
|
+
let records;
|
|
175
|
+
try {
|
|
176
|
+
records = JSON.parse(await readFile(installationReceiptPath(), "utf8"));
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
await mkdir(path.dirname(installationReceiptPath()), { recursive: true });
|
|
182
|
+
await writeFile(installationReceiptPath(), `${JSON.stringify(records.filter((entry) => entry.provider !== provider), null, 2)}\n`);
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=provider-installer.js.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { atomicWrite } from "@ocean/core/shared/fs-utils.js";
|
|
6
|
+
import { CHARTER_DIR, oceanPath } from "@ocean/core/shared/paths.js";
|
|
7
|
+
// Clients that cannot @-import a file (Codex, Gemini) carry a verbatim copy of the charter's
|
|
8
|
+
// core.md between these markers in their own rules file. Claude @-imports core.md and needs
|
|
9
|
+
// no copy. Text outside the markers is the owner's and is never touched.
|
|
10
|
+
const BEGIN = "<!-- ai-sync:generated -->";
|
|
11
|
+
const END = "<!-- ai-sync:end -->";
|
|
12
|
+
// Relative to HOME.
|
|
13
|
+
export const CHARTER_COPIES = [
|
|
14
|
+
{ client: "codex", file: ".codex/AGENTS.md" },
|
|
15
|
+
{ client: "gemini", file: ".gemini/GEMINI.md" },
|
|
16
|
+
];
|
|
17
|
+
function hash(text) {
|
|
18
|
+
return createHash("sha256").update(text).digest("hex").slice(0, 16);
|
|
19
|
+
}
|
|
20
|
+
function block(client, core) {
|
|
21
|
+
return `${BEGIN}
|
|
22
|
+
<!-- source: ~/ocean/charter/ | hash: ${hash(core)} | client: ${client} -->
|
|
23
|
+
<!-- DO NOT EDIT INSIDE THE MARKERS. Edit ~/ocean/charter/core.md and re-copy it here. -->
|
|
24
|
+
<!-- Anything you write OUTSIDE the markers is yours and is preserved. -->
|
|
25
|
+
|
|
26
|
+
${core}`;
|
|
27
|
+
}
|
|
28
|
+
// The copy is only rewritten when its body is still exactly what its header hash says was
|
|
29
|
+
// written: an edit inside the markers is reported, never overwritten.
|
|
30
|
+
function handEdited(current) {
|
|
31
|
+
const recorded = current.match(/\|\s*hash:\s*([0-9a-f]{16})\s*\|/)?.[1];
|
|
32
|
+
const body = current.split("\n").slice(5).join("\n");
|
|
33
|
+
return !recorded || hash(body) !== recorded;
|
|
34
|
+
}
|
|
35
|
+
export async function syncCharterCopies(home = os.homedir()) {
|
|
36
|
+
let core;
|
|
37
|
+
try {
|
|
38
|
+
core = `${(await readFile(oceanPath(CHARTER_DIR, "core.md"), "utf8")).trimEnd()}\n`;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return CHARTER_COPIES.map((copy) => ({ ...copy, status: "no-core" }));
|
|
42
|
+
}
|
|
43
|
+
const results = [];
|
|
44
|
+
for (const copy of CHARTER_COPIES) {
|
|
45
|
+
const file = path.join(home, copy.file);
|
|
46
|
+
const report = (status) => results.push({ ...copy, status });
|
|
47
|
+
let text;
|
|
48
|
+
try {
|
|
49
|
+
text = await readFile(file, "utf8");
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
report("missing");
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const start = text.indexOf(BEGIN);
|
|
56
|
+
const end = text.indexOf(END);
|
|
57
|
+
if (start === -1 || end < start) {
|
|
58
|
+
report("no-markers");
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const current = text.slice(start, end);
|
|
62
|
+
const next = block(copy.client, core);
|
|
63
|
+
if (current === next) {
|
|
64
|
+
report("current");
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (handEdited(current)) {
|
|
68
|
+
report("hand-edited");
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
await atomicWrite(file, `${text.slice(0, start)}${next}${text.slice(end)}`);
|
|
72
|
+
report("updated");
|
|
73
|
+
}
|
|
74
|
+
return results;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=charter-copies.js.map
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { chmod, copyFile, mkdir, readFile, stat, writeFile, } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
function defaultClaudeCodeSettingsPath() {
|
|
5
|
+
if (process.platform === "darwin")
|
|
6
|
+
return path.join(os.homedir(), "Library", "Application Support", "Code", "User", "settings.json");
|
|
7
|
+
if (process.platform === "win32")
|
|
8
|
+
return path.join(process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming"), "Code", "User", "settings.json");
|
|
9
|
+
return path.join(os.homedir(), ".config", "Code", "User", "settings.json");
|
|
10
|
+
}
|
|
11
|
+
const WRAPPER_KEY = "claudeCode.claudeProcessWrapper";
|
|
12
|
+
// VS Code settings are JSONC: comments and trailing commas are legal.
|
|
13
|
+
function stripJsonc(source) {
|
|
14
|
+
let out = "";
|
|
15
|
+
let i = 0;
|
|
16
|
+
while (i < source.length) {
|
|
17
|
+
const ch = source[i];
|
|
18
|
+
if (ch === '"') {
|
|
19
|
+
let j = i + 1;
|
|
20
|
+
while (j < source.length && source[j] !== '"')
|
|
21
|
+
j += source[j] === "\\" ? 2 : 1;
|
|
22
|
+
out += source.slice(i, j + 1);
|
|
23
|
+
i = j + 1;
|
|
24
|
+
}
|
|
25
|
+
else if (ch === "/" && source[i + 1] === "/") {
|
|
26
|
+
while (i < source.length && source[i] !== "\n")
|
|
27
|
+
i++;
|
|
28
|
+
}
|
|
29
|
+
else if (ch === "/" && source[i + 1] === "*") {
|
|
30
|
+
const end = source.indexOf("*/", i + 2);
|
|
31
|
+
i = end === -1 ? source.length : end + 2;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
if (ch === "}" || ch === "]")
|
|
35
|
+
out = out.replace(/,\s*$/, "");
|
|
36
|
+
out += ch;
|
|
37
|
+
i++;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
function parseSettings(source) {
|
|
43
|
+
try {
|
|
44
|
+
return {
|
|
45
|
+
settings: JSON.parse(source),
|
|
46
|
+
strict: true,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return {
|
|
51
|
+
settings: JSON.parse(stripJsonc(source)),
|
|
52
|
+
strict: false,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Edit the key in place so a JSONC file keeps its comments and formatting.
|
|
57
|
+
function setWrapperKey(source, wrapper) {
|
|
58
|
+
const entry = `"${WRAPPER_KEY}": ${JSON.stringify(wrapper)}`;
|
|
59
|
+
const existing = new RegExp(`"${WRAPPER_KEY.replace(".", "\\.")}"\\s*:\\s*"(?:[^"\\\\]|\\\\.)*"`);
|
|
60
|
+
if (existing.test(source))
|
|
61
|
+
return source.replace(existing, () => entry);
|
|
62
|
+
const open = source.indexOf("{");
|
|
63
|
+
const rest = stripJsonc(source.slice(open + 1)).trim();
|
|
64
|
+
return `${source.slice(0, open + 1)}\n ${entry}${rest === "}" ? "" : ","}${source.slice(open + 1)}`;
|
|
65
|
+
}
|
|
66
|
+
export async function configureClaudeCodeWrapper(wrapperManager, settingsPath = defaultClaudeCodeSettingsPath(), apply = false) {
|
|
67
|
+
const wrapper = wrapperManager.desktopWrapperPath("claude");
|
|
68
|
+
let source = "{}\n";
|
|
69
|
+
try {
|
|
70
|
+
source = await readFile(settingsPath, "utf8");
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (error.code !== "ENOENT")
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
let parsed;
|
|
77
|
+
try {
|
|
78
|
+
parsed = parseSettings(source);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
throw new Error(`Claude Code settings are not valid JSON or JSONC: ${settingsPath}`);
|
|
82
|
+
}
|
|
83
|
+
const { settings, strict } = parsed;
|
|
84
|
+
const updated = strict
|
|
85
|
+
? `${JSON.stringify({ ...settings, [WRAPPER_KEY]: wrapper }, null, 2)}\n`
|
|
86
|
+
: setWrapperKey(source, wrapper);
|
|
87
|
+
const changed = settings[WRAPPER_KEY] !== wrapper;
|
|
88
|
+
if (!apply || !changed)
|
|
89
|
+
return { settingsPath, wrapper, apply, changed, backup: null };
|
|
90
|
+
await mkdir(path.dirname(settingsPath), { recursive: true });
|
|
91
|
+
let backup = null;
|
|
92
|
+
try {
|
|
93
|
+
const metadata = await stat(settingsPath);
|
|
94
|
+
backup = `${settingsPath}.ocean-backup-${new Date().toISOString().replace(/[:.]/g, "-")}`;
|
|
95
|
+
await copyFile(settingsPath, backup);
|
|
96
|
+
await writeFile(settingsPath, updated, "utf8");
|
|
97
|
+
await chmod(settingsPath, metadata.mode & 0o777);
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
if (backup)
|
|
101
|
+
throw error;
|
|
102
|
+
await writeFile(settingsPath, updated, "utf8");
|
|
103
|
+
}
|
|
104
|
+
return { settingsPath, wrapper, apply, changed, backup };
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=claude-vscode.js.map
|