@calltelemetry/ct-lab-mcp 0.1.2 → 0.1.4

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.
Files changed (77) hide show
  1. package/dist/src/appliance/commands.d.ts +4 -0
  2. package/dist/src/appliance/commands.d.ts.map +1 -1
  3. package/dist/src/appliance/commands.js +32 -0
  4. package/dist/src/appliance/commands.js.map +1 -1
  5. package/dist/src/appliance/mock.d.ts.map +1 -1
  6. package/dist/src/appliance/mock.js +42 -0
  7. package/dist/src/appliance/mock.js.map +1 -1
  8. package/dist/src/appliance/types.d.ts +8 -1
  9. package/dist/src/appliance/types.d.ts.map +1 -1
  10. package/dist/src/appliance/types.js.map +1 -1
  11. package/dist/src/credentials/resolver.js +1 -1
  12. package/dist/src/credentials/resolver.js.map +1 -1
  13. package/dist/src/hypervisors/proxmox/client.d.ts +1 -0
  14. package/dist/src/hypervisors/proxmox/client.d.ts.map +1 -1
  15. package/dist/src/hypervisors/proxmox/client.js +63 -3
  16. package/dist/src/hypervisors/proxmox/client.js.map +1 -1
  17. package/dist/src/server.d.ts +3 -1
  18. package/dist/src/server.d.ts.map +1 -1
  19. package/dist/src/server.js +14 -4
  20. package/dist/src/server.js.map +1 -1
  21. package/dist/src/slot/gc.d.ts +11 -2
  22. package/dist/src/slot/gc.d.ts.map +1 -1
  23. package/dist/src/slot/gc.js +20 -2
  24. package/dist/src/slot/gc.js.map +1 -1
  25. package/dist/src/slot/manager.d.ts +34 -3
  26. package/dist/src/slot/manager.d.ts.map +1 -1
  27. package/dist/src/slot/manager.js +572 -3
  28. package/dist/src/slot/manager.js.map +1 -1
  29. package/dist/src/slot/types.d.ts +96 -1
  30. package/dist/src/slot/types.d.ts.map +1 -1
  31. package/dist/src/slot/types.js.map +1 -1
  32. package/dist/src/telemetry/index.d.ts +3 -0
  33. package/dist/src/telemetry/index.d.ts.map +1 -0
  34. package/dist/src/telemetry/index.js +3 -0
  35. package/dist/src/telemetry/index.js.map +1 -0
  36. package/dist/src/telemetry/registry.d.ts +43 -0
  37. package/dist/src/telemetry/registry.d.ts.map +1 -0
  38. package/dist/src/telemetry/registry.js +146 -0
  39. package/dist/src/telemetry/registry.js.map +1 -0
  40. package/dist/src/telemetry/types.d.ts +37 -0
  41. package/dist/src/telemetry/types.d.ts.map +1 -0
  42. package/dist/src/telemetry/types.js +6 -0
  43. package/dist/src/telemetry/types.js.map +1 -0
  44. package/dist/src/tools/appliance-exec.js +1 -1
  45. package/dist/src/tools/appliance-exec.js.map +1 -1
  46. package/dist/src/tools/factory-reset.d.ts +54 -0
  47. package/dist/src/tools/factory-reset.d.ts.map +1 -0
  48. package/dist/src/tools/factory-reset.js +215 -0
  49. package/dist/src/tools/factory-reset.js.map +1 -0
  50. package/dist/src/tools/index.d.ts +4 -1
  51. package/dist/src/tools/index.d.ts.map +1 -1
  52. package/dist/src/tools/index.js +25 -8
  53. package/dist/src/tools/index.js.map +1 -1
  54. package/dist/src/tools/list-hosts.d.ts +2 -2
  55. package/dist/src/tools/list-inventory.d.ts +3 -3
  56. package/dist/src/tools/provision-tools.d.ts +122 -21
  57. package/dist/src/tools/provision-tools.d.ts.map +1 -1
  58. package/dist/src/tools/provision-tools.js +384 -1
  59. package/dist/src/tools/provision-tools.js.map +1 -1
  60. package/dist/src/tools/purge-slots.d.ts +52 -0
  61. package/dist/src/tools/purge-slots.d.ts.map +1 -0
  62. package/dist/src/tools/purge-slots.js +107 -0
  63. package/dist/src/tools/purge-slots.js.map +1 -0
  64. package/dist/src/tools/slot-tools.d.ts +92 -30
  65. package/dist/src/tools/slot-tools.d.ts.map +1 -1
  66. package/dist/src/tools/slot-tools.js +67 -5
  67. package/dist/src/tools/slot-tools.js.map +1 -1
  68. package/dist/src/tools/vm-action.d.ts +22 -22
  69. package/dist/src/utils/formatters.d.ts +38 -4
  70. package/dist/src/utils/formatters.d.ts.map +1 -1
  71. package/dist/src/utils/formatters.js +179 -23
  72. package/dist/src/utils/formatters.js.map +1 -1
  73. package/dist/src/utils/license.d.ts +27 -0
  74. package/dist/src/utils/license.d.ts.map +1 -0
  75. package/dist/src/utils/license.js +59 -0
  76. package/dist/src/utils/license.js.map +1 -0
  77. package/package.json +1 -1
