@tolinax/ayoune-cli 2026.8.2 → 2026.10.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 (71) hide show
  1. package/lib/api/apiCallHandler.js +1 -1
  2. package/lib/api/apiClient.js +74 -62
  3. package/lib/commands/_registry.js +296 -0
  4. package/lib/commands/aggregate/_shared.js +21 -0
  5. package/lib/commands/aggregate/_stageBuilders.js +295 -0
  6. package/lib/commands/aggregate/exec.js +51 -0
  7. package/lib/commands/aggregate/index.js +38 -0
  8. package/lib/commands/aggregate/list.js +43 -0
  9. package/lib/commands/aggregate/models.js +43 -0
  10. package/lib/commands/aggregate/run.js +53 -0
  11. package/lib/commands/aggregate/save.js +53 -0
  12. package/lib/commands/aggregate/validate.js +47 -0
  13. package/lib/commands/aggregate/wizard.js +174 -0
  14. package/lib/commands/createAggregateCommand.js +5 -658
  15. package/lib/commands/createDeployCommand.js +5 -642
  16. package/lib/commands/createProgram.js +251 -161
  17. package/lib/commands/createSelfHostUpdateCommand.js +1 -20
  18. package/lib/commands/createServicesCommand.js +4 -5
  19. package/lib/commands/createSetupCommand.js +57 -5
  20. package/lib/commands/createWhoAmICommand.js +5 -5
  21. package/lib/commands/deploy/_token.js +8 -0
  22. package/lib/commands/deploy/alerts.js +43 -0
  23. package/lib/commands/deploy/clusters.js +62 -0
  24. package/lib/commands/deploy/dashboard.js +31 -0
  25. package/lib/commands/deploy/deployments.js +216 -0
  26. package/lib/commands/deploy/index.js +31 -0
  27. package/lib/commands/deploy/pipelines.js +82 -0
  28. package/lib/commands/deploy/plans.js +147 -0
  29. package/lib/commands/deploy/pods.js +70 -0
  30. package/lib/commands/deploy/repos.js +63 -0
  31. package/lib/commands/functions/_shared.js +38 -0
  32. package/lib/commands/functions/_validateSource.js +50 -0
  33. package/lib/commands/functions/create.js +109 -0
  34. package/lib/commands/functions/delete.js +40 -0
  35. package/lib/commands/functions/deploy.js +91 -0
  36. package/lib/commands/functions/get.js +31 -0
  37. package/lib/commands/functions/index.js +48 -0
  38. package/lib/commands/functions/invoke.js +75 -0
  39. package/lib/commands/functions/list.js +41 -0
  40. package/lib/commands/functions/logs.js +76 -0
  41. package/lib/commands/functions/rollback.js +44 -0
  42. package/lib/commands/functions/versions.js +32 -0
  43. package/lib/commands/local/_context.js +42 -0
  44. package/lib/commands/local/down.js +50 -0
  45. package/lib/commands/local/exec.js +45 -0
  46. package/lib/commands/local/index.js +40 -0
  47. package/lib/commands/local/logs.js +38 -0
  48. package/lib/commands/local/ps.js +41 -0
  49. package/lib/commands/local/pull.js +40 -0
  50. package/lib/commands/local/restart.js +31 -0
  51. package/lib/commands/local/up.js +80 -0
  52. package/lib/commands/provision/_detectTools.js +52 -0
  53. package/lib/commands/provision/_stateFile.js +36 -0
  54. package/lib/commands/provision/_wizard.js +60 -0
  55. package/lib/commands/provision/aws.js +107 -0
  56. package/lib/commands/provision/azure.js +113 -0
  57. package/lib/commands/provision/destroy.js +119 -0
  58. package/lib/commands/provision/digitalocean.js +82 -0
  59. package/lib/commands/provision/gcp.js +118 -0
  60. package/lib/commands/provision/hetzner.js +220 -0
  61. package/lib/commands/provision/index.js +44 -0
  62. package/lib/commands/provision/status.js +44 -0
  63. package/lib/helpers/dateFormat.js +119 -0
  64. package/lib/helpers/dockerCompose.js +143 -0
  65. package/lib/helpers/formatDocument.js +4 -5
  66. package/lib/helpers/logo.js +86 -13
  67. package/lib/helpers/saveFile.js +4 -9
  68. package/lib/models/getModelsInModules.js +6 -8
  69. package/lib/models/getModuleFromCollection.js +2 -2
  70. package/lib/operations/handleCollectionOperation.js +2 -3
  71. package/package.json +2 -12
