@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.
- 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/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/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-hub.js +220 -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,119 @@
|
|
|
1
|
+
import { openSessionStore } from "@ocean/loop/infrastructure/persistence/session-store.js";
|
|
2
|
+
import { BrowserService, } from "@ocean/services/browser/browser-service.js";
|
|
3
|
+
import { BrowserSessionManager, browserDownloadsPath, } from "@ocean/services/browser/browser-session.js";
|
|
4
|
+
import { BrowserTaskRunner } from "@ocean/services/browser/browser-task-runner.js";
|
|
5
|
+
import { PlaywrightBrowserProvider } from "../../infrastructure/providers/playwright-browser-provider.js";
|
|
6
|
+
export async function runBrowserCommand(action, args) {
|
|
7
|
+
const store = await openSessionStore();
|
|
8
|
+
const manager = new BrowserSessionManager(store, new BrowserService(new PlaywrightBrowserProvider()));
|
|
9
|
+
const runner = new BrowserTaskRunner(manager);
|
|
10
|
+
try {
|
|
11
|
+
if (action === "detect")
|
|
12
|
+
return print(await manager.detect());
|
|
13
|
+
if (action === "open") {
|
|
14
|
+
const profile = flag(args, "--profile") ?? "default";
|
|
15
|
+
const portText = flag(args, "--port");
|
|
16
|
+
const port = portText === undefined ? undefined : Number(portText);
|
|
17
|
+
if (port !== undefined &&
|
|
18
|
+
(!Number.isInteger(port) || port <= 0 || port > 65_535))
|
|
19
|
+
throw new Error("Browser port must be an integer between 1 and 65535.");
|
|
20
|
+
return print(await manager.open(profile, port));
|
|
21
|
+
}
|
|
22
|
+
if (action === "show")
|
|
23
|
+
return print(manager.show(required(args, 0, "session-id")));
|
|
24
|
+
if (action === "events")
|
|
25
|
+
return print(manager.events(required(args, 0, "session-id")));
|
|
26
|
+
if (action === "close")
|
|
27
|
+
return print(await manager.close(required(args, 0, "session-id")));
|
|
28
|
+
if (action === "approve")
|
|
29
|
+
return print(await manager.approve(required(args, 0, "session-id")));
|
|
30
|
+
const sessionId = required(args, 0, "session-id");
|
|
31
|
+
if (action === "navigate")
|
|
32
|
+
return print(await manager.navigate(sessionId, required(args, 1, "url"), args.includes("--approve"), numberFlag(args, "--timeout")));
|
|
33
|
+
if (action === "read")
|
|
34
|
+
return print(await manager.read(sessionId));
|
|
35
|
+
if (action === "observe")
|
|
36
|
+
return print(await manager.observe(sessionId));
|
|
37
|
+
if (action === "extract")
|
|
38
|
+
return print(await manager.extract(sessionId, required(args, 1, "selector"), flag(args, "--attribute") ?? null));
|
|
39
|
+
if (action === "click")
|
|
40
|
+
return print(await manager.click(sessionId, required(args, 1, "selector"), parseExpectation(args), numberFlag(args, "--timeout")));
|
|
41
|
+
if (action === "type")
|
|
42
|
+
return print(await manager.type(sessionId, required(args, 1, "selector"), required(args, 2, "text"), !args.includes("--no-clear")));
|
|
43
|
+
if (action === "select")
|
|
44
|
+
return print(await manager.select(sessionId, required(args, 1, "selector"), required(args, 2, "value")));
|
|
45
|
+
if (action === "replace-text")
|
|
46
|
+
return print(await manager.replaceText(sessionId, required(args, 1, "selector"), required(args, 2, "old-text"), required(args, 3, "new-text"), numberFlag(args, "--occurrence")));
|
|
47
|
+
if (action === "scroll")
|
|
48
|
+
return print(await manager.scroll(sessionId, numberFlag(args, "--delta") ?? Number(args[1] ?? "600")));
|
|
49
|
+
if (action === "wait")
|
|
50
|
+
return print(await manager.wait(sessionId, {
|
|
51
|
+
selector: flag(args, "--selector"),
|
|
52
|
+
urlContains: flag(args, "--url-contains"),
|
|
53
|
+
timeoutMs: numberFlag(args, "--timeout"),
|
|
54
|
+
}));
|
|
55
|
+
if (action === "upload") {
|
|
56
|
+
const selector = required(args, 1, "selector");
|
|
57
|
+
const paths = args.slice(2).filter((value) => !value.startsWith("--"));
|
|
58
|
+
if (!paths.length)
|
|
59
|
+
throw new Error("Usage: ocean browser upload <session-id> <selector> <path>... --approve");
|
|
60
|
+
return print(await manager.upload(sessionId, selector, paths, args.includes("--approve")));
|
|
61
|
+
}
|
|
62
|
+
if (action === "download") {
|
|
63
|
+
const selector = required(args, 1, "selector");
|
|
64
|
+
const destination = flag(args, "--destination") ?? browserDownloadsPath(sessionId);
|
|
65
|
+
return print(await manager.download(sessionId, selector, destination, args.includes("--approve"), numberFlag(args, "--timeout")));
|
|
66
|
+
}
|
|
67
|
+
if (action === "submit")
|
|
68
|
+
return print(await manager.submit(sessionId, required(args, 1, "selector"), args.includes("--approve"), numberFlag(args, "--timeout")));
|
|
69
|
+
if (action === "run") {
|
|
70
|
+
const taskPath = required(args, 1, "task-file");
|
|
71
|
+
const task = await BrowserTaskRunner.fromFile(taskPath);
|
|
72
|
+
return print(await runner.run(sessionId, task, args.includes("--approve")));
|
|
73
|
+
}
|
|
74
|
+
usage();
|
|
75
|
+
process.exitCode = 1;
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
store.close();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function parseExpectation(args) {
|
|
82
|
+
const value = flag(args, "--expect");
|
|
83
|
+
if (value === "navigates" || value === "stays")
|
|
84
|
+
return { kind: value };
|
|
85
|
+
if (value?.startsWith("count:")) {
|
|
86
|
+
const separator = value.lastIndexOf(":");
|
|
87
|
+
const selector = value.slice("count:".length, separator);
|
|
88
|
+
const expected = Number(value.slice(separator + 1));
|
|
89
|
+
if (selector && Number.isInteger(expected) && expected >= 0)
|
|
90
|
+
return { kind: "selector-count", selector, expected };
|
|
91
|
+
}
|
|
92
|
+
throw new Error("Click requires --expect navigates|stays|count:<selector>:<expected>");
|
|
93
|
+
}
|
|
94
|
+
function flag(args, name) {
|
|
95
|
+
const index = args.indexOf(name);
|
|
96
|
+
return index >= 0 ? args[index + 1] : undefined;
|
|
97
|
+
}
|
|
98
|
+
function numberFlag(args, name) {
|
|
99
|
+
const value = flag(args, name);
|
|
100
|
+
if (value === undefined)
|
|
101
|
+
return undefined;
|
|
102
|
+
const parsed = Number(value);
|
|
103
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
104
|
+
throw new Error(`${name} must be a positive number.`);
|
|
105
|
+
return parsed;
|
|
106
|
+
}
|
|
107
|
+
function required(args, index, name) {
|
|
108
|
+
const value = args[index];
|
|
109
|
+
if (!value || value.startsWith("--"))
|
|
110
|
+
throw new Error(`Usage error: missing ${name}.`);
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
function print(value) {
|
|
114
|
+
console.log(JSON.stringify(value, null, 2));
|
|
115
|
+
}
|
|
116
|
+
function usage() {
|
|
117
|
+
console.error("Usage: ocean browser detect|open|show|events|close|approve|navigate|read|observe|extract|click|type|replace-text|select|scroll|wait|upload|download|submit|run");
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=browser-command.js.map
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { appendFile, mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { syncCaptureInbox } from "@ocean/brain/capture/inbox-sync.js";
|
|
5
|
+
import { syncMemoryIndexes } from "@ocean/brain/memory/index-sync.js";
|
|
6
|
+
import { validateSessionEntryContract } from "@ocean/core/domain/sessions/entry-contract.js";
|
|
7
|
+
import { localDate } from "@ocean/core/shared/local-date.js";
|
|
8
|
+
import { KNOWLEDGE_DIR, oceanPath, oceanRoot, PERSONAL_DIR, PROJECTS_DIR, WORKSPACE_PROJECT_ID, } from "@ocean/core/shared/paths.js";
|
|
9
|
+
import { openSessionStore } from "@ocean/loop/infrastructure/persistence/session-store.js";
|
|
10
|
+
import { sessionProjectId } from "@ocean/loop/sessions/session-projects.js";
|
|
11
|
+
import { redactRuntimeText } from "../../infrastructure/observability/runtime-logger.js";
|
|
12
|
+
export async function runCaptureCommand(action, args) {
|
|
13
|
+
const store = await openSessionStore();
|
|
14
|
+
try {
|
|
15
|
+
if (action === "add") {
|
|
16
|
+
const content = redactRuntimeText(args.join(" ").trim());
|
|
17
|
+
if (!content)
|
|
18
|
+
throw new Error("Usage: ocean capture add <idea>");
|
|
19
|
+
const sessionId = randomUUID();
|
|
20
|
+
store.create({
|
|
21
|
+
sessionId,
|
|
22
|
+
provider: "manual",
|
|
23
|
+
providerSessionId: null,
|
|
24
|
+
parentSessionId: null,
|
|
25
|
+
profile: "manual-capture",
|
|
26
|
+
profileIdentity: "",
|
|
27
|
+
workingDirectory: oceanRoot(),
|
|
28
|
+
projectId: await sessionProjectId(oceanRoot()),
|
|
29
|
+
resumeData: null,
|
|
30
|
+
});
|
|
31
|
+
store.appendEvent(sessionId, "session_entry_contract", JSON.stringify(validateSessionEntryContract({
|
|
32
|
+
entryPoint: "ocean-run",
|
|
33
|
+
controlLevel: "full-head",
|
|
34
|
+
inputCapture: "semantic",
|
|
35
|
+
contextTransport: "manual-capture",
|
|
36
|
+
policyEnforcement: "explicit-capture-operation",
|
|
37
|
+
promotion: "explicit-review",
|
|
38
|
+
resume: "unsupported",
|
|
39
|
+
})));
|
|
40
|
+
store.updateStatus(sessionId, "running");
|
|
41
|
+
store.appendEvent(sessionId, "user_input", content);
|
|
42
|
+
store.updateStatus(sessionId, "completed");
|
|
43
|
+
store.scanCaptureItems(sessionId);
|
|
44
|
+
await syncCaptureInbox(store);
|
|
45
|
+
const [item] = store
|
|
46
|
+
.listCaptureItems("new")
|
|
47
|
+
.filter((candidate) => candidate.sessionId === sessionId);
|
|
48
|
+
if (!item)
|
|
49
|
+
throw new Error("Capture item was not created");
|
|
50
|
+
console.log(JSON.stringify({ captureId: item.captureId, sessionId, status: "new" }));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (action === "scan") {
|
|
54
|
+
const sessionId = args[0];
|
|
55
|
+
const scanned = store.scanCaptureItems(sessionId);
|
|
56
|
+
await syncCaptureInbox(store);
|
|
57
|
+
console.log(JSON.stringify({ scanned }));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (action === "list") {
|
|
61
|
+
console.log(JSON.stringify(store.listCaptureItems(args[0] ?? "new"), null, 2));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (action === "promote" || action === "discard") {
|
|
65
|
+
const captureId = Number(args[0]);
|
|
66
|
+
if (!Number.isInteger(captureId))
|
|
67
|
+
throw new Error(`Usage: ocean capture ${action} <capture-id> [target]`);
|
|
68
|
+
if (action === "promote") {
|
|
69
|
+
const item = store.getCaptureItem(captureId);
|
|
70
|
+
if (!item)
|
|
71
|
+
throw new Error(`Capture item not found: ${captureId}`);
|
|
72
|
+
await promote(item, args[1]);
|
|
73
|
+
}
|
|
74
|
+
store.updateCaptureStatus(captureId, action === "promote" ? "promoted" : "discarded", args[1]);
|
|
75
|
+
await syncCaptureInbox(store);
|
|
76
|
+
console.log(JSON.stringify({
|
|
77
|
+
captureId,
|
|
78
|
+
status: action === "promote" ? "promoted" : "discarded",
|
|
79
|
+
target: args[1] ?? null,
|
|
80
|
+
}));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
console.error("Usage: ocean capture add <idea>|scan [session-id]|list [status]|promote <id> <target>|discard <id>");
|
|
84
|
+
process.exitCode = 1;
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
store.close();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function promote(item, target = "knowledge/results") {
|
|
91
|
+
if (target.startsWith("memory/")) {
|
|
92
|
+
const relative = target.slice("memory/".length);
|
|
93
|
+
if (!relative.endsWith(".md") || relative.includes(".."))
|
|
94
|
+
throw new Error("Memory target must be an existing canonical .md file.");
|
|
95
|
+
const file = path.resolve(oceanPath(PERSONAL_DIR), relative);
|
|
96
|
+
const memoryRoot = path.resolve(oceanPath(PERSONAL_DIR)) + path.sep;
|
|
97
|
+
if (!file.startsWith(memoryRoot))
|
|
98
|
+
throw new Error(`Memory target must stay inside ${PERSONAL_DIR}.`);
|
|
99
|
+
const header = `\n\n## Captured note — ${localDate()}\n\n${item.content.trim()}\n\n_Source: session-capture-${item.captureId}; review status: unverified._\n`;
|
|
100
|
+
await appendFile(file, header);
|
|
101
|
+
await syncMemoryIndexes(true);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (target === "memory") {
|
|
105
|
+
throw new Error("Use an explicit canonical file, for example memory/goals.md.");
|
|
106
|
+
}
|
|
107
|
+
if (target === "backlog") {
|
|
108
|
+
await appendFile(oceanPath(PROJECTS_DIR, "backlog.md"), `\n- [TODO] (P2) ${WORKSPACE_PROJECT_ID} — ${item.content.replace(/\s+/g, " ").trim()} {${localDate()}}\n`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const match = target.match(/^knowledge\/(architecture|decisions|discoveries|failures|research|results|solutions|references)$/);
|
|
112
|
+
if (!match)
|
|
113
|
+
throw new Error("Target must be knowledge/<kind>, memory/<file>.md, or backlog.");
|
|
114
|
+
const safe = item.content
|
|
115
|
+
.toLowerCase()
|
|
116
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
117
|
+
.replace(/^-|-$/g, "")
|
|
118
|
+
.slice(0, 60);
|
|
119
|
+
const slug = safe || `capture-${item.captureId}`;
|
|
120
|
+
const file = path.join(oceanPath(KNOWLEDGE_DIR, match[1], `${slug}-${item.captureId}.md`));
|
|
121
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
122
|
+
await writeFile(file, `---\nname: ${slug}-${item.captureId}\ndescription: Candidate promoted from a reviewed session input\nmetadata:\n type: ${match[1] === "results" ? "task-result" : match[1].slice(0, -1)}\n domain: ${match[1]}\n status: current\n verification: unverified\n source: session-capture-${item.captureId}\n---\n\n${item.content.trim()}\n`);
|
|
123
|
+
await syncMemoryIndexes(true);
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=capture-command.js.map
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { buildOceanBootstrap } from "@ocean/context/context/resource-injection.js";
|
|
5
|
+
import { enginePath, oceanPath, SYSTEM_DIR } from "@ocean/core/shared/paths.js";
|
|
6
|
+
import { resolveProject } from "@ocean/core/shared/project-resolution.js";
|
|
7
|
+
import { claudeNativeHookStatus } from "@ocean/loop/hooks/session-start-hook.js";
|
|
8
|
+
import { openSessionStore } from "@ocean/loop/infrastructure/persistence/session-store.js";
|
|
9
|
+
import { findProvider, loadProviderRegistry, resolveOriginalExecutable, } from "../../infrastructure/providers/provider-registry.js";
|
|
10
|
+
import { providerWrapperName, providerWrapperPath, shimDirectory, } from "../../infrastructure/wrappers/wrapper-manager.js";
|
|
11
|
+
export async function runClientTestCommand(providerName, json = false) {
|
|
12
|
+
const cwd = path.resolve(process.cwd());
|
|
13
|
+
const project = await resolveProject(cwd);
|
|
14
|
+
const bootstrap = buildOceanBootstrap(project);
|
|
15
|
+
const providers = providerName
|
|
16
|
+
? [findProvider(providerName)]
|
|
17
|
+
: loadProviderRegistry();
|
|
18
|
+
const store = await openSessionStore();
|
|
19
|
+
const reports = await Promise.all(providers.map((provider) => buildReport(provider, cwd, project, bootstrap, store)));
|
|
20
|
+
store.close();
|
|
21
|
+
if (json) {
|
|
22
|
+
console.log(JSON.stringify(providerName ? reports[0] : reports, null, 2));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
for (const report of reports)
|
|
26
|
+
printReport(report);
|
|
27
|
+
}
|
|
28
|
+
async function buildReport(provider, cwd, project, bootstrap, store) {
|
|
29
|
+
let executable = null;
|
|
30
|
+
try {
|
|
31
|
+
executable = resolveOriginalExecutable(provider.command);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
/* Report missing provider below. */
|
|
35
|
+
}
|
|
36
|
+
const latest = store.list().find((session) => session.provider === provider.id) ?? null;
|
|
37
|
+
const events = latest ? store.listEvents(latest.sessionId) : [];
|
|
38
|
+
const entry = events.find((event) => event.type === "session_entry_contract");
|
|
39
|
+
const manifest = events.find((event) => event.type === "ocean_bootstrap");
|
|
40
|
+
const entryBoundary = provider.id === "claude"
|
|
41
|
+
? await claudeEntryBoundaryStatus()
|
|
42
|
+
: {
|
|
43
|
+
kind: "wrapper-only",
|
|
44
|
+
detail: `No native hook is wired for this provider. A plain ${provider.command} runs natively; ${providerWrapperName(provider.command)} routes it through Ocean (ocean intercept --client).`,
|
|
45
|
+
};
|
|
46
|
+
return {
|
|
47
|
+
provider: provider.id,
|
|
48
|
+
routing: {
|
|
49
|
+
cwd,
|
|
50
|
+
command: provider.command,
|
|
51
|
+
shim: providerWrapperPath(provider.command),
|
|
52
|
+
shimDirectory: shimDirectory(),
|
|
53
|
+
nativeExecutable: executable,
|
|
54
|
+
oceanEngine: enginePath("dist", "main.js"),
|
|
55
|
+
},
|
|
56
|
+
project,
|
|
57
|
+
ocean: {
|
|
58
|
+
root: oceanPath(),
|
|
59
|
+
bootstrapBytes: bootstrap.manifest.bytes,
|
|
60
|
+
bootstrapTransport: bootstrap.manifest.transport,
|
|
61
|
+
sessionStore: oceanPath(SYSTEM_DIR, "sessions", "sessions.sqlite"),
|
|
62
|
+
runtimeLogs: oceanPath(SYSTEM_DIR, "runtime", "logs", "runtime.jsonl"),
|
|
63
|
+
sessionSummaries: oceanPath(SYSTEM_DIR, "sessions", "summaries"),
|
|
64
|
+
},
|
|
65
|
+
entryBoundary,
|
|
66
|
+
// The bootstrap is always delivered as an environment variable when launched through the
|
|
67
|
+
// Ocean shim; whether the provider itself reads it natively (a hook, a config convention)
|
|
68
|
+
// is unverified until that provider's own consumption is tested — never claimed PROVEN here.
|
|
69
|
+
transport: "NOT PROVEN: bootstrap-env (delivered as OCEAN_BOOTSTRAP; native provider consumption unverified)",
|
|
70
|
+
providerOwnedPaths: provider.id === "claude"
|
|
71
|
+
? [
|
|
72
|
+
path.join(os.homedir(), ".claude"),
|
|
73
|
+
path.join(os.homedir(), ".claude", "CLAUDE.md"),
|
|
74
|
+
path.join(cwd, "CLAUDE.md"),
|
|
75
|
+
path.join(cwd, ".claude", "CLAUDE.md"),
|
|
76
|
+
].map((file) => ({ path: file, exists: existsSync(file) }))
|
|
77
|
+
: [
|
|
78
|
+
{
|
|
79
|
+
path: "provider-managed (not controlled by Ocean)",
|
|
80
|
+
exists: null,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
latestSession: latest
|
|
84
|
+
? {
|
|
85
|
+
sessionId: latest.sessionId,
|
|
86
|
+
status: latest.status,
|
|
87
|
+
entryContract: entry ? JSON.parse(entry.data) : null,
|
|
88
|
+
bootstrapManifest: manifest ? JSON.parse(manifest.data) : null,
|
|
89
|
+
}
|
|
90
|
+
: null,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
async function claudeEntryBoundaryStatus() {
|
|
94
|
+
const hook = await claudeNativeHookStatus();
|
|
95
|
+
if (hook.registered) {
|
|
96
|
+
return {
|
|
97
|
+
kind: "native-hook-registered",
|
|
98
|
+
detail: `SessionStart hook is registered in ${hook.settingsPath} and points at ${hook.scriptPath}. Its actual invocation by a live Claude Code session is still unverified here — this only proves registration.`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (hook.scriptInstalled) {
|
|
102
|
+
return {
|
|
103
|
+
kind: "native-hook-available-not-registered",
|
|
104
|
+
detail: `Hook script exists at ${hook.scriptPath} but hooks.SessionStart in ${hook.settingsPath} does not reference it. Until it does, only ${providerWrapperName("claude")} routes Claude through Ocean.`,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
kind: "wrapper-only",
|
|
109
|
+
detail: `No native SessionStart hook script found. Only ${providerWrapperName("claude")} routes Claude through Ocean (ocean intercept --client claude).`,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function printReport(report) {
|
|
113
|
+
console.log(`Ocean client test: ${report.provider}`);
|
|
114
|
+
console.log(`cwd: ${report.routing.cwd}`);
|
|
115
|
+
console.log(`shim: ${report.routing.shim}`);
|
|
116
|
+
console.log(`native executable: ${report.routing.nativeExecutable ?? "NOT FOUND outside Ocean shims"}`);
|
|
117
|
+
console.log(`Ocean engine: ${report.routing.oceanEngine}`);
|
|
118
|
+
console.log(`project: ${report.project.status}${report.project.status === "bound" ? ` (${report.project.projectId}, confidence: ${report.project.confidence})` : ""}`);
|
|
119
|
+
console.log(`entry boundary: ${report.entryBoundary.kind} — ${report.entryBoundary.detail}`);
|
|
120
|
+
console.log(`Ocean bootstrap bytes: ${report.ocean.bootstrapBytes}`);
|
|
121
|
+
console.log(`Ocean saves sessions: ${report.ocean.sessionStore}`);
|
|
122
|
+
console.log(`Ocean saves runtime logs: ${report.ocean.runtimeLogs}`);
|
|
123
|
+
console.log(`Ocean saves summaries: ${report.ocean.sessionSummaries}`);
|
|
124
|
+
console.log(`transport: ${report.transport}`);
|
|
125
|
+
console.log("Provider-owned paths:");
|
|
126
|
+
for (const file of report.providerOwnedPaths)
|
|
127
|
+
console.log(`- ${file.exists === null ? "not controlled" : file.exists ? "present" : "absent"}: ${file.path}`);
|
|
128
|
+
if (report.latestSession)
|
|
129
|
+
console.log(`latest session: ${report.latestSession.sessionId} [${report.latestSession.status}] — inspect with: ocean session events ${report.latestSession.sessionId}`);
|
|
130
|
+
else
|
|
131
|
+
console.log("latest session: none");
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=client-test-command.js.map
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { readCallLedger, summarizeCallLedger, } from "@ocean/services/infrastructure/mcp/call-ledger.js";
|
|
2
|
+
import { applyClients, attributeLedger, confirmClient, defaultClientEnv, planClients, recordVerification, repairClients, restoreClients, temporaryEntryProblem, verifyClient, } from "../../application/clients/clients.js";
|
|
3
|
+
import { applyHooks, planHooks, restoreHooks, } from "../../application/clients/hooks.js";
|
|
4
|
+
import { applyNative, planNative, restoreNative, } from "../../application/clients/native.js";
|
|
5
|
+
import { applyOpencodePlugin, planOpencodePlugin, restoreOpencodePlugin, } from "../../application/clients/opencode-plugin.js";
|
|
6
|
+
const usage = "Usage: ocean clients detect|plan [--json] | apply [--client <id>…] [--yes] | verify [<id>…] | confirm <id>… --label in-app|headless|connected|blocked [--via <evidence>] [--reason <why>] | ledger | repair | restore [--batch <id>]";
|
|
7
|
+
// `ocean clients` (T-296 P2/P3b): every installed AI client's `ocean` MCP entry and, where the
|
|
8
|
+
// client has a verified session-start event, Ocean's session hook. `apply` shows the plan until
|
|
9
|
+
// --yes; `repair` only re-applies clients already approved; `restore` undoes an apply batch
|
|
10
|
+
// (entries and hooks) from the journal.
|
|
11
|
+
export async function runClientsCommand(args) {
|
|
12
|
+
const [action = "detect", ...rest] = args;
|
|
13
|
+
const json = rest.includes("--json");
|
|
14
|
+
const values = (flag) => rest.flatMap((arg, index) => (rest[index - 1] === flag ? [arg] : []));
|
|
15
|
+
let result;
|
|
16
|
+
if (action === "detect" || action === "plan") {
|
|
17
|
+
const plans = await planClients();
|
|
18
|
+
// `apply` refuses a temporary engine; the plan says so up front (T-300).
|
|
19
|
+
const problem = temporaryEntryProblem(defaultClientEnv().entry);
|
|
20
|
+
if (problem && action === "plan")
|
|
21
|
+
console.error(`Warning: apply would refuse this plan — ${problem}`);
|
|
22
|
+
if (!json) {
|
|
23
|
+
for (const plan of plans) {
|
|
24
|
+
if (action === "detect" && !plan.installed)
|
|
25
|
+
continue;
|
|
26
|
+
const ops = plan.ops.map((op) => `${op.kind} ${op.key}`).join(", ");
|
|
27
|
+
console.log(`${plan.client.padEnd(15)} ${plan.status.padEnd(14)} ${plan.tier.padEnd(13)} ${plan.installed ? plan.evidence.join("; ") : "not installed"}${ops ? ` → ${ops}` : ""}${plan.reason ? ` (${plan.reason})` : ""}`);
|
|
28
|
+
}
|
|
29
|
+
if (action === "plan")
|
|
30
|
+
for (const native of await planNative())
|
|
31
|
+
console.log(`${`${native.client} memory`.padEnd(15)} ${native.status.padEnd(14)} ${native.setting
|
|
32
|
+
? `${native.file} ${native.setting}`
|
|
33
|
+
: (native.reason ?? "")}${native.setting && native.reason ? ` (${native.reason})` : ""}`);
|
|
34
|
+
if (action === "plan")
|
|
35
|
+
for (const hook of await planHooks())
|
|
36
|
+
console.log(`${`${hook.client} hook`.padEnd(15)} ${hook.status.padEnd(14)} ${hook.file} ${hook.event}${hook.reason ? ` (${hook.reason})` : ""}${hook.snippet ? `; add ${hook.snippet}` : ""}`);
|
|
37
|
+
if (action === "plan")
|
|
38
|
+
for (const plugin of await planOpencodePlugin())
|
|
39
|
+
console.log(`${"opencode hook".padEnd(15)} ${plugin.status.padEnd(14)} ${plugin.file} plugin${plugin.reason ? ` (${plugin.reason})` : ""}`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
result =
|
|
43
|
+
action === "detect"
|
|
44
|
+
? plans.filter((plan) => plan.installed)
|
|
45
|
+
: {
|
|
46
|
+
clients: plans,
|
|
47
|
+
hooks: await planHooks(),
|
|
48
|
+
plugins: await planOpencodePlugin(),
|
|
49
|
+
native: await planNative(),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
else if (action === "apply") {
|
|
53
|
+
const clients = values("--client");
|
|
54
|
+
if (!rest.includes("--yes")) {
|
|
55
|
+
const pending = (await planClients()).filter((plan) => plan.status === "change" &&
|
|
56
|
+
(!clients.length || clients.includes(plan.client)));
|
|
57
|
+
const hooks = (await planHooks()).filter((hook) => hook.status === "change" &&
|
|
58
|
+
(!clients.length || clients.includes(hook.client)));
|
|
59
|
+
const native = (await planNative()).filter((plan) => plan.status === "change" &&
|
|
60
|
+
(!clients.length || clients.includes(plan.client)));
|
|
61
|
+
const plugins = (await planOpencodePlugin()).filter((plan) => plan.status === "change" &&
|
|
62
|
+
(!clients.length || clients.includes(plan.client)));
|
|
63
|
+
console.log(JSON.stringify({ dryRun: true, pending, hooks, plugins, native }, null, 2));
|
|
64
|
+
console.error("Dry run: pass --yes to write these client configs.");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const selected = clients.length ? clients : undefined;
|
|
68
|
+
const applied = await applyClients({ clients: selected });
|
|
69
|
+
result = {
|
|
70
|
+
...applied,
|
|
71
|
+
hooks: (await applyHooks({ clients: selected, batch: applied.batch }))
|
|
72
|
+
.results,
|
|
73
|
+
plugins: (await applyOpencodePlugin({ clients: selected, batch: applied.batch })).results,
|
|
74
|
+
native: (await applyNative({ clients: selected, batch: applied.batch }))
|
|
75
|
+
.results,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
else if (action === "verify") {
|
|
79
|
+
const ids = rest.filter((arg) => !arg.startsWith("--"));
|
|
80
|
+
const targets = ids.length
|
|
81
|
+
? ids
|
|
82
|
+
: (await planClients())
|
|
83
|
+
.filter((plan) => plan.installed && plan.status === "current")
|
|
84
|
+
.map((plan) => plan.client);
|
|
85
|
+
result = [];
|
|
86
|
+
for (const id of targets) {
|
|
87
|
+
const check = await verifyClient(id);
|
|
88
|
+
await recordVerification(id, check.ok);
|
|
89
|
+
result.push(check);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else if (action === "confirm") {
|
|
93
|
+
const flags = ["--label", "--via", "--reason"];
|
|
94
|
+
const ids = rest.filter((arg, index) => !arg.startsWith("--") && !flags.includes(rest[index - 1]));
|
|
95
|
+
const label = values("--label")[0];
|
|
96
|
+
if (!ids.length || !label)
|
|
97
|
+
throw new Error("Usage: ocean clients confirm <id>… --label in-app|headless|connected|blocked [--via <evidence>] [--reason <why>]");
|
|
98
|
+
for (const id of ids)
|
|
99
|
+
await confirmClient(id, {
|
|
100
|
+
label: label,
|
|
101
|
+
via: values("--via")[0],
|
|
102
|
+
reason: values("--reason")[0],
|
|
103
|
+
});
|
|
104
|
+
result = { confirmed: ids, label };
|
|
105
|
+
}
|
|
106
|
+
else if (action === "ledger")
|
|
107
|
+
// Which client called which Ocean tool, and when last — the in-app evidence (T-307).
|
|
108
|
+
result = attributeLedger(summarizeCallLedger(await readCallLedger()));
|
|
109
|
+
else if (action === "repair")
|
|
110
|
+
result = await repairClients();
|
|
111
|
+
else if (action === "restore") {
|
|
112
|
+
const restored = await restoreClients({ batch: values("--batch")[0] });
|
|
113
|
+
result = restored.batch
|
|
114
|
+
? {
|
|
115
|
+
...restored,
|
|
116
|
+
hooks: await restoreHooks({ batch: restored.batch }),
|
|
117
|
+
plugins: await restoreOpencodePlugin({ batch: restored.batch }),
|
|
118
|
+
native: await restoreNative({ batch: restored.batch }),
|
|
119
|
+
}
|
|
120
|
+
: restored;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
console.error(usage);
|
|
124
|
+
process.exitCode = 1;
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
console.log(JSON.stringify(result, null, 2));
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=clients-command.js.map
|