@@ -73,14 +73,14 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
73
73
  template?: string | number | undefined;
74
74
  description?: string | undefined;
75
75
  start?: boolean | undefined;
76
+ memoryMb?: number | undefined;
76
77
  host?: string | undefined;
77
78
  vmid?: string | number | undefined;
78
- vlan?: number | undefined;
79
+ ipAddress?: string | undefined;
79
80
  linearIssue?: string | undefined;
81
+ vlan?: number | undefined;
80
82
  slotClaimId?: string | undefined;
81
83
  cpuCores?: number | undefined;
82
- ipAddress?: string | undefined;
83
- memoryMb?: number | undefined;
84
84
  requester?: string | undefined;
85
85
  targetHost?: string | undefined;
86
86
  sourceVmIdOrName?: string | number | undefined;
@@ -97,15 +97,15 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
97
97
  template?: string | number | undefined;
98
98
  description?: string | undefined;
99
99
  start?: boolean | undefined;
100
+ hypervisorType?: "proxmox" | "esxi" | "any" | undefined;
101
+ memoryMb?: number | undefined;
100
102
  host?: string | undefined;
101
103
  vmid?: string | number | undefined;
102
- vlan?: number | undefined;
104
+ ipAddress?: string | undefined;
103
105
  linearIssue?: string | undefined;
106
+ vlan?: number | undefined;
104
107
  slotClaimId?: string | undefined;
105
108
  cpuCores?: number | undefined;
106
- ipAddress?: string | undefined;
107
- hypervisorType?: "proxmox" | "esxi" | "any" | undefined;
108
- memoryMb?: number | undefined;
109
109
  requester?: string | undefined;
110
110
  targetHost?: string | undefined;
111
111
  sourceVmIdOrName?: string | number | undefined;
@@ -127,14 +127,14 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
127
127
  template?: string | number | undefined;
128
128
  description?: string | undefined;
129
129
  start?: boolean | undefined;
130
+ memoryMb?: number | undefined;
130
131
  host?: string | undefined;
131
132
  vmid?: string | number | undefined;
132
- vlan?: number | undefined;
133
+ ipAddress?: string | undefined;
133
134
  linearIssue?: string | undefined;
135
+ vlan?: number | undefined;
134
136
  slotClaimId?: string | undefined;
135
137
  cpuCores?: number | undefined;
136
- ipAddress?: string | undefined;
137
- memoryMb?: number | undefined;
138
138
  requester?: string | undefined;
139
139
  targetHost?: string | undefined;
140
140
  sourceVmIdOrName?: string | number | undefined;
@@ -151,15 +151,15 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
151
151
  template?: string | number | undefined;
152
152
  description?: string | undefined;
153
153
  start?: boolean | undefined;
154
+ hypervisorType?: "proxmox" | "esxi" | "any" | undefined;
155
+ memoryMb?: number | undefined;
154
156
  host?: string | undefined;
155
157
  vmid?: string | number | undefined;
156
- vlan?: number | undefined;
158
+ ipAddress?: string | undefined;
157
159
  linearIssue?: string | undefined;
160
+ vlan?: number | undefined;
158
161
  slotClaimId?: string | undefined;
159
162
  cpuCores?: number | undefined;
160
- ipAddress?: string | undefined;
161
- hypervisorType?: "proxmox" | "esxi" | "any" | undefined;
162
- memoryMb?: number | undefined;
163
163
  requester?: string | undefined;
164
164
  targetHost?: string | undefined;
