@tacuchi/agent-workflow-cli 5.8.0 → 5.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/application/checkpoint/markdown.js +1 -1
- package/dist/application/checkpoint/markdown.js.map +1 -1
- package/dist/application/checkpoint-write-service.js +1 -1
- package/dist/application/checkpoint-write-service.js.map +1 -1
- package/dist/application/history-table.js +1 -1
- package/dist/application/history-table.js.map +1 -1
- package/dist/application/hook-branch-check.d.ts +1 -1
- package/dist/application/mcp-connections-service.d.ts +37 -0
- package/dist/application/mcp-connections-service.d.ts.map +1 -0
- package/dist/application/mcp-connections-service.js +97 -0
- package/dist/application/mcp-connections-service.js.map +1 -0
- package/dist/application/mcp-dbhub-launcher.d.ts +1 -0
- package/dist/application/mcp-dbhub-launcher.d.ts.map +1 -1
- package/dist/application/mcp-dbhub-launcher.js +15 -3
- package/dist/application/mcp-dbhub-launcher.js.map +1 -1
- package/dist/application/mcp-doctor-service.d.ts +1 -0
- package/dist/application/mcp-doctor-service.d.ts.map +1 -1
- package/dist/application/mcp-doctor-service.js +6 -6
- package/dist/application/mcp-doctor-service.js.map +1 -1
- package/dist/application/mcp-setup-service.d.ts +1 -0
- package/dist/application/mcp-setup-service.d.ts.map +1 -1
- package/dist/application/mcp-setup-service.js +4 -4
- package/dist/application/mcp-setup-service.js.map +1 -1
- package/dist/application/multiroot-service.js +2 -2
- package/dist/application/multiroot-service.js.map +1 -1
- package/dist/application/paths-service.d.ts +1 -0
- package/dist/application/paths-service.d.ts.map +1 -1
- package/dist/application/paths-service.js +3 -0
- package/dist/application/paths-service.js.map +1 -1
- package/dist/application/phase-next-service.js +1 -1
- package/dist/application/phase-next-service.js.map +1 -1
- package/dist/application/plugin-doctor/manifests.d.ts +1 -1
- package/dist/application/plugin-doctor/manifests.d.ts.map +1 -1
- package/dist/application/plugin-doctor/manifests.js +9 -9
- package/dist/application/plugin-doctor/manifests.js.map +1 -1
- package/dist/application/release-data/artifacts.js +3 -3
- package/dist/application/release-data/artifacts.js.map +1 -1
- package/dist/application/release-data/sessions.js +3 -3
- package/dist/application/release-data/sessions.js.map +1 -1
- package/dist/application/self/bootstrap.js +4 -4
- package/dist/application/self/bootstrap.js.map +1 -1
- package/dist/application/self/mcp-config.d.ts +23 -15
- package/dist/application/self/mcp-config.d.ts.map +1 -1
- package/dist/application/self/mcp-config.js +392 -148
- package/dist/application/self/mcp-config.js.map +1 -1
- package/dist/application/session-create-service.js +8 -8
- package/dist/application/session-create-service.js.map +1 -1
- package/dist/application/sessions-service.d.ts.map +1 -1
- package/dist/application/sessions-service.js +2 -2
- package/dist/application/sessions-service.js.map +1 -1
- package/dist/application/upgrade-hub-mode-service.js +1 -1
- package/dist/application/upgrade-hub-mode-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/workspace-mode-service.js +1 -1
- package/dist/application/workspace-mode-service.js.map +1 -1
- package/dist/cli/commands/hub-init.js +1 -1
- package/dist/cli/commands/hub-init.js.map +1 -1
- package/dist/cli/commands/mcp.d.ts.map +1 -1
- package/dist/cli/commands/mcp.js +40 -2
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/project-md-upsert.js +1 -1
- package/dist/cli/commands/project-md-upsert.js.map +1 -1
- package/dist/cli/commands/self.js +1 -1
- package/dist/cli/commands/self.js.map +1 -1
- package/dist/cli/commands/session-close.js +1 -1
- package/dist/cli/commands/session-close.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/session-resume.js +1 -1
- package/dist/cli/commands/session-resume.js.map +1 -1
- package/dist/cli/commands/sources.js +1 -1
- package/dist/cli/commands/sources.js.map +1 -1
- package/dist/cli/interactive-menu.js +1 -1
- package/dist/cli/interactive-menu.js.map +1 -1
- package/dist/cli/main.js +3 -3
- package/dist/cli/main.js.map +1 -1
- package/dist/domain/mcp-entry.d.ts +10 -2
- package/dist/domain/mcp-entry.d.ts.map +1 -1
- package/dist/domain/mcp-entry.js +28 -9
- package/dist/domain/mcp-entry.js.map +1 -1
- package/dist/runtime/types.d.ts +1 -1
- package/dist/runtime/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/skills/agent-workflow/MANUAL-FUNCIONAL.md +4 -4
- package/skills/agent-workflow/MANUAL-TECNICO.md +4 -4
- package/skills/agent-workflow/SKILL.md +3 -3
- package/skills/agent-workflow/docs/TEST-PLAN.md +16 -16
- package/skills/agent-workflow/references/dev-only.md +1 -1
- package/skills/agent-workflow/references/doctor.md +1 -1
- package/skills/agent-workflow/references/history.md +1 -1
- package/skills/agent-workflow/references/hooks.md +2 -2
- package/skills/agent-workflow/references/mcp.md +8 -5
- package/skills/agent-workflow/references/orchestration.md +2 -2
- package/skills/agent-workflow/references/self.md +1 -1
- package/skills/agent-workflow/references/session-mgmt.md +1 -1
|
@@ -1,132 +1,216 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { buildMcpEntry, mcpEntryNameFor, normalizeDsnVarName, validateDsnVarName, validateMcpInstance, } from "../../domain/mcp-entry.js";
|
|
3
|
+
import { readBootstrapDsn } from "../dsn-reader-service.js";
|
|
4
|
+
import { deleteMcpConnection, readMcpConnections, upsertMcpConnection, } from "../mcp-connections-service.js";
|
|
4
5
|
import { runMcpDoctor } from "../mcp-doctor-service.js";
|
|
6
|
+
import { readMcpEntry } from "../mcp-host-reader.js";
|
|
5
7
|
import { runMcpRemove } from "../mcp-remove-service.js";
|
|
6
8
|
import { runMcpSetup } from "../mcp-setup-service.js";
|
|
7
9
|
export async function selfMcpConfig(args, ctx, prompts) {
|
|
8
10
|
const prompt = prompts ?? (await loadPrompts());
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
+
const resolved = await resolveAction(args, prompt);
|
|
12
|
+
switch (resolved.action) {
|
|
13
|
+
case "list":
|
|
14
|
+
return resolved.fromArgs ? listConnections(ctx) : listConnectionsMenu(args, ctx, prompt);
|
|
15
|
+
case "use-env":
|
|
16
|
+
return useExistingDsnVar(args, ctx, prompt);
|
|
17
|
+
case "create-env":
|
|
18
|
+
return createDsnEnvHelp(args, prompt);
|
|
19
|
+
case "install-claude":
|
|
20
|
+
return runConnectionAction(args, ctx, prompt, resolved.action);
|
|
21
|
+
case "install-codex":
|
|
22
|
+
return runConnectionAction(args, ctx, prompt, resolved.action);
|
|
23
|
+
case "doctor":
|
|
24
|
+
return runConnectionAction(args, ctx, prompt, resolved.action);
|
|
25
|
+
case "remove":
|
|
26
|
+
return runConnectionAction(args, ctx, prompt, resolved.action);
|
|
27
|
+
case "cancel":
|
|
28
|
+
return {
|
|
29
|
+
ok: true,
|
|
30
|
+
data: { action: "cancel", connection: null, summary: "Operación cancelada." },
|
|
31
|
+
exitCode: 0,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function listConnections(ctx) {
|
|
36
|
+
const connections = connectionViews(ctx);
|
|
37
|
+
return {
|
|
38
|
+
ok: true,
|
|
39
|
+
data: {
|
|
40
|
+
action: "list",
|
|
41
|
+
connection: null,
|
|
42
|
+
connections,
|
|
43
|
+
table: formatConnectionsTable(connections),
|
|
44
|
+
summary: connections.length > 0
|
|
45
|
+
? `${connections.length} conexión(es) MCP registradas.`
|
|
46
|
+
: "No hay conexiones MCP registradas.",
|
|
47
|
+
},
|
|
48
|
+
exitCode: 0,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async function listConnectionsMenu(args, ctx, prompts) {
|
|
52
|
+
const connections = connectionViews(ctx);
|
|
53
|
+
if (connections.length === 0) {
|
|
11
54
|
return {
|
|
12
55
|
ok: true,
|
|
13
|
-
data: {
|
|
56
|
+
data: {
|
|
57
|
+
action: "list",
|
|
58
|
+
connection: null,
|
|
59
|
+
connections,
|
|
60
|
+
table: formatConnectionsTable(connections),
|
|
61
|
+
summary: "No hay conexiones MCP registradas. Primero usa una DSN env var existente.",
|
|
62
|
+
},
|
|
14
63
|
exitCode: 0,
|
|
15
64
|
};
|
|
16
65
|
}
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
66
|
+
const action = await prompts.select({
|
|
67
|
+
message: formatConnectionsTable(connections),
|
|
68
|
+
default: "install-claude",
|
|
69
|
+
choices: [
|
|
70
|
+
{ name: "Instalar/Actualizar en Claude Code", value: "install-claude" },
|
|
71
|
+
{ name: "Instalar/Actualizar en Codex", value: "install-codex" },
|
|
72
|
+
{ name: "Diagnosticar", value: "doctor" },
|
|
73
|
+
{ name: "Eliminar", value: "remove" },
|
|
74
|
+
{ name: "Cancelar", value: "cancel" },
|
|
75
|
+
],
|
|
76
|
+
});
|
|
77
|
+
if (action === "cancel") {
|
|
78
|
+
return {
|
|
79
|
+
ok: true,
|
|
80
|
+
data: {
|
|
81
|
+
action,
|
|
82
|
+
connection: null,
|
|
83
|
+
connections,
|
|
84
|
+
table: formatConnectionsTable(connections),
|
|
85
|
+
summary: "Operación cancelada.",
|
|
86
|
+
},
|
|
87
|
+
exitCode: 0,
|
|
88
|
+
};
|
|
29
89
|
}
|
|
90
|
+
return runConnectionAction(args, ctx, prompts, action);
|
|
30
91
|
}
|
|
31
|
-
function
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
: {
|
|
49
|
-
...writeDsnValue(ctx.paths, { key: request.connection.dsn_key, value: dsn }),
|
|
50
|
-
dry_run: false,
|
|
92
|
+
async function useExistingDsnVar(args, ctx, prompts) {
|
|
93
|
+
const name = await resolveConnectionName(args, prompts);
|
|
94
|
+
const dsnVar = await resolveDsnVar(args, prompts, name);
|
|
95
|
+
if (!isDsnVisible(ctx, dsnVar)) {
|
|
96
|
+
return {
|
|
97
|
+
ok: false,
|
|
98
|
+
error: {
|
|
99
|
+
code: "DSN_VAR_NOT_VISIBLE",
|
|
100
|
+
message: `${dsnVar} no está visible en el entorno actual ni en ${ctx.paths.userDsnFile()}.`,
|
|
101
|
+
},
|
|
102
|
+
data: {
|
|
103
|
+
action: "use-env",
|
|
104
|
+
connection: null,
|
|
105
|
+
env_help: buildEnvHelp(dsnVar, name),
|
|
106
|
+
summary: `Exporta ${dsnVar} y vuelve a registrar la conexión.`,
|
|
107
|
+
},
|
|
108
|
+
exitCode: 1,
|
|
51
109
|
};
|
|
52
|
-
const setup = setupConnection(request, ctx);
|
|
53
|
-
if ("ok" in setup) {
|
|
54
|
-
return refusal(request.action, request.connection, setup.hint, setup.exitCode);
|
|
55
110
|
}
|
|
56
|
-
const
|
|
111
|
+
const write = upsertMcpConnection(ctx.paths, { name, dsnVar });
|
|
112
|
+
const connection = connectionView(ctx, {
|
|
113
|
+
name: write.connection.name,
|
|
114
|
+
dsnVar: write.connection.dsnVar,
|
|
115
|
+
dsnPresent: true,
|
|
116
|
+
});
|
|
57
117
|
return {
|
|
58
|
-
ok:
|
|
118
|
+
ok: true,
|
|
59
119
|
data: {
|
|
60
|
-
action:
|
|
61
|
-
connection
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
summary: `
|
|
120
|
+
action: "use-env",
|
|
121
|
+
connection,
|
|
122
|
+
connections: connectionViews(ctx),
|
|
123
|
+
table: formatConnectionsTable(connectionViews(ctx)),
|
|
124
|
+
registry: { path: write.path, changed: true },
|
|
125
|
+
summary: `Conexión '${connection.nombre}' registrada con ${connection.dsn_var}.`,
|
|
66
126
|
},
|
|
67
|
-
|
|
68
|
-
? {
|
|
69
|
-
error: {
|
|
70
|
-
code: "MCP_CONFIG_PARTIAL",
|
|
71
|
-
message: `${setup.errors.length} error(es) durante setup; ver data.setup.errors`,
|
|
72
|
-
},
|
|
73
|
-
}
|
|
74
|
-
: {}),
|
|
75
|
-
exitCode: setup.errors.length > 0 ? 1 : 0,
|
|
127
|
+
exitCode: 0,
|
|
76
128
|
};
|
|
77
129
|
}
|
|
78
|
-
function
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
return refusal(request.action, request.connection, setup.hint, setup.exitCode);
|
|
82
|
-
}
|
|
83
|
-
const doctor = doctorConnection(request, ctx);
|
|
130
|
+
async function createDsnEnvHelp(args, prompts) {
|
|
131
|
+
const name = await resolveConnectionName(args, prompts);
|
|
132
|
+
const dsnVar = await resolveDsnVar(args, prompts, name);
|
|
84
133
|
return {
|
|
85
|
-
ok:
|
|
134
|
+
ok: true,
|
|
86
135
|
data: {
|
|
87
|
-
action:
|
|
88
|
-
connection:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
summary: `Instalación MCP aplicada para ${request.connection.server_name}.`,
|
|
136
|
+
action: "create-env",
|
|
137
|
+
connection: null,
|
|
138
|
+
env_help: buildEnvHelp(dsnVar, name),
|
|
139
|
+
summary: `Comandos sugeridos para crear ${dsnVar}. No se instaló ningún host.`,
|
|
92
140
|
},
|
|
93
|
-
exitCode:
|
|
141
|
+
exitCode: 0,
|
|
94
142
|
};
|
|
95
143
|
}
|
|
96
|
-
function
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
144
|
+
async function runConnectionAction(args, ctx, prompts, action) {
|
|
145
|
+
const connection = await resolveRegisteredConnection(args, ctx, prompts);
|
|
146
|
+
if (connection === null) {
|
|
147
|
+
return {
|
|
148
|
+
ok: false,
|
|
149
|
+
error: {
|
|
150
|
+
code: "NO_MCP_CONNECTIONS",
|
|
151
|
+
message: "No hay conexiones MCP registradas. Usa primero 'self mcp use-env'.",
|
|
152
|
+
},
|
|
153
|
+
data: { action, connection: null, summary: "No hay conexiones MCP registradas." },
|
|
154
|
+
exitCode: 1,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
switch (action) {
|
|
158
|
+
case "install-claude":
|
|
159
|
+
return installConnection(args, ctx, connection, "claude");
|
|
160
|
+
case "install-codex":
|
|
161
|
+
return installConnection(args, ctx, connection, "codex");
|
|
162
|
+
case "doctor":
|
|
163
|
+
return doctorConnection(ctx, connection);
|
|
164
|
+
case "remove":
|
|
165
|
+
return removeConnection(args, ctx, connection);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function installConnection(args, ctx, connection, host) {
|
|
169
|
+
const setup = runMcpSetup(ctx.env, {
|
|
170
|
+
hosts: [host],
|
|
171
|
+
instances: [connection.name],
|
|
100
172
|
scope: "workspace",
|
|
101
|
-
|
|
173
|
+
dsnVars: { [connection.name]: connection.dsnVar },
|
|
174
|
+
dryRun: args.flags.has("--dry-run"),
|
|
102
175
|
});
|
|
103
|
-
if ("ok" in
|
|
104
|
-
return refusal(
|
|
105
|
-
|
|
106
|
-
const
|
|
176
|
+
if ("ok" in setup)
|
|
177
|
+
return refusal(hostAction(host), connectionView(ctx, connection), setup.hint);
|
|
178
|
+
const doctor = runDoctor(ctx, connection, [host]);
|
|
179
|
+
const hasErrors = setup.errors.length > 0;
|
|
107
180
|
return {
|
|
108
|
-
ok:
|
|
181
|
+
ok: !hasErrors,
|
|
109
182
|
data: {
|
|
110
|
-
action:
|
|
111
|
-
connection:
|
|
112
|
-
|
|
183
|
+
action: hostAction(host),
|
|
184
|
+
connection: connectionView(ctx, connection),
|
|
185
|
+
connections: connectionViews(ctx),
|
|
186
|
+
table: formatConnectionsTable(connectionViews(ctx)),
|
|
187
|
+
setup,
|
|
113
188
|
doctor,
|
|
114
|
-
summary: `
|
|
189
|
+
summary: `Conexión '${connection.name}' instalada/actualizada en ${hostLabel(host)}.`,
|
|
115
190
|
},
|
|
116
|
-
|
|
191
|
+
...(hasErrors
|
|
192
|
+
? {
|
|
193
|
+
error: {
|
|
194
|
+
code: "MCP_SETUP_PARTIAL",
|
|
195
|
+
message: `${setup.errors.length} error(es) durante setup; ver data.setup.errors`,
|
|
196
|
+
},
|
|
197
|
+
}
|
|
198
|
+
: {}),
|
|
199
|
+
exitCode: hasErrors ? 1 : 0,
|
|
117
200
|
};
|
|
118
201
|
}
|
|
119
|
-
function
|
|
120
|
-
const doctor =
|
|
202
|
+
function doctorConnection(ctx, connection) {
|
|
203
|
+
const doctor = runDoctor(ctx, connection, ["claude", "codex"]);
|
|
204
|
+
const allOk = doctor.summary.ok === doctor.reports.length;
|
|
121
205
|
return {
|
|
122
|
-
ok:
|
|
206
|
+
ok: allOk,
|
|
123
207
|
data: {
|
|
124
|
-
action:
|
|
125
|
-
connection:
|
|
208
|
+
action: "doctor",
|
|
209
|
+
connection: connectionView(ctx, connection),
|
|
126
210
|
doctor,
|
|
127
|
-
summary: `Diagnóstico MCP ejecutado para ${
|
|
211
|
+
summary: `Diagnóstico MCP ejecutado para '${connection.name}'.`,
|
|
128
212
|
},
|
|
129
|
-
...(
|
|
213
|
+
...(allOk
|
|
130
214
|
? {}
|
|
131
215
|
: {
|
|
132
216
|
error: {
|
|
@@ -134,50 +218,122 @@ function runDoctorAction(request, ctx) {
|
|
|
134
218
|
message: `${doctor.reports.length - doctor.summary.ok}/${doctor.reports.length} entradas con drift`,
|
|
135
219
|
},
|
|
136
220
|
}),
|
|
137
|
-
exitCode:
|
|
221
|
+
exitCode: allOk ? 0 : 1,
|
|
138
222
|
};
|
|
139
223
|
}
|
|
140
|
-
function
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
224
|
+
function removeConnection(args, ctx, connection) {
|
|
225
|
+
const dryRun = args.flags.has("--dry-run");
|
|
226
|
+
const remove = runMcpRemove(ctx.env, {
|
|
227
|
+
hosts: ["claude", "codex"],
|
|
228
|
+
instances: [connection.name],
|
|
144
229
|
scope: "workspace",
|
|
145
|
-
dryRun
|
|
230
|
+
dryRun,
|
|
146
231
|
});
|
|
232
|
+
if ("ok" in remove)
|
|
233
|
+
return refusal("remove", connectionView(ctx, connection), remove.hint);
|
|
234
|
+
const hasErrors = remove.errors.length > 0;
|
|
235
|
+
const deleted = !dryRun && !hasErrors ? deleteMcpConnection(ctx.paths, connection) : null;
|
|
236
|
+
return {
|
|
237
|
+
ok: !hasErrors,
|
|
238
|
+
data: {
|
|
239
|
+
action: "remove",
|
|
240
|
+
connection: connectionView(ctx, connection),
|
|
241
|
+
connections: connectionViews(ctx),
|
|
242
|
+
table: formatConnectionsTable(connectionViews(ctx)),
|
|
243
|
+
remove,
|
|
244
|
+
...(deleted ? { registry: { path: deleted.path, changed: deleted.removed } } : {}),
|
|
245
|
+
summary: dryRun
|
|
246
|
+
? `Previsualización de eliminación para '${connection.name}'.`
|
|
247
|
+
: `Conexión '${connection.name}' eliminada de Claude Code, Codex y del registro local.`,
|
|
248
|
+
},
|
|
249
|
+
...(hasErrors
|
|
250
|
+
? {
|
|
251
|
+
error: {
|
|
252
|
+
code: "MCP_REMOVE_PARTIAL",
|
|
253
|
+
message: `${remove.errors.length} error(es) durante remove; ver data.remove.errors`,
|
|
254
|
+
},
|
|
255
|
+
}
|
|
256
|
+
: {}),
|
|
257
|
+
exitCode: hasErrors ? 1 : 0,
|
|
258
|
+
};
|
|
147
259
|
}
|
|
148
|
-
function
|
|
260
|
+
function runDoctor(ctx, connection, hosts) {
|
|
149
261
|
return runMcpDoctor(ctx.env, ctx.paths, {
|
|
150
|
-
hosts
|
|
151
|
-
instances: [
|
|
262
|
+
hosts,
|
|
263
|
+
instances: [connection.name],
|
|
152
264
|
scope: "workspace",
|
|
265
|
+
dsnVars: { [connection.name]: connection.dsnVar },
|
|
153
266
|
});
|
|
154
267
|
}
|
|
155
|
-
|
|
156
|
-
|
|
268
|
+
function connectionViews(ctx) {
|
|
269
|
+
return readMcpConnections(ctx.paths, ctx.env).map((connection) => connectionView(ctx, connection));
|
|
270
|
+
}
|
|
271
|
+
function connectionView(ctx, connection) {
|
|
157
272
|
return {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
273
|
+
nombre: connection.name,
|
|
274
|
+
server_name: mcpEntryNameFor(connection.name),
|
|
275
|
+
dsn_var: connection.dsnVar,
|
|
276
|
+
dsn_visible: isDsnVisible(ctx, connection.dsnVar),
|
|
277
|
+
instalado: {
|
|
278
|
+
claude_code: installStatus(ctx, connection, "claude"),
|
|
279
|
+
codex: installStatus(ctx, connection, "codex"),
|
|
280
|
+
},
|
|
161
281
|
};
|
|
162
282
|
}
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
283
|
+
function installStatus(ctx, connection, host) {
|
|
284
|
+
const entry = buildMcpEntry(connection.name, connection.dsnVar);
|
|
285
|
+
const snapshot = readMcpEntry(host, resolve(ctx.env.cwd()), entry.name);
|
|
286
|
+
if (!snapshot.exists)
|
|
287
|
+
return "no";
|
|
288
|
+
if (snapshot.command !== entry.command)
|
|
289
|
+
return "drift";
|
|
290
|
+
if (!arraysEqual(snapshot.args ?? [], entry.args))
|
|
291
|
+
return "drift";
|
|
292
|
+
if (!recordsEqual(snapshot.env ?? {}, entry.env))
|
|
293
|
+
return "drift";
|
|
294
|
+
return "si";
|
|
295
|
+
}
|
|
296
|
+
function isDsnVisible(ctx, dsnVar) {
|
|
297
|
+
if (ctx.env.get(dsnVar))
|
|
298
|
+
return true;
|
|
299
|
+
const dsn = readBootstrapDsn(ctx.paths);
|
|
300
|
+
return Boolean(dsn.values[dsnVar]);
|
|
301
|
+
}
|
|
302
|
+
function formatConnectionsTable(connections) {
|
|
303
|
+
const header = "| nombre | DSN var (nombre) | Instalado en Claude Code | Instalado en Codex |";
|
|
304
|
+
const separator = "|---|---|---|---|";
|
|
305
|
+
if (connections.length === 0)
|
|
306
|
+
return `${header}\n${separator}`;
|
|
307
|
+
const rows = connections.map((item) => `| ${item.nombre} | ${item.dsn_var} | ${item.instalado.claude_code} | ${item.instalado.codex} |`);
|
|
308
|
+
return [header, separator, ...rows].join("\n");
|
|
309
|
+
}
|
|
310
|
+
async function resolveRegisteredConnection(args, ctx, prompts) {
|
|
311
|
+
const connections = readMcpConnections(ctx.paths, ctx.env);
|
|
312
|
+
if (connections.length === 0)
|
|
313
|
+
return null;
|
|
314
|
+
const raw = args.values.get("name") ?? args.values.get("instance");
|
|
315
|
+
if (raw !== undefined) {
|
|
316
|
+
const validation = validateMcpInstance(raw);
|
|
317
|
+
if (!validation.ok)
|
|
318
|
+
throw new Error(validation.error);
|
|
319
|
+
const found = connections.find((item) => item.name === validation.value);
|
|
320
|
+
if (found === undefined) {
|
|
321
|
+
throw new Error(`conexión MCP no registrada: '${validation.value}'`);
|
|
322
|
+
}
|
|
323
|
+
return found;
|
|
324
|
+
}
|
|
325
|
+
const selected = await prompts.select({
|
|
326
|
+
message: "Conexión",
|
|
327
|
+
default: connections[0]?.name ?? "cert",
|
|
328
|
+
choices: connections.map((item) => ({
|
|
329
|
+
name: `${item.name} (${item.dsnVar})`,
|
|
330
|
+
value: item.name,
|
|
331
|
+
})),
|
|
177
332
|
});
|
|
333
|
+
return connections.find((item) => item.name === selected) ?? null;
|
|
178
334
|
}
|
|
179
|
-
async function
|
|
180
|
-
const raw = args.values.get("
|
|
335
|
+
async function resolveConnectionName(args, prompts) {
|
|
336
|
+
const raw = args.values.get("name") ?? args.values.get("instance");
|
|
181
337
|
if (raw !== undefined) {
|
|
182
338
|
const validation = validateMcpInstance(raw);
|
|
183
339
|
if (!validation.ok)
|
|
@@ -185,7 +341,7 @@ async function resolveInstance(args, prompts) {
|
|
|
185
341
|
return validation.value;
|
|
186
342
|
}
|
|
187
343
|
const value = await prompts.input({
|
|
188
|
-
message: "Nombre de conexión
|
|
344
|
+
message: "Nombre de conexión",
|
|
189
345
|
default: "cert",
|
|
190
346
|
validate: (input) => {
|
|
191
347
|
const validation = validateMcpInstance(input);
|
|
@@ -197,41 +353,129 @@ async function resolveInstance(args, prompts) {
|
|
|
197
353
|
throw new Error(validation.error);
|
|
198
354
|
return validation.value;
|
|
199
355
|
}
|
|
200
|
-
async function
|
|
201
|
-
const raw = args.values.get("
|
|
202
|
-
if (raw !== undefined)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
356
|
+
async function resolveDsnVar(args, prompts, name) {
|
|
357
|
+
const raw = args.values.get("dsn-var") ?? args.values.get("var");
|
|
358
|
+
if (raw !== undefined) {
|
|
359
|
+
const validation = validateDsnVarName(raw);
|
|
360
|
+
if (!validation.ok)
|
|
361
|
+
throw new Error(validation.error);
|
|
362
|
+
return validation.value;
|
|
363
|
+
}
|
|
364
|
+
const value = await prompts.input({
|
|
365
|
+
message: "Nombre de variable DSN",
|
|
366
|
+
default: defaultDsnVar(name),
|
|
367
|
+
validate: (input) => {
|
|
368
|
+
const validation = validateDsnVarName(input);
|
|
369
|
+
return validation.ok ? true : validation.error;
|
|
370
|
+
},
|
|
212
371
|
});
|
|
213
|
-
|
|
372
|
+
const validation = validateDsnVarName(value);
|
|
373
|
+
if (!validation.ok)
|
|
374
|
+
throw new Error(validation.error);
|
|
375
|
+
return validation.value;
|
|
376
|
+
}
|
|
377
|
+
function defaultDsnVar(name) {
|
|
378
|
+
const normalized = name.toUpperCase().replace(/-/g, "_");
|
|
379
|
+
if (normalized === "CERT")
|
|
380
|
+
return "DB_CERT_DSN";
|
|
381
|
+
if (normalized === "PROD")
|
|
382
|
+
return "DB_PROD_DSN";
|
|
383
|
+
return `DB_${normalized}_DSN`;
|
|
384
|
+
}
|
|
385
|
+
function buildEnvHelp(dsnVar, name) {
|
|
386
|
+
const variable = normalizeDsnVarName(dsnVar);
|
|
387
|
+
const platform = process.platform === "win32" ? "windows" : process.platform;
|
|
388
|
+
const commands = process.platform === "win32"
|
|
389
|
+
? [
|
|
390
|
+
`$env:${variable} = "<DSN>"`,
|
|
391
|
+
`[Environment]::SetEnvironmentVariable("${variable}", "<DSN>", "User")`,
|
|
392
|
+
]
|
|
393
|
+
: [
|
|
394
|
+
`export ${variable}='<DSN>'`,
|
|
395
|
+
`printf '%s\\n' "export ${variable}='<DSN>'" >> ${shellStartupFile()}`,
|
|
396
|
+
];
|
|
397
|
+
return {
|
|
398
|
+
platform,
|
|
399
|
+
variable,
|
|
400
|
+
commands,
|
|
401
|
+
next_step: `agent-workflow self mcp use-env --name ${name} --dsn-var ${variable}`,
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function shellStartupFile() {
|
|
405
|
+
const shell = process.env.SHELL ?? "";
|
|
406
|
+
if (shell.endsWith("/zsh"))
|
|
407
|
+
return "~/.zshenv";
|
|
408
|
+
if (shell.endsWith("/bash"))
|
|
409
|
+
return "~/.bashrc";
|
|
410
|
+
return "~/.profile";
|
|
411
|
+
}
|
|
412
|
+
async function loadPrompts() {
|
|
413
|
+
const prompts = await import("@inquirer/prompts");
|
|
414
|
+
return {
|
|
415
|
+
select: prompts.select,
|
|
416
|
+
input: prompts.input,
|
|
417
|
+
};
|
|
214
418
|
}
|
|
215
|
-
function
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
419
|
+
async function resolveAction(args, prompts) {
|
|
420
|
+
const raw = args.values.get("action") ?? args.rest[1];
|
|
421
|
+
if (isAction(raw))
|
|
422
|
+
return { action: raw, fromArgs: true };
|
|
423
|
+
return {
|
|
424
|
+
action: await prompts.select({
|
|
425
|
+
message: "Configurar MCP database (dbhub)",
|
|
426
|
+
default: "list",
|
|
427
|
+
choices: [
|
|
428
|
+
{ name: "Listar conexiones existentes", value: "list" },
|
|
429
|
+
{ name: "Utilizar DSN env var existente", value: "use-env" },
|
|
430
|
+
{ name: "Crear DSN env var", value: "create-env" },
|
|
431
|
+
{ name: "Cancelar", value: "cancel" },
|
|
432
|
+
],
|
|
433
|
+
}),
|
|
434
|
+
fromArgs: false,
|
|
435
|
+
};
|
|
221
436
|
}
|
|
222
437
|
function isAction(value) {
|
|
223
|
-
return (value === "
|
|
224
|
-
value === "
|
|
225
|
-
value === "
|
|
438
|
+
return (value === "list" ||
|
|
439
|
+
value === "use-env" ||
|
|
440
|
+
value === "create-env" ||
|
|
441
|
+
value === "install-claude" ||
|
|
442
|
+
value === "install-codex" ||
|
|
226
443
|
value === "doctor" ||
|
|
444
|
+
value === "remove" ||
|
|
227
445
|
value === "cancel");
|
|
228
446
|
}
|
|
229
|
-
function
|
|
447
|
+
function hostAction(host) {
|
|
448
|
+
return host === "claude" ? "install-claude" : "install-codex";
|
|
449
|
+
}
|
|
450
|
+
function hostLabel(host) {
|
|
451
|
+
return host === "claude" ? "Claude Code" : "Codex";
|
|
452
|
+
}
|
|
453
|
+
function refusal(action, connection, message) {
|
|
230
454
|
return {
|
|
231
455
|
ok: false,
|
|
232
456
|
error: { code: "GLOBAL_REQUIRES_FORCE", message },
|
|
233
457
|
data: { action, connection, summary: message },
|
|
234
|
-
exitCode,
|
|
458
|
+
exitCode: 2,
|
|
235
459
|
};
|
|
236
460
|
}
|
|
461
|
+
function arraysEqual(a, b) {
|
|
462
|
+
if (a.length !== b.length)
|
|
463
|
+
return false;
|
|
464
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
465
|
+
if (a[i] !== b[i])
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
return true;
|
|
469
|
+
}
|
|
470
|
+
function recordsEqual(a, b) {
|
|
471
|
+
const keysA = Object.keys(a).sort();
|
|
472
|
+
const keysB = Object.keys(b).sort();
|
|
473
|
+
if (!arraysEqual(keysA, keysB))
|
|
474
|
+
return false;
|
|
475
|
+
for (const key of keysA) {
|
|
476
|
+
if (a[key] !== b[key])
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
return true;
|
|
480
|
+
}
|
|
237
481
|
//# sourceMappingURL=mcp-config.js.map
|