@tacuchi/agent-workflow-cli 22.4.2 → 24.0.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/README.md +39 -3
- package/dist/adapters/git-cli.js +59 -17
- package/dist/adapters/git-cli.js.map +1 -1
- package/dist/adapters/node-file-system.js +55 -1
- package/dist/adapters/node-file-system.js.map +1 -1
- package/dist/adapters/postgres-readonly-tools.js +277 -0
- package/dist/adapters/postgres-readonly-tools.js.map +1 -0
- package/dist/application/artifacts-service.js +2 -2
- package/dist/application/artifacts-service.js.map +1 -1
- package/dist/application/capability/dispatcher.js +2 -2
- package/dist/application/capability/dispatcher.js.map +1 -1
- package/dist/application/capability/installed-inventory.js +2 -2
- package/dist/application/capability/installed-inventory.js.map +1 -1
- package/dist/application/capability/readiness.js +1 -1
- package/dist/application/capability/readiness.js.map +1 -1
- package/dist/application/check-branch-service.js +2 -2
- package/dist/application/check-branch-service.js.map +1 -1
- package/dist/application/checkpoint/files-touched.js +271 -0
- package/dist/application/checkpoint/files-touched.js.map +1 -0
- package/dist/application/checkpoint/markdown.js +61 -8
- package/dist/application/checkpoint/markdown.js.map +1 -1
- package/dist/application/checkpoint/state-reader.js +8 -2
- package/dist/application/checkpoint/state-reader.js.map +1 -1
- package/dist/application/checkpoint-write-service.js +16 -5
- package/dist/application/checkpoint-write-service.js.map +1 -1
- package/dist/application/claims-ledger.js +330 -0
- package/dist/application/claims-ledger.js.map +1 -0
- package/dist/application/claims-recovery.js +338 -0
- package/dist/application/claims-recovery.js.map +1 -0
- package/dist/application/database-mcp-server.js +168 -0
- package/dist/application/database-mcp-server.js.map +1 -0
- package/dist/application/database-mcp-stdio.js +137 -0
- package/dist/application/database-mcp-stdio.js.map +1 -0
- package/dist/application/database-tool-catalog.js +226 -0
- package/dist/application/database-tool-catalog.js.map +1 -0
- package/dist/application/dev-only-services.js +154 -44
- package/dist/application/dev-only-services.js.map +1 -1
- package/dist/application/dsn-reader-service.js +1 -1
- package/dist/application/elicitation-server.js +382 -0
- package/dist/application/elicitation-server.js.map +1 -0
- package/dist/application/elicitation-stdio.js +48 -0
- package/dist/application/elicitation-stdio.js.map +1 -0
- package/dist/application/flow/advance.js +86 -5
- package/dist/application/flow/advance.js.map +1 -1
- package/dist/application/flow/checkout-observation.js +152 -0
- package/dist/application/flow/checkout-observation.js.map +1 -0
- package/dist/application/flow/flow-service.js +72 -13
- package/dist/application/flow/flow-service.js.map +1 -1
- package/dist/application/flow/internal-actions.js +460 -1
- package/dist/application/flow/internal-actions.js.map +1 -1
- package/dist/application/flow/internal-drive.js +22 -9
- package/dist/application/flow/internal-drive.js.map +1 -1
- package/dist/application/flow/prove.js +205 -0
- package/dist/application/flow/prove.js.map +1 -0
- package/dist/application/flow/run-journey.js +15 -0
- package/dist/application/flow/run-journey.js.map +1 -0
- package/dist/application/flow/run-projection.js +15 -3
- package/dist/application/flow/run-projection.js.map +1 -1
- package/dist/application/flow/run-state-service.js +19 -4
- package/dist/application/flow/run-state-service.js.map +1 -1
- package/dist/application/flow/submit.js +250 -72
- package/dist/application/flow/submit.js.map +1 -1
- package/dist/application/generate-launch-service.js +4 -4
- package/dist/application/generate-launch-service.js.map +1 -1
- package/dist/application/git-flow-service.js +9 -0
- package/dist/application/git-flow-service.js.map +1 -1
- package/dist/application/local-proposal.js +9 -0
- package/dist/application/local-proposal.js.map +1 -1
- package/dist/application/lock-service.js +96 -28
- package/dist/application/lock-service.js.map +1 -1
- package/dist/application/logging/log-events.js +19 -3
- package/dist/application/logging/log-events.js.map +1 -1
- package/dist/application/logging/logger.js +2 -2
- package/dist/application/logging/logger.js.map +1 -1
- package/dist/application/mcp-connections-service.js +115 -41
- package/dist/application/mcp-connections-service.js.map +1 -1
- package/dist/application/mcp-doctor-service.js +98 -54
- package/dist/application/mcp-doctor-service.js.map +1 -1
- package/dist/application/mcp-entry-classification.js +60 -0
- package/dist/application/mcp-entry-classification.js.map +1 -0
- package/dist/application/mcp-host-reader.js +114 -81
- package/dist/application/mcp-host-reader.js.map +1 -1
- package/dist/application/mcp-host-receipt-service.js +399 -0
- package/dist/application/mcp-host-receipt-service.js.map +1 -0
- package/dist/application/mcp-host-receipt-store.js +122 -0
- package/dist/application/mcp-host-receipt-store.js.map +1 -0
- package/dist/application/mcp-host-receipts.js +28 -0
- package/dist/application/mcp-host-receipts.js.map +1 -0
- package/dist/application/mcp-host-writer.js +321 -126
- package/dist/application/mcp-host-writer.js.map +1 -1
- package/dist/application/mcp-launch-probe-service.js +142 -0
- package/dist/application/mcp-launch-probe-service.js.map +1 -0
- package/dist/application/mcp-migration-service.js +351 -0
- package/dist/application/mcp-migration-service.js.map +1 -0
- package/dist/application/mcp-native-host-check-service.js +154 -0
- package/dist/application/mcp-native-host-check-service.js.map +1 -0
- package/dist/application/mcp-receipt-registration-service.js +105 -0
- package/dist/application/mcp-receipt-registration-service.js.map +1 -0
- package/dist/application/mcp-receipt-removal-service.js +62 -0
- package/dist/application/mcp-receipt-removal-service.js.map +1 -0
- package/dist/application/mcp-remove-service.js +84 -21
- package/dist/application/mcp-remove-service.js.map +1 -1
- package/dist/application/mcp-scope-common.js +2 -4
- package/dist/application/mcp-scope-common.js.map +1 -1
- package/dist/application/mcp-setup-service.js +60 -21
- package/dist/application/mcp-setup-service.js.map +1 -1
- package/dist/application/mcp-stdio-probe.js +526 -0
- package/dist/application/mcp-stdio-probe.js.map +1 -0
- package/dist/application/mcp-test-connection-service.js +23 -50
- package/dist/application/mcp-test-connection-service.js.map +1 -1
- package/dist/application/merge-state-service.js +10 -16
- package/dist/application/merge-state-service.js.map +1 -1
- package/dist/application/multiroot/claude.js +34 -15
- package/dist/application/multiroot/claude.js.map +1 -1
- package/dist/application/multiroot/codex.js +9 -5
- package/dist/application/multiroot/codex.js.map +1 -1
- package/dist/application/multiroot-service.js +20 -11
- package/dist/application/multiroot-service.js.map +1 -1
- package/dist/application/parsers/project-block.js +25 -2
- package/dist/application/parsers/project-block.js.map +1 -1
- package/dist/application/paths-service.js +30 -31
- package/dist/application/paths-service.js.map +1 -1
- package/dist/application/plan-exec-batch-service.js +549 -0
- package/dist/application/plan-exec-batch-service.js.map +1 -0
- package/dist/application/plan-exec-decision-service.js +273 -0
- package/dist/application/plan-exec-decision-service.js.map +1 -0
- package/dist/application/project-md-service.js +2 -2
- package/dist/application/project-md-service.js.map +1 -1
- package/dist/application/project-md-upsert-service.js +4 -4
- package/dist/application/project-md-upsert-service.js.map +1 -1
- package/dist/application/project-tab-data.js +12 -7
- package/dist/application/project-tab-data.js.map +1 -1
- package/dist/application/release-data-service.js +2 -2
- package/dist/application/release-data-service.js.map +1 -1
- package/dist/application/resume-service.js +32 -11
- package/dist/application/resume-service.js.map +1 -1
- package/dist/application/retirement/apply.js +84 -0
- package/dist/application/retirement/apply.js.map +1 -1
- package/dist/application/retirement/attribution.js +31 -1
- package/dist/application/retirement/attribution.js.map +1 -1
- package/dist/application/retirement/graph.js +19 -1
- package/dist/application/retirement/graph.js.map +1 -1
- package/dist/application/retirement/journal.js +11 -0
- package/dist/application/retirement/journal.js.map +1 -1
- package/dist/application/retirement/prepare.js +89 -3
- package/dist/application/retirement/prepare.js.map +1 -1
- package/dist/application/retirement/preview.js +16 -0
- package/dist/application/retirement/preview.js.map +1 -1
- package/dist/application/retirement/resolve.js +65 -0
- package/dist/application/retirement/resolve.js.map +1 -1
- package/dist/application/self/host-states.js +2 -0
- package/dist/application/self/host-states.js.map +1 -1
- package/dist/application/self/install-skill.js +21 -17
- package/dist/application/self/install-skill.js.map +1 -1
- package/dist/application/self/mcp-config.js +392 -84
- package/dist/application/self/mcp-config.js.map +1 -1
- package/dist/application/self/mcp-via-state.js +62 -0
- package/dist/application/self/mcp-via-state.js.map +1 -0
- package/dist/application/self/uninstall.js +15 -5
- package/dist/application/self/uninstall.js.map +1 -1
- package/dist/application/session-close-service.js +24 -1
- package/dist/application/session-close-service.js.map +1 -1
- package/dist/application/session-create-service.js +7 -0
- package/dist/application/session-create-service.js.map +1 -1
- package/dist/application/session-resume-service.js +2 -2
- package/dist/application/session-resume-service.js.map +1 -1
- package/dist/application/sessions-service.js +2 -4
- package/dist/application/sessions-service.js.map +1 -1
- package/dist/application/skills-resolver-service.js +4 -4
- package/dist/application/skills-resolver-service.js.map +1 -1
- package/dist/application/source-boundary-policy.js +18 -7
- package/dist/application/source-boundary-policy.js.map +1 -1
- package/dist/application/source-remove-service.js +12 -1
- package/dist/application/source-remove-service.js.map +1 -1
- package/dist/application/sources-service.js +2 -2
- package/dist/application/sources-service.js.map +1 -1
- package/dist/application/status-service.js +4 -0
- package/dist/application/status-service.js.map +1 -1
- package/dist/application/visibility-doctor-service.js +1 -1
- package/dist/application/visibility-doctor-service.js.map +1 -1
- package/dist/application/workline-index-service.js +182 -27
- package/dist/application/workline-index-service.js.map +1 -1
- package/dist/application/workspace-init-service.js +89 -271
- package/dist/application/workspace-init-service.js.map +1 -1
- package/dist/application/workspace-materialization-service.js +300 -0
- package/dist/application/workspace-materialization-service.js.map +1 -0
- package/dist/application/worktree-service.js +32 -3
- package/dist/application/worktree-service.js.map +1 -1
- package/dist/cli/commands/capability.js +4 -7
- package/dist/cli/commands/capability.js.map +1 -1
- package/dist/cli/commands/claims.js +72 -0
- package/dist/cli/commands/claims.js.map +1 -0
- package/dist/cli/commands/dev-only.js +73 -25
- package/dist/cli/commands/dev-only.js.map +1 -1
- package/dist/cli/commands/flow.js +145 -42
- package/dist/cli/commands/flow.js.map +1 -1
- package/dist/cli/commands/index.js +4 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/mcp.js +752 -76
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/multiroot.js +40 -7
- package/dist/cli/commands/multiroot.js.map +1 -1
- package/dist/cli/commands/resume.js +9 -3
- package/dist/cli/commands/resume.js.map +1 -1
- package/dist/cli/commands/retirement.js +9 -0
- package/dist/cli/commands/retirement.js.map +1 -1
- package/dist/cli/commands/session-create.js +1 -1
- package/dist/cli/commands/session-create.js.map +1 -1
- package/dist/cli/commands/skills.js +1 -1
- package/dist/cli/commands/skills.js.map +1 -1
- package/dist/cli/commands/status.js +72 -24
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/tool.js +141 -0
- package/dist/cli/commands/tool.js.map +1 -0
- package/dist/cli/commands/workspace-init.js +5 -5
- package/dist/cli/commands/workspace-init.js.map +1 -1
- package/dist/cli/help-groups.js +6 -1
- package/dist/cli/help-groups.js.map +1 -1
- package/dist/cli/main.js +272 -67
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/parser.js +8 -1
- package/dist/cli/parser.js.map +1 -1
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli/render.js +15 -2
- package/dist/cli/render.js.map +1 -1
- package/dist/cli/tui/app.js +3 -2
- package/dist/cli/tui/app.js.map +1 -1
- package/dist/cli/tui/components/workspace-init-form.js +7 -7
- package/dist/cli/tui/components/workspace-init-form.js.map +1 -1
- package/dist/cli/tui/data/workflow-content.js +2 -2
- package/dist/cli/tui/data/workflow-content.js.map +1 -1
- package/dist/cli/tui/tabs/config-tab.js +4 -3
- package/dist/cli/tui/tabs/config-tab.js.map +1 -1
- package/dist/cli/tui/tabs/mcp-tab-helpers.js +72 -1
- package/dist/cli/tui/tabs/mcp-tab-helpers.js.map +1 -1
- package/dist/cli/tui/tabs/mcp-tab.js +101 -42
- package/dist/cli/tui/tabs/mcp-tab.js.map +1 -1
- package/dist/cli/tui/tabs/project-tab.js +18 -21
- package/dist/cli/tui/tabs/project-tab.js.map +1 -1
- package/dist/cli/tui/workspace-root.js +13 -0
- package/dist/cli/tui/workspace-root.js.map +1 -0
- package/dist/domain/capability/protocol.js +2 -2
- package/dist/domain/capability/protocol.js.map +1 -1
- package/dist/domain/database-tools.js +445 -0
- package/dist/domain/database-tools.js.map +1 -0
- package/dist/domain/degradation-notice.js +60 -0
- package/dist/domain/degradation-notice.js.map +1 -0
- package/dist/domain/elicitation.js +152 -0
- package/dist/domain/elicitation.js.map +1 -0
- package/dist/domain/flow/answer.js +173 -42
- package/dist/domain/flow/answer.js.map +1 -1
- package/dist/domain/flow/authority.js +184 -127
- package/dist/domain/flow/authority.js.map +1 -1
- package/dist/domain/flow/directive.js +47 -1
- package/dist/domain/flow/directive.js.map +1 -1
- package/dist/domain/flow/rules.js +7 -4
- package/dist/domain/flow/rules.js.map +1 -1
- package/dist/domain/flow/run-state.js +636 -31
- package/dist/domain/flow/run-state.js.map +1 -1
- package/dist/domain/harnesses.js +16 -0
- package/dist/domain/harnesses.js.map +1 -1
- package/dist/domain/mcp-entry.js +164 -28
- package/dist/domain/mcp-entry.js.map +1 -1
- package/dist/domain/postgres-object-search.js +320 -0
- package/dist/domain/postgres-object-search.js.map +1 -0
- package/dist/domain/redaction.js +47 -0
- package/dist/domain/redaction.js.map +1 -0
- package/dist/domain/retirement/proposal.js +1 -0
- package/dist/domain/retirement/proposal.js.map +1 -1
- package/dist/domain/structured-choice-stamp.js +32 -4
- package/dist/domain/structured-choice-stamp.js.map +1 -1
- package/dist/domain/workline-mcp-entry.js +34 -0
- package/dist/domain/workline-mcp-entry.js.map +1 -0
- package/dist/ports/postgres-tools.js +10 -0
- package/dist/ports/postgres-tools.js.map +1 -0
- package/dist/runtime/namespace-resolver.js +104 -18
- package/dist/runtime/namespace-resolver.js.map +1 -1
- package/package.json +5 -1
- package/skills/w/README.md +2 -2
- package/skills/w/SKILL.md +9 -10
- package/skills/w/artifacts/README.md +1 -1
- package/skills/w/commands/README.md +3 -3
- package/skills/w/commands/persist.md +3 -1
- package/skills/w/commands/resume.md +1 -1
- package/skills/w/commands/spec-new.md +2 -2
- package/skills/w/commands/status.md +1 -1
- package/skills/w/commands/workspace-init.md +9 -10
- package/skills/w/context/MANIFEST.json +1 -4
- package/skills/w/harness/HARNESS.md +1 -1
- package/skills/w/loops/quick-loop/LOOP.md +1 -1
- package/skills/w/modules/PROMPT-CONTINUITY.md +1 -1
- package/skills/w/modules/WORKSPACE-SCAFFOLD.md +17 -20
- package/skills/w/schemas/capability-descriptor.schema.json +1 -1
- package/dist/application/mcp-dbhub-launcher.js +0 -216
- package/dist/application/mcp-dbhub-launcher.js.map +0 -1
package/dist/cli/commands/mcp.js
CHANGED
|
@@ -1,16 +1,33 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
|
+
import { isDeepStrictEqual } from "node:util";
|
|
5
|
+
import { PostgresReadonlyTools } from "../../adapters/postgres-readonly-tools.js";
|
|
6
|
+
import { runDatabaseMcpStdio } from "../../application/database-mcp-stdio.js";
|
|
7
|
+
import { DatabaseToolCatalog } from "../../application/database-tool-catalog.js";
|
|
4
8
|
import { runHarness } from "../../application/dev-only-services.js";
|
|
9
|
+
import { runElicitationStdio } from "../../application/elicitation-stdio.js";
|
|
5
10
|
import { resolveMcpConnectionSelection, } from "../../application/mcp-connections-service.js";
|
|
6
|
-
import { DbhubLauncherError, runDbhubLauncher } from "../../application/mcp-dbhub-launcher.js";
|
|
7
11
|
import { runMcpDoctor } from "../../application/mcp-doctor-service.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
12
|
+
import { classifyMcpEntry } from "../../application/mcp-entry-classification.js";
|
|
13
|
+
import { readMcpEntry } from "../../application/mcp-host-reader.js";
|
|
14
|
+
import { digestMcpReceiptDescriptor, } from "../../application/mcp-host-receipt-service.js";
|
|
15
|
+
import { openMcpHostReceiptService, registerPersistedMcpDescriptor, } from "../../application/mcp-host-receipts.js";
|
|
16
|
+
import { probePersistedMcpSetupEntries } from "../../application/mcp-launch-probe-service.js";
|
|
17
|
+
import { runMcpMigration, } from "../../application/mcp-migration-service.js";
|
|
18
|
+
import { checkNativeMcpHosts, recordNativeMcpHostChecks, } from "../../application/mcp-native-host-check-service.js";
|
|
19
|
+
import { registerMcpSetupReceipts } from "../../application/mcp-receipt-registration-service.js";
|
|
20
|
+
import { removeMcpRemoveReceipts } from "../../application/mcp-receipt-removal-service.js";
|
|
21
|
+
import { runMcpRemove, } from "../../application/mcp-remove-service.js";
|
|
22
|
+
import { runMcpSetup, } from "../../application/mcp-setup-service.js";
|
|
23
|
+
import { runMcpStdioProbe } from "../../application/mcp-stdio-probe.js";
|
|
10
24
|
import { buildWarpPostInstallHint, formatWarpPostInstallHint, } from "../../application/mcp-warp-postinstall-hint.js";
|
|
11
25
|
import { resolveWarpGlobalMcpPath, resolveWarpProjectMcpPath, } from "../../application/multiroot/warp.js";
|
|
26
|
+
import { PathsService } from "../../application/paths-service.js";
|
|
27
|
+
import { ensureWorklineMaterialized, previewWorklineMaterialization, } from "../../application/workspace-materialization-service.js";
|
|
28
|
+
import { DATABASE_TOOL_DESCRIPTORS } from "../../domain/database-tools.js";
|
|
12
29
|
import { MCP_FILE_HOSTS, harnessById } from "../../domain/harnesses.js";
|
|
13
|
-
import { mcpEntryNameFor } from "../../domain/mcp-entry.js";
|
|
30
|
+
import { buildMcpEntry, mcpEntryNameFor, } from "../../domain/mcp-entry.js";
|
|
14
31
|
import { fail } from "../render.js";
|
|
15
32
|
// File-writing hosts — single source in the domain (excludes oz, which has no
|
|
16
33
|
// file writer). The TUI's host picker reads the same list.
|
|
@@ -18,16 +35,21 @@ const FILE_HOSTS = MCP_FILE_HOSTS;
|
|
|
18
35
|
const HOST_VALUES = new Set([...FILE_HOSTS, "all"]);
|
|
19
36
|
export const mcpCommand = {
|
|
20
37
|
name: "mcp",
|
|
21
|
-
describe: "MCP server tooling.
|
|
38
|
+
describe: "MCP server tooling. `serve` corre el servidor de elicitation de Workline y `serve-db` sirve las tools PostgreSQL; ambas reservan stdout para JSON-RPC. `dbhub` queda como alias deprecado de serve-db. Subcomandos: serve | serve-db [--instance i] | dbhub [--instance i] | setup/remove/doctor [--host h] [--instance i|--all-connections] [--workspace dir] [--global] [--dry-run] [--force] | migrate [--host h] [--instance i|--all-connections] [--workspace dir] [--global] [--apply --force] | warp-status.",
|
|
22
39
|
async execute(args, ctx) {
|
|
23
40
|
const subcommand = args.rest[0];
|
|
24
41
|
if ((subcommand === "setup" ||
|
|
25
42
|
subcommand === "remove" ||
|
|
26
43
|
subcommand === "doctor" ||
|
|
44
|
+
subcommand === "migrate" ||
|
|
27
45
|
subcommand === "warp-status") &&
|
|
28
46
|
args.rest.length > 1) {
|
|
29
47
|
return fail("INVALID_INPUT", "Los subcomandos MCP no aceptan una conexión posicional. Usá --instance <nombre> o --all-connections cuando el subcomando permite fan-out.");
|
|
30
48
|
}
|
|
49
|
+
if (subcommand === "serve")
|
|
50
|
+
return runServeSub(args);
|
|
51
|
+
if (subcommand === "serve-db")
|
|
52
|
+
return runServeDbSub(args, ctx);
|
|
31
53
|
if (subcommand === "dbhub")
|
|
32
54
|
return runDbhubSub(args, ctx);
|
|
33
55
|
if (subcommand === "setup")
|
|
@@ -36,39 +58,140 @@ export const mcpCommand = {
|
|
|
36
58
|
return runRemoveSub(args, ctx);
|
|
37
59
|
if (subcommand === "doctor")
|
|
38
60
|
return runDoctorSub(args, ctx);
|
|
61
|
+
if (subcommand === "migrate")
|
|
62
|
+
return await runMigrateSub(args, ctx);
|
|
39
63
|
if (subcommand === "warp-status")
|
|
40
64
|
return runWarpStatusSub(args, ctx);
|
|
41
|
-
return fail("INVALID_INPUT", "mcp requiere subcomando: dbhub [--instance <nombre>] | setup | remove | doctor | warp-status");
|
|
65
|
+
return fail("INVALID_INPUT", "mcp requiere subcomando: serve | serve-db [--instance <nombre>] | dbhub [--instance <nombre>] | setup | remove | doctor | migrate | warp-status");
|
|
42
66
|
},
|
|
43
67
|
};
|
|
68
|
+
/**
|
|
69
|
+
* El servidor propio, hablando por la entrada y la salida de ESTE proceso.
|
|
70
|
+
*
|
|
71
|
+
* No devuelve datos para renderizar y no puede: la salida estándar ya está tomada
|
|
72
|
+
* por el protocolo, así que cualquier cosa que el renderizador imprimiera ahí
|
|
73
|
+
* corrompería la sesión del cliente. Por eso `data` viaja indefinido.
|
|
74
|
+
*/
|
|
75
|
+
async function runServeSub(args) {
|
|
76
|
+
// Qué host lo lanzó: el propio host lo dice al registrarlo, porque el servidor
|
|
77
|
+
// no puede detectarlo desde adentro. Sólo habilita una vía observada; no se usa
|
|
78
|
+
// para atribuir una negativa o cancelación posterior.
|
|
79
|
+
const declared = args.values.get("host");
|
|
80
|
+
const spec = declared === undefined ? null : harnessById(declared);
|
|
81
|
+
await runElicitationStdio({
|
|
82
|
+
input: process.stdin,
|
|
83
|
+
output: process.stdout,
|
|
84
|
+
diagnostics: process.stderr,
|
|
85
|
+
via: spec?.structuredChoice.mcpElicitation ?? {
|
|
86
|
+
available: false,
|
|
87
|
+
reason: "el servidor se lanzó sin declarar en qué host corre (--host)",
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
return { ok: true, exitCode: 0 };
|
|
91
|
+
}
|
|
44
92
|
async function runDbhubSub(args, ctx) {
|
|
93
|
+
process.stderr.write("aw mcp dbhub está deprecado; usá 'aw mcp serve-db'.\n");
|
|
94
|
+
return await runServeDbSub(args, ctx);
|
|
95
|
+
}
|
|
96
|
+
async function runServeDbSub(args, ctx) {
|
|
45
97
|
if (args.rest[1] !== undefined) {
|
|
46
|
-
return
|
|
98
|
+
return serveBootstrapFailure("mcp serve-db no acepta una conexión posicional. Usá --instance <nombre>.");
|
|
47
99
|
}
|
|
100
|
+
if (args.flags.has("--instance"))
|
|
101
|
+
return serveBootstrapFailure("--instance requiere un nombre.");
|
|
48
102
|
const connections = resolveConnections(args, ctx, false);
|
|
49
103
|
if (!("value" in connections))
|
|
50
|
-
return
|
|
104
|
+
return serveBootstrapFailure("No se pudo resolver la conexión MCP.");
|
|
51
105
|
const connection = connections.value[0];
|
|
52
106
|
if (connection === undefined) {
|
|
53
|
-
return
|
|
107
|
+
return serveBootstrapFailure("No hay conexiones MCP seleccionadas para serve-db.");
|
|
54
108
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
109
|
+
const catalog = new DatabaseToolCatalog({
|
|
110
|
+
paths: ctx.paths,
|
|
111
|
+
env: ctx.env,
|
|
112
|
+
postgres: new PostgresReadonlyTools(),
|
|
113
|
+
});
|
|
114
|
+
const onHostLoadObserved = resolveHostLoadObservation(args, ctx, connection);
|
|
115
|
+
await runDatabaseMcpStdio({
|
|
116
|
+
input: process.stdin,
|
|
117
|
+
output: process.stdout,
|
|
118
|
+
diagnostics: process.stderr,
|
|
119
|
+
catalog,
|
|
120
|
+
connection: connection.name,
|
|
121
|
+
...(onHostLoadObserved === undefined ? {} : { onHostLoadObserved }),
|
|
122
|
+
});
|
|
123
|
+
return { ok: true, data: undefined, exitCode: 0 };
|
|
124
|
+
}
|
|
125
|
+
/** A stdio server may never put a CLI envelope on its JSON-RPC stdout. */
|
|
126
|
+
function serveBootstrapFailure(message) {
|
|
127
|
+
process.stderr.write(`aw mcp serve-db: ${message}\n`);
|
|
128
|
+
return {
|
|
129
|
+
ok: false,
|
|
130
|
+
error: { code: "MCP_SERVER_BOOTSTRAP_FAILED", message },
|
|
131
|
+
exitCode: 2,
|
|
132
|
+
suppressOutput: true,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Only an exact persisted descriptor can clear reload_required. A local doctor
|
|
137
|
+
* sets AW_MCP_PROBE, so its initialized notification remains launch evidence
|
|
138
|
+
* and cannot impersonate a real host reload.
|
|
139
|
+
*/
|
|
140
|
+
function resolveHostLoadObservation(args, ctx, connection) {
|
|
141
|
+
if (process.env.AW_MCP_PROBE === "1")
|
|
142
|
+
return undefined;
|
|
143
|
+
const rawHost = args.values.get("host");
|
|
144
|
+
const rawScope = args.values.get("scope");
|
|
145
|
+
if (rawHost === undefined || rawScope === undefined)
|
|
146
|
+
return undefined;
|
|
147
|
+
if (!FILE_HOSTS.includes(rawHost))
|
|
148
|
+
return undefined;
|
|
149
|
+
if (rawScope !== "workspace" && rawScope !== "global")
|
|
150
|
+
return undefined;
|
|
151
|
+
const host = rawHost;
|
|
152
|
+
const scope = rawScope;
|
|
153
|
+
if (scope !== "global")
|
|
154
|
+
return undefined;
|
|
155
|
+
const entry = buildMcpEntry(connection.name, connection.dsnVar, {
|
|
156
|
+
host,
|
|
157
|
+
scope,
|
|
158
|
+
namespace: ctx.paths.namespace,
|
|
159
|
+
});
|
|
160
|
+
// `process.argv` begins with Node and the entrypoint. The rest must be the
|
|
161
|
+
// descriptor byte-for-byte; a hand-written `serve-db` command is useful but
|
|
162
|
+
// is not evidence that a configured host loaded its registered descriptor.
|
|
163
|
+
if (process.execPath !== entry.command || !isDeepStrictEqual(process.argv.slice(1), entry.args)) {
|
|
164
|
+
return undefined;
|
|
65
165
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
166
|
+
const descriptorDigest = digestMcpReceiptDescriptor({
|
|
167
|
+
command: entry.command,
|
|
168
|
+
args: entry.args,
|
|
169
|
+
});
|
|
170
|
+
return async () => {
|
|
171
|
+
try {
|
|
172
|
+
const snapshot = readMcpEntry(host, ctx.env.homeDir(), entry.name, scope);
|
|
173
|
+
const classification = classifyMcpEntry(host, snapshot, entry, connection);
|
|
174
|
+
if (classification.state !== "current" ||
|
|
175
|
+
snapshot.command === undefined ||
|
|
176
|
+
snapshot.args === undefined ||
|
|
177
|
+
digestMcpReceiptDescriptor({ command: snapshot.command, args: snapshot.args }) !==
|
|
178
|
+
descriptorDigest) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
await openMcpHostReceiptService(ctx.paths).observeHostLoad({
|
|
182
|
+
host,
|
|
183
|
+
scope,
|
|
184
|
+
connection: connection.name,
|
|
185
|
+
descriptorDigest,
|
|
186
|
+
});
|
|
69
187
|
}
|
|
70
|
-
|
|
71
|
-
|
|
188
|
+
catch {
|
|
189
|
+
// A stale/missing receipt must not contaminate MCP stdout. The host has
|
|
190
|
+
// already received a valid initialized lifecycle response; doctor will
|
|
191
|
+
// surface the missing operational evidence separately.
|
|
192
|
+
process.stderr.write("aw mcp serve-db: no se pudo registrar la carga observada del host\n");
|
|
193
|
+
}
|
|
194
|
+
};
|
|
72
195
|
}
|
|
73
196
|
async function runSetupSub(args, ctx) {
|
|
74
197
|
const hosts = resolveHosts(args, ctx);
|
|
@@ -77,47 +200,155 @@ async function runSetupSub(args, ctx) {
|
|
|
77
200
|
const connections = resolveConnections(args, ctx, true);
|
|
78
201
|
if (!("value" in connections))
|
|
79
202
|
return connections;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
203
|
+
// All workspace-scoped MCP artifacts share Workline's resolved root. An
|
|
204
|
+
// explicit --workspace remains an intentional override; raw process cwd is
|
|
205
|
+
// only the invocation coordinate and may be a source subdirectory.
|
|
206
|
+
const workspace = args.values.get("workspace") ?? ctx.paths.workspaceDir();
|
|
207
|
+
const scopeInput = args.flags.has("--global")
|
|
208
|
+
? { scope: "global" }
|
|
209
|
+
: { scope: "workspace", workspace };
|
|
210
|
+
const result = await runMcpSetupWithMaterialization(ctx, {
|
|
83
211
|
hosts: hosts.value,
|
|
84
212
|
connections: connections.value,
|
|
85
|
-
|
|
86
|
-
...
|
|
213
|
+
namespace: ctx.paths.namespace,
|
|
214
|
+
...scopeInput,
|
|
87
215
|
dryRun: args.flags.has("--dry-run"),
|
|
88
|
-
|
|
216
|
+
...(args.flags.has("--force") ? { globalApproval: "explicit-cli-force" } : {}),
|
|
89
217
|
});
|
|
90
218
|
if ("ok" in result) {
|
|
91
219
|
return fail("GLOBAL_REQUIRES_FORCE", result.hint, result, result.exitCode);
|
|
92
220
|
}
|
|
93
|
-
const
|
|
94
|
-
const warpHints = !
|
|
95
|
-
? buildWarpHintsFor(hosts.value, connections.value.map((connection) => connection.name), scope,
|
|
221
|
+
const hasProblems = result.errors.length > 0 || result.conflicts.length > 0;
|
|
222
|
+
const warpHints = !hasProblems
|
|
223
|
+
? buildWarpHintsFor(hosts.value, connections.value.map((connection) => connection.name), scopeInput.scope, workspace)
|
|
96
224
|
: [];
|
|
97
225
|
return {
|
|
98
|
-
ok: !
|
|
226
|
+
ok: !hasProblems,
|
|
99
227
|
data: { ...result, ...(warpHints.length > 0 ? { warp_hints: warpHints } : {}) },
|
|
100
|
-
...(
|
|
228
|
+
...(hasProblems
|
|
101
229
|
? {
|
|
102
230
|
error: {
|
|
103
231
|
code: "MCP_SETUP_PARTIAL",
|
|
104
|
-
message: `${result.errors.length} error(es) durante setup; ver data.errors`,
|
|
232
|
+
message: `${result.errors.length} error(es) y ${result.conflicts.length} conflicto(s) durante setup; ver data.errors y data.conflicts`,
|
|
105
233
|
},
|
|
106
234
|
}
|
|
107
235
|
: {}),
|
|
108
|
-
exitCode:
|
|
236
|
+
exitCode: hasProblems ? 1 : 0,
|
|
109
237
|
};
|
|
110
238
|
}
|
|
111
|
-
|
|
239
|
+
/**
|
|
240
|
+
* MCP writers use host-native synchronous file APIs, so the generic guarded
|
|
241
|
+
* filesystem cannot observe their mutation. Preflight with the writer's own
|
|
242
|
+
* dry-run path, materialize only when that preflight has a real effect, then
|
|
243
|
+
* run the actual write. This preserves pure conflicts/skips and puts the
|
|
244
|
+
* canonical marker in place before the first host config is touched.
|
|
245
|
+
*/
|
|
246
|
+
async function runMcpSetupWithMaterialization(ctx, input) {
|
|
247
|
+
if (input.scope !== "workspace") {
|
|
248
|
+
const result = runMcpSetup(ctx.env, input);
|
|
249
|
+
return "ok" in result ? result : await attachSetupReceipts(ctx, input, result);
|
|
250
|
+
}
|
|
251
|
+
const preview = runMcpSetup(ctx.env, { ...input, dryRun: true });
|
|
252
|
+
if ("ok" in preview)
|
|
253
|
+
return preview;
|
|
254
|
+
if (!hasPendingMcpMutation(preview.applied)) {
|
|
255
|
+
if (input.dryRun)
|
|
256
|
+
return preview;
|
|
257
|
+
const result = runMcpSetup(ctx.env, input);
|
|
258
|
+
return "ok" in result ? result : await attachSetupReceipts(ctx, input, result);
|
|
259
|
+
}
|
|
260
|
+
const materialization = await materializeMcpWorkspace(ctx, input.workspace, input.dryRun === true);
|
|
261
|
+
if (input.dryRun)
|
|
262
|
+
return { ...preview, materialization };
|
|
263
|
+
const result = runMcpSetup(ctx.env, input);
|
|
264
|
+
if ("ok" in result)
|
|
265
|
+
return result;
|
|
266
|
+
const withReceipts = await attachSetupReceipts(ctx, input, result);
|
|
267
|
+
return { ...withReceipts, materialization };
|
|
268
|
+
}
|
|
269
|
+
async function attachSetupReceipts(ctx, input, result) {
|
|
270
|
+
const receipts = await registerMcpSetupReceipts(ctx.paths, input, result);
|
|
271
|
+
const withReceipts = {
|
|
272
|
+
...result,
|
|
273
|
+
errors: [...result.errors, ...receipts.errors],
|
|
274
|
+
...(receipts.registered.length === 0 ? {} : { receipts: receipts.registered }),
|
|
275
|
+
};
|
|
276
|
+
const launch = await probePersistedMcpSetupEntries(ctx.paths, withReceipts, receipts.probeTargets);
|
|
277
|
+
const native = await checkNativeMcpHosts(receipts.probeTargets);
|
|
278
|
+
await recordNativeMcpHostChecks({
|
|
279
|
+
paths: ctx.paths,
|
|
280
|
+
scope: withReceipts.scope,
|
|
281
|
+
scopeDir: withReceipts.scope_dir,
|
|
282
|
+
targets: receipts.probeTargets,
|
|
283
|
+
checks: native.checks,
|
|
284
|
+
});
|
|
285
|
+
if (receipts.registered.length === 0 &&
|
|
286
|
+
receipts.errors.length === 0 &&
|
|
287
|
+
launch.probes.length === 0 &&
|
|
288
|
+
launch.errors.length === 0 &&
|
|
289
|
+
native.checks.length === 0 &&
|
|
290
|
+
native.errors.length === 0) {
|
|
291
|
+
return result;
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
...withReceipts,
|
|
295
|
+
errors: [...withReceipts.errors, ...launch.errors, ...native.errors],
|
|
296
|
+
...(launch.probes.length === 0 ? {} : { launch_probes: launch.probes }),
|
|
297
|
+
...(native.checks.length === 0 ? {} : { native_checks: native.checks }),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
async function runMcpRemoveWithMaterialization(ctx, input) {
|
|
301
|
+
if (input.scope !== "workspace") {
|
|
302
|
+
const result = runMcpRemove(ctx.env, input);
|
|
303
|
+
return "ok" in result ? result : await attachRemoveReceipts(ctx, input, result);
|
|
304
|
+
}
|
|
305
|
+
const preview = runMcpRemove(ctx.env, { ...input, dryRun: true });
|
|
306
|
+
if ("ok" in preview)
|
|
307
|
+
return preview;
|
|
308
|
+
if (!hasPendingMcpMutation(preview.removed)) {
|
|
309
|
+
if (input.dryRun)
|
|
310
|
+
return preview;
|
|
311
|
+
const result = runMcpRemove(ctx.env, input);
|
|
312
|
+
return "ok" in result ? result : await attachRemoveReceipts(ctx, input, result);
|
|
313
|
+
}
|
|
314
|
+
const materialization = await materializeMcpWorkspace(ctx, input.workspace, input.dryRun === true);
|
|
315
|
+
if (input.dryRun)
|
|
316
|
+
return { ...preview, materialization };
|
|
317
|
+
const result = runMcpRemove(ctx.env, input);
|
|
318
|
+
if ("ok" in result)
|
|
319
|
+
return result;
|
|
320
|
+
const withReceipts = await attachRemoveReceipts(ctx, input, result);
|
|
321
|
+
return { ...withReceipts, materialization };
|
|
322
|
+
}
|
|
323
|
+
async function attachRemoveReceipts(ctx, input, result) {
|
|
324
|
+
const errors = await removeMcpRemoveReceipts(ctx.paths, input, result);
|
|
325
|
+
return errors.length === 0 ? result : { ...result, errors: [...result.errors, ...errors] };
|
|
326
|
+
}
|
|
327
|
+
function hasPendingMcpMutation(results) {
|
|
328
|
+
return results.some((result) => result.action === "dry-run");
|
|
329
|
+
}
|
|
330
|
+
async function materializeMcpWorkspace(ctx, workspace, dryRun) {
|
|
331
|
+
const root = resolve(workspace);
|
|
332
|
+
const currentRoot = resolve(ctx.paths.workspaceDir());
|
|
333
|
+
const paths = root === currentRoot
|
|
334
|
+
? ctx.paths
|
|
335
|
+
: new PathsService(ctx.paths.namespace, ctx.env.homeDir(), root);
|
|
336
|
+
const fs = ctx.rawFs ?? ctx.fs;
|
|
337
|
+
return dryRun
|
|
338
|
+
? await previewWorklineMaterialization(fs, paths)
|
|
339
|
+
: await ensureWorklineMaterialized(fs, paths);
|
|
340
|
+
}
|
|
341
|
+
function buildWarpHintsFor(hosts, instances, scope, workspace) {
|
|
112
342
|
if (!hosts.includes("warp"))
|
|
113
343
|
return [];
|
|
114
344
|
const file = scope === "global"
|
|
115
345
|
? (resolveWarpGlobalMcpPath() ?? "~/.warp/.mcp.json")
|
|
116
|
-
: resolveWarpProjectMcpPath(resolve(workspace
|
|
346
|
+
: resolveWarpProjectMcpPath(resolve(workspace));
|
|
117
347
|
return instances.map((instance) => buildWarpPostInstallHint(mcpEntryNameFor(instance), scope, file));
|
|
118
348
|
}
|
|
119
|
-
async function runWarpStatusSub(
|
|
120
|
-
const
|
|
349
|
+
async function runWarpStatusSub(args, ctx) {
|
|
350
|
+
const workspace = args.values.get("workspace") ?? ctx.paths.workspaceDir();
|
|
351
|
+
const projectFile = resolveWarpProjectMcpPath(resolve(workspace));
|
|
121
352
|
const globalFile = resolveWarpGlobalMcpPath() ?? `${homedir()}/.warp/.mcp.json`;
|
|
122
353
|
const sources = [
|
|
123
354
|
{ scope: "workspace", file: projectFile },
|
|
@@ -136,7 +367,7 @@ async function runWarpStatusSub(_args, ctx) {
|
|
|
136
367
|
reports,
|
|
137
368
|
summary: anyDetected
|
|
138
369
|
? "Archivos .warp/.mcp.json detectados. Activá 'File-based MCP Servers' en Warp Settings si todavía no lo hiciste."
|
|
139
|
-
: "No se encontró .warp/.mcp.json en
|
|
370
|
+
: "No se encontró .warp/.mcp.json en el workspace ni en home. Primero registrá una conexión con 'agent-workflow mcp setup --host warp'.",
|
|
140
371
|
},
|
|
141
372
|
exitCode: 0,
|
|
142
373
|
};
|
|
@@ -162,64 +393,516 @@ async function runRemoveSub(args, ctx) {
|
|
|
162
393
|
const connections = resolveConnections(args, ctx, true);
|
|
163
394
|
if (!("value" in connections))
|
|
164
395
|
return connections;
|
|
165
|
-
const workspace = args.values.get("workspace");
|
|
166
|
-
const
|
|
396
|
+
const workspace = args.values.get("workspace") ?? ctx.paths.workspaceDir();
|
|
397
|
+
const scopeInput = args.flags.has("--global")
|
|
398
|
+
? { scope: "global" }
|
|
399
|
+
: { scope: "workspace", workspace };
|
|
400
|
+
const result = await runMcpRemoveWithMaterialization(ctx, {
|
|
167
401
|
hosts: hosts.value,
|
|
168
402
|
connections: connections.value,
|
|
169
|
-
|
|
170
|
-
...
|
|
403
|
+
namespace: ctx.paths.namespace,
|
|
404
|
+
...scopeInput,
|
|
171
405
|
dryRun: args.flags.has("--dry-run"),
|
|
172
|
-
|
|
406
|
+
...(args.flags.has("--force") ? { globalApproval: "explicit-cli-force" } : {}),
|
|
173
407
|
});
|
|
174
408
|
if ("ok" in result) {
|
|
175
409
|
return fail("GLOBAL_REQUIRES_FORCE", result.hint, result, result.exitCode);
|
|
176
410
|
}
|
|
177
|
-
const
|
|
411
|
+
const hasProblems = result.errors.length > 0 || result.conflicts.length > 0;
|
|
178
412
|
return {
|
|
179
|
-
ok: !
|
|
413
|
+
ok: !hasProblems,
|
|
180
414
|
data: result,
|
|
181
|
-
...(
|
|
415
|
+
...(hasProblems
|
|
182
416
|
? {
|
|
183
417
|
error: {
|
|
184
418
|
code: "MCP_REMOVE_PARTIAL",
|
|
185
|
-
message: `${result.errors.length} error(es) durante remove; ver data.errors`,
|
|
419
|
+
message: `${result.errors.length} error(es) y ${result.conflicts.length} conflicto(s) durante remove; ver data.errors y data.conflicts`,
|
|
186
420
|
},
|
|
187
421
|
}
|
|
188
422
|
: {}),
|
|
189
|
-
exitCode:
|
|
423
|
+
exitCode: hasProblems ? 1 : 0,
|
|
190
424
|
};
|
|
191
425
|
}
|
|
192
426
|
async function runDoctorSub(args, ctx) {
|
|
427
|
+
const input = resolveDoctorInput(args, ctx);
|
|
428
|
+
if (!("value" in input))
|
|
429
|
+
return input;
|
|
430
|
+
const data = runMcpDoctor(ctx.env, ctx.paths, {
|
|
431
|
+
hosts: input.value.hosts,
|
|
432
|
+
connections: input.value.connections,
|
|
433
|
+
namespace: ctx.paths.namespace,
|
|
434
|
+
...input.value.scope,
|
|
435
|
+
});
|
|
436
|
+
if (input.value.probeMode !== undefined) {
|
|
437
|
+
await probeDoctorReports(data, input.value.probeMode, ctx, input.value.connections);
|
|
438
|
+
}
|
|
439
|
+
return doctorCommandResult(data);
|
|
440
|
+
}
|
|
441
|
+
function resolveDoctorInput(args, ctx) {
|
|
193
442
|
const hosts = resolveHosts(args, ctx);
|
|
194
443
|
if (!("value" in hosts))
|
|
195
444
|
return hosts;
|
|
196
445
|
const connections = resolveConnections(args, ctx, true);
|
|
197
446
|
if (!("value" in connections))
|
|
198
447
|
return connections;
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
448
|
+
const probeMode = resolveDoctorProbeMode(args);
|
|
449
|
+
if (!("value" in probeMode))
|
|
450
|
+
return probeMode;
|
|
451
|
+
const workspace = args.values.get("workspace") ?? ctx.paths.workspaceDir();
|
|
452
|
+
return {
|
|
453
|
+
value: {
|
|
454
|
+
hosts: hosts.value,
|
|
455
|
+
connections: connections.value,
|
|
456
|
+
probeMode: probeMode.value,
|
|
457
|
+
scope: args.flags.has("--global") ? { scope: "global" } : { scope: "workspace", workspace },
|
|
458
|
+
},
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
async function probeDoctorReports(data, mode, ctx, connections) {
|
|
462
|
+
const receipts = openMcpHostReceiptService(ctx.paths);
|
|
463
|
+
const catalog = new DatabaseToolCatalog({
|
|
464
|
+
paths: ctx.paths,
|
|
465
|
+
env: ctx.env,
|
|
466
|
+
postgres: new PostgresReadonlyTools(),
|
|
205
467
|
});
|
|
206
|
-
const
|
|
468
|
+
for (const report of data.reports) {
|
|
469
|
+
const connection = connections.find((candidate) => candidate.name === report.instance);
|
|
470
|
+
if (connection === undefined) {
|
|
471
|
+
report.probe = { mode, outcome: "failed", phase: "spawn", code: "CONNECTION_NOT_FOUND" };
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
await probeDoctorReport(report, data, mode, receipts, catalog, ctx, connection);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
async function probeDoctorReport(report, data, mode, receipts, catalog, ctx, connection) {
|
|
478
|
+
const readiness = doctorProbeReadiness(report, mode);
|
|
479
|
+
if (readiness !== undefined) {
|
|
480
|
+
report.probe = readiness;
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
const snapshot = doctorProbeSnapshot(report, data, mode, ctx, connection);
|
|
484
|
+
if (snapshot === undefined)
|
|
485
|
+
return;
|
|
486
|
+
const probe = await runMcpStdioProbe({
|
|
487
|
+
descriptor: {
|
|
488
|
+
command: snapshot.command,
|
|
489
|
+
args: snapshot.args,
|
|
490
|
+
...(snapshot.env === undefined ? {} : { env: snapshot.env }),
|
|
491
|
+
},
|
|
492
|
+
mode,
|
|
493
|
+
expectedTools: DATABASE_TOOL_DESCRIPTORS,
|
|
494
|
+
});
|
|
495
|
+
if (!probe.ok) {
|
|
496
|
+
report.probe = { mode: probe.mode, outcome: "failed", phase: probe.phase, code: probe.code };
|
|
497
|
+
await recordReceiptProbeBestEffort(receipts, report, snapshot, "failed", probe.phase);
|
|
498
|
+
await inspectDoctorSafety(mode, catalog, report);
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
const phase = probe.mode === "data" ? "tools/call" : "tools/list";
|
|
502
|
+
report.probe = { mode: probe.mode, outcome: "passed", phase };
|
|
503
|
+
await recordReceiptProbeBestEffort(receipts, report, snapshot, "passed", phase);
|
|
504
|
+
await inspectDoctorSafety(mode, catalog, report);
|
|
505
|
+
}
|
|
506
|
+
async function inspectDoctorSafety(mode, catalog, report) {
|
|
507
|
+
if (mode !== "data")
|
|
508
|
+
return;
|
|
509
|
+
report.safety = safetyFromRoleOutcome((await catalog.inspectRole(report.instance)).inspection);
|
|
510
|
+
}
|
|
511
|
+
function doctorProbeReadiness(report, mode) {
|
|
512
|
+
if (report.launch_mode === "path-dependent") {
|
|
513
|
+
return { mode, outcome: "failed", phase: "spawn", code: "PATH_DEPENDENT_DESCRIPTOR" };
|
|
514
|
+
}
|
|
515
|
+
if (report.status !== "ok") {
|
|
516
|
+
return { mode, outcome: "failed", phase: "spawn", code: "CONFIGURATION_NOT_READY" };
|
|
517
|
+
}
|
|
518
|
+
return undefined;
|
|
519
|
+
}
|
|
520
|
+
function doctorProbeSnapshot(report, data, mode, ctx, connection) {
|
|
521
|
+
try {
|
|
522
|
+
const entry = buildMcpEntry(connection.name, connection.dsnVar, {
|
|
523
|
+
host: report.host,
|
|
524
|
+
scope: data.scope,
|
|
525
|
+
namespace: ctx.paths.namespace,
|
|
526
|
+
});
|
|
527
|
+
const snapshot = readMcpEntry(report.host, data.scope_dir, entry.name, data.scope);
|
|
528
|
+
if (snapshot.command !== undefined &&
|
|
529
|
+
snapshot.args !== undefined &&
|
|
530
|
+
classifyMcpEntry(report.host, snapshot, entry, connection).state === "current") {
|
|
531
|
+
return {
|
|
532
|
+
command: snapshot.command,
|
|
533
|
+
args: snapshot.args,
|
|
534
|
+
...(snapshot.env === undefined ? {} : { env: snapshot.env }),
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
catch {
|
|
539
|
+
// The status remains actionable below without exposing a host config error.
|
|
540
|
+
}
|
|
541
|
+
report.probe = { mode, outcome: "failed", phase: "spawn", code: "DESCRIPTOR_UNREADABLE" };
|
|
542
|
+
return undefined;
|
|
543
|
+
}
|
|
544
|
+
function doctorCommandResult(data) {
|
|
207
545
|
const total = data.reports.length;
|
|
208
|
-
const
|
|
546
|
+
const affected = data.reports.filter(isDoctorReportAffected).length;
|
|
547
|
+
if (affected === 0)
|
|
548
|
+
return { ok: true, data, exitCode: 0 };
|
|
209
549
|
return {
|
|
210
|
-
ok:
|
|
550
|
+
ok: false,
|
|
211
551
|
data,
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
: {
|
|
552
|
+
error: {
|
|
553
|
+
code: "MCP_DOCTOR_DRIFT",
|
|
554
|
+
message: `${affected}/${total} entradas con drift, probe o seguridad pendiente (ver data.reports)`,
|
|
555
|
+
},
|
|
556
|
+
exitCode: 1,
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
function isDoctorReportAffected(report) {
|
|
560
|
+
return (report.status !== "ok" ||
|
|
561
|
+
report.probe?.outcome === "failed" ||
|
|
562
|
+
(report.safety !== undefined && report.safety.status !== "safe"));
|
|
563
|
+
}
|
|
564
|
+
function resolveDoctorProbeMode(args) {
|
|
565
|
+
if (args.flags.has("--probe")) {
|
|
566
|
+
return fail("INVALID_INPUT", "--probe requiere launch o data.", undefined, 2);
|
|
567
|
+
}
|
|
568
|
+
const value = args.values.get("probe");
|
|
569
|
+
if (value === undefined)
|
|
570
|
+
return { value: undefined };
|
|
571
|
+
if (value === "launch" || value === "data")
|
|
572
|
+
return { value };
|
|
573
|
+
return fail("INVALID_INPUT", "--probe acepta launch o data.", undefined, 2);
|
|
574
|
+
}
|
|
575
|
+
async function recordReceiptProbeBestEffort(service, report, descriptor, outcome, phase) {
|
|
576
|
+
try {
|
|
577
|
+
const identity = { host: report.host, scope: report.scope, connection: report.instance };
|
|
578
|
+
const receipt = await service.find(identity);
|
|
579
|
+
const descriptorDigest = digestMcpReceiptDescriptor(descriptor);
|
|
580
|
+
if (receipt === undefined || receipt.descriptor_digest !== descriptorDigest) {
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
await service.recordLaunchProbe({ ...identity, descriptorDigest, outcome, phase });
|
|
584
|
+
}
|
|
585
|
+
catch {
|
|
586
|
+
// Probe output remains useful even if an optional receipt update is busy.
|
|
587
|
+
// The next setup/doctor exposes reload state from the last durable receipt.
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
export function safetyFromRoleOutcome(inspection) {
|
|
591
|
+
if ("success" in inspection) {
|
|
592
|
+
return {
|
|
593
|
+
status: "blocked",
|
|
594
|
+
superuser: false,
|
|
595
|
+
write_capable: false,
|
|
596
|
+
create_role: false,
|
|
597
|
+
create_database: false,
|
|
598
|
+
unsafe_server_role: false,
|
|
599
|
+
code: inspection.code,
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
const status = inspection.superuser
|
|
603
|
+
? "blocked"
|
|
604
|
+
: inspection.canWrite ||
|
|
605
|
+
inspection.canCreateRole ||
|
|
606
|
+
inspection.canCreateDatabase ||
|
|
607
|
+
inspection.unsafeServerRole === true
|
|
608
|
+
? "warning"
|
|
609
|
+
: "safe";
|
|
610
|
+
return {
|
|
611
|
+
status,
|
|
612
|
+
superuser: inspection.superuser,
|
|
613
|
+
write_capable: inspection.canWrite,
|
|
614
|
+
create_role: inspection.canCreateRole,
|
|
615
|
+
create_database: inspection.canCreateDatabase,
|
|
616
|
+
unsafe_server_role: inspection.unsafeServerRole === true,
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
async function runMigrateSub(args, ctx) {
|
|
620
|
+
const input = resolveMigrationInput(args, ctx);
|
|
621
|
+
if (!("value" in input))
|
|
622
|
+
return input;
|
|
623
|
+
const migration = runMcpMigration(ctx.env, {
|
|
624
|
+
hosts: input.value.hosts,
|
|
625
|
+
connections: input.value.connections,
|
|
626
|
+
namespace: ctx.paths.namespace,
|
|
627
|
+
...input.value.scope,
|
|
628
|
+
...(input.value.apply ? { apply: true } : {}),
|
|
629
|
+
...(input.value.apply && input.value.scope.scope === "global"
|
|
630
|
+
? { globalApproval: true }
|
|
631
|
+
: {}),
|
|
632
|
+
});
|
|
633
|
+
if (isMcpMigrationRefusal(migration)) {
|
|
634
|
+
return fail("GLOBAL_REQUIRES_FORCE", migration.hint, undefined, 2);
|
|
635
|
+
}
|
|
636
|
+
if (input.value.apply)
|
|
637
|
+
await finalizeMigration(ctx, migration, input.value);
|
|
638
|
+
return migrationCommandResult(migration, input.value.apply);
|
|
639
|
+
}
|
|
640
|
+
function resolveMigrationInput(args, ctx) {
|
|
641
|
+
if (args.flags.has("--dry-run") && args.flags.has("--apply")) {
|
|
642
|
+
return fail("INVALID_INPUT", "mcp migrate usa preview por defecto; no combines --dry-run con --apply.", undefined, 2);
|
|
643
|
+
}
|
|
644
|
+
const apply = args.flags.has("--apply");
|
|
645
|
+
if (apply && !args.flags.has("--force")) {
|
|
646
|
+
return fail("MCP_MIGRATE_REQUIRES_FORCE", "La migración escribe configuraciones de host. Revisá el preview y repetí con --apply --force.", undefined, 2);
|
|
647
|
+
}
|
|
648
|
+
const hosts = resolveHosts(args, ctx);
|
|
649
|
+
if (!("value" in hosts))
|
|
650
|
+
return hosts;
|
|
651
|
+
const connections = resolveConnections(args, ctx, true);
|
|
652
|
+
if (!("value" in connections))
|
|
653
|
+
return connections;
|
|
654
|
+
const workspace = args.values.get("workspace") ?? ctx.paths.workspaceDir();
|
|
655
|
+
return {
|
|
656
|
+
value: {
|
|
657
|
+
apply,
|
|
658
|
+
hosts: hosts.value,
|
|
659
|
+
connections: connections.value,
|
|
660
|
+
scope: args.flags.has("--global") ? { scope: "global" } : { scope: "workspace", workspace },
|
|
661
|
+
},
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
async function finalizeMigration(ctx, data, input) {
|
|
665
|
+
const finalization = {
|
|
666
|
+
receipts: [],
|
|
667
|
+
receiptErrors: [],
|
|
668
|
+
readbackErrors: [],
|
|
669
|
+
nativeErrors: [],
|
|
670
|
+
probeTargets: [],
|
|
671
|
+
};
|
|
672
|
+
const receipts = openMcpHostReceiptService(ctx.paths);
|
|
673
|
+
for (const item of data.items) {
|
|
674
|
+
await finalizeRetiredAliasReceipts(ctx, data, input, receipts, finalization, item);
|
|
675
|
+
await finalizeMigrationItem(ctx, data, input, receipts, finalization, item);
|
|
676
|
+
}
|
|
677
|
+
attachMigrationFinalization(data, finalization);
|
|
678
|
+
if (data.scope === "global")
|
|
679
|
+
await finalizeMigrationProbes(ctx, data, finalization);
|
|
680
|
+
}
|
|
681
|
+
async function finalizeRetiredAliasReceipts(ctx, data, input, receipts, finalization, item) {
|
|
682
|
+
if (data.scope !== "global" || item.retirements === undefined)
|
|
683
|
+
return;
|
|
684
|
+
const connection = input.connections.find((candidate) => candidate.name === item.instance);
|
|
685
|
+
if (connection === undefined)
|
|
686
|
+
return;
|
|
687
|
+
for (const retirement of item.retirements) {
|
|
688
|
+
if (retirement.action !== "retire-known-legacy" || retirement.readback_state !== "missing") {
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
const aliasConnection = { ...connection, name: retirement.instance };
|
|
692
|
+
const entry = buildMcpEntry(aliasConnection.name, aliasConnection.dsnVar, {
|
|
693
|
+
host: item.host,
|
|
694
|
+
scope: data.scope,
|
|
695
|
+
namespace: ctx.paths.namespace,
|
|
696
|
+
});
|
|
697
|
+
try {
|
|
698
|
+
const snapshot = readMcpEntry(item.host, data.scope_dir, entry.name, data.scope);
|
|
699
|
+
if (classifyMcpEntry(item.host, snapshot, entry, aliasConnection).state !== "missing") {
|
|
700
|
+
finalization.readbackErrors.push({
|
|
701
|
+
host: item.host,
|
|
702
|
+
instance: retirement.instance,
|
|
703
|
+
message: "La entrada MCP legacy reapareció antes de retirar su recibo; se conserva la evidencia operativa.",
|
|
704
|
+
});
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
await receipts.remove({
|
|
708
|
+
host: item.host,
|
|
709
|
+
scope: data.scope,
|
|
710
|
+
connection: retirement.instance,
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
catch {
|
|
714
|
+
finalization.receiptErrors.push({
|
|
715
|
+
host: item.host,
|
|
716
|
+
instance: retirement.instance,
|
|
717
|
+
message: "La entrada MCP legacy se retiró, pero no se pudo retirar su recibo operativo.",
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
async function finalizeMigrationItem(ctx, data, input, receipts, finalization, item) {
|
|
723
|
+
const write = eligibleMigrationWrite(item);
|
|
724
|
+
if (write === undefined)
|
|
725
|
+
return;
|
|
726
|
+
const connection = input.connections.find((candidate) => candidate.name === item.instance);
|
|
727
|
+
if (connection === undefined)
|
|
728
|
+
return;
|
|
729
|
+
if (write.partial !== undefined) {
|
|
730
|
+
finalization.readbackErrors.push({
|
|
731
|
+
host: item.host,
|
|
732
|
+
instance: connection.name,
|
|
733
|
+
message: write.partial.message,
|
|
734
|
+
});
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
if (item.readback_state !== "current") {
|
|
738
|
+
finalization.readbackErrors.push(migrationReadbackError(item.host, connection.name));
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
if (data.scope !== "global")
|
|
742
|
+
return;
|
|
743
|
+
const current = readCurrentMigrationEntry(ctx, data, item, connection);
|
|
744
|
+
if (current === undefined) {
|
|
745
|
+
finalization.readbackErrors.push(migrationReadbackError(item.host, connection.name));
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
const registration = await registerMigrationReceipt(ctx, receipts, item, connection, current.descriptor);
|
|
749
|
+
if (registration === "skipped")
|
|
750
|
+
return;
|
|
751
|
+
if (registration === undefined) {
|
|
752
|
+
finalization.receiptErrors.push({
|
|
753
|
+
host: item.host,
|
|
754
|
+
instance: connection.name,
|
|
755
|
+
message: "No se pudo registrar el recibo MCP; revisá el estado antes de recargar el host.",
|
|
756
|
+
});
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
finalization.receipts.push({
|
|
760
|
+
host: item.host,
|
|
761
|
+
instance: connection.name,
|
|
762
|
+
descriptor_digest: registration.descriptor_digest,
|
|
763
|
+
reload_required: true,
|
|
764
|
+
});
|
|
765
|
+
finalization.probeTargets.push({
|
|
766
|
+
host: item.host,
|
|
767
|
+
instance: connection.name,
|
|
768
|
+
target: item.target,
|
|
769
|
+
entry: current.entry,
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
function eligibleMigrationWrite(item) {
|
|
773
|
+
// A retirement can fail only after qtc-* was written and reread. That
|
|
774
|
+
// partial migration still changed a live descriptor and must receive the
|
|
775
|
+
// same revocable receipt/reload signal as a fully retired alias; the item
|
|
776
|
+
// remains failed overall and the command still reports recovery work.
|
|
777
|
+
if (item.write?.action === "written" || item.write?.action === "skipped-idempotent") {
|
|
778
|
+
return item.write;
|
|
779
|
+
}
|
|
780
|
+
return undefined;
|
|
781
|
+
}
|
|
782
|
+
function readCurrentMigrationEntry(ctx, data, item, connection) {
|
|
783
|
+
try {
|
|
784
|
+
const entry = buildMcpEntry(connection.name, connection.dsnVar, {
|
|
785
|
+
host: item.host,
|
|
786
|
+
scope: data.scope,
|
|
787
|
+
namespace: ctx.paths.namespace,
|
|
788
|
+
});
|
|
789
|
+
const snapshot = readMcpEntry(item.host, data.scope_dir, entry.name, data.scope);
|
|
790
|
+
if (snapshot.command === undefined ||
|
|
791
|
+
snapshot.args === undefined ||
|
|
792
|
+
classifyMcpEntry(item.host, snapshot, entry, connection).state !== "current") {
|
|
793
|
+
return undefined;
|
|
794
|
+
}
|
|
795
|
+
return { entry, descriptor: { command: snapshot.command, args: snapshot.args } };
|
|
796
|
+
}
|
|
797
|
+
catch {
|
|
798
|
+
return undefined;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
async function registerMigrationReceipt(ctx, receipts, item, connection, descriptor) {
|
|
802
|
+
try {
|
|
803
|
+
const identity = { host: item.host, scope: "global", connection: connection.name };
|
|
804
|
+
// A qtc descriptor can be byte-identical while this migration retired a
|
|
805
|
+
// legacy alias from the same live host config. That is still a mutation:
|
|
806
|
+
// renew the receipt so reload_required cannot remain falsely cleared from
|
|
807
|
+
// an earlier host load that may still expose the retired alias.
|
|
808
|
+
if (item.write?.action === "skipped-idempotent" && item.configuration_changed !== true) {
|
|
809
|
+
const existing = await receipts.find(identity);
|
|
810
|
+
if (existing !== undefined &&
|
|
811
|
+
existing.descriptor_digest === digestMcpReceiptDescriptor(descriptor)) {
|
|
812
|
+
return "skipped";
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
return await registerPersistedMcpDescriptor(ctx.paths, {
|
|
816
|
+
host: item.host,
|
|
817
|
+
scope: "global",
|
|
818
|
+
connection: connection.name,
|
|
819
|
+
entry: descriptor,
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
catch {
|
|
823
|
+
return undefined;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
function migrationReadbackError(host, instance) {
|
|
827
|
+
return {
|
|
828
|
+
host,
|
|
829
|
+
instance,
|
|
830
|
+
message: "La entrada MCP escrita no coincidió al leerla de vuelta; no se registró recibo ni se debe recargar el host.",
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
function attachMigrationFinalization(data, state) {
|
|
834
|
+
if (state.receipts.length > 0)
|
|
835
|
+
data.receipts = state.receipts;
|
|
836
|
+
if (state.receiptErrors.length > 0)
|
|
837
|
+
data.receipt_errors = state.receiptErrors;
|
|
838
|
+
if (state.readbackErrors.length > 0)
|
|
839
|
+
data.readback_errors = state.readbackErrors;
|
|
840
|
+
if (state.nativeErrors.length > 0)
|
|
841
|
+
data.native_errors = state.nativeErrors;
|
|
842
|
+
}
|
|
843
|
+
async function finalizeMigrationProbes(ctx, data, state) {
|
|
844
|
+
const launch = await probePersistedMcpSetupEntries(ctx.paths, {
|
|
845
|
+
scope: "global",
|
|
846
|
+
scope_dir: data.scope_dir,
|
|
847
|
+
dry_run: false,
|
|
848
|
+
applied: [],
|
|
849
|
+
skipped: [],
|
|
850
|
+
conflicts: [],
|
|
851
|
+
errors: [],
|
|
852
|
+
}, state.probeTargets);
|
|
853
|
+
if (launch.probes.length > 0)
|
|
854
|
+
data.launch_probes = launch.probes;
|
|
855
|
+
if (launch.errors.length > 0) {
|
|
856
|
+
data.probe_errors = launch.errors.map((error) => ({
|
|
857
|
+
host: error.host,
|
|
858
|
+
instance: error.instance,
|
|
859
|
+
message: error.message,
|
|
860
|
+
}));
|
|
861
|
+
}
|
|
862
|
+
const native = await checkNativeMcpHosts(state.probeTargets);
|
|
863
|
+
await recordNativeMcpHostChecks({
|
|
864
|
+
paths: ctx.paths,
|
|
865
|
+
scope: "global",
|
|
866
|
+
scopeDir: data.scope_dir,
|
|
867
|
+
targets: state.probeTargets,
|
|
868
|
+
checks: native.checks,
|
|
869
|
+
});
|
|
870
|
+
if (native.checks.length > 0)
|
|
871
|
+
data.native_checks = native.checks;
|
|
872
|
+
state.nativeErrors.push(...native.errors.map((error) => ({
|
|
873
|
+
host: error.host,
|
|
874
|
+
instance: error.instance,
|
|
875
|
+
message: error.message,
|
|
876
|
+
})));
|
|
877
|
+
if (state.nativeErrors.length > 0)
|
|
878
|
+
data.native_errors = state.nativeErrors;
|
|
879
|
+
}
|
|
880
|
+
function migrationCommandResult(data, apply) {
|
|
881
|
+
const blocked = data.items.filter((item) => item.action === "blocked").length;
|
|
882
|
+
const failedWrites = data.items.filter((item) => item.write?.action === "conflict" || item.action === "failed").length;
|
|
883
|
+
const receiptFailures = data.receipt_errors?.length ?? 0;
|
|
884
|
+
const readbackFailures = data.readback_errors?.length ?? 0;
|
|
885
|
+
const probeFailures = data.probe_errors?.length ?? 0;
|
|
886
|
+
const nativeFailures = data.native_errors?.length ?? 0;
|
|
887
|
+
const problems = blocked + failedWrites + receiptFailures + readbackFailures + probeFailures + nativeFailures;
|
|
888
|
+
const hasProblems = apply && problems > 0;
|
|
889
|
+
return {
|
|
890
|
+
ok: !hasProblems,
|
|
891
|
+
data,
|
|
892
|
+
...(hasProblems
|
|
893
|
+
? {
|
|
215
894
|
error: {
|
|
216
|
-
code: "
|
|
217
|
-
message: `${
|
|
895
|
+
code: "MCP_MIGRATE_PARTIAL",
|
|
896
|
+
message: `${problems} entrada(s) no se migraron, no dejaron recibo, no pasaron readback, no fueron launchable o no pudieron verificarse con su host.`,
|
|
218
897
|
},
|
|
219
|
-
}
|
|
220
|
-
|
|
898
|
+
}
|
|
899
|
+
: {}),
|
|
900
|
+
exitCode: hasProblems ? 1 : 0,
|
|
221
901
|
};
|
|
222
902
|
}
|
|
903
|
+
function isMcpMigrationRefusal(value) {
|
|
904
|
+
return "ok" in value && value.ok === false;
|
|
905
|
+
}
|
|
223
906
|
/** Exported for tests: host-harness detection must cover every file-writing host. */
|
|
224
907
|
export function resolveHosts(args, ctx) {
|
|
225
908
|
const flag = args.values.get("host");
|
|
@@ -257,7 +940,7 @@ function resolveConnections(args, ctx, allowAll) {
|
|
|
257
940
|
return fail("INVALID_INPUT", "--all-connections no recibe valor.");
|
|
258
941
|
}
|
|
259
942
|
if (allConnections && !allowAll) {
|
|
260
|
-
return fail("MCP_CONNECTION_SELECTION_CONFLICT", "mcp
|
|
943
|
+
return fail("MCP_CONNECTION_SELECTION_CONFLICT", "mcp serve-db ejecuta una sola conexión; usá --instance <nombre>.");
|
|
261
944
|
}
|
|
262
945
|
const instance = args.values.get("instance");
|
|
263
946
|
const selection = resolveMcpConnectionSelection(ctx.paths, {
|
|
@@ -268,11 +951,4 @@ function resolveConnections(args, ctx, allowAll) {
|
|
|
268
951
|
return fail(selection.code, selection.message);
|
|
269
952
|
return { value: selection.connections };
|
|
270
953
|
}
|
|
271
|
-
function clampExit(code) {
|
|
272
|
-
if (code === 0)
|
|
273
|
-
return 0;
|
|
274
|
-
if (code === 2)
|
|
275
|
-
return 2;
|
|
276
|
-
return 1;
|
|
277
|
-
}
|
|
278
954
|
//# sourceMappingURL=mcp.js.map
|