165
165
  sourceVmIdOrName?: string | number | undefined;
@@ -181,14 +181,14 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
181
181
  template?: string | number | undefined;
182
182
  description?: string | undefined;
183
183
  start?: boolean | undefined;
184
+ memoryMb?: number | undefined;
184
185
  host?: string | undefined;
185
186
  vmid?: string | number | undefined;
186
- vlan?: number | undefined;
187
+ ipAddress?: string | undefined;
187
188
  linearIssue?: string | undefined;
189
+ vlan?: number | undefined;
188
190
  slotClaimId?: string | undefined;
189
191
  cpuCores?: number | undefined;
190
- ipAddress?: string | undefined;
191
- memoryMb?: number | undefined;
192
192
  requester?: string | undefined;
193
193
  targetHost?: string | undefined;
194
194
  sourceVmIdOrName?: string | number | undefined;
@@ -205,15 +205,15 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
205
205
  template?: string | number | undefined;
206
206
  description?: string | undefined;
207
207
  start?: boolean | undefined;
208
+ hypervisorType?: "proxmox" | "esxi" | "any" | undefined;
209
+ memoryMb?: number | undefined;
208
210
  host?: string | undefined;
209
211
  vmid?: string | number | undefined;
210
- vlan?: number | undefined;
212
+ ipAddress?: string | undefined;
211
213
  linearIssue?: string | undefined;
214
+ vlan?: number | undefined;
212
215
  slotClaimId?: string | undefined;
213
216
  cpuCores?: number | undefined;
214
- ipAddress?: string | undefined;
215
- hypervisorType?: "proxmox" | "esxi" | "any" | undefined;
216
- memoryMb?: number | undefined;
217
217
  requester?: string | undefined;
218
218
  targetHost?: string | undefined;
219
219
  sourceVmIdOrName?: string | number | undefined;
@@ -228,4 +228,105 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
228
228
  export type ProvisionVmInput = z.infer<typeof ProvisionVmInputSchema>;
229
229
  export type ProvisionVmRawInput = z.input<typeof ProvisionVmInputSchema>;
230
230
  export declare function handleProvisionVm(rawArgs: ProvisionVmRawInput, context: ToolContext): Promise<CallToolResult>;
