@tolinax/ayoune-cli 2026.3.1 → 2026.4.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.
Files changed (107) hide show
  1. package/data/contextSlots.js +189 -0
  2. package/data/defaultActions.js +9 -0
  3. package/data/modelsAndRights.js +3245 -0
  4. package/data/modules.js +127 -0
  5. package/data/operations.js +5 -0
  6. package/data/services.js +139 -0
  7. package/index.js +11 -0
  8. package/lib/api/apiCallHandler.js +72 -0
  9. package/lib/api/apiClient.js +108 -0
  10. package/lib/api/auditCallHandler.js +21 -0
  11. package/lib/api/decodeToken.js +4 -0
  12. package/lib/api/handleAPIError.js +61 -0
  13. package/lib/api/login.js +45 -0
  14. package/lib/api/searchClient.js +119 -0
  15. package/lib/commands/createAccessCommand.js +126 -0
  16. package/lib/commands/createActionsCommand.js +140 -0
  17. package/lib/commands/createAiCommand.js +188 -0
  18. package/lib/commands/createAliasCommand.js +104 -0
  19. package/lib/commands/createAuditCommand.js +45 -0
  20. package/lib/commands/createBatchCommand.js +291 -0
  21. package/lib/commands/createCompletionsCommand.js +172 -0
  22. package/lib/commands/createConfigCommand.js +202 -0
  23. package/lib/commands/createContextCommand.js +163 -0
  24. package/lib/commands/createCopyCommand.js +36 -0
  25. package/lib/commands/createCreateCommand.js +47 -0
  26. package/lib/commands/createDeleteCommand.js +96 -0
  27. package/lib/commands/createDeployCommand.js +642 -0
  28. package/lib/commands/createDescribeCommand.js +44 -0
  29. package/lib/commands/createEditCommand.js +48 -0
  30. package/lib/commands/createEventsCommand.js +60 -0
  31. package/lib/commands/createExecCommand.js +212 -0
  32. package/lib/commands/createExportCommand.js +216 -0
  33. package/lib/commands/createGetCommand.js +46 -0
  34. package/lib/commands/createJobsCommand.js +163 -0
  35. package/lib/commands/createListCommand.js +48 -0
  36. package/lib/commands/createLoginCommand.js +30 -0
  37. package/lib/commands/createLogoutCommand.js +21 -0
  38. package/lib/commands/createModulesCommand.js +147 -0
  39. package/lib/commands/createMonitorCommand.js +276 -0
  40. package/lib/commands/createPermissionsCommand.js +233 -0
  41. package/lib/commands/createProgram.js +211 -0
  42. package/lib/commands/createSearchCommand.js +251 -0
  43. package/lib/commands/createSelfHostUpdateCommand.js +166 -0
  44. package/lib/commands/createServicesCommand.js +225 -0
  45. package/lib/commands/createSetupCommand.js +305 -0
  46. package/lib/commands/createStatusCommand.js +147 -0
  47. package/lib/commands/createStorageCommand.js +53 -0
  48. package/lib/commands/createStreamCommand.js +50 -0
  49. package/lib/commands/createSyncCommand.js +174 -0
  50. package/lib/commands/createTemplateCommand.js +231 -0
  51. package/lib/commands/createUpdateCommand.js +112 -0
  52. package/lib/commands/createUsersCommand.js +275 -0
  53. package/lib/commands/createWebhooksCommand.js +149 -0
  54. package/lib/commands/createWhoAmICommand.js +90 -0
  55. package/lib/exitCodes.js +6 -0
  56. package/lib/helpers/addSpacesToCamelCase.js +5 -0
  57. package/lib/helpers/cliError.js +24 -0
  58. package/lib/helpers/config.js +7 -0
  59. package/lib/helpers/configLoader.js +66 -0
  60. package/lib/helpers/contextInjector.js +65 -0
  61. package/lib/helpers/contextResolver.js +70 -0
  62. package/lib/helpers/contextStore.js +46 -0
  63. package/lib/helpers/formatDocument.js +176 -0
  64. package/lib/helpers/handleResponseFormatOptions.js +134 -0
  65. package/lib/helpers/initializeSettings.js +14 -0
  66. package/lib/helpers/localStorage.js +4 -0
  67. package/lib/helpers/logo.js +48 -0
  68. package/lib/helpers/makeRandomToken.js +27 -0
  69. package/lib/helpers/parseInt.js +7 -0
  70. package/lib/helpers/requireArg.js +9 -0
  71. package/lib/helpers/resolveCollectionArgs.js +36 -0
  72. package/lib/helpers/sanitizeFields.js +18 -0
  73. package/lib/helpers/saveFile.js +39 -0
  74. package/lib/helpers/secureStorage.js +72 -0
  75. package/lib/helpers/tokenPayload.js +21 -0
  76. package/lib/helpers/updateNotifier.js +49 -0
  77. package/lib/models/getCollections.js +15 -0
  78. package/lib/models/getModelsInModules.js +13 -0
  79. package/lib/models/getModuleFromCollection.js +10 -0
  80. package/lib/operations/handleAuditOperation.js +22 -0
  81. package/lib/operations/handleCollectionOperation.js +91 -0
  82. package/lib/operations/handleCopySingleOperation.js +30 -0
  83. package/lib/operations/handleCreateSingleOperation.js +38 -0
  84. package/lib/operations/handleDeleteSingleOperation.js +14 -0
  85. package/lib/operations/handleDescribeSingleOperation.js +45 -0
  86. package/lib/operations/handleEditOperation.js +51 -0
  87. package/lib/operations/handleEditRawOperation.js +35 -0
  88. package/lib/operations/handleGetOperation.js +35 -0
  89. package/lib/operations/handleGetSingleOperation.js +20 -0
  90. package/lib/operations/handleListOperation.js +67 -0
  91. package/lib/operations/handleSingleAuditOperation.js +27 -0
  92. package/lib/prompts/promptAudits.js +15 -0
  93. package/lib/prompts/promptCollection.js +13 -0
  94. package/lib/prompts/promptCollectionInModule.js +13 -0
  95. package/lib/prompts/promptCollectionWithModule.js +15 -0
  96. package/lib/prompts/promptConfirm.js +12 -0
  97. package/lib/prompts/promptDefaultAction.js +13 -0
  98. package/lib/prompts/promptEntry.js +19 -0
  99. package/lib/prompts/promptFileName.js +12 -0
  100. package/lib/prompts/promptFilePath.js +18 -0
  101. package/lib/prompts/promptModule.js +22 -0
  102. package/lib/prompts/promptName.js +11 -0
  103. package/lib/prompts/promptOperation.js +13 -0
  104. package/lib/socket/customerSocketClient.js +13 -0
  105. package/lib/socket/socketClient.js +12 -0
  106. package/lib/types.js +1 -0
  107. package/package.json +13 -10
