@calltelemetry/ct-lab-mcp 0.1.5 → 0.3.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 +29 -1
- package/dist/bin/ct-lab-mcp.d.ts +3 -0
- package/dist/bin/ct-lab-mcp.d.ts.map +1 -1
- package/dist/bin/ct-lab-mcp.js +47 -8
- package/dist/bin/ct-lab-mcp.js.map +1 -1
- package/dist/src/appliance/commands.d.ts.map +1 -1
- package/dist/src/appliance/commands.js +24 -0
- package/dist/src/appliance/commands.js.map +1 -1
- package/dist/src/appliance/executor.d.ts.map +1 -1
- package/dist/src/appliance/executor.js +7 -1
- package/dist/src/appliance/executor.js.map +1 -1
- package/dist/src/appliance/mock.d.ts +6 -0
- package/dist/src/appliance/mock.d.ts.map +1 -1
- package/dist/src/appliance/mock.js +27 -6
- package/dist/src/appliance/mock.js.map +1 -1
- package/dist/src/appliance/types.d.ts +20 -1
- package/dist/src/appliance/types.d.ts.map +1 -1
- package/dist/src/appliance/types.js.map +1 -1
- package/dist/src/canonical/bridge.d.ts +28 -0
- package/dist/src/canonical/bridge.d.ts.map +1 -0
- package/dist/src/canonical/bridge.js +179 -0
- package/dist/src/canonical/bridge.js.map +1 -0
- package/dist/src/canonical/cli.d.ts +8 -0
- package/dist/src/canonical/cli.d.ts.map +1 -0
- package/dist/src/canonical/cli.js +9 -0
- package/dist/src/canonical/cli.js.map +1 -0
- package/dist/src/canonical/index.d.ts +4 -0
- package/dist/src/canonical/index.d.ts.map +1 -0
- package/dist/src/canonical/index.js +4 -0
- package/dist/src/canonical/index.js.map +1 -0
- package/dist/src/canonical/types.d.ts +32 -0
- package/dist/src/canonical/types.d.ts.map +1 -0
- package/dist/src/canonical/types.js +2 -0
- package/dist/src/canonical/types.js.map +1 -0
- package/dist/src/credentials/errors.d.ts +16 -0
- package/dist/src/credentials/errors.d.ts.map +1 -0
- package/dist/src/credentials/errors.js +21 -0
- package/dist/src/credentials/errors.js.map +1 -0
- package/dist/src/credentials/resolver.d.ts +16 -0
- package/dist/src/credentials/resolver.d.ts.map +1 -1
- package/dist/src/credentials/resolver.js +104 -10
- package/dist/src/credentials/resolver.js.map +1 -1
- package/dist/src/credentials/types.d.ts +8 -0
- package/dist/src/credentials/types.d.ts.map +1 -1
- package/dist/src/diagnostics/collector.d.ts +37 -0
- package/dist/src/diagnostics/collector.d.ts.map +1 -0
- package/dist/src/diagnostics/collector.js +452 -0
- package/dist/src/diagnostics/collector.js.map +1 -0
- package/dist/src/diagnostics/types.d.ts +103 -0
- package/dist/src/diagnostics/types.d.ts.map +1 -0
- package/dist/src/diagnostics/types.js +8 -0
- package/dist/src/diagnostics/types.js.map +1 -0
- package/dist/src/hypervisors/esxi/client.d.ts +1 -0
- package/dist/src/hypervisors/esxi/client.d.ts.map +1 -1
- package/dist/src/hypervisors/esxi/client.js +15 -3
- package/dist/src/hypervisors/esxi/client.js.map +1 -1
- package/dist/src/hypervisors/esxi/mock.d.ts +1 -0
- package/dist/src/hypervisors/esxi/mock.d.ts.map +1 -1
- package/dist/src/hypervisors/esxi/mock.js +4 -0
- package/dist/src/hypervisors/esxi/mock.js.map +1 -1
- package/dist/src/hypervisors/manager.d.ts.map +1 -1
- package/dist/src/hypervisors/manager.js +37 -3
- package/dist/src/hypervisors/manager.js.map +1 -1
- package/dist/src/hypervisors/proxmox/client.d.ts +13 -1
- package/dist/src/hypervisors/proxmox/client.d.ts.map +1 -1
- package/dist/src/hypervisors/proxmox/client.js +485 -4
- package/dist/src/hypervisors/proxmox/client.js.map +1 -1
- package/dist/src/hypervisors/proxmox/mock.d.ts +1 -0
- package/dist/src/hypervisors/proxmox/mock.d.ts.map +1 -1
- package/dist/src/hypervisors/proxmox/mock.js +4 -0
- package/dist/src/hypervisors/proxmox/mock.js.map +1 -1
- package/dist/src/hypervisors/types.d.ts +119 -2
- package/dist/src/hypervisors/types.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/proxmox/mock.d.ts +1 -1
- package/dist/src/proxmox/mock.d.ts.map +1 -1
- package/dist/src/proxmox/mock.js +103 -1
- package/dist/src/proxmox/mock.js.map +1 -1
- package/dist/src/server.d.ts +2 -0
- package/dist/src/server.d.ts.map +1 -1
- package/dist/src/server.js +4 -0
- package/dist/src/server.js.map +1 -1
- package/dist/src/slot/manager.d.ts +26 -1
- package/dist/src/slot/manager.d.ts.map +1 -1
- package/dist/src/slot/manager.js +278 -1
- package/dist/src/slot/manager.js.map +1 -1
- package/dist/src/slot/types.d.ts +49 -0
- package/dist/src/slot/types.d.ts.map +1 -1
- package/dist/src/slot/types.js +18 -0
- package/dist/src/slot/types.js.map +1 -1
- package/dist/src/tools/canonical-guard.d.ts +7 -0
- package/dist/src/tools/canonical-guard.d.ts.map +1 -0
- package/dist/src/tools/canonical-guard.js +38 -0
- package/dist/src/tools/canonical-guard.js.map +1 -0
- package/dist/src/tools/credential-status.d.ts +29 -0
- package/dist/src/tools/credential-status.d.ts.map +1 -0
- package/dist/src/tools/credential-status.js +81 -0
- package/dist/src/tools/credential-status.js.map +1 -0
- package/dist/src/tools/cube-provision.d.ts +288 -0
- package/dist/src/tools/cube-provision.d.ts.map +1 -0
- package/dist/src/tools/cube-provision.js +892 -0
- package/dist/src/tools/cube-provision.js.map +1 -0
- package/dist/src/tools/cube-tools.d.ts +137 -0
- package/dist/src/tools/cube-tools.d.ts.map +1 -0
- package/dist/src/tools/cube-tools.js +292 -0
- package/dist/src/tools/cube-tools.js.map +1 -0
- package/dist/src/tools/cucm-emulator-tool.d.ts +85 -0
- package/dist/src/tools/cucm-emulator-tool.d.ts.map +1 -0
- package/dist/src/tools/cucm-emulator-tool.js +191 -0
- package/dist/src/tools/cucm-emulator-tool.js.map +1 -0
- package/dist/src/tools/cucm-trunk.d.ts +165 -0
- package/dist/src/tools/cucm-trunk.d.ts.map +1 -0
- package/dist/src/tools/cucm-trunk.js +354 -0
- package/dist/src/tools/cucm-trunk.js.map +1 -0
- package/dist/src/tools/diagnostics.d.ts +58 -0
- package/dist/src/tools/diagnostics.d.ts.map +1 -0
- package/dist/src/tools/diagnostics.js +212 -0
- package/dist/src/tools/diagnostics.js.map +1 -0
- package/dist/src/tools/guest-agent.d.ts +34 -0
- package/dist/src/tools/guest-agent.d.ts.map +1 -0
- package/dist/src/tools/guest-agent.js +133 -0
- package/dist/src/tools/guest-agent.js.map +1 -0
- package/dist/src/tools/index.d.ts +11 -0
- package/dist/src/tools/index.d.ts.map +1 -1
- package/dist/src/tools/index.js +154 -1
- package/dist/src/tools/index.js.map +1 -1
- package/dist/src/tools/lab-preflight.d.ts +21 -0
- package/dist/src/tools/lab-preflight.d.ts.map +1 -0
- package/dist/src/tools/lab-preflight.js +22 -0
- package/dist/src/tools/lab-preflight.js.map +1 -0
- package/dist/src/tools/list-hosts.d.ts +11 -0
- package/dist/src/tools/list-hosts.d.ts.map +1 -1
- package/dist/src/tools/list-hosts.js.map +1 -1
- package/dist/src/tools/list-inventory.d.ts.map +1 -1
- package/dist/src/tools/list-inventory.js +21 -0
- package/dist/src/tools/list-inventory.js.map +1 -1
- package/dist/src/tools/network-tools.d.ts +65 -0
- package/dist/src/tools/network-tools.d.ts.map +1 -0
- package/dist/src/tools/network-tools.js +200 -0
- package/dist/src/tools/network-tools.js.map +1 -0
- package/dist/src/tools/provision-tools.d.ts.map +1 -1
- package/dist/src/tools/provision-tools.js +26 -0
- package/dist/src/tools/provision-tools.js.map +1 -1
- package/dist/src/tools/purge-slots.d.ts.map +1 -1
- package/dist/src/tools/purge-slots.js +4 -0
- package/dist/src/tools/purge-slots.js.map +1 -1
- package/dist/src/tools/server-management.d.ts +51 -0
- package/dist/src/tools/server-management.d.ts.map +1 -0
- package/dist/src/tools/server-management.js +175 -0
- package/dist/src/tools/server-management.js.map +1 -0
- package/dist/src/tools/slot-tools.d.ts.map +1 -1
- package/dist/src/tools/slot-tools.js +7 -0
- package/dist/src/tools/slot-tools.js.map +1 -1
- package/dist/src/tools/storage-tools.d.ts +177 -0
- package/dist/src/tools/storage-tools.d.ts.map +1 -1
- package/dist/src/tools/storage-tools.js +420 -0
- package/dist/src/tools/storage-tools.js.map +1 -1
- package/dist/src/tools/update-cli.d.ts +62 -0
- package/dist/src/tools/update-cli.d.ts.map +1 -0
- package/dist/src/tools/update-cli.js +142 -0
- package/dist/src/tools/update-cli.js.map +1 -0
- package/dist/src/tools/vm-action.d.ts +6 -6
- package/dist/src/tools/vm-action.d.ts.map +1 -1
- package/dist/src/tools/vm-action.js +7 -1
- package/dist/src/tools/vm-action.js.map +1 -1
- package/dist/src/types/mcp.d.ts +5 -1
- package/dist/src/types/mcp.d.ts.map +1 -1
- package/dist/src/types/mcp.js +14 -0
- package/dist/src/types/mcp.js.map +1 -1
- package/dist/src/types/proxmox.d.ts +1 -0
- package/dist/src/types/proxmox.d.ts.map +1 -1
- package/dist/src/utils/formatters.d.ts +7 -0
- package/dist/src/utils/formatters.d.ts.map +1 -1
- package/dist/src/utils/formatters.js +48 -5
- package/dist/src/utils/formatters.js.map +1 -1
- package/package.json +2 -3
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { CanonicalCommandResult, LabPreflightInput, LabPreflightResult } from "./types.js";
|
|
2
|
+
export interface CanonicalLabBridgeOptions {
|
|
3
|
+
cliPath?: string;
|
|
4
|
+
inventoryFile?: string;
|
|
5
|
+
args?: string[];
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
required?: boolean;
|
|
8
|
+
runner?: (args: string[]) => Promise<Pick<CanonicalCommandResult, "exitCode" | "stdout" | "stderr">>;
|
|
9
|
+
}
|
|
10
|
+
export declare class CanonicalBridgeError extends Error {
|
|
11
|
+
readonly code = "CANONICAL_INVENTORY_UNAVAILABLE";
|
|
12
|
+
constructor(message: string);
|
|
13
|
+
}
|
|
14
|
+
export declare class CanonicalLabBridge {
|
|
15
|
+
private readonly cliPath?;
|
|
16
|
+
private readonly inventoryFile?;
|
|
17
|
+
private readonly prefixArgs;
|
|
18
|
+
private readonly timeoutMs;
|
|
19
|
+
private readonly required;
|
|
20
|
+
private readonly runner?;
|
|
21
|
+
constructor(options?: CanonicalLabBridgeOptions);
|
|
22
|
+
get isConfigured(): boolean;
|
|
23
|
+
/** The canonical CLI owns identity and lease policy in this mode. */
|
|
24
|
+
get isRequired(): boolean;
|
|
25
|
+
run(args: string[]): Promise<CanonicalCommandResult>;
|
|
26
|
+
preflight(input: LabPreflightInput): Promise<LabPreflightResult>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../../src/canonical/bridge.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAK3F,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,sBAAsB,EAAE,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC;CACtG;AAED,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,qCAAqC;gBAEtC,OAAO,EAAE,MAAM;CAI5B;AAiBD,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAW;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAsC;gBAElD,OAAO,GAAE,yBAA8B;IASnD,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,qEAAqE;IACrE,IAAI,UAAU,IAAI,OAAO,CAExB;IAEK,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC;IA6CpD,SAAS,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;CA8FvE"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { defaultRedactor } from "../utils/redactor.js";
|
|
4
|
+
const execFileAsync = promisify(execFile);
|
|
5
|
+
export class CanonicalBridgeError extends Error {
|
|
6
|
+
code = "CANONICAL_INVENTORY_UNAVAILABLE";
|
|
7
|
+
constructor(message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "CanonicalBridgeError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function redact(text) {
|
|
13
|
+
let result = defaultRedactor.redactString(text);
|
|
14
|
+
// Preserve machine-readable evidence while redacting JSON object keys
|
|
15
|
+
// recursively (plain string regexes cannot safely cover escaped values).
|
|
16
|
+
try {
|
|
17
|
+
const parsed = JSON.parse(text);
|
|
18
|
+
result = JSON.stringify(defaultRedactor.redactObject(parsed));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// Non-JSON CLI output is already covered by the string redactor above.
|
|
22
|
+
}
|
|
23
|
+
return result
|
|
24
|
+
.replace(/(["']?(?:DOPPLER_TOKEN|LAB_[A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD)|password|passwd|token|secret|private_key|privateKey)["']?)\s*([:=])\s*(["']?)[^\s,"'}]+\3/gi, "$1$2[REDACTED]")
|
|
25
|
+
.replace(/(authorization\s*:\s*bearer\s+)["']?[^\s"']+["']?/gi, "$1[REDACTED]");
|
|
26
|
+
}
|
|
27
|
+
export class CanonicalLabBridge {
|
|
28
|
+
cliPath;
|
|
29
|
+
inventoryFile;
|
|
30
|
+
prefixArgs;
|
|
31
|
+
timeoutMs;
|
|
32
|
+
required;
|
|
33
|
+
runner;
|
|
34
|
+
constructor(options = {}) {
|
|
35
|
+
this.cliPath = options.cliPath || process.env.CT_LAB_CANONICAL_CLI;
|
|
36
|
+
this.inventoryFile = options.inventoryFile || process.env.LAB_INVENTORY_FILE;
|
|
37
|
+
this.prefixArgs = options.args || [];
|
|
38
|
+
this.timeoutMs = options.timeoutMs ?? Number(process.env.CT_LAB_CANONICAL_TIMEOUT_MS || 30_000);
|
|
39
|
+
this.required = options.required ?? process.env.CT_LAB_CANONICAL_REQUIRED === "1";
|
|
40
|
+
this.runner = options.runner;
|
|
41
|
+
}
|
|
42
|
+
get isConfigured() {
|
|
43
|
+
return Boolean(this.runner || this.cliPath);
|
|
44
|
+
}
|
|
45
|
+
/** The canonical CLI owns identity and lease policy in this mode. */
|
|
46
|
+
get isRequired() {
|
|
47
|
+
return this.required;
|
|
48
|
+
}
|
|
49
|
+
async run(args) {
|
|
50
|
+
if (!this.isConfigured) {
|
|
51
|
+
throw new CanonicalBridgeError("canonical lab CLI is not configured");
|
|
52
|
+
}
|
|
53
|
+
const command = [
|
|
54
|
+
...(this.inventoryFile ? ["--file", this.inventoryFile] : []),
|
|
55
|
+
...args,
|
|
56
|
+
];
|
|
57
|
+
if (this.runner) {
|
|
58
|
+
const result = await this.runner(command);
|
|
59
|
+
return {
|
|
60
|
+
command,
|
|
61
|
+
exitCode: result.exitCode,
|
|
62
|
+
stdout: redact(result.stdout),
|
|
63
|
+
stderr: redact(result.stderr),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const result = await execFileAsync(this.cliPath, [...this.prefixArgs, ...command], {
|
|
68
|
+
env: process.env,
|
|
69
|
+
timeout: this.timeoutMs,
|
|
70
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
71
|
+
encoding: "utf8",
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
command,
|
|
75
|
+
exitCode: 0,
|
|
76
|
+
stdout: redact(result.stdout),
|
|
77
|
+
stderr: redact(result.stderr),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
const exitCode = typeof error?.code === "number" ? error.code : 1;
|
|
82
|
+
return {
|
|
83
|
+
command,
|
|
84
|
+
exitCode,
|
|
85
|
+
stdout: redact(String(error?.stdout || "")),
|
|
86
|
+
stderr: redact(String(error?.stderr || error?.message || "")),
|
|
87
|
+
timedOut: Boolean(error?.killed),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async preflight(input) {
|
|
92
|
+
const base = {
|
|
93
|
+
assetId: input.assetId,
|
|
94
|
+
linearIssue: input.linearIssue,
|
|
95
|
+
evidence: {},
|
|
96
|
+
};
|
|
97
|
+
if (!this.isConfigured) {
|
|
98
|
+
if (this.required) {
|
|
99
|
+
return {
|
|
100
|
+
...base,
|
|
101
|
+
status: "blocked",
|
|
102
|
+
blockedAt: "configuration",
|
|
103
|
+
checks: { inventory: "blocked", identity: "not_run", ownership: "not_run", releaseLine: "not_run" },
|
|
104
|
+
message: "canonical lab CLI is not configured",
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
throw new CanonicalBridgeError("canonical lab CLI is not configured");
|
|
108
|
+
}
|
|
109
|
+
const inventory = await this.run(["validate"]);
|
|
110
|
+
base.evidence.inventory = inventory;
|
|
111
|
+
if (inventory.exitCode !== 0) {
|
|
112
|
+
return {
|
|
113
|
+
...base,
|
|
114
|
+
status: "blocked",
|
|
115
|
+
blockedAt: "inventory",
|
|
116
|
+
checks: { inventory: "blocked", identity: "not_run", ownership: "not_run", releaseLine: "not_run" },
|
|
117
|
+
message: inventory.stderr || inventory.stdout || "canonical inventory validation failed",
|
|
118
|
+
command: inventory.command,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const identity = await this.run(["vm", "status", "--asset-id", input.assetId]);
|
|
122
|
+
base.evidence.identity = identity;
|
|
123
|
+
if (identity.exitCode !== 0) {
|
|
124
|
+
return {
|
|
125
|
+
...base,
|
|
126
|
+
status: "blocked",
|
|
127
|
+
blockedAt: "identity",
|
|
128
|
+
checks: { inventory: "passed", identity: "blocked", ownership: "not_run", releaseLine: "not_run" },
|
|
129
|
+
message: identity.stderr || identity.stdout || "canonical VM identity validation failed",
|
|
130
|
+
command: identity.command,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const ownership = await this.run(["context", "--asset-id", input.assetId]);
|
|
134
|
+
base.evidence.ownership = ownership;
|
|
135
|
+
let ownsIssue = false;
|
|
136
|
+
try {
|
|
137
|
+
const payload = JSON.parse(ownership.stdout);
|
|
138
|
+
ownsIssue = (payload.linear_context || []).some((entry) => entry.key?.toUpperCase() === input.linearIssue.toUpperCase() && entry.status === "available");
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
ownsIssue = false;
|
|
142
|
+
}
|
|
143
|
+
if (ownership.exitCode !== 0 || !ownsIssue) {
|
|
144
|
+
return {
|
|
145
|
+
...base,
|
|
146
|
+
status: "blocked",
|
|
147
|
+
blockedAt: "ownership",
|
|
148
|
+
checks: { inventory: "passed", identity: "passed", ownership: "blocked", releaseLine: "not_run" },
|
|
149
|
+
message: ownership.stderr || "canonical asset is not provably owned by the supplied Linear issue",
|
|
150
|
+
command: ownership.command,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const releaseLine = await this.run([
|
|
154
|
+
"fleet",
|
|
155
|
+
"versions",
|
|
156
|
+
"--asset-id",
|
|
157
|
+
input.assetId,
|
|
158
|
+
"--format",
|
|
159
|
+
"json",
|
|
160
|
+
]);
|
|
161
|
+
base.evidence.releaseLine = releaseLine;
|
|
162
|
+
if (releaseLine.exitCode !== 0) {
|
|
163
|
+
return {
|
|
164
|
+
...base,
|
|
165
|
+
status: "blocked",
|
|
166
|
+
blockedAt: "releaseLine",
|
|
167
|
+
checks: { inventory: "passed", identity: "passed", ownership: "passed", releaseLine: "blocked" },
|
|
168
|
+
message: releaseLine.stderr || releaseLine.stdout || "release-line qualification failed",
|
|
169
|
+
command: releaseLine.command,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
...base,
|
|
174
|
+
status: "passed",
|
|
175
|
+
checks: { inventory: "passed", identity: "passed", ownership: "passed", releaseLine: "passed" },
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../../src/canonical/bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { CanonicalLabBridge } from "./bridge.js";
|
|
2
|
+
import { LabPreflightInput, LabPreflightResult } from "./types.js";
|
|
3
|
+
export interface CanonicalPreflightResponse {
|
|
4
|
+
exitCode: 0 | 2;
|
|
5
|
+
result: LabPreflightResult;
|
|
6
|
+
}
|
|
7
|
+
export declare function runCanonicalPreflight(input: LabPreflightInput, bridge?: CanonicalLabBridge): Promise<CanonicalPreflightResponse>;
|
|
8
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../../src/canonical/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEnE,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC;IAChB,MAAM,EAAE,kBAAkB,CAAC;CAC5B;AAED,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,iBAAiB,EACxB,MAAM,qBAA6C,GAClD,OAAO,CAAC,0BAA0B,CAAC,CAMrC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CanonicalLabBridge } from "./bridge.js";
|
|
2
|
+
export async function runCanonicalPreflight(input, bridge = new CanonicalLabBridge({ required: true })) {
|
|
3
|
+
const result = await bridge.preflight(input);
|
|
4
|
+
return {
|
|
5
|
+
exitCode: result.status === "passed" ? 0 : 2,
|
|
6
|
+
result,
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../../src/canonical/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAQjD,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,KAAwB,EACxB,MAAM,GAAG,IAAI,kBAAkB,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAEnD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC7C,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM;KACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/canonical/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/canonical/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface CanonicalCommandResult {
|
|
2
|
+
command: string[];
|
|
3
|
+
exitCode: number;
|
|
4
|
+
stdout: string;
|
|
5
|
+
stderr: string;
|
|
6
|
+
timedOut?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface LabPreflightInput {
|
|
9
|
+
assetId: string;
|
|
10
|
+
linearIssue: string;
|
|
11
|
+
}
|
|
12
|
+
export interface LabPreflightResult {
|
|
13
|
+
status: "passed" | "blocked";
|
|
14
|
+
assetId: string;
|
|
15
|
+
linearIssue: string;
|
|
16
|
+
checks: {
|
|
17
|
+
inventory: "passed" | "blocked";
|
|
18
|
+
identity: "passed" | "blocked" | "not_run";
|
|
19
|
+
ownership: "passed" | "blocked" | "not_run";
|
|
20
|
+
releaseLine: "passed" | "blocked" | "not_run";
|
|
21
|
+
};
|
|
22
|
+
blockedAt?: "inventory" | "identity" | "ownership" | "releaseLine" | "configuration";
|
|
23
|
+
message?: string;
|
|
24
|
+
command?: string[];
|
|
25
|
+
evidence: {
|
|
26
|
+
inventory?: CanonicalCommandResult;
|
|
27
|
+
identity?: CanonicalCommandResult;
|
|
28
|
+
ownership?: CanonicalCommandResult;
|
|
29
|
+
releaseLine?: CanonicalCommandResult;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/canonical/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE;QACN,SAAS,EAAE,QAAQ,GAAG,SAAS,CAAC;QAChC,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;QAC3C,SAAS,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;QAC5C,WAAW,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;KAC/C,CAAC;IACF,SAAS,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,eAAe,CAAC;IACrF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE;QACR,SAAS,CAAC,EAAE,sBAAsB,CAAC;QACnC,QAAQ,CAAC,EAAE,sBAAsB,CAAC;QAClC,SAAS,CAAC,EAAE,sBAAsB,CAAC;QACnC,WAAW,CAAC,EAAE,sBAAsB,CAAC;KACtC,CAAC;CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/canonical/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured credential resolution failures.
|
|
3
|
+
*
|
|
4
|
+
* These errors exist to make one thing impossible: silently booting the MCP
|
|
5
|
+
* server (or executing a tool) against synthetic/mock hypervisor and
|
|
6
|
+
* appliance state without the caller having explicitly opted in. See QA-395.
|
|
7
|
+
*/
|
|
8
|
+
export type CredentialResolutionFailureReason = "no_credentials_found" | "doppler_unreachable" | "partial_credentials";
|
|
9
|
+
export declare class CredentialResolutionError extends Error {
|
|
10
|
+
readonly code = "CREDENTIAL_RESOLUTION_ERROR";
|
|
11
|
+
readonly reason: CredentialResolutionFailureReason;
|
|
12
|
+
readonly remediation: string;
|
|
13
|
+
readonly missing: string[];
|
|
14
|
+
constructor(message: string, reason: CredentialResolutionFailureReason, remediation: string, missing?: string[]);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/credentials/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,iCAAiC,GACzC,sBAAsB,GACtB,qBAAqB,GACrB,qBAAqB,CAAC;AAE1B,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,IAAI,iCAAiC;IAC9C,QAAQ,CAAC,MAAM,EAAE,iCAAiC,CAAC;IACnD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;gBAGzB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,iCAAiC,EACzC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,MAAM,EAAO;CAQzB"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured credential resolution failures.
|
|
3
|
+
*
|
|
4
|
+
* These errors exist to make one thing impossible: silently booting the MCP
|
|
5
|
+
* server (or executing a tool) against synthetic/mock hypervisor and
|
|
6
|
+
* appliance state without the caller having explicitly opted in. See QA-395.
|
|
7
|
+
*/
|
|
8
|
+
export class CredentialResolutionError extends Error {
|
|
9
|
+
code = "CREDENTIAL_RESOLUTION_ERROR";
|
|
10
|
+
reason;
|
|
11
|
+
remediation;
|
|
12
|
+
missing;
|
|
13
|
+
constructor(message, reason, remediation, missing = []) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "CredentialResolutionError";
|
|
16
|
+
this.reason = reason;
|
|
17
|
+
this.remediation = remediation;
|
|
18
|
+
this.missing = missing;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/credentials/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IACzC,IAAI,GAAG,6BAA6B,CAAC;IACrC,MAAM,CAAoC;IAC1C,WAAW,CAAS;IACpB,OAAO,CAAW;IAE3B,YACE,OAAe,EACf,MAAyC,EACzC,WAAmB,EACnB,UAAoB,EAAE;QAEtB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;QACxC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF"}
|
|
@@ -10,8 +10,24 @@ export declare class CredentialResolver {
|
|
|
10
10
|
private readonly defaultLabProtectedVmids;
|
|
11
11
|
private readonly defaultMiniProtectedVmids;
|
|
12
12
|
private readonly defaultEsxiProtectedVms;
|
|
13
|
+
/**
|
|
14
|
+
* Returns true only when mock mode was EXPLICITLY requested by the caller,
|
|
15
|
+
* either to force it immediately or to permit it as a fallback when real
|
|
16
|
+
* resolution is incomplete/unavailable. Mock mode must never engage
|
|
17
|
+
* implicitly — see QA-395 (ct-lab-mcp fabricated success against
|
|
18
|
+
* nonexistent hosts when Doppler resolution silently failed).
|
|
19
|
+
*/
|
|
20
|
+
private isForceMock;
|
|
21
|
+
private isMockPermitted;
|
|
13
22
|
/**
|
|
14
23
|
* Resolve all lab credentials through the 3-tier cascade.
|
|
24
|
+
*
|
|
25
|
+
* Mock substitution (full OR partial, i.e. filling in a single missing
|
|
26
|
+
* host) NEVER happens implicitly. If real resolution is incomplete and
|
|
27
|
+
* the caller has not explicitly permitted mock mode (forceMock,
|
|
28
|
+
* CT_LAB_MCP_ALLOW_MOCK=1, CT_LAB_MOCK_MODE=true, PROXMOX_MOCK_MODE=true,
|
|
29
|
+
* or NODE_ENV=test-mock), this throws CredentialResolutionError instead of
|
|
30
|
+
* returning a synthetic success. See QA-395.
|
|
15
31
|
*/
|
|
16
32
|
resolve(options?: ResolutionOptions): Promise<CredentialResolutionResult>;
|
|
17
33
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../../src/credentials/resolver.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAKL,iBAAiB,EACjB,0BAA0B,EAE3B,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../../src/credentials/resolver.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAKL,iBAAiB,EACjB,0BAA0B,EAE3B,MAAM,YAAY,CAAC;AAcpB,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAwB;IACjE,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAgB;IAC1D,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAQtC;IAEF;;;;;;OAMG;IACH,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,eAAe;IASvB;;;;;;;;;OASG;IACG,OAAO,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,0BAA0B,CAAC;IAuLnF;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAmGhC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA0F7B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA+F5B,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,qBAAqB;IAwB7B,OAAO,CAAC,kBAAkB;IAiB1B,OAAO,CAAC,uBAAuB;IAQ/B,OAAO,CAAC,kBAAkB;CAwB3B;AAED,eAAO,MAAM,kBAAkB,oBAA2B,CAAC"}
|
|
@@ -8,6 +8,13 @@
|
|
|
8
8
|
import { DopplerClient } from "./doppler.js";
|
|
9
9
|
import { registerSecret } from "../utils/redactor.js";
|
|
10
10
|
import { logger } from "../utils/logger.js";
|
|
11
|
+
import { CredentialResolutionError } from "./errors.js";
|
|
12
|
+
const ALLOW_MOCK_REMEDIATION = "This is intentional: ct-lab-mcp never silently substitutes fabricated " +
|
|
13
|
+
"hypervisor/appliance state for missing credentials (QA-395). Either " +
|
|
14
|
+
"supply real credentials (LAB_PROXMOX_*, LAB_PROXMOX_MINIS_*, GOVC_*/LAB_ESXI_*, " +
|
|
15
|
+
"LAB_VM_CREDENTIALS_*) directly or via DOPPLER_TOKEN (+ DOPPLER_PROJECT/DOPPLER_CONFIG), " +
|
|
16
|
+
"or explicitly opt into offline mock mode with CT_LAB_MCP_ALLOW_MOCK=1 " +
|
|
17
|
+
"(or --force-mock / forceMock) if this is a genuine test/offline run.";
|
|
11
18
|
export class CredentialResolver {
|
|
12
19
|
defaultLabProtectedVmids = [100, 101, 102, 104];
|
|
13
20
|
defaultMiniProtectedVmids = [];
|
|
@@ -20,15 +27,39 @@ export class CredentialResolver {
|
|
|
20
27
|
"k8s-master",
|
|
21
28
|
"esxi-template-base"
|
|
22
29
|
];
|
|
30
|
+
/**
|
|
31
|
+
* Returns true only when mock mode was EXPLICITLY requested by the caller,
|
|
32
|
+
* either to force it immediately or to permit it as a fallback when real
|
|
33
|
+
* resolution is incomplete/unavailable. Mock mode must never engage
|
|
34
|
+
* implicitly — see QA-395 (ct-lab-mcp fabricated success against
|
|
35
|
+
* nonexistent hosts when Doppler resolution silently failed).
|
|
36
|
+
*/
|
|
37
|
+
isForceMock(options, env) {
|
|
38
|
+
return Boolean(options.forceMock ||
|
|
39
|
+
env.CT_LAB_MOCK_MODE === "true" ||
|
|
40
|
+
env.PROXMOX_MOCK_MODE === "true" ||
|
|
41
|
+
env.NODE_ENV === "test-mock");
|
|
42
|
+
}
|
|
43
|
+
isMockPermitted(options, env) {
|
|
44
|
+
return (this.isForceMock(options, env) ||
|
|
45
|
+
Boolean(options.allowMock) ||
|
|
46
|
+
env.CT_LAB_MCP_ALLOW_MOCK === "1" ||
|
|
47
|
+
env.CT_LAB_MCP_ALLOW_MOCK === "true");
|
|
48
|
+
}
|
|
23
49
|
/**
|
|
24
50
|
* Resolve all lab credentials through the 3-tier cascade.
|
|
51
|
+
*
|
|
52
|
+
* Mock substitution (full OR partial, i.e. filling in a single missing
|
|
53
|
+
* host) NEVER happens implicitly. If real resolution is incomplete and
|
|
54
|
+
* the caller has not explicitly permitted mock mode (forceMock,
|
|
55
|
+
* CT_LAB_MCP_ALLOW_MOCK=1, CT_LAB_MOCK_MODE=true, PROXMOX_MOCK_MODE=true,
|
|
56
|
+
* or NODE_ENV=test-mock), this throws CredentialResolutionError instead of
|
|
57
|
+
* returning a synthetic success. See QA-395.
|
|
25
58
|
*/
|
|
26
59
|
async resolve(options = {}) {
|
|
27
60
|
const env = options.env || process.env;
|
|
28
|
-
const forceMock = options
|
|
29
|
-
|
|
30
|
-
env.PROXMOX_MOCK_MODE === "true" ||
|
|
31
|
-
env.NODE_ENV === "test-mock";
|
|
61
|
+
const forceMock = this.isForceMock(options, env);
|
|
62
|
+
const mockPermitted = this.isMockPermitted(options, env);
|
|
32
63
|
if (forceMock) {
|
|
33
64
|
logger.info("Mock mode forced via configuration. Utilizing Tier 3 (Mock Fallback).");
|
|
34
65
|
return this.createMockResult();
|
|
@@ -39,12 +70,16 @@ export class CredentialResolver {
|
|
|
39
70
|
this.extractProxmoxHostConfig("proxmox-mini", "LAB_PROXMOX_MINI_", env, 4, this.defaultMiniProtectedVmids);
|
|
40
71
|
const directEsxi = this.extractEsxiHostConfig(env);
|
|
41
72
|
const directVmCreds = this.extractVmCredentials(env);
|
|
42
|
-
// If all essential direct credentials exist, return Tier 1
|
|
73
|
+
// If all essential direct credentials exist, return Tier 1 (fully real, no mock involved)
|
|
43
74
|
if (directLab && directMini && directEsxi) {
|
|
44
75
|
logger.info("Resolved full credentials for Proxmox and ESXi hosts from Direct Environment Variables (Tier 1).");
|
|
45
76
|
const proxmoxHosts = [directLab, directMini];
|
|
46
77
|
const esxiHosts = [directEsxi];
|
|
47
|
-
const vmCredentials = directVmCreds || this.createMockVmCredentials();
|
|
78
|
+
const vmCredentials = directVmCreds || (mockPermitted ? this.createMockVmCredentials() : null);
|
|
79
|
+
if (!vmCredentials) {
|
|
80
|
+
throw new CredentialResolutionError("Proxmox and ESXi hypervisor credentials resolved, but no appliance SSH " +
|
|
81
|
+
"credentials (LAB_VM_CREDENTIALS / LAB_VM_CREDENTIALS_USERNAME+PASSWORD) were found.", "partial_credentials", ALLOW_MOCK_REMEDIATION, ["vmCredentials"]);
|
|
82
|
+
}
|
|
48
83
|
this.registerAllSecrets(proxmoxHosts, esxiHosts, vmCredentials);
|
|
49
84
|
return {
|
|
50
85
|
tier: "direct_env",
|
|
@@ -76,6 +111,21 @@ export class CredentialResolver {
|
|
|
76
111
|
const dopplerEsxi = directEsxi || this.extractEsxiHostConfig(mergedSecrets);
|
|
77
112
|
const dopplerVmCreds = directVmCreds || this.extractVmCredentials(mergedSecrets);
|
|
78
113
|
if (dopplerLab || dopplerMini || dopplerEsxi) {
|
|
114
|
+
const fullyResolved = Boolean(dopplerLab && dopplerMini && dopplerEsxi && dopplerVmCreds);
|
|
115
|
+
if (!fullyResolved && !mockPermitted) {
|
|
116
|
+
const missing = [];
|
|
117
|
+
if (!dopplerLab)
|
|
118
|
+
missing.push("proxmox-lab");
|
|
119
|
+
if (!dopplerMini)
|
|
120
|
+
missing.push("proxmox-mini");
|
|
121
|
+
if (!dopplerEsxi)
|
|
122
|
+
missing.push("esxi");
|
|
123
|
+
if (!dopplerVmCreds)
|
|
124
|
+
missing.push("vmCredentials");
|
|
125
|
+
throw new CredentialResolutionError(`Doppler API resolved credentials for [${[dopplerLab && "proxmox-lab", dopplerMini && "proxmox-mini", dopplerEsxi && "esxi"]
|
|
126
|
+
.filter(Boolean)
|
|
127
|
+
.join(", ") || "none"}] but is missing [${missing.join(", ")}].`, "partial_credentials", ALLOW_MOCK_REMEDIATION, missing);
|
|
128
|
+
}
|
|
79
129
|
const proxmoxHosts = [];
|
|
80
130
|
proxmoxHosts.push(dopplerLab || this.createMockProxmoxHost("proxmox-lab", 2, this.defaultLabProtectedVmids));
|
|
81
131
|
proxmoxHosts.push(dopplerMini || this.createMockProxmoxHost("proxmox-mini", 4, this.defaultMiniProtectedVmids));
|
|
@@ -95,13 +145,29 @@ export class CredentialResolver {
|
|
|
95
145
|
}
|
|
96
146
|
}
|
|
97
147
|
catch (err) {
|
|
148
|
+
if (err instanceof CredentialResolutionError) {
|
|
149
|
+
throw err;
|
|
150
|
+
}
|
|
98
151
|
const msg = err instanceof Error ? err.message : String(err);
|
|
99
|
-
|
|
152
|
+
if (!mockPermitted) {
|
|
153
|
+
throw new CredentialResolutionError(`Doppler API resolution failed: ${msg}`, "doppler_unreachable", ALLOW_MOCK_REMEDIATION);
|
|
154
|
+
}
|
|
155
|
+
logger.warn(`Doppler API resolution failed: ${msg}. Mock fallback explicitly permitted; falling back to partial / Tier 3.`);
|
|
100
156
|
}
|
|
101
157
|
}
|
|
102
158
|
// --- Partial Direct Env Fallback ---
|
|
103
159
|
if (directLab || directMini || directEsxi) {
|
|
104
|
-
|
|
160
|
+
if (!mockPermitted) {
|
|
161
|
+
const missing = [];
|
|
162
|
+
if (!directLab)
|
|
163
|
+
missing.push("proxmox-lab");
|
|
164
|
+
if (!directMini)
|
|
165
|
+
missing.push("proxmox-mini");
|
|
166
|
+
if (!directEsxi)
|
|
167
|
+
missing.push("esxi");
|
|
168
|
+
throw new CredentialResolutionError(`Partial direct-environment credentials resolved for [${[directLab && "proxmox-lab", directMini && "proxmox-mini", directEsxi && "esxi"].filter(Boolean).join(", ")}] but missing [${missing.join(", ")}].`, "partial_credentials", ALLOW_MOCK_REMEDIATION, missing);
|
|
169
|
+
}
|
|
170
|
+
logger.info("Engaging partial direct environment resolution with mock fallback for missing targets (explicitly permitted).");
|
|
105
171
|
const proxmoxHosts = [];
|
|
106
172
|
proxmoxHosts.push(directLab || this.createMockProxmoxHost("proxmox-lab", 2, this.defaultLabProtectedVmids));
|
|
107
173
|
proxmoxHosts.push(directMini || this.createMockProxmoxHost("proxmox-mini", 4, this.defaultMiniProtectedVmids));
|
|
@@ -118,7 +184,11 @@ export class CredentialResolver {
|
|
|
118
184
|
};
|
|
119
185
|
}
|
|
120
186
|
// --- Tier 3: Mock Fallback Mode ---
|
|
121
|
-
|
|
187
|
+
if (!mockPermitted) {
|
|
188
|
+
throw new CredentialResolutionError("No hypervisor or appliance credentials were found in the environment, direct " +
|
|
189
|
+
"config, or Doppler (DOPPLER_TOKEN was not set or not provided).", "no_credentials_found", ALLOW_MOCK_REMEDIATION);
|
|
190
|
+
}
|
|
191
|
+
logger.info("No hypervisor credentials found. Mock fallback explicitly permitted; engaging Tier 3 (In-Memory Mock Fallback).");
|
|
122
192
|
return this.createMockResult();
|
|
123
193
|
}
|
|
124
194
|
/**
|
|
@@ -337,13 +407,37 @@ export class CredentialResolver {
|
|
|
337
407
|
const port = portRaw ? parseInt(portRaw, 10) : 2222;
|
|
338
408
|
if (username || password || privateKey) {
|
|
339
409
|
return {
|
|
340
|
-
username: username ? username.trim() : "
|
|
410
|
+
username: username ? username.trim() : "calltelemetry",
|
|
341
411
|
password: password ? password.trim() : undefined,
|
|
342
412
|
privateKey: privateKey ? privateKey.trim() : undefined,
|
|
343
413
|
passphrase: passphrase ? passphrase.trim() : undefined,
|
|
344
414
|
port: isNaN(port) ? 2222 : port
|
|
345
415
|
};
|
|
346
416
|
}
|
|
417
|
+
// 3. Check for specific Doppler secret patterns: LAB_VM_CREDENTIALS_<UUID> or LAB_VM_CT_*
|
|
418
|
+
for (const [key, val] of Object.entries(record)) {
|
|
419
|
+
if (key.startsWith("LAB_VM_CREDENTIALS_") && val && typeof val === "string" && val.startsWith("{")) {
|
|
420
|
+
try {
|
|
421
|
+
const parsed = JSON.parse(val);
|
|
422
|
+
if (parsed?.ssh?.password || parsed?.password) {
|
|
423
|
+
return {
|
|
424
|
+
username: parsed.ssh?.username || parsed.username || "calltelemetry",
|
|
425
|
+
password: parsed.ssh?.password || parsed.password,
|
|
426
|
+
privateKey: parsed.ssh?.privateKey || parsed.privateKey,
|
|
427
|
+
port: parsed.ssh?.port ? Number(parsed.ssh.port) : 2222
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
catch { }
|
|
432
|
+
}
|
|
433
|
+
if (key.includes("LAB_VM") && key.includes("PASSWORD") && val) {
|
|
434
|
+
return {
|
|
435
|
+
username: record.LAB_VM_CT_086_STABLE_QA_USERNAME || "calltelemetry",
|
|
436
|
+
password: val.trim(),
|
|
437
|
+
port: 2222
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
}
|
|
347
441
|
return null;
|
|
348
442
|
}
|
|
349
443
|
createMockResult() {
|