231
+ export declare const AutoProvisionVmInputShape: {
232
+ template: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
233
+ sourceVmIdOrName: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
234
+ name: z.ZodOptional<z.ZodString>;
235
+ newVmName: z.ZodOptional<z.ZodString>;
236
+ host: z.ZodOptional<z.ZodString>;
237
+ targetHost: z.ZodOptional<z.ZodString>;
238
+ hypervisorType: z.ZodDefault<z.ZodOptional<z.ZodEnum<["proxmox", "esxi", "any"]>>>;
239
+ targetVersion: z.ZodOptional<z.ZodString>;
240
+ version: z.ZodOptional<z.ZodString>;
241
+ workloadType: z.ZodOptional<z.ZodEnum<["uat", "perf", "soak", "dev", "regression", "reproduction", "demo"]>>;
242
+ label: z.ZodOptional<z.ZodString>;
243
+ linearIssue: z.ZodOptional<z.ZodString>;
244
+ branch: z.ZodOptional<z.ZodString>;
245
+ tags: z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodString]>>;
246
+ requester: z.ZodOptional<z.ZodString>;
247
+ ttlMinutes: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
248
+ durationMinutes: z.ZodOptional<z.ZodNumber>;
249
+ vlan: z.ZodOptional<z.ZodNumber>;
250
+ ipAddress: z.ZodOptional<z.ZodString>;
251
+ networkBridge: z.ZodOptional<z.ZodString>;
252
+ storage: z.ZodOptional<z.ZodString>;
253
+ waitForReady: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
254
+ readyTimeoutSeconds: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
255
+ };
256
+ export declare const AutoProvisionVmInputSchema: z.ZodObject<{
257
+ template: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
258
+ sourceVmIdOrName: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
259
+ name: z.ZodOptional<z.ZodString>;
260
+ newVmName: z.ZodOptional<z.ZodString>;
261
+ host: z.ZodOptional<z.ZodString>;
262
+ targetHost: z.ZodOptional<z.ZodString>;
263
+ hypervisorType: z.ZodDefault<z.ZodOptional<z.ZodEnum<["proxmox", "esxi", "any"]>>>;
264
+ targetVersion: z.ZodOptional<z.ZodString>;
265
+ version: z.ZodOptional<z.ZodString>;
266
+ workloadType: z.ZodOptional<z.ZodEnum<["uat", "perf", "soak", "dev", "regression", "reproduction", "demo"]>>;
267
+ label: z.ZodOptional<z.ZodString>;
268
+ linearIssue: z.ZodOptional<z.ZodString>;
269
+ branch: z.ZodOptional<z.ZodString>;
270
+ tags: z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodString]>>;
271
+ requester: z.ZodOptional<z.ZodString>;
272
+ ttlMinutes: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
273
+ durationMinutes: z.ZodOptional<z.ZodNumber>;
274
+ vlan: z.ZodOptional<z.ZodNumber>;
275
+ ipAddress: z.ZodOptional<z.ZodString>;
276
+ networkBridge: z.ZodOptional<z.ZodString>;
277
+ storage: z.ZodOptional<z.ZodString>;
278
+ waitForReady: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
279
+ readyTimeoutSeconds: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
280
+ }, "strip", z.ZodTypeAny, {
281
+ hypervisorType: "proxmox" | "esxi" | "any";
282
+ ttlMinutes: number;
283
+ waitForReady: boolean;
284
+ readyTimeoutSeconds: number;
285
+ storage?: string | undefined;
286
+ name?: string | undefined;
287
+ tags?: string | string[] | undefined;
288
+ template?: string | number | undefined;
289
+ version?: string | undefined;
290
+ durationMinutes?: number | undefined;
291
+ host?: string | undefined;
292
+ ipAddress?: string | undefined;
293
+ targetVersion?: string | undefined;
294
+ workloadType?: "uat" | "perf" | "soak" | "dev" | "regression" | "reproduction" | "demo" | undefined;
295
+ label?: string | undefined;
296
+ linearIssue?: string | undefined;
297
+ branch?: string | undefined;
298
+ vlan?: number | undefined;
299
+ requester?: string | undefined;
300
+ targetHost?: string | undefined;
301
+ sourceVmIdOrName?: string | number | undefined;
302
+ newVmName?: string | undefined;
303
+ networkBridge?: string | undefined;
304
+ }, {
305
+ storage?: string | undefined;
306
+ name?: string | undefined;
307
+ tags?: string | string[] | undefined;
308
+ template?: string | number | undefined;
309
+ version?: string | undefined;
310
+ hypervisorType?: "proxmox" | "esxi" | "any" | undefined;
311
+ durationMinutes?: number | undefined;
312
+ ttlMinutes?: number | undefined;
313
+ host?: string | undefined;
314
+ ipAddress?: string | undefined;
315
+ targetVersion?: string | undefined;
316
+ workloadType?: "uat" | "perf" | "soak" | "dev" | "regression" | "reproduction" | "demo" | undefined;
317
+ label?: string | undefined;
318
+ linearIssue?: string | undefined;
319
+ branch?: string | undefined;
320
+ vlan?: number | undefined;
321
+ requester?: string | undefined;
322
+ targetHost?: string | undefined;
323
+ sourceVmIdOrName?: string | number | undefined;
324
+ newVmName?: string | undefined;
325
+ networkBridge?: string | undefined;
326
+ waitForReady?: boolean | undefined;
327
+ readyTimeoutSeconds?: number | undefined;
328
+ }>;
329
+ export type AutoProvisionVmInput = z.infer<typeof AutoProvisionVmInputSchema>;
330
+ export type AutoProvisionVmRawInput = z.input<typeof AutoProvisionVmInputSchema>;
331
+ export declare function handleAutoProvisionVm(rawArgs: AutoProvisionVmRawInput, context: ToolContext): Promise<CallToolResult>;
231
332
  //# sourceMappingURL=provision-tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"provision-tools.d.ts","sourceRoot":"","sources":["../../../src/tools/provision-tools.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAA2C,MAAM,iBAAiB,CAAC;AAE1F,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAU9C,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmHjC,CAAC;AAEF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAShC,CAAC;AAEJ,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACtE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEzE,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,mBAAmB,EAC5B,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC,CAuKzB"}