@@ -0,0 +1,174 @@
1
+ // `ay aggregate wizard` — interactive pipeline builder.
2
+ //
3
+ // 5-step flow:
4
+ // 1. List + select target model
5
+ // 2. Fetch + display fields for that model (preview)
6
+ // 3. Loop: pick stage type → run the stage builder → append to pipeline
7
+ // until the user picks "Done"
8
+ // 4. Run a 5-row preview of the assembled pipeline so the user sees real
9
+ // output before committing
10
+ // 5. Choose: execute / save / both / print / cancel
11
+ //
12
+ // The stage-builder helpers are in `_stageBuilders.ts` so this file stays
13
+ // focused on the orchestration. Wizard requires a TTY — exits with EXIT_MISUSE
14
+ // otherwise.
15
+ import chalk from "chalk";
16
+ import inquirer from "inquirer";
17
+ import { apiCallHandler } from "../../api/apiCallHandler.js";
18
+ import { handleResponseFormatOptions } from "../../helpers/handleResponseFormatOptions.js";
19
+ import { spinner } from "../../../index.js";
20
+ import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../../exitCodes.js";
21
+ import { cliError } from "../../helpers/cliError.js";
22
+ import { initializeSettings } from "../../helpers/initializeSettings.js";
23
+ import { wrapAggResult } from "./_shared.js";
24
+ import { buildStage } from "./_stageBuilders.js";
25
+ export function addWizardSubcommand(agg, rootProgram) {
26
+ agg
27
+ .command("wizard")
28
+ .alias("wiz")
29
+ .description("Interactive pipeline builder wizard")
30
+ .action(async (options) => {
31
+ var _a, _b, _c, _d, _e;
32
+ try {
33
+ const opts = { ...rootProgram.opts(), ...options };
34
+ if (!process.stdin.isTTY) {
35
+ cliError("Wizard requires an interactive terminal (TTY)", EXIT_MISUSE);
36
+ }
37
+ // Step 1: Select model
38
+ initializeSettings();
39
+ spinner.start({ text: "Loading models...", color: "magenta" });
40
+ const modelsRes = await apiCallHandler("aggregation", "models", "get");
41
+ spinner.stop();
42
+ const modelChoices = (modelsRes.payload || []).map((m) => ({
43
+ name: `${m.name} ${chalk.dim(`(${m.module})`)}`,
44
+ value: m.name,
45
+ }));
46
+ const { selectedModel } = await inquirer.prompt([
47
+ {
48
+ type: "search-list",
49
+ name: "selectedModel",
50
+ message: "Select a model:",
51
+ choices: modelChoices,
52
+ },
53
+ ]);
54
+ // Step 2: Fetch and display fields
55
+ spinner.start({ text: `Loading fields for ${selectedModel}...`, color: "magenta" });
56
+ const fieldsRes = await apiCallHandler("aggregation", `models/${selectedModel}/fields`, "get");
57
+ spinner.stop();
58
+ const fields = ((_a = fieldsRes.payload) === null || _a === void 0 ? void 0 : _a.fields) || [];
59
+ const fieldNames = fields.map((f) => f.field);
60
+ console.log(chalk.cyan(`\n Fields for ${selectedModel}:\n`));
61
+ for (const f of fields) {
62
+ const ref = f.ref ? chalk.dim(` -> ${f.ref}`) : "";
63
+ const req = f.required ? chalk.yellow(" *") : "";
64
+ console.log(` ${chalk.white(f.field)} ${chalk.dim(`(${f.type})`)}${ref}${req}`);
65
+ }
66
+ console.log();
67
+ // Step 3: Build stages iteratively
68
+ const pipeline = [];
69
+ let addMore = true;
70
+ while (addMore) {
71
+ const { stageType } = await inquirer.prompt([
72
+ {
73
+ type: "list",
74
+ name: "stageType",
75
+ message: `Add stage ${pipeline.length + 1}:`,
76
+ choices: [
77
+ { name: "$match — Filter documents", value: "$match" },
78
+ { name: "$group — Group and aggregate", value: "$group" },
79
+ { name: "$sort — Sort results", value: "$sort" },
80
+ { name: "$project — Include/exclude fields", value: "$project" },
81
+ { name: "$unwind — Deconstruct array field", value: "$unwind" },
82
+ { name: "$lookup — Join with another collection", value: "$lookup" },
83
+ { name: "$limit — Limit results", value: "$limit" },
84
+ { name: "$skip — Skip results", value: "$skip" },
85
+ { name: "$count — Count documents", value: "$count" },
86
+ { name: "$addFields — Add computed fields", value: "$addFields" },
87
+ new inquirer.Separator(),
88
+ { name: "Raw JSON stage", value: "$raw" },
89
+ new inquirer.Separator(),
90
+ { name: chalk.green("Done — preview and execute"), value: "$done" },
91
+ ],
92
+ },
93
+ ]);
94
+ if (stageType === "$done") {
95
+ addMore = false;
96
+ break;
97
+ }
98
+ const stage = await buildStage(stageType, fieldNames, fields);
99
+ if (stage) {
100
+ pipeline.push(stage);
101
+ console.log(chalk.dim(` Added: ${JSON.stringify(stage)}\n`));
102
+ }
103
+ }
104
+ if (pipeline.length === 0) {
105
+ cliError("Pipeline is empty — add at least one stage", EXIT_MISUSE);
106
+ }
107
+ // Step 4: Preview
108
+ console.log(chalk.cyan("\n Pipeline:\n"));
109
+ console.log(chalk.dim(" " + JSON.stringify(pipeline, null, 2).split("\n").join("\n ")));
110
+ console.log();
111
+ spinner.start({ text: "Running preview (first 5 results)...", color: "magenta" });
112
+ const previewPipeline = [...pipeline, { $limit: 5 }];
113
+ const previewRes = await apiCallHandler("aggregation", selectedModel, "post", previewPipeline);
114
+ spinner.stop();
115
+ const previewWrapped = wrapAggResult(previewRes);
116
+ handleResponseFormatOptions({ ...opts, responseFormat: "yaml" }, previewWrapped);
117
+ // Step 5: Action choice
118
+ const { action } = await inquirer.prompt([
119
+ {
120
+ type: "list",
121
+ name: "action",
122
+ message: "What would you like to do?",
123
+ choices: [
124
+ { name: "Execute full pipeline", value: "execute" },
125
+ { name: "Save as reusable query", value: "save" },
126
+ { name: "Execute and save", value: "both" },
127
+ { name: "Print pipeline JSON", value: "print" },
128
+ { name: "Cancel", value: "cancel" },
129
+ ],
130
+ },
131
+ ]);
132
+ if (action === "execute" || action === "both") {
133
+ spinner.start({ text: "Executing full pipeline...", color: "magenta" });
134
+ const res = await apiCallHandler("aggregation", selectedModel, "post", pipeline);
135
+ const wrapped = wrapAggResult(res);
136
+ handleResponseFormatOptions(opts, wrapped);
137
+ spinner.success({
138
+ text: `Pipeline returned ${(_c = (_b = wrapped.meta) === null || _b === void 0 ? void 0 : _b.resultCount) !== null && _c !== void 0 ? _c : "?"} results`,
139
+ });
140
+ }
141
+ if (action === "save" || action === "both") {
142
+ const { queryName } = await inquirer.prompt([
143
+ {
144
+ type: "input",
145
+ name: "queryName",
146
+ message: "Query name:",
147
+ validate: (v) => v.length > 0 || "Name is required",
148
+ },
149
+ ]);
150
+ spinner.start({ text: "Saving query...", color: "magenta" });
151
+ const body = {
152
+ name: queryName,
153
+ dataSource: {
154
+ source: "aggregation",
155
+ aggregationModel: selectedModel,
156
+ aggregationPipeline: JSON.stringify(pipeline),
157
+ },
158
+ cache: { enabled: false },
159
+ };
160
+ const saveRes = await apiCallHandler("config", "queries", "post", body, {
161
+ responseFormat: "json",
162
+ });
163
+ const slug = ((_d = saveRes.payload) === null || _d === void 0 ? void 0 : _d.slug) || ((_e = saveRes.payload) === null || _e === void 0 ? void 0 : _e.name) || queryName;
164
+ spinner.success({ text: `Query saved — run with: ay agg exec ${slug}` });
165
+ }
166
+ if (action === "print") {
167
+ console.log(JSON.stringify(pipeline, null, 2));
168
+ }
169
+ }
170
+ catch (e) {
171
+ cliError(e.message || "Wizard failed", EXIT_GENERAL_ERROR);
172
+ }
173
+ });
174
+ }