@@ -0,0 +1,642 @@
1
+ import chalk from "chalk";
2
+ import { getModuleBaseUrl } from "../api/apiClient.js";
3
+ import { apiCallHandler } from "../api/apiCallHandler.js";
4
+ import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
5
+ import { saveFile } from "../helpers/saveFile.js";
6
+ import { secureStorage } from "../helpers/secureStorage.js";
7
+ import { spinner } from "../../index.js";
8
+ import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
9
+ import { cliError } from "../helpers/cliError.js";
10
+ function getToken() {
11
+ return secureStorage.getItem("token") || process.env.AYOUNE_TOKEN || "";
12
+ }
13
+ export function createDeployCommand(program) {
14
+ const deploy = program
15
+ .command("deploy")
16
+ .alias("dp")
17
+ .description("Manage deployments, pods, clusters, pipelines, and plans via DevOps API");
18
+ // ─── DEPLOYMENTS ───────────────────────────────────────
19
+ // ay deploy list
20
+ deploy
21
+ .command("list")
22
+ .alias("ls")
23
+ .description("List deployments across clusters")
24
+ .option("--cluster <name>", "Filter by cluster name")
25
+ .option("--namespace <ns>", "Filter by namespace")
26
+ .option("--health <status>", "Filter: healthy, degraded, crashed, pending")
27
+ .option("-q, --search <term>", "Search by name")
28
+ .option("-l, --limit <number>", "Limit", parseInt, 50)
29
+ .option("-p, --page <number>", "Page", parseInt, 1)
30
+ .action(async (options) => {
31
+ var _a, _b;
32
+ try {
33
+ const opts = { ...program.opts(), ...options };
34
+ spinner.start({ text: "Fetching deployments...", color: "magenta" });
35
+ const params = {
36
+ page: opts.page,
37
+ limit: opts.limit,
38
+ responseFormat: opts.responseFormat,
39
+ verbosity: opts.verbosity,
40
+ hideMeta: opts.hideMeta,
41
+ };
42
+ if (opts.cluster)
43
+ params.cluster = opts.cluster;
44
+ if (opts.namespace)
45
+ params.namespace = opts.namespace;
46
+ if (opts.health)
47
+ params.health = opts.health;
48
+ if (opts.search)
49
+ params.q = opts.search;
50
+ const res = await apiCallHandler("devops", "deployments", "get", null, params);
51
+ const { result: fmtResult, meta: fmtMeta } = handleResponseFormatOptions(opts, res);
52
+ const total = (_b = (_a = fmtMeta === null || fmtMeta === void 0 ? void 0 : fmtMeta.pageInfo) === null || _a === void 0 ? void 0 : _a.totalEntries) !== null && _b !== void 0 ? _b : (Array.isArray(fmtResult) ? fmtResult.length : 0);
53
+ spinner.success({ text: `Found ${total} deployments` });
54
+ spinner.stop();
55
+ if (opts.save)
56
+ await saveFile("deploy-list", opts, res);
57
+ }
58
+ catch (e) {
59
+ cliError(e.message || "Failed to list deployments", EXIT_GENERAL_ERROR);
60
+ }
61
+ });
62
+ // ay deploy get <id>
63
+ deploy
64
+ .command("get <id>")
65
+ .description("Get deployment details by ID")
66
+ .action(async (id, options) => {
67
+ try {
68
+ const opts = { ...program.opts(), ...options };
69
+ spinner.start({ text: `Fetching deployment ${id}...`, color: "magenta" });
70
+ const res = await apiCallHandler("devops", `deployments/${id}`, "get", null, {
71
+ responseFormat: opts.responseFormat,
72
+ verbosity: opts.verbosity,
73
+ });
74
+ handleResponseFormatOptions(opts, res);
75
+ spinner.success({ text: `Deployment ${id} loaded` });
76
+ spinner.stop();
77
+ }
78
+ catch (e) {
79
+ cliError(e.message || "Failed to get deployment", EXIT_GENERAL_ERROR);
80
+ }
81
+ });
82
+ // ay deploy logs <id>
83
+ deploy
84
+ .command("logs <id>")
85
+ .description("Get deployment logs (supports live streaming)")
86
+ .addHelpText("after", `
87
+ Examples:
88
+ ay deploy logs 64a1b2c3d4e5 Get recent logs
89
+ ay deploy logs 64a1b2c3d4e5 --follow Stream logs in real-time (SSE)
90
+ ay deploy logs 64a1b2c3d4e5 --tail 100 Last 100 log lines`)
91
+ .option("-f, --follow", "Stream logs in real-time (Server-Sent Events)")
92
+ .option("--tail <lines>", "Number of recent log lines", parseInt, 50)
93
+ .action(async (id, options) => {
94
+ try {
95
+ const opts = { ...program.opts(), ...options };
96
+ if (opts.follow) {
97
+ // SSE streaming mode
98
+ spinner.start({ text: `Streaming logs for deployment ${id}...`, color: "magenta" });
99
+ spinner.stop();
100
+ console.error(chalk.dim(` Streaming logs for ${id} (Ctrl+C to stop)\n`));
101
+ const baseUrl = getModuleBaseUrl("devops");
102
+ const url = `${baseUrl}/deployments/${id}/logs?follow=true&tail=${opts.tail}`;
103
+ const https = await import("https");
104
+ const { URL } = await import("url");
105
+ const parsed = new URL(url);
106
+ const req = https.get({
107
+ hostname: parsed.hostname,
108
+ path: `${parsed.pathname}${parsed.search}`,
109
+ headers: {
110
+ Authorization: `Bearer ${getToken()}`,
111
+ Accept: "text/event-stream",
112
+ },
113
+ }, (res) => {
114
+ if (res.statusCode !== 200) {
115
+ cliError(`Error: HTTP ${res.statusCode}`, EXIT_GENERAL_ERROR);
116
+ }
117
+ res.setEncoding("utf-8");
118
+ res.on("data", (chunk) => {
119
+ // Parse SSE: lines starting with "data: "
120
+ const lines = chunk.split("\n");
121
+ for (const line of lines) {
122
+ if (line.startsWith("data: ")) {
123
+ const data = line.slice(6);
124
+ try {
125
+ const parsed = JSON.parse(data);
126
+ if (opts.responseFormat === "json") {
127
+ console.log(JSON.stringify(parsed));
128
+ }
129
+ else {
130
+ const ts = parsed.timestamp || "";
131
+ const level = parsed.level || "info";
132
+ const msg = parsed.message || data;
133
+ console.log(`${chalk.dim(ts)} ${chalk.cyan(level)} ${msg}`);
134
+ }
135
+ }
136
+ catch (_a) {
137
+ console.log(data);
138
+ }
139
+ }
140
+ }
141
+ });
142
+ res.on("end", () => {
143
+ console.error(chalk.dim("\n Stream ended."));
144
+ });
145
+ });
146
+ req.on("error", (e) => {
147
+ cliError(`Stream error: ${e.message}`, EXIT_GENERAL_ERROR);
148
+ });
149
+ // Keep alive until Ctrl+C
150
+ process.on("SIGINT", () => {
151
+ req.destroy();
152
+ process.exit(0);
153
+ });
154
+ // Prevent Node from exiting
155
+ await new Promise(() => { });
156
+ }
157
+ else {
158
+ // One-shot log fetch
159
+ spinner.start({ text: `Fetching logs for deployment ${id}...`, color: "magenta" });
160
+ const res = await apiCallHandler("devops", `deployments/${id}/logs`, "get", null, {
161
+ tail: opts.tail,
162
+ responseFormat: opts.responseFormat,
163
+ verbosity: opts.verbosity,
164
+ });
165
+ handleResponseFormatOptions(opts, res);
166
+ spinner.success({ text: `Logs loaded for deployment ${id}` });
167
+ spinner.stop();
168
+ if (opts.save)
169
+ await saveFile("deploy-logs", opts, res);
170
+ }
171
+ }
172
+ catch (e) {
173
+ cliError(e.message || "Failed to fetch logs", EXIT_GENERAL_ERROR);
174
+ }
175
+ });
176
+ // ay deploy scale <deployment> <replicas>
177
+ deploy
178
+ .command("scale <deployment> <replicas>")
179
+ .description("Scale a deployment to N replicas")
180
+ .action(async (deployment, replicas, options) => {
181
+ try {
182
+ const opts = { ...program.opts(), ...options };
183
+ spinner.start({ text: `Scaling ${deployment} to ${replicas} replicas...`, color: "magenta" });
184
+ const res = await apiCallHandler("devops", "deployments/scale", "post", {
185
+ deployment,
186
+ replicas: parseInt(replicas, 10),
187
+ }, { responseFormat: opts.responseFormat });
188
+ handleResponseFormatOptions(opts, res);
189
+ spinner.success({ text: `Scaled ${deployment} to ${replicas} replicas` });
190
+ spinner.stop();
191
+ }
192
+ catch (e) {
193
+ cliError(e.message || "Failed to scale deployment", EXIT_GENERAL_ERROR);
194
+ }
195
+ });
196
+ // ay deploy restart <deployment>
197
+ deploy
198
+ .command("restart <deployment>")
199
+ .description("Restart a deployment (rolling restart)")
200
+ .action(async (deployment, options) => {
201
+ try {
202
+ const opts = { ...program.opts(), ...options };
203
+ spinner.start({ text: `Restarting ${deployment}...`, color: "magenta" });
204
+ const res = await apiCallHandler("devops", "deployments/restart", "post", {
205
+ deployment,
206
+ }, { responseFormat: opts.responseFormat });
207
+ handleResponseFormatOptions(opts, res);
208
+ spinner.success({ text: `Restart initiated for ${deployment}` });
209
+ spinner.stop();
210
+ }
211
+ catch (e) {
212
+ cliError(e.message || "Failed to restart deployment", EXIT_GENERAL_ERROR);
213
+ }
214
+ });
215
+ // ─── PODS ──────────────────────────────────────────────
216
+ // ay deploy pods
217
+ deploy
218
+ .command("pods")
219
+ .description("List pods across clusters")
220
+ .option("--cluster <name>", "Filter by cluster")
221
+ .option("--namespace <ns>", "Filter by namespace")
222
+ .option("--status <status>", "Filter: Running, Pending, Failed, Succeeded, Unknown")
223
+ .option("--deployment <name>", "Filter by deployment")
224
+ .option("-l, --limit <number>", "Limit", parseInt, 100)
225
+ .action(async (options) => {
226
+ var _a, _b, _c;
227
+ try {
228
+ const opts = { ...program.opts(), ...options };
229
+ spinner.start({ text: "Fetching pods...", color: "magenta" });
230
+ const params = {
231
+ limit: opts.limit,
232
+ responseFormat: opts.responseFormat,
233
+ verbosity: opts.verbosity,
234
+ };
235
+ if (opts.cluster)
236
+ params.cluster = opts.cluster;
237
+ if (opts.namespace)
238
+ params.namespace = opts.namespace;
239
+ if (opts.status)
240
+ params.status = opts.status;
241
+ if (opts.deployment)
242
+ params.deployment = opts.deployment;
243
+ const res = await apiCallHandler("devops", "pods", "get", null, params);
244
+ handleResponseFormatOptions(opts, res);
245
+ const total = (_c = (_b = (_a = res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo) === null || _b === void 0 ? void 0 : _b.totalEntries) !== null && _c !== void 0 ? _c : 0;
246
+ spinner.success({ text: `Found ${total} pods` });
247
+ spinner.stop();
248
+ if (opts.save)
249
+ await saveFile("deploy-pods", opts, res);
250
+ }
251
+ catch (e) {
252
+ cliError(e.message || "Failed to list pods", EXIT_GENERAL_ERROR);
253
+ }
254
+ });
255
+ // ay deploy pod-delete <id>
256
+ deploy
257
+ .command("pod-delete <id>")
258
+ .description("Delete a pod (triggers recreation by controller)")
259
+ .action(async (id, options) => {
260
+ try {
261
+ const opts = { ...program.opts(), ...options };
262
+ spinner.start({ text: `Deleting pod ${id}...`, color: "magenta" });
263
+ const res = await apiCallHandler("devops", `pods/${id}`, "delete", null, {
264
+ responseFormat: opts.responseFormat,
265
+ });
266
+ handleResponseFormatOptions(opts, res);
267
+ spinner.success({ text: `Pod ${id} deleted` });
268
+ spinner.stop();
269
+ }
270
+ catch (e) {
271
+ cliError(e.message || "Failed to delete pod", EXIT_GENERAL_ERROR);
272
+ }
273
+ });
274
+ // ─── CLUSTERS ──────────────────────────────────────────
275
+ // ay deploy clusters
276
+ deploy
277
+ .command("clusters")
278
+ .alias("cl")
279
+ .description("List Kubernetes clusters and their health status")
280
+ .addHelpText("after", `
281
+ Examples:
282
+ ay deploy clusters List all clusters
283
+ ay deploy clusters -r table Show clusters in table format`)
284
+ .option("-l, --limit <number>", "Limit", parseInt, 50)
285
+ .action(async (options) => {
286
+ var _a, _b, _c;
287
+ try {
288
+ const opts = { ...program.opts(), ...options };
289
+ spinner.start({ text: "Fetching clusters...", color: "magenta" });
290
+ const res = await apiCallHandler("devops", "clusters", "get", null, {
291
+ limit: opts.limit,
292
+ responseFormat: opts.responseFormat,
293
+ verbosity: opts.verbosity,
294
+ });
295
+ handleResponseFormatOptions(opts, res);
296
+ const total = (_c = (_b = (_a = res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo) === null || _b === void 0 ? void 0 : _b.totalEntries) !== null && _c !== void 0 ? _c : 0;
297
+ spinner.success({ text: `Found ${total} clusters` });
298
+ spinner.stop();
299
+ if (opts.save)
300
+ await saveFile("deploy-clusters", opts, res);
301
+ }
302
+ catch (e) {
303
+ cliError(e.message || "Failed to list clusters", EXIT_GENERAL_ERROR);
304
+ }
305
+ });
306
+ // ay deploy cluster-sync <id>
307
+ deploy
308
+ .command("cluster-sync <id>")
309
+ .description("Trigger sync for a Kubernetes cluster")
310
+ .action(async (id, options) => {
311
+ try {
312
+ const opts = { ...program.opts(), ...options };
313
+ spinner.start({ text: `Syncing cluster ${id}...`, color: "magenta" });
314
+ const res = await apiCallHandler("devops", `clusters/${id}/sync`, "post", null, {
315
+ responseFormat: opts.responseFormat,
316
+ });
317
+ handleResponseFormatOptions(opts, res);
318
+ spinner.success({ text: `Cluster ${id} sync initiated` });
319
+ spinner.stop();
320
+ }
321
+ catch (e) {
322
+ cliError(e.message || "Failed to sync cluster", EXIT_GENERAL_ERROR);
323
+ }
324
+ });
325
+ // ─── ALERTS ────────────────────────────────────────────
326
+ // ay deploy alerts
327
+ deploy
328
+ .command("alerts")
329
+ .description("List active deployment alerts")
330
+ .option("--severity <level>", "Filter: critical, warning, info")
331
+ .option("--type <type>", "Filter: pod_crash, oom_killed, image_pull_error, deployment_failed, pipeline_failed, cluster_unreachable, high_restart_count, custom")
332
+ .option("-l, --limit <number>", "Limit", parseInt, 50)
333
+ .action(async (options) => {
334
+ var _a, _b, _c;
335
+ try {
336
+ const opts = { ...program.opts(), ...options };
337
+ spinner.start({ text: "Fetching alerts...", color: "magenta" });
338
+ const params = {
339
+ limit: opts.limit,
340
+ responseFormat: opts.responseFormat,
341
+ verbosity: opts.verbosity,
342
+ };
343
+ if (opts.severity)
344
+ params.severity = opts.severity;
345
+ if (opts.type)
346
+ params.type = opts.type;
347
+ const res = await apiCallHandler("devops", "alerts", "get", null, params);
348
+ handleResponseFormatOptions(opts, res);
349
+ const total = (_c = (_b = (_a = res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo) === null || _b === void 0 ? void 0 : _b.totalEntries) !== null && _c !== void 0 ? _c : 0;
350
+ spinner.success({ text: `Found ${total} alerts` });
351
+ spinner.stop();
352
+ if (opts.save)
353
+ await saveFile("deploy-alerts", opts, res);
354
+ }
355
+ catch (e) {
356
+ cliError(e.message || "Failed to fetch alerts", EXIT_GENERAL_ERROR);
357
+ }
358
+ });
359
+ // ─── PIPELINES ─────────────────────────────────────────
360
+ // ay deploy pipelines
361
+ deploy
362
+ .command("pipelines")
363
+ .alias("pipe")
364
+ .description("List recent CI/CD pipelines")
365
+ .option("--repo <slug>", "Filter by repository slug")
366
+ .option("--result <result>", "Filter: SUCCESSFUL, FAILED, STOPPED, EXPIRED")
367
+ .option("-l, --limit <number>", "Limit", parseInt, 20)
368
+ .action(async (options) => {
369
+ var _a, _b, _c;
370
+ try {
371
+ const opts = { ...program.opts(), ...options };
372
+ spinner.start({ text: "Fetching pipelines...", color: "magenta" });
373
+ const params = {
374
+ limit: opts.limit,
375
+ responseFormat: opts.responseFormat,
376
+ verbosity: opts.verbosity,
377
+ };
378
+ if (opts.repo)
379
+ params.repository = opts.repo;
380
+ if (opts.result)
381
+ params.result = opts.result;
382
+ const res = await apiCallHandler("devops", "pipelines", "get", null, params);
383
+ handleResponseFormatOptions(opts, res);
384
+ const total = (_c = (_b = (_a = res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo) === null || _b === void 0 ? void 0 : _b.totalEntries) !== null && _c !== void 0 ? _c : 0;
385
+ spinner.success({ text: `Found ${total} pipelines` });
386
+ spinner.stop();
387
+ if (opts.save)
388
+ await saveFile("deploy-pipelines", opts, res);
389
+ }
390
+ catch (e) {
391
+ cliError(e.message || "Failed to fetch pipelines", EXIT_GENERAL_ERROR);
392
+ }
393
+ });
394
+ // ay deploy trigger <repo>
395
+ deploy
396
+ .command("trigger <repo>")
397
+ .description("Trigger a CI/CD pipeline for a repository")
398
+ .addHelpText("after", `
399
+ Examples:
400
+ ay deploy trigger ayoune-cli Trigger pipeline for ayoune-cli
401
+ ay deploy trigger ayoune-cli --branch feature/new Use specific branch
402
+ ay deploy trigger ayoune-cli --target staging Custom pipeline target`)
403
+ .option("--branch <branch>", "Branch to trigger pipeline on", "master")
404
+ .option("--target <target>", "Pipeline target name")
405
+ .action(async (repo, options) => {
406
+ try {
407
+ const opts = { ...program.opts(), ...options };
408
+ spinner.start({ text: `Triggering pipeline for ${repo}...`, color: "magenta" });
409
+ const body = {
410
+ repositorySlug: repo,
411
+ branch: opts.branch,
412
+ };
413
+ if (opts.target)
414
+ body.target = opts.target;
415
+ const res = await apiCallHandler("devops", "pipelines/trigger", "post", body, {
416
+ responseFormat: opts.responseFormat,
417
+ });
418
+ handleResponseFormatOptions(opts, res);
419
+ spinner.success({ text: `Pipeline triggered for ${repo} (branch: ${opts.branch})` });
420
+ spinner.stop();
421
+ }
422
+ catch (e) {
423
+ cliError(e.message || "Failed to trigger pipeline", EXIT_GENERAL_ERROR);
424
+ }
425
+ });
426
+ // ─── DEPLOYMENT PLANS ──────────────────────────────────
427
+ const plans = deploy
428
+ .command("plans")
429
+ .description("Manage multi-step deployment plans");
430
+ // ay deploy plans list
431
+ plans
432
+ .command("list")
433
+ .alias("ls")
434
+ .description("List deployment plans")
435
+ .option("-l, --limit <number>", "Limit", parseInt, 50)
436
+ .option("-p, --page <number>", "Page", parseInt, 1)
437
+ .action(async (options) => {
438
+ var _a, _b, _c;
439
+ try {
440
+ const opts = { ...program.opts(), ...options };
441
+ spinner.start({ text: "Fetching deployment plans...", color: "magenta" });
442
+ const res = await apiCallHandler("devops", "deployment-plans", "get", null, {
443
+ page: opts.page,
444
+ limit: opts.limit,
445
+ responseFormat: opts.responseFormat,
446
+ verbosity: opts.verbosity,
447
+ });
448
+ handleResponseFormatOptions(opts, res);
449
+ const total = (_c = (_b = (_a = res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo) === null || _b === void 0 ? void 0 : _b.totalEntries) !== null && _c !== void 0 ? _c : 0;
450
+ spinner.success({ text: `Found ${total} deployment plans` });
451
+ spinner.stop();
452
+ if (opts.save)
453
+ await saveFile("deploy-plans", opts, res);
454
+ }
455
+ catch (e) {
456
+ cliError(e.message || "Failed to list deployment plans", EXIT_GENERAL_ERROR);
457
+ }
458
+ });
459
+ // ay deploy plans get <id>
460
+ plans
461
+ .command("get <id>")
462
+ .description("Get deployment plan details")
463
+ .action(async (id, options) => {
464
+ try {
465
+ const opts = { ...program.opts(), ...options };
466
+ spinner.start({ text: `Fetching plan ${id}...`, color: "magenta" });
467
+ const res = await apiCallHandler("devops", `deployment-plans/${id}`, "get", null, {
468
+ responseFormat: opts.responseFormat,
469
+ verbosity: opts.verbosity,
470
+ });
471
+ handleResponseFormatOptions(opts, res);
472
+ spinner.success({ text: `Plan ${id} loaded` });
473
+ spinner.stop();
474
+ }
475
+ catch (e) {
476
+ cliError(e.message || "Failed to get plan", EXIT_GENERAL_ERROR);
477
+ }
478
+ });
479
+ // ay deploy plans create --body '{...}'
480
+ plans
481
+ .command("create")
482
+ .description("Create a new deployment plan")
483
+ .option("--body <json>", "Plan definition as JSON")
484
+ .option("--body-file <path>", "Read plan definition from file")
485
+ .action(async (options) => {
486
+ var _a;
487
+ try {
488
+ const opts = { ...program.opts(), ...options };
489
+ let body = null;
490
+ if (opts.body) {
491
+ try {
492
+ body = JSON.parse(opts.body);
493
+ }
494
+ catch (_b) {
495
+ cliError("Invalid JSON in --body", EXIT_GENERAL_ERROR);
496
+ }
497
+ }
498
+ else if (opts.bodyFile) {
499
+ const fs = await import("fs");
500
+ const content = fs.readFileSync(opts.bodyFile, "utf-8");
501
+ try {
502
+ body = JSON.parse(content);
503
+ }
504
+ catch (_c) {
505
+ cliError(`Invalid JSON in file: ${opts.bodyFile}`, EXIT_GENERAL_ERROR);
506
+ }
507
+ }
508
+ if (!body) {
509
+ cliError("Provide plan definition via --body or --body-file", EXIT_GENERAL_ERROR);
510
+ }
511
+ spinner.start({ text: "Creating deployment plan...", color: "magenta" });
512
+ const res = await apiCallHandler("devops", "deployment-plans", "post", body, {
513
+ responseFormat: opts.responseFormat,
514
+ });
515
+ handleResponseFormatOptions(opts, res);
516
+ const planId = ((_a = res.payload) === null || _a === void 0 ? void 0 : _a._id) || "unknown";
517
+ spinner.success({ text: `Deployment plan ${planId} created` });
518
+ spinner.stop();
519
+ }
520
+ catch (e) {
521
+ cliError(e.message || "Failed to create plan", EXIT_GENERAL_ERROR);
522
+ }
523
+ });
524
+ // ay deploy plans execute <id>
525
+ plans
526
+ .command("execute <id>")
527
+ .alias("exec")
528
+ .description("Execute a deployment plan")
529
+ .action(async (id, options) => {
530
+ try {
531
+ const opts = { ...program.opts(), ...options };
532
+ spinner.start({ text: `Executing deployment plan ${id}...`, color: "magenta" });
533
+ const res = await apiCallHandler("devops", `deployment-plans/${id}/execute`, "post", null, {
534
+ responseFormat: opts.responseFormat,
535
+ });
536
+ handleResponseFormatOptions(opts, res);
537
+ spinner.success({ text: `Plan ${id} execution started` });
538
+ spinner.stop();
539
+ }
540
+ catch (e) {
541
+ cliError(e.message || "Failed to execute plan", EXIT_GENERAL_ERROR);
542
+ }
543
+ });
544
+ // ay deploy plans delete <id>
545
+ plans
546
+ .command("delete <id>")
547
+ .alias("rm")
548
+ .description("Delete a deployment plan")
549
+ .action(async (id, options) => {
550
+ try {
551
+ const opts = { ...program.opts(), ...options };
552
+ spinner.start({ text: `Deleting plan ${id}...`, color: "magenta" });
553
+ const res = await apiCallHandler("devops", `deployment-plans/${id}`, "delete", null, {
554
+ responseFormat: opts.responseFormat,
555
+ });
556
+ handleResponseFormatOptions(opts, res);
557
+ spinner.success({ text: `Plan ${id} deleted` });
558
+ spinner.stop();
559
+ }
560
+ catch (e) {
561
+ cliError(e.message || "Failed to delete plan", EXIT_GENERAL_ERROR);
562
+ }
563
+ });
564
+ // ─── REPOSITORIES ──────────────────────────────────────
565
+ const repos = deploy
566
+ .command("repos")
567
+ .description("Manage git repositories");
568
+ // ay deploy repos list
569
+ repos
570
+ .command("list")
571
+ .alias("ls")
572
+ .description("List registered repositories")
573
+ .option("--provider <provider>", "Filter: bitbucket, github")
574
+ .option("-l, --limit <number>", "Limit", parseInt, 50)
575
+ .option("-p, --page <number>", "Page", parseInt, 1)
576
+ .action(async (options) => {
577
+ var _a, _b, _c;
578
+ try {
579
+ const opts = { ...program.opts(), ...options };
580
+ spinner.start({ text: "Fetching repositories...", color: "magenta" });
581
+ const params = {
582
+ page: opts.page,
583
+ limit: opts.limit,
584
+ responseFormat: opts.responseFormat,
585
+ verbosity: opts.verbosity,
586
+ };
587
+ if (opts.provider)
588
+ params.provider = opts.provider;
589
+ const res = await apiCallHandler("devops", "repositories", "get", null, params);
590
+ handleResponseFormatOptions(opts, res);
591
+ const total = (_c = (_b = (_a = res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo) === null || _b === void 0 ? void 0 : _b.totalEntries) !== null && _c !== void 0 ? _c : 0;
592
+ spinner.success({ text: `Found ${total} repositories` });
593
+ spinner.stop();
594
+ if (opts.save)
595
+ await saveFile("deploy-repos", opts, res);
596
+ }
597
+ catch (e) {
598
+ cliError(e.message || "Failed to list repositories", EXIT_GENERAL_ERROR);
599
+ }
600
+ });
601
+ // ay deploy repos sync <id>
602
+ repos
603
+ .command("sync <id>")
604
+ .description("Sync a repository with its remote provider")
605
+ .action(async (id, options) => {
606
+ try {
607
+ const opts = { ...program.opts(), ...options };
608
+ spinner.start({ text: `Syncing repository ${id}...`, color: "magenta" });
609
+ const res = await apiCallHandler("devops", `repositories/${id}/sync`, "post", null, {
610
+ responseFormat: opts.responseFormat,
611
+ });
612
+ handleResponseFormatOptions(opts, res);
613
+ spinner.success({ text: `Repository ${id} sync initiated` });
614
+ spinner.stop();
615
+ }
616
+ catch (e) {
617
+ cliError(e.message || "Failed to sync repository", EXIT_GENERAL_ERROR);
618
+ }
619
+ });
620
+ // ─── DASHBOARD ─────────────────────────────────────────
621
+ // ay deploy dashboard
622
+ deploy
623
+ .command("dashboard")
624
+ .alias("dash")
625
+ .description("Show deployment overview dashboard (clusters, deployments, alerts, pipelines)")
626
+ .action(async (options) => {
627
+ try {
628
+ const opts = { ...program.opts(), ...options };
629
+ spinner.start({ text: "Fetching dashboard...", color: "magenta" });
630
+ const res = await apiCallHandler("devops", "dashboard", "get", null, {
631
+ responseFormat: opts.responseFormat,
632
+ verbosity: opts.verbosity,
633
+ });
634
+ handleResponseFormatOptions(opts, res);
635
+ spinner.success({ text: "Dashboard loaded" });
636
+ spinner.stop();
637
+ }
638
+ catch (e) {
639
+ cliError(e.message || "Failed to load dashboard", EXIT_GENERAL_ERROR);
640
+ }
641
+ });
642
+ }