1
+ {"version":3,"file":"provision-tools.d.ts","sourceRoot":"","sources":["../../../src/tools/provision-tools.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAA2C,MAAM,iBAAiB,CAAC;AAM1F,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAW9C,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmHjC,CAAC;AAEF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAShC,CAAC;AAEJ,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACtE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEzE,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,mBAAmB,EAC5B,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC,CAuKzB;AAMD,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;CA6GrC,CAAC;AAEF,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAsC,CAAC;AAC9E,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC9E,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAEjF,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,uBAAuB,EAChC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC,CAuSzB"}
@@ -6,9 +6,10 @@
6
6
  */
7
7
  import { z } from "zod";
8
8
  import { ProxmoxErrorCode, createToolErrorResult } from "../types/mcp.js";
9
- import { formatProvisionResult } from "../utils/formatters.js";
9
+ import { formatProvisionResult, formatAutoProvisionResult, } from "../utils/formatters.js";
10
10
  import { CapacityExceededError, GlobalCapacityExceededError, DatastoreCapacityExceededError, } from "../slot/types.js";
11
11
  import { ProtectedVmViolationError } from "../guardrails/types.js";
12
+ import { applianceSshExecutor } from "../appliance/executor.js";
12
13
  export const ProvisionVmInputShape = {
13
14
  sourceVmIdOrName: z
14
15
  .union([z.string(), z.number()])
@@ -286,4 +287,386 @@ export async function handleProvisionVm(rawArgs, context) {
286
287
  return createToolErrorResult(ProxmoxErrorCode.UPSTREAM_API_ERROR, msg);
287
288
  }
288
289
  }
290
+ // ============================================================================
291
+ // Auto Provision VM (Milestone M4)
292
+ // ============================================================================
293
+ export const AutoProvisionVmInputShape = {
294
+ template: z
295
+ .union([z.string(), z.number()])
296
+ .optional()
297
+ .describe("Source VM template name or numeric VMID to clone from (e.g. '9000', 'CT-ALMALINUX9-TEMPLATE', 'CT-087-STABLE-TEMPLATE')."),
298
+ sourceVmIdOrName: z
299
+ .union([z.string(), z.number()])
300
+ .optional()
301
+ .describe("Alias for template."),
302
+ name: z
303
+ .string()
304
+ .optional()
305
+ .describe("Target VM name (e.g. 'ct-appliance-uat-105'). Auto-generated if omitted."),
306
+ newVmName: z
307
+ .string()
308
+ .optional()
309
+ .describe("Alias for name."),
310
+ host: z
311
+ .string()
312
+ .optional()
313
+ .describe("Target hypervisor host ('auto', 'proxmox-lab', 'proxmox-mini', 'esxi-intel-192-168-123-176'). Defaults to 'auto' for least-loaded placement."),
314
+ targetHost: z
315
+ .string()
316
+ .optional()
317
+ .describe("Alias for host."),
318
+ hypervisorType: z
319
+ .enum(["proxmox", "esxi", "any"])
320
+ .optional()
321
+ .default("any")
322
+ .describe("Target hypervisor platform preference: 'proxmox', 'esxi', or 'any'."),
323
+ targetVersion: z
324
+ .string()
325
+ .optional()
326
+ .describe("Call Telemetry release version to deploy/pin (e.g. '0.8.7-stable', '0.8.8')."),
327
+ version: z
328
+ .string()
329
+ .optional()
330
+ .describe("Alias for targetVersion."),
331
+ workloadType: z
332
+ .enum(["uat", "perf", "soak", "dev", "regression", "reproduction", "demo"])
333
+ .optional()
334
+ .describe("Workload category for the test slot lease (e.g. 'uat', 'perf', 'dev')."),
335
+ label: z
336
+ .string()
337
+ .optional()
338
+ .describe("Human-readable label for the slot claim and VM."),
339
+ linearIssue: z
340
+ .string()
341
+ .optional()
342
+ .describe("Linear issue key tracking this test VM (e.g. 'CTUAT-693')."),
343
+ branch: z
344
+ .string()
345
+ .optional()
346
+ .describe("Git feature/fix branch name."),
347
+ tags: z
348
+ .union([z.array(z.string()), z.string()])
349
+ .optional()
350
+ .describe("Custom tags to attach to the VM and slot lease."),
351
+ requester: z
352
+ .string()
353
+ .optional()
354
+ .describe("Requester name or agent identifier."),
355
+ ttlMinutes: z
356
+ .number()
357
+ .int()
358
+ .min(1)
359
+ .max(1440)
360
+ .optional()
361
+ .default(60)
362
+ .describe("Slot lease TTL in minutes (default: 60)."),
363
+ durationMinutes: z
364
+ .number()
365
+ .int()
366
+ .min(1)
367
+ .max(1440)
368
+ .optional()
369
+ .describe("Alias for ttlMinutes."),
370
+ vlan: z
371
+ .number()
372
+ .int()
373
+ .min(1)
374
+ .max(4094)
375
+ .optional()
376
+ .describe("VLAN tag assignment (e.g. 124, 125)."),
377
+ ipAddress: z
378
+ .string()
379
+ .optional()
380
+ .describe("Static IP address assignment for the guest VM (e.g. '192.168.124.105')."),
381
+ networkBridge: z
382
+ .string()
383
+ .optional()
384
+ .describe("Network bridge interface (e.g. 'vmbr0', 'VM Network')."),
385
+ storage: z
386
+ .string()
387
+ .optional()
388
+ .describe("Target storage pool / datastore name."),
389
+ waitForReady: z
390
+ .boolean()
391
+ .optional()
392
+ .default(true)
393
+ .describe("Whether to poll SSH:2222 / API health until the VM is ready (default: true)."),
394
+ readyTimeoutSeconds: z
395
+ .number()
396
+ .int()
397
+ .min(5)
398
+ .max(600)
399
+ .optional()
400
+ .default(180)
401
+ .describe("Readiness polling timeout in seconds (default: 180)."),
402
+ };
403
+ export const AutoProvisionVmInputSchema = z.object(AutoProvisionVmInputShape);
404
+ export async function handleAutoProvisionVm(rawArgs, context) {
405
+ let claimedSlot;
406
+ let createdVmId;
407
+ let createdHost;
408
+ const startTime = Date.now();
409
+ try {
410
+ const parseResult = AutoProvisionVmInputSchema.safeParse(rawArgs);
411
+ if (!parseResult.success) {
412
+ const errMsg = parseResult.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join("; ");
413
+ return createToolErrorResult(ProxmoxErrorCode.VALIDATION_ERROR, errMsg);
414
+ }
415
+ const args = parseResult.data;
416
+ const rawHost = args.host || args.targetHost;
417
+ const targetHost = !rawHost || rawHost === "auto" ? undefined : rawHost;
418
+ const hypervisorType = args.hypervisorType || "any";
419
+ const targetVersion = args.targetVersion || args.version;
420
+ const workloadType = args.workloadType || "uat";
421
+ const linearIssue = args.linearIssue;
422
+ const label = args.label;
423
+ const branch = args.branch;
424
+ const durationMinutes = args.durationMinutes ?? args.ttlMinutes ?? 60;
425
+ const vmName = args.name ||
426
+ args.newVmName ||
427
+ `ct-appliance-${workloadType}-${Date.now().toString(36)}`;
428
+ const requester = args.requester || linearIssue || label || vmName;
429
+ const waitForReady = args.waitForReady !== false;
430
+ const readyTimeoutSeconds = args.readyTimeoutSeconds ?? 180;
431
+ let parsedTags = [];
432
+ if (args.tags) {
433
+ if (Array.isArray(args.tags)) {
434
+ parsedTags = args.tags;
435
+ }
436
+ else if (typeof args.tags === "string") {
437
+ parsedTags = args.tags.split(/[;, ]+/).map((t) => t.trim()).filter(Boolean);
438
+ }
439
+ }
440
+ // 1. Atomically Claim 8GB Slot Lease
441
+ claimedSlot = await context.slotManager.claimSlot({
442
+ tag: label || linearIssue || vmName,
443
+ requester,
444
+ linearIssue: linearIssue || (vmName.startsWith("CTUAT-") ? vmName : undefined),
445
+ label,
446
+ workloadType,
447
+ targetVersion,
448
+ branch,
449
+ customTags: parsedTags,
450
+ targetHost,
451
+ hypervisorType,
452
+ durationMinutes,
453
+ purpose: `Auto-provisioned ${workloadType} VM: ${vmName}`,
454
+ });
455
+ const hostId = claimedSlot.hostId;
456
+ createdHost = hostId;
457
+ const hypType = hostId.startsWith("esxi") ? "esxi" : "proxmox";
458
+ // 2. Resolve Base Template
459
+ let template = args.template ?? args.sourceVmIdOrName;
460
+ if (!template && context.hypervisorManager) {
461
+ try {
462
+ const vms = await context.hypervisorManager.listUnifiedInventory({
463
+ includeTemplates: true,
464
+ host: hostId,
465
+ });
466
+ const templateVm = vms.find((v) => v.isTemplate) ||
467
+ vms.find((v) => v.name.toLowerCase().includes("template") ||
468
+ v.name.toLowerCase().includes("tmpl") ||
469
+ (v.tags && v.tags.some((t) => t.toLowerCase().includes("template"))));
470
+ if (templateVm) {
471
+ template = templateVm.id || templateVm.name;
472
+ }
473
+ }
474
+ catch {
475
+ // fallback to default IDs
476
+ }
477
+ }
478
+ if (!template) {
479
+ if (hypType === "esxi") {
480
+ template = "CT-087-STABLE-TEMPLATE";
481
+ }
482
+ else if (hostId === "proxmox-mini") {
483
+ template = "200";
484
+ }
485
+ else {
486
+ template = "102";
487
+ }
488
+ }
489
+ // 3. Clone VM from Template
490
+ const cloneParams = {
491
+ sourceVmIdOrName: template,
492
+ newVmName: vmName,
493
+ targetHost: hostId,
494
+ hypervisorType: hypType,
495
+ fullClone: true,
496
+ description: `Auto-provisioned ${workloadType} VM (${linearIssue || label || "disposable"})`,
497
+ memoryBytes: 8192 * 1024 * 1024,
498
+ cpuCores: 2,
499
+ vlan: args.vlan,
500
+ ipAddress: args.ipAddress,
501
+ networkBridge: args.networkBridge,
502
+ storage: args.storage,
503
+ linearIssue,
504
+ startOnDeploy: true,
505
+ tags: ["disposable", workloadType, ...(targetVersion ? [targetVersion] : []), ...parsedTags],
506
+ requester,
507
+ slotClaimId: claimedSlot.claimId,
508
+ };
509
+ let provisionResult;
510
+ if (context.hypervisorManager) {
511
+ provisionResult = await context.hypervisorManager.provisionVm(cloneParams);
512
+ }
513
+ else if (context.proxmoxManager) {
514
+ const client = context.proxmoxManager.getClient(hostId);
515
+ const targetVmid = 500;
516
+ const sourceNum = typeof template === "number" ? template : parseInt(String(template), 10) || 9000;
517
+ const cloneTask = await client.cloneVm(sourceNum, targetVmid, vmName, { full: true });
518
+ await client.startVm(targetVmid).catch(() => { });
519
+ provisionResult = {
520
+ vmId: String(targetVmid),
521
+ name: vmName,
522
+ hostName: hostId,
523
+ hypervisor: "proxmox",
524
+ status: "running",
525
+ ipAddresses: args.ipAddress ? [args.ipAddress] : [],
526
+ vlan: args.vlan,
527
+ linearIssue,
528
+ slotClaimId: claimedSlot.claimId,
529
+ taskUpid: cloneTask.upid,
530
+ createdAt: new Date().toISOString(),
531
+ };
532
+ }
533
+ else {
534
+ throw new Error("No hypervisor manager available to provision VM.");
535
+ }
536
+ createdVmId = provisionResult.vmId;
537
+ // 4. Attach VM ID/Name to Slot Lease
538
+ await context.slotManager.attachVm(claimedSlot.claimId, provisionResult.vmId, provisionResult.name);
539
+ // 5. Determine Guest IP Address
540
+ const assignedIp = args.ipAddress ||
541
+ (provisionResult.ipAddresses && provisionResult.ipAddresses.length > 0
542
+ ? provisionResult.ipAddresses[0]
543
+ : `192.168.124.${100 + (Number(provisionResult.vmId) % 100 || 5)}`);
544
+ // 6. Polling Loop for SSH / Health Readiness
545
+ let healthStatus = "unverified";
546
+ let readyDurationMs = Date.now() - startTime;
547
+ if (waitForReady) {
548
+ const executor = context.applianceExecutor || applianceSshExecutor;
549
+ const pollStart = Date.now();
550
+ const timeoutMs = readyTimeoutSeconds * 1000;
551
+ let isReady = false;
552
+ while (Date.now() - pollStart < timeoutMs) {
553
+ try {
554
+ const connected = await executor.testConnection(assignedIp, 2222, 2000);
555
+ if (connected) {
556
+ isReady = true;
557
+ healthStatus = "healthy";
558
+ break;
559
+ }
560
+ }
561
+ catch {
562
+ // continue polling
563
+ }
564
+ const elapsed = Date.now() - pollStart;
565
+ if (elapsed + 1000 < timeoutMs) {
566
+ await new Promise((r) => setTimeout(r, Math.min(1000, timeoutMs - elapsed)));
567
+ }
568
+ else {
569
+ break;
570
+ }
571
+ }
572
+ readyDurationMs = Date.now() - startTime;
573
+ if (!isReady) {
574
+ throw new Error(`VM readiness timed out after ${readyTimeoutSeconds}s on ${assignedIp}:2222 (healthStatus unverified).`);
575
+ }
576
+ }
577
+ // 7. Deploy/Pin Target Release Version if Requested
578
+ let deployedVersion;
579
+ if (targetVersion) {
580
+ const executor = context.applianceExecutor || applianceSshExecutor;
581
+ const verRes = await executor.execute({
582
+ targetIp: assignedIp,
583
+ port: 2222,
584
+ commandType: "set_version",
585
+ args: { version: targetVersion },
586
+ timeoutMs: 60000,
587
+ });
588
+ if (verRes.exitCode !== 0) {
589
+ throw new Error(`Failed to deploy requested version '${targetVersion}' on ${assignedIp}: ${verRes.stderr || verRes.stdout}`);
590
+ }
591
+ deployedVersion = targetVersion;
592
+ }
593
+ // 8. Return Verified Endpoints & Summary
594
+ const autoResult = {
595
+ success: true,
596
+ slotId: claimedSlot.slotId,
597
+ claimId: claimedSlot.claimId,
598
+ vmId: provisionResult.vmId,
599
+ vmName: provisionResult.name,
600
+ host: claimedSlot.hostId,
601
+ hypervisorType: hypType,
602
+ ipAddress: assignedIp,
603
+ sshEndpoint: {
604
+ host: assignedIp,
605
+ port: 2222,
606
+ user: "admin",
607
+ },
608
+ apiEndpoint: `https://${assignedIp}:443`,
609
+ targetVersion,
610
+ deployedVersion,
611
+ workloadType,
612
+ label,
613
+ linearIssue,
614
+ branch,
615
+ tags: parsedTags,
616
+ expiresAt: claimedSlot.expiresAt,
617
+ readyDurationMs,
618
+ healthStatus,
619
+ };
620
+ const formatted = formatAutoProvisionResult(autoResult);
621
+ return {
622
+ content: [
623
+ {
624
+ type: "text",
625
+ text: formatted.markdown,
626
+ },
627
+ ],
628
+ structuredContent: formatted.structuredContent,
629
+ };
630
+ }
631
+ catch (error) {
632
+ // Atomic Rollback: Release claimed slot if lease was acquired
633
+ if (claimedSlot) {
634
+ try {
635
+ await context.slotManager.releaseSlot(claimedSlot.claimId);
636
+ }
637
+ catch {
638
+ // ignore lease rollback error
639
+ }
640
+ }
641
+ // Atomic Rollback: Destroy created VM on hypervisor if created before failure
642
+ if (createdVmId && createdHost && context.hypervisorManager) {
643
+ try {
644
+ await context.hypervisorManager.vmAction({
645
+ targetHost: createdHost,
646
+ vmIdOrName: String(createdVmId),
647
+ action: "destroy",
648
+ force: true,
649
+ });
650
+ }
651
+ catch {
652
+ // ignore VM teardown error
653
+ }
654
+ }
655
+ if (error instanceof ProtectedVmViolationError) {
656
+ return createToolErrorResult(ProxmoxErrorCode.PROTECTED_VM_ACTION_DENIED, error.message, {
657
+ vmid: error.vmid,
658
+ vmName: error.vmName,
659
+ action: error.action,
660
+ reason: error.reason,
661
+ });
662
+ }
663
+ if (error instanceof CapacityExceededError ||
664
+ error instanceof GlobalCapacityExceededError ||
665
+ error instanceof DatastoreCapacityExceededError) {
666
+ return createToolErrorResult(ProxmoxErrorCode.CAPACITY_EXCEEDED, error.message);
667
+ }
668
+ const msg = error instanceof Error ? error.message : String(error);
669
+ return createToolErrorResult(ProxmoxErrorCode.UPSTREAM_API_ERROR, msg);
670
+ }
671
+ }
289
672
  //# sourceMappingURL=provision-tools.js.map