@tacuchi/agent-workflow-cli 25.1.2 → 25.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/README.md +2 -1
- package/dist/adapters/node-process.js +35 -0
- package/dist/adapters/node-process.js.map +1 -1
- package/dist/application/amend-service.js +393 -0
- package/dist/application/amend-service.js.map +1 -0
- package/dist/application/doctor/actions.js +158 -0
- package/dist/application/doctor/actions.js.map +1 -0
- package/dist/application/doctor/apply.js +399 -0
- package/dist/application/doctor/apply.js.map +1 -0
- package/dist/application/doctor/auth-dsn.js +143 -0
- package/dist/application/doctor/auth-dsn.js.map +1 -0
- package/dist/application/doctor/auth-flow.js +28 -0
- package/dist/application/doctor/auth-flow.js.map +1 -0
- package/dist/application/doctor/auth-registry.js +12 -0
- package/dist/application/doctor/auth-registry.js.map +1 -0
- package/dist/application/doctor/hosts.js +41 -0
- package/dist/application/doctor/hosts.js.map +1 -0
- package/dist/application/doctor/native-host-state.js +211 -0
- package/dist/application/doctor/native-host-state.js.map +1 -0
- package/dist/application/doctor/prepare.js +262 -0
- package/dist/application/doctor/prepare.js.map +1 -0
- package/dist/application/doctor/provider-installation.js +124 -0
- package/dist/application/doctor/provider-installation.js.map +1 -0
- package/dist/application/doctor/provider-mcps.js +650 -0
- package/dist/application/doctor/provider-mcps.js.map +1 -0
- package/dist/application/doctor/provider-plugins-hooks.js +112 -0
- package/dist/application/doctor/provider-plugins-hooks.js.map +1 -0
- package/dist/application/doctor/provider-skills.js +212 -0
- package/dist/application/doctor/provider-skills.js.map +1 -0
- package/dist/application/doctor/provider-tools-auth.js +201 -0
- package/dist/application/doctor/provider-tools-auth.js.map +1 -0
- package/dist/application/doctor/provider-visibility.js +149 -0
- package/dist/application/doctor/provider-visibility.js.map +1 -0
- package/dist/application/doctor/repair-runner.js +169 -0
- package/dist/application/doctor/repair-runner.js.map +1 -0
- package/dist/application/doctor/report.js +155 -0
- package/dist/application/doctor/report.js.map +1 -0
- package/dist/application/doctor/types.js +5 -0
- package/dist/application/doctor/types.js.map +1 -0
- package/dist/application/flow/advance.js +62 -9
- package/dist/application/flow/advance.js.map +1 -1
- package/dist/application/flow/flow-service.js +18 -7
- package/dist/application/flow/flow-service.js.map +1 -1
- package/dist/application/flow/run-projection.js +6 -1
- package/dist/application/flow/run-projection.js.map +1 -1
- package/dist/application/flow/submit.js +143 -3
- package/dist/application/flow/submit.js.map +1 -1
- package/dist/application/mcp-doctor-service.js +1 -1
- package/dist/application/mcp-doctor-service.js.map +1 -1
- package/dist/application/mcp-host-reader.js +134 -26
- package/dist/application/mcp-host-reader.js.map +1 -1
- package/dist/application/mcp-host-receipt-service.js +9 -7
- package/dist/application/mcp-host-receipt-service.js.map +1 -1
- package/dist/application/self/install-skill.js +10 -2
- package/dist/application/self/install-skill.js.map +1 -1
- package/dist/application/self/mcp-config.js +1 -1
- package/dist/application/self/mcp-config.js.map +1 -1
- package/dist/application/session-narrative.js +15 -0
- package/dist/application/session-narrative.js.map +1 -1
- package/dist/application/source-boundary-policy.js +87 -6
- package/dist/application/source-boundary-policy.js.map +1 -1
- package/dist/cli/commands/amend.js +122 -0
- package/dist/cli/commands/amend.js.map +1 -0
- package/dist/cli/commands/doctor.js +300 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/index.js +4 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/dispatch-plan.js +47 -0
- package/dist/cli/dispatch-plan.js.map +1 -0
- package/dist/cli/help-groups.js +8 -0
- package/dist/cli/help-groups.js.map +1 -1
- package/dist/cli/main.js +54 -10
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/parser.js +20 -0
- package/dist/cli/parser.js.map +1 -1
- package/dist/domain/doctor/auth.js +65 -0
- package/dist/domain/doctor/auth.js.map +1 -0
- package/dist/domain/doctor/model.js +82 -0
- package/dist/domain/doctor/model.js.map +1 -0
- package/dist/domain/doctor/operations.js +114 -0
- package/dist/domain/doctor/operations.js.map +1 -0
- package/dist/domain/flow/authority.js +8 -0
- package/dist/domain/flow/authority.js.map +1 -1
- package/dist/domain/flow/run-state.js +181 -25
- package/dist/domain/flow/run-state.js.map +1 -1
- package/dist/domain/redaction.js +40 -0
- package/dist/domain/redaction.js.map +1 -1
- package/package.json +1 -1
- package/skills/w/SKILL.md +2 -1
- package/skills/w/commands/README.md +2 -1
- package/skills/w/commands/doctor.md +31 -0
- package/skills/w/context/MANIFEST.json +4 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quién recibe una acción, y en un solo lugar.
|
|
3
|
+
*
|
|
4
|
+
* Los proveedores sugieren (`finding.proposal`); acá se decide. Es una sola
|
|
5
|
+
* función a propósito: AC-08 promete que sólo los recursos atribuibles a
|
|
6
|
+
* Workline se modifican, y una promesa que se cumple en seis archivos distintos
|
|
7
|
+
* es seis lugares donde puede dejar de cumplirse. Los ajenos, los ambiguos y los
|
|
8
|
+
* que tienen una credencial embebida salen de acá con guía y sin acción, sin
|
|
9
|
+
* importar lo que el proveedor haya sugerido.
|
|
10
|
+
*
|
|
11
|
+
* También es donde nace el orden: `depends_on` se resuelve entre las acciones de
|
|
12
|
+
* la MISMA corrida, porque una dependencia sólo puede nombrar algo que también
|
|
13
|
+
* se va a ejecutar.
|
|
14
|
+
*/
|
|
15
|
+
import { custodyViolation } from "../../domain/doctor/auth.js";
|
|
16
|
+
import { doctorOperation } from "../../domain/doctor/operations.js";
|
|
17
|
+
/** El sano nunca lleva acción: no hay nada que reparar. */
|
|
18
|
+
const REPAIRABLE_STATES = new Set(["warning", "blocking", "unverified"]);
|
|
19
|
+
/**
|
|
20
|
+
* La categoría que admite UNA sola operación, y por qué esa.
|
|
21
|
+
*
|
|
22
|
+
* En `tools-auth` el recurso no es un archivo: es un secreto de una persona. La
|
|
23
|
+
* única acción admisible ahí es correr el flujo que su proveedor declaró —el que
|
|
24
|
+
* hereda la terminal, para que el valor vaya de la persona al programa sin pasar
|
|
25
|
+
* por el CLI—. Cualquier otra operación sobre esta categoría implicaría que el
|
|
26
|
+
* CLI escribe la credencial en algún lado, y entonces la custodia del secreto
|
|
27
|
+
* pasa a ser suya, que es exactamente lo que no puede pasar.
|
|
28
|
+
*
|
|
29
|
+
* Con el catálogo de hoy esto NO habilita nada: ningún proveedor real declara
|
|
30
|
+
* flujo, así que ninguna variable DSN recibe una acción. Lo que habilita es la
|
|
31
|
+
* maquinaria, probada con un doble.
|
|
32
|
+
*/
|
|
33
|
+
const CATEGORY_ONLY_OP = new Map([["tools-auth", "auth.flow"]]);
|
|
34
|
+
export function annotateRepairs(findings) {
|
|
35
|
+
const eligible = new Set(findings.filter(isEligible).map((finding) => finding.id));
|
|
36
|
+
// Instalar el bundle en un host es la precondición de armar sus hooks: si las
|
|
37
|
+
// dos acciones entran al mismo lote, la de hooks va después. Se resuelve entre
|
|
38
|
+
// las acciones de ESTA corrida, no contra el catálogo, porque una dependencia
|
|
39
|
+
// que nombra algo que nadie va a ejecutar bloquea para siempre.
|
|
40
|
+
const installByHost = new Map();
|
|
41
|
+
for (const finding of findings) {
|
|
42
|
+
if (!eligible.has(finding.id))
|
|
43
|
+
continue;
|
|
44
|
+
if (finding.proposal?.op === "self.install-skill")
|
|
45
|
+
installByHost.set(finding.host, finding.id);
|
|
46
|
+
}
|
|
47
|
+
return findings.map((finding) => {
|
|
48
|
+
const { proposal, ...rest } = finding;
|
|
49
|
+
if (!eligible.has(finding.id))
|
|
50
|
+
return withoutAction(rest, finding);
|
|
51
|
+
const action = buildAction(finding, installByHost);
|
|
52
|
+
if (action === null)
|
|
53
|
+
return withoutAction(rest, finding);
|
|
54
|
+
return { ...rest, remediation: { ...finding.remediation, kind: "supported", action } };
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* El gate, y nada más que el gate.
|
|
59
|
+
*
|
|
60
|
+
* Cinco condiciones, y cada una es una forma distinta de que una acción no
|
|
61
|
+
* corresponda: nadie la sugirió, el recurso no es nuestro, el estado no tiene
|
|
62
|
+
* nada que reparar, la categoría no admite esa operación, o la evidencia dice
|
|
63
|
+
* que hay una credencial embebida — y sobre un archivo que guarda un secreto de
|
|
64
|
+
* otra persona no se escribe, aunque el recurso pareciera nuestro.
|
|
65
|
+
*
|
|
66
|
+
* La custodia de un flujo se comprueba acá TAMBIÉN, y no es una guarda repetida:
|
|
67
|
+
* las dos deciden cosas distintas. El proveedor de diagnóstico decide el ESTADO
|
|
68
|
+
* PUBLICADO —es el único que puede degradar su propio hallazgo a bloqueante con
|
|
69
|
+
* la razón, y por eso la persona se entera— y esta decide la ELEGIBILIDAD. Hacen
|
|
70
|
+
* falta las dos porque el proveedor de `tools-auth` es UN productor de
|
|
71
|
+
* sugerencias y este es el punto por donde pasa TODA acción: `flow` vive en la
|
|
72
|
+
* sugerencia compartida, así que cualquier proveedor de cualquier categoría podría
|
|
73
|
+
* adjuntar uno, y sellar un `argv` con una credencial adentro es el defecto más
|
|
74
|
+
* caro que este plan puede tener. Cada una tiene su propia prueba, así que
|
|
75
|
+
* ninguna tapa a la otra.
|
|
76
|
+
*/
|
|
77
|
+
function isEligible(finding) {
|
|
78
|
+
if (finding.proposal === undefined)
|
|
79
|
+
return false;
|
|
80
|
+
if (finding.ownership !== "ours")
|
|
81
|
+
return false;
|
|
82
|
+
if (!REPAIRABLE_STATES.has(finding.state))
|
|
83
|
+
return false;
|
|
84
|
+
const only = CATEGORY_ONLY_OP.get(finding.category);
|
|
85
|
+
if (only !== undefined && finding.proposal.op !== only)
|
|
86
|
+
return false;
|
|
87
|
+
return !hasEmbeddedCredentialEvidence(finding);
|
|
88
|
+
}
|
|
89
|
+
/** La evidencia que el proveedor de MCPs escribe cuando encuentra una credencial. */
|
|
90
|
+
function hasEmbeddedCredentialEvidence(finding) {
|
|
91
|
+
return finding.evidence.some((line) => line.includes("credencial embebida"));
|
|
92
|
+
}
|
|
93
|
+
function buildAction(finding, installByHost) {
|
|
94
|
+
const hint = finding.proposal;
|
|
95
|
+
if (hint === undefined)
|
|
96
|
+
return null;
|
|
97
|
+
const spec = doctorOperation(hint.op);
|
|
98
|
+
// Una sugerencia que nombra una operación que el catálogo no declara se
|
|
99
|
+
// descarta en silencio hacia la guía manual: el catálogo es la autoridad sobre
|
|
100
|
+
// qué se puede ejecutar, y una acción sin especificación no tiene ni clases de
|
|
101
|
+
// efecto que aprobar.
|
|
102
|
+
if (spec === null)
|
|
103
|
+
return null;
|
|
104
|
+
// Un flujo que no puede preservar la custodia NO se vuelve acción, venga de
|
|
105
|
+
// donde venga. Es la misma regla del dominio, aplicada en el punto por donde
|
|
106
|
+
// toda acción pasa: acá no hay a quién explicarle la razón —eso lo hace el
|
|
107
|
+
// hallazgo del proveedor—, pero sí hay qué impedir.
|
|
108
|
+
if (hint.flow !== undefined && custodyViolation(hint.flow) !== null)
|
|
109
|
+
return null;
|
|
110
|
+
const dependsOn = [];
|
|
111
|
+
if (hint.op === "self.install-hooks") {
|
|
112
|
+
const install = installByHost.get(finding.host);
|
|
113
|
+
if (install !== undefined)
|
|
114
|
+
dependsOn.push(install);
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
op: spec.op,
|
|
118
|
+
args: { ...hint.args },
|
|
119
|
+
// Las clases de la operación MÁS las que el flujo declara. Aprobar un lote
|
|
120
|
+
// que corre un flujo que sale a la red bajo una aprobación que sólo cubría
|
|
121
|
+
// `execute` sería una autorización que no autorizó lo que pasó.
|
|
122
|
+
effects: [...new Set([...spec.effects, ...(hint.flow?.effects ?? [])])],
|
|
123
|
+
depends_on: dependsOn,
|
|
124
|
+
expected: spec.expected,
|
|
125
|
+
// El `argv` queda SELLADO en la acción: aprobar un lote que ejecuta un
|
|
126
|
+
// comando es aprobar sus tokens exactos, y quien los corre no vuelve a
|
|
127
|
+
// resolverlos.
|
|
128
|
+
...(hint.flow === undefined ? {} : { argv: [...hint.flow.argv] }),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Sin acción, y con la guía que ya traía.
|
|
133
|
+
*
|
|
134
|
+
* `kind` se degrada a `manual` sólo si había una sugerencia: un hallazgo que
|
|
135
|
+
* nunca propuso nada conserva el `none` o el `manual` que su proveedor decidió,
|
|
136
|
+
* porque «no hay acción segura» y «hay que hacerlo a mano» son respuestas
|
|
137
|
+
* distintas y el proveedor es el que sabe cuál es.
|
|
138
|
+
*/
|
|
139
|
+
function withoutAction(rest, original) {
|
|
140
|
+
if (original.proposal === undefined) {
|
|
141
|
+
return { ...rest, remediation: { ...original.remediation, action: null } };
|
|
142
|
+
}
|
|
143
|
+
const spec = doctorOperation(original.proposal.op);
|
|
144
|
+
const guidance = original.remediation.guidance.length > 0
|
|
145
|
+
? original.remediation.guidance
|
|
146
|
+
: spec === null
|
|
147
|
+
? []
|
|
148
|
+
: [spec.verb(original.proposal.args)];
|
|
149
|
+
return {
|
|
150
|
+
...rest,
|
|
151
|
+
remediation: {
|
|
152
|
+
kind: original.remediation.kind === "none" ? "none" : "manual",
|
|
153
|
+
action: null,
|
|
154
|
+
guidance,
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.js","sourceRoot":"","sources":["../../../src/application/doctor/actions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import { redactSensitiveText } from "../../domain/redaction.js";
|
|
2
|
+
import { withCwdLock } from "../lock-service.js";
|
|
3
|
+
import { prepareDoctorBatch, } from "./prepare.js";
|
|
4
|
+
import { runDoctor } from "./report.js";
|
|
5
|
+
export async function applyDoctorBatch(ctx, input, deps = {}) {
|
|
6
|
+
if (input.approval === undefined || input.approval.length === 0) {
|
|
7
|
+
return {
|
|
8
|
+
ok: false,
|
|
9
|
+
rejection: {
|
|
10
|
+
code: "APPROVAL_REQUIRED",
|
|
11
|
+
message: "no se ejecuta ninguna reparación sin la aprobación de su vista previa",
|
|
12
|
+
candidates: [],
|
|
13
|
+
action: "corré `aw doctor prepare --select …`, leé la vista previa y aprobá su digest",
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
const approved = { ...input, approval: input.approval };
|
|
18
|
+
const locked = await withCwdLock(ctx.fs, ctx.paths, () => runUnderLock(ctx, approved, deps));
|
|
19
|
+
if ("error" in locked) {
|
|
20
|
+
return {
|
|
21
|
+
ok: false,
|
|
22
|
+
rejection: {
|
|
23
|
+
code: "LOCK_BUSY",
|
|
24
|
+
message: locked.error,
|
|
25
|
+
candidates: [],
|
|
26
|
+
action: "esperá a que se libere el lock del workspace y volvé a aplicar",
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
return locked;
|
|
31
|
+
}
|
|
32
|
+
async function runUnderLock(ctx,
|
|
33
|
+
// `approval` ya quedó comprobado por el llamador; el tipo lo dice acá para que
|
|
34
|
+
// el rechazo pueda nombrar los DOS digests sin un `?? ""` que inventaría uno.
|
|
35
|
+
input, deps) {
|
|
36
|
+
// 1 — la propuesta se computa OTRA VEZ, desde el estado vivo.
|
|
37
|
+
const prepared = await prepareDoctorBatch(ctx, input, deps);
|
|
38
|
+
if (!prepared.ok) {
|
|
39
|
+
// Un rechazo de selección puede significar dos cosas muy distintas: que la
|
|
40
|
+
// persona escribió mal el id, o que el recurso YA está arreglado y su
|
|
41
|
+
// hallazgo desapareció. La segunda es un reintento del mismo digest sobre
|
|
42
|
+
// algo que ya no tiene nada que hacer, y responder un error ahí haría que
|
|
43
|
+
// repetir una aprobación se lea como un fallo.
|
|
44
|
+
const settled = await alreadySettled(ctx, input, deps);
|
|
45
|
+
if (settled !== null)
|
|
46
|
+
return { ok: true, result: settled };
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
rejection: { ...prepared.rejection, candidates: prepared.rejection.candidates },
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (prepared.kind === "listing") {
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
rejection: {
|
|
56
|
+
code: "APPROVAL_REQUIRED",
|
|
57
|
+
message: "no hay ninguna acción seleccionada: una aprobación sin selección no aprueba nada",
|
|
58
|
+
candidates: [],
|
|
59
|
+
action: "repetí la corrida con los mismos `--select` que produjeron ese digest",
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const proposal = prepared.proposal;
|
|
64
|
+
// 2 — lo aprobado tiene que ser lo que se aplicaría. Los DOS digests viajan en
|
|
65
|
+
// el rechazo: sin ellos la persona no puede saber si cambió el estado o si
|
|
66
|
+
// aprobó otra vista previa.
|
|
67
|
+
if (proposal.digest !== input.approval) {
|
|
68
|
+
return {
|
|
69
|
+
ok: false,
|
|
70
|
+
rejection: {
|
|
71
|
+
code: "EVIDENCE_MISSING",
|
|
72
|
+
// Los dos digests van en el MENSAJE y no sólo en `candidates`, porque la
|
|
73
|
+
// proyección humana de un rechazo imprime el código y el mensaje y nada
|
|
74
|
+
// más: dejarlos únicamente en los detalles los publicaba en el JSON y los
|
|
75
|
+
// escondía en la terminal, que es donde la persona está parada cuando
|
|
76
|
+
// esto pasa. Y sin los dos no puede distinguir «aprobé otra vista previa»
|
|
77
|
+
// de «el estado se movió».
|
|
78
|
+
message: `lo aprobado no es lo que se aplicaría: cambió el lote, o cambió alguno de los archivos que se leyeron para decidirlo (aprobado ${input.approval}, vigente ${proposal.digest}). Volvé a correr \`prepare\`, leé la vista previa vigente y aprobá ese digest`,
|
|
79
|
+
candidates: [input.approval, proposal.digest],
|
|
80
|
+
action: "volvé a correr `prepare`, leé la vista previa vigente y aprobá ese digest",
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// 3 — recién acá se toca la máquina.
|
|
85
|
+
return { ok: true, result: await execute(ctx, input, proposal.batch, proposal.digest, deps) };
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Si el lote aprobado ya no tiene nada que hacer, decilo — y no como un error.
|
|
89
|
+
*
|
|
90
|
+
* Sólo cuenta como `already` cuando el hallazgo YA NO ESTÁ y su categoría se
|
|
91
|
+
* pudo comprobar en este host. La ausencia de un hallazgo prueba algo únicamente
|
|
92
|
+
* cuando alguien miró: si la cobertura de esa categoría quedó `unavailable` o
|
|
93
|
+
* `skipped`, el recurso puede estar igual de roto y nadie lo sabe, y ahí `already`
|
|
94
|
+
* sería exactamente la mentira que este modelo existe para no decir.
|
|
95
|
+
*/
|
|
96
|
+
async function alreadySettled(ctx, input, deps) {
|
|
97
|
+
const select = [...(input.select ?? [])];
|
|
98
|
+
if (select.length === 0)
|
|
99
|
+
return null;
|
|
100
|
+
const report = await runDoctor(ctx, input, deps);
|
|
101
|
+
const settled = [];
|
|
102
|
+
for (const id of select) {
|
|
103
|
+
const how = settlementOf(report, id);
|
|
104
|
+
if (how === null)
|
|
105
|
+
return null;
|
|
106
|
+
settled.push(how);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
status: "already",
|
|
110
|
+
digest: input.approval,
|
|
111
|
+
actions: select.map((id, index) => ({
|
|
112
|
+
finding_id: id,
|
|
113
|
+
op: "—",
|
|
114
|
+
host: id.split("/")[0] ?? "—",
|
|
115
|
+
resource: id,
|
|
116
|
+
// `skipped` y no `applied`: en esta corrida NO se ejecutó nada. Decir
|
|
117
|
+
// «aplicada» sobre una acción que nadie corrió dejaba la proyección
|
|
118
|
+
// afirmando «✔ <id>» debajo de un «Resumen: 0 aplicadas», que es la clase
|
|
119
|
+
// de contradicción que hace dudar del informe entero.
|
|
120
|
+
status: "skipped",
|
|
121
|
+
reason: "no se ejecutó: el recurso ya no reporta el problema",
|
|
122
|
+
recheck: "resolved",
|
|
123
|
+
recheck_detail: settled[index] ?? "el recurso ya no reporta el problema",
|
|
124
|
+
})),
|
|
125
|
+
summary: {
|
|
126
|
+
applied: 0,
|
|
127
|
+
failed: 0,
|
|
128
|
+
skipped: select.length,
|
|
129
|
+
blocked: 0,
|
|
130
|
+
resolved: select.length,
|
|
131
|
+
},
|
|
132
|
+
exit_code: 0,
|
|
133
|
+
reason: "nada que hacer: los recursos aprobados ya no reportan el problema",
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Por qué este recurso ya está saldado, o `null` si no lo está.
|
|
138
|
+
*
|
|
139
|
+
* Dos formas de estarlo, y las dos hacen falta. Un hallazgo que DESAPARECIÓ es
|
|
140
|
+
* la obvia. La otra es el hallazgo que sigue ahí y ya no tiene nada que hacer:
|
|
141
|
+
* los proveedores emiten el sano en vez de callarse —«no estaba mal» y «nadie
|
|
142
|
+
* miró» son respuestas distintas—, así que arreglar el recurso a mano entre la
|
|
143
|
+
* vista previa y la aprobación NO lo hace desaparecer, lo vuelve `healthy`. Sin
|
|
144
|
+
* esta segunda forma, repetir una aprobación sobre algo que ya está bien
|
|
145
|
+
* devolvía `SELECTION_NOT_ACTIONABLE` con salida 1: un error sobre un recurso
|
|
146
|
+
* sano, y la promesa de que un reintento idéntico responde `already` sólo se
|
|
147
|
+
* cumplía cuando el recurso dejaba de existir.
|
|
148
|
+
*
|
|
149
|
+
* Y las dos exigen lo mismo: que la categoría se haya COMPROBADO en este host.
|
|
150
|
+
* La ausencia de un hallazgo prueba algo únicamente cuando alguien miró; si la
|
|
151
|
+
* cobertura quedó `unavailable` o `skipped`, el recurso puede estar igual de
|
|
152
|
+
* roto y nadie lo sabe.
|
|
153
|
+
*/
|
|
154
|
+
function settlementOf(report, id) {
|
|
155
|
+
const finding = report.findings.find((candidate) => candidate.id === id);
|
|
156
|
+
if (finding !== undefined) {
|
|
157
|
+
const inert = finding.state === "healthy" && finding.remediation.action === null;
|
|
158
|
+
if (!inert)
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
const covered = coverageFor(report, id);
|
|
162
|
+
if (!covered.ok)
|
|
163
|
+
return null;
|
|
164
|
+
return finding === undefined
|
|
165
|
+
? `el hallazgo ya no aparece y su categoría se comprobó en esta corrida (${covered.where})`
|
|
166
|
+
: `el recurso ya reporta sano y no propone ninguna acción (${covered.where})`;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Si alguien miró la categoría de este hallazgo en su host, y dónde lo dice.
|
|
170
|
+
*
|
|
171
|
+
* La misma pregunta la hacen dos caminos —el `already` y la recomprobación de
|
|
172
|
+
* una acción cuyo hallazgo desapareció—, así que vive una sola vez. Un id que no
|
|
173
|
+
* nombra host y categoría falla CERRADO: no se puede afirmar cobertura sobre algo
|
|
174
|
+
* que no se puede ubicar.
|
|
175
|
+
*/
|
|
176
|
+
function coverageFor(report, id) {
|
|
177
|
+
const parts = id.split("/");
|
|
178
|
+
const host = parts[0];
|
|
179
|
+
const category = parts[1];
|
|
180
|
+
if (host === undefined || category === undefined || parts.length < 3) {
|
|
181
|
+
return { ok: false, why: `el id '${id}' no nombra host y categoría` };
|
|
182
|
+
}
|
|
183
|
+
const entries = report.coverage.filter((entry) => entry.category === category && (entry.host === host || entry.host === "workspace"));
|
|
184
|
+
const checked = entries.find((entry) => entry.state === "checked");
|
|
185
|
+
if (checked !== undefined)
|
|
186
|
+
return { ok: true, where: `${category} en ${checked.host}` };
|
|
187
|
+
const gap = entries[0];
|
|
188
|
+
return {
|
|
189
|
+
ok: false,
|
|
190
|
+
why: gap === undefined
|
|
191
|
+
? `la categoría ${category} no declaró cobertura para ${host}`
|
|
192
|
+
: `la cobertura de ${category} en ${gap.host} quedó ${gap.state}: ${gap.reason ?? "sin razón declarada"}`,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* El orden topológico ya viene del lote; acá se respeta y se registra.
|
|
197
|
+
*
|
|
198
|
+
* Una acción fallida no detiene al lote: marca `skipped` a todo lo que dependía
|
|
199
|
+
* de ella —transitivamente, porque el dependiente de un omitido también queda
|
|
200
|
+
* sin su precondición— y deja seguir a las independientes. Eso es lo que hace
|
|
201
|
+
* que el resultado parcial sirva: la persona ve exactamente hasta dónde llegó.
|
|
202
|
+
*/
|
|
203
|
+
async function execute(ctx, input, batch, digest, deps) {
|
|
204
|
+
const executor = deps.executor ?? realExecutor;
|
|
205
|
+
const reports = [];
|
|
206
|
+
const failed = new Map();
|
|
207
|
+
for (const action of batch.actions) {
|
|
208
|
+
const blocker = action.depends_on.find((id) => failed.has(id));
|
|
209
|
+
if (blocker !== undefined) {
|
|
210
|
+
failed.set(action.finding_id, `quedó sin su precondición: ${blocker}`);
|
|
211
|
+
reports.push({
|
|
212
|
+
...identity(action),
|
|
213
|
+
status: "skipped",
|
|
214
|
+
reason: `no se ejecutó porque ${blocker} no aplicó`,
|
|
215
|
+
recheck: "unverified",
|
|
216
|
+
recheck_detail: "no se recomprobó: la acción no llegó a correr",
|
|
217
|
+
});
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
const outcome = await run(executor, action, ctx);
|
|
221
|
+
if (outcome.status !== "applied") {
|
|
222
|
+
failed.set(action.finding_id, outcome.detail);
|
|
223
|
+
reports.push({
|
|
224
|
+
...identity(action),
|
|
225
|
+
status: outcome.status,
|
|
226
|
+
reason: outcome.detail,
|
|
227
|
+
recheck: outcome.status === "blocked" ? "blocked" : "unverified",
|
|
228
|
+
recheck_detail: outcome.status === "blocked"
|
|
229
|
+
? "la operación se declaró bloqueada: el recurso queda como estaba"
|
|
230
|
+
: "no se recomprobó: la acción no aplicó",
|
|
231
|
+
});
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
const recheck = await recheckOf(ctx, input, action, deps);
|
|
235
|
+
reports.push({ ...identity(action), status: "applied", reason: null, ...recheck });
|
|
236
|
+
}
|
|
237
|
+
return summarize(digest, reports);
|
|
238
|
+
}
|
|
239
|
+
function identity(action) {
|
|
240
|
+
return {
|
|
241
|
+
finding_id: action.finding_id,
|
|
242
|
+
op: action.op,
|
|
243
|
+
host: action.host,
|
|
244
|
+
resource: action.resource,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
/** Un ejecutor que lanza es una acción FALLIDA, nunca una corrida caída. */
|
|
248
|
+
async function run(executor, action, ctx) {
|
|
249
|
+
try {
|
|
250
|
+
return await executor(action, ctx);
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
// El mensaje de una excepción es el único texto de este resultado que no
|
|
254
|
+
// viene del informe ya redactado, y `run` existe justamente para atrapar lo
|
|
255
|
+
// que nadie previó — así que puede traer cualquier cosa. El comando devuelve
|
|
256
|
+
// siempre `ok:true` (el veredicto va en el exit code), y la última línea de
|
|
257
|
+
// defensa del CLI sólo redacta la rama de error: acá no hay nadie más abajo.
|
|
258
|
+
return {
|
|
259
|
+
status: "failed",
|
|
260
|
+
detail: redactSensitiveText(`la operación lanzó: ${messageOf(error)}`),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* La recomprobación: el MISMO proveedor releyendo el recurso.
|
|
266
|
+
*
|
|
267
|
+
* Nunca se deduce del resultado de la operación. Una operación que devolvió
|
|
268
|
+
* «apliqué» y un recurso que quedó sano son dos hechos distintos, y presentar el
|
|
269
|
+
* primero como el segundo es exactamente la validación omitida que AC-12
|
|
270
|
+
* prohíbe. Y una recomprobación que no corrió o que falló queda `unverified`,
|
|
271
|
+
* jamás `resolved`.
|
|
272
|
+
*
|
|
273
|
+
* Corre con el ALCANCE de la corrida —los mismos `--host`, `--only` y
|
|
274
|
+
* `--skip-native` que la persona pidió— y no con opciones inventadas acá. No es
|
|
275
|
+
* cosmético: `--skip-native` existe porque inspeccionar los MCP nativos LANZA los
|
|
276
|
+
* servidores del host, y una recomprobación que lo descartara volvería a hacer,
|
|
277
|
+
* una vez por acción aplicada, exactamente la sonda que la persona declinó.
|
|
278
|
+
*
|
|
279
|
+
* Y pide VERIFICAR, no sólo releer: eso es lo que hace que «quedó resuelto»
|
|
280
|
+
* dependa de la observación del proveedor y no del código de salida del programa.
|
|
281
|
+
* La autorización con la que verifica es EXACTAMENTE la que la persona dio en la
|
|
282
|
+
* invocación, nunca más: pedir un vacío significa «verificá hasta donde puedas
|
|
283
|
+
* sin permisos extra», y cada proveedor degrada lo que no alcance diciéndolo.
|
|
284
|
+
*
|
|
285
|
+
* Y no se amplía con las clases de la acción, aunque estén aprobadas. Una acción
|
|
286
|
+
* aprobada con `network_external` autoriza a ESA acción a salir de la máquina, no
|
|
287
|
+
* a la relectura del entorno entero: la verificación de un informe no tiene
|
|
288
|
+
* alcance por sujeto, así que sumar esa clase acá conectaría contra todas las
|
|
289
|
+
* credenciales registradas para recomprobar una.
|
|
290
|
+
*/
|
|
291
|
+
async function recheckOf(ctx, input, action, deps) {
|
|
292
|
+
if (deps.recheck === false) {
|
|
293
|
+
return { recheck: "unverified", recheck_detail: "la recomprobación quedó desactivada" };
|
|
294
|
+
}
|
|
295
|
+
const verify = [...(input.verify ?? [])];
|
|
296
|
+
let report;
|
|
297
|
+
try {
|
|
298
|
+
report = await runDoctor(ctx, { ...input, verify }, deps);
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
return {
|
|
302
|
+
recheck: "unverified",
|
|
303
|
+
recheck_detail: redactSensitiveText(`la recomprobación no pudo correr: ${messageOf(error)}`),
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
const finding = report.findings.find((candidate) => candidate.id === action.finding_id);
|
|
307
|
+
if (finding === undefined) {
|
|
308
|
+
// Que el hallazgo no esté NO prueba nada por sí solo: prueba algo cuando
|
|
309
|
+
// alguien miró. Si la categoría de ese hallazgo quedó sin comprobar en este
|
|
310
|
+
// host —un proveedor que se cayó durante la relectura se lleva TODOS sus
|
|
311
|
+
// hallazgos—, declarar `resolved` sería exactamente la validación omitida
|
|
312
|
+
// presentada como superada que AC-12 prohíbe.
|
|
313
|
+
const covered = coverageFor(report, action.finding_id);
|
|
314
|
+
return covered.ok
|
|
315
|
+
? {
|
|
316
|
+
recheck: "resolved",
|
|
317
|
+
recheck_detail: `el hallazgo ya no aparece en el informe (${covered.where})`,
|
|
318
|
+
}
|
|
319
|
+
: {
|
|
320
|
+
recheck: "unverified",
|
|
321
|
+
recheck_detail: `el hallazgo no aparece, pero nadie lo comprobó: ${covered.why}`,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
return { ...classify(finding.state), recheck_detail: `el informe lo reporta ${finding.state}` };
|
|
325
|
+
}
|
|
326
|
+
function classify(state) {
|
|
327
|
+
if (state === "healthy")
|
|
328
|
+
return { recheck: "resolved" };
|
|
329
|
+
if (state === "unverified")
|
|
330
|
+
return { recheck: "unverified" };
|
|
331
|
+
if (state === "blocking")
|
|
332
|
+
return { recheck: "blocked" };
|
|
333
|
+
return { recheck: "pending" };
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* El veredicto del lote, y el exit code sale de él.
|
|
337
|
+
*
|
|
338
|
+
* `completed` exige las dos cosas: que todas aplicaran Y que todas se
|
|
339
|
+
* recomprobaran resueltas. Un lote donde todo aplicó pero algo quedó `pending`
|
|
340
|
+
* no está completo — el recurso sigue reportando el problema, y llamarlo
|
|
341
|
+
* completo sería declarar resuelto algo que nadie verificó.
|
|
342
|
+
*/
|
|
343
|
+
function summarize(digest, actions) {
|
|
344
|
+
const count = (status) => actions.filter((action) => action.status === status).length;
|
|
345
|
+
const applied = count("applied");
|
|
346
|
+
const resolved = actions.filter((action) => action.recheck === "resolved").length;
|
|
347
|
+
const summary = {
|
|
348
|
+
applied,
|
|
349
|
+
failed: count("failed"),
|
|
350
|
+
skipped: count("skipped"),
|
|
351
|
+
blocked: count("blocked"),
|
|
352
|
+
resolved,
|
|
353
|
+
};
|
|
354
|
+
if (applied === 0) {
|
|
355
|
+
return {
|
|
356
|
+
status: "failed",
|
|
357
|
+
digest,
|
|
358
|
+
actions,
|
|
359
|
+
summary,
|
|
360
|
+
exit_code: 1,
|
|
361
|
+
reason: "ninguna acción del lote llegó a aplicarse",
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
if (applied === actions.length && resolved === actions.length) {
|
|
365
|
+
return {
|
|
366
|
+
status: "completed",
|
|
367
|
+
digest,
|
|
368
|
+
actions,
|
|
369
|
+
summary,
|
|
370
|
+
exit_code: 0,
|
|
371
|
+
reason: "todas las acciones aplicaron y su recomprobación las declara resueltas",
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
return {
|
|
375
|
+
status: "partial",
|
|
376
|
+
digest,
|
|
377
|
+
actions,
|
|
378
|
+
summary,
|
|
379
|
+
exit_code: 1,
|
|
380
|
+
reason: `${applied} de ${actions.length} aplicaron; el resto falló, quedó omitido o sin recomprobación concluyente`,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
function messageOf(error) {
|
|
384
|
+
return error instanceof Error ? error.message : String(error);
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* El ejecutor real: cada operación llama a la función que YA sabe escribir.
|
|
388
|
+
*
|
|
389
|
+
* Se construye un `ParsedArgs` explícito para las que lo esperan, campo por
|
|
390
|
+
* campo — nunca parseando una cadena. El `verb` del catálogo existe para
|
|
391
|
+
* MOSTRAR; ejecutar una reparación pasando su texto a un shell sería una
|
|
392
|
+
* inyección esperando el nombre de recurso equivocado, y los nombres de este
|
|
393
|
+
* informe vienen de archivos que escribió otra persona.
|
|
394
|
+
*/
|
|
395
|
+
export const realExecutor = async (action, ctx) => {
|
|
396
|
+
const { runDoctorRepair } = await import("./repair-runner.js");
|
|
397
|
+
return runDoctorRepair(action, ctx);
|
|
398
|
+
};
|
|
399
|
+
//# sourceMappingURL=apply.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../../../src/application/doctor/apply.ts"],"names":[],"mappings":"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"}
|