@tolinax/ayoune-cli 2026.8.2 → 2026.9.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 (36) hide show
  1. package/lib/api/apiCallHandler.js +1 -1
  2. package/lib/api/apiClient.js +74 -62
  3. package/lib/commands/_registry.js +279 -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/createServicesCommand.js +4 -5
  18. package/lib/commands/createWhoAmICommand.js +5 -5
  19. package/lib/commands/deploy/_token.js +8 -0
  20. package/lib/commands/deploy/alerts.js +43 -0
  21. package/lib/commands/deploy/clusters.js +62 -0
  22. package/lib/commands/deploy/dashboard.js +31 -0
  23. package/lib/commands/deploy/deployments.js +216 -0
  24. package/lib/commands/deploy/index.js +31 -0
  25. package/lib/commands/deploy/pipelines.js +82 -0
  26. package/lib/commands/deploy/plans.js +147 -0
  27. package/lib/commands/deploy/pods.js +70 -0
  28. package/lib/commands/deploy/repos.js +63 -0
  29. package/lib/helpers/dateFormat.js +119 -0
  30. package/lib/helpers/formatDocument.js +4 -5
  31. package/lib/helpers/logo.js +86 -13
  32. package/lib/helpers/saveFile.js +4 -9
  33. package/lib/models/getModelsInModules.js +6 -8
  34. package/lib/models/getModuleFromCollection.js +2 -2
  35. package/lib/operations/handleCollectionOperation.js +2 -3
  36. package/package.json +2 -12
@@ -0,0 +1,119 @@
1
+ // Tiny date helpers used across the CLI for output formatting.
2
+ //
3
+ // These exist so we can drop the `moment` dependency (~67KB, deprecated) from
4
+ // the CLI without losing the small set of features it actually provided —
5
+ // `format()`, `fromNow()`, and `unix()`. We don't need a full date library
6
+ // here; the formatting we do is fixed-pattern and the only "intelligent" bit
7
+ // is the relative-time string.
8
+ /**
9
+ * Pad an integer to two digits with a leading zero.
10
+ */
11
+ function pad2(n) {
12
+ return n < 10 ? "0" + n : String(n);
13
+ }
14
+ /**
15
+ * Format a date as `YYYY-MM-DD HH:mm:ss` (UTC offset = local). Replaces
16
+ * `moment(d).format("YYYY-MM-DD HH:mm:ss")`.
17
+ */
18
+ export function formatIsoLocal(d) {
19
+ const date = d instanceof Date ? d : new Date(d);
20
+ if (isNaN(date.getTime()))
21
+ return String(d);
22
+ return (date.getFullYear() +
23
+ "-" +
24
+ pad2(date.getMonth() + 1) +
25
+ "-" +
26
+ pad2(date.getDate()) +
27
+ " " +
28
+ pad2(date.getHours()) +
29
+ ":" +
30
+ pad2(date.getMinutes()) +
31
+ ":" +
32
+ pad2(date.getSeconds()));
33
+ }
34
+ /**
35
+ * Filename-safe timestamp `YYYY_DD_MM_HH_mm_ss`. Matches the existing
36
+ * `saveFile.ts` filename convention bit-for-bit (note: day before month) so
37
+ * we don't break any user that's grepping for old filenames.
38
+ */
39
+ export function timestampForFilename(d = new Date()) {
40
+ return (d.getFullYear() +
41
+ "_" +
42
+ pad2(d.getDate()) +
43
+ "_" +
44
+ pad2(d.getMonth() + 1) +
45
+ "_" +
46
+ pad2(d.getHours()) +
47
+ "_" +
48
+ pad2(d.getMinutes()) +
49
+ "_" +
50
+ pad2(d.getSeconds()));
51
+ }
52
+ /**
53
+ * Parse a Unix timestamp (seconds, not ms) into a Date. Replaces
54
+ * `moment.unix(value)`.
55
+ */
56
+ export function fromUnix(seconds) {
57
+ return new Date(seconds * 1000);
58
+ }
59
+ /**
60
+ * Check that a date value is valid. Replaces `moment(d).isValid()`.
61
+ */
62
+ export function isValidDate(d) {
63
+ const date = d instanceof Date ? d : new Date(d);
64
+ return !isNaN(date.getTime());
65
+ }
66
+ /**
67
+ * "x ago" / "in x" relative-time string. Replaces `moment(d).fromNow()`.
68
+ *
69
+ * Approximation good enough for CLI output — uses the same coarse buckets
70
+ * moment uses (just now, X minutes ago, X hours ago, X days ago, X months
71
+ * ago, X years ago). Future dates ("in X days") supported too, since we use
72
+ * it for token `exp` fields.
73
+ */
74
+ export function fromNow(d, now = new Date()) {
75
+ const date = d instanceof Date ? d : new Date(d);
76
+ if (isNaN(date.getTime()))
77
+ return "invalid date";
78
+ const diffMs = now.getTime() - date.getTime();
79
+ const past = diffMs >= 0;
80
+ const abs = Math.abs(diffMs);
81
+ const sec = Math.round(abs / 1000);
82
+ const min = Math.round(sec / 60);
83
+ const hr = Math.round(min / 60);
84
+ const day = Math.round(hr / 24);
85
+ const mon = Math.round(day / 30);
86
+ const yr = Math.round(day / 365);
87
+ let phrase;
88
+ if (sec < 45)
89
+ phrase = "a few seconds";
90
+ else if (sec < 90)
91
+ phrase = "a minute";
92
+ else if (min < 45)
93
+ phrase = `${min} minutes`;
94
+ else if (min < 90)
95
+ phrase = "an hour";
96
+ else if (hr < 22)
97
+ phrase = `${hr} hours`;
98
+ else if (hr < 36)
99
+ phrase = "a day";
100
+ else if (day < 26)
101
+ phrase = `${day} days`;
102
+ else if (day < 45)
103
+ phrase = "a month";
104
+ else if (day < 320)
105
+ phrase = `${mon} months`;
106
+ else if (day < 548)
107
+ phrase = "a year";
108
+ else
109
+ phrase = `${yr} years`;
110
+ return past ? `${phrase} ago` : `in ${phrase}`;
111
+ }
112
+ /**
113
+ * `a.isBefore(b)` equivalent. Replaces `moment(a).isBefore(moment(b))`.
114
+ */
115
+ export function isBefore(a, b) {
116
+ const da = a instanceof Date ? a : new Date(a);
117
+ const db = b instanceof Date ? b : new Date(b);
118
+ return da.getTime() < db.getTime();
119
+ }
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import moment from 'moment';
2
+ import { formatIsoLocal, fromNow, isValidDate } from './dateFormat.js';
3
3
  import { addSpacesToCamelCase } from './addSpacesToCamelCase.js';
4
4
  const SKIP_FIELDS = new Set(['__v', 'password', 'hash', 'salt']);
5
5
  function isDateField(key, value) {
@@ -10,11 +10,10 @@ function isDateField(key, value) {
10
10
  return false;
11
11
  }
12
12
  function formatDate(value) {
13
- const m = moment(value);
14
- if (!m.isValid())
13
+ if (!isValidDate(value))
15
14
  return String(value);
16
- const formatted = m.format('YYYY-MM-DD HH:mm:ss');
17
- const relative = m.fromNow();
15
+ const formatted = formatIsoLocal(value);
16
+ const relative = fromNow(value);
18
17
  return `${formatted} ${chalk.dim(`(${relative})`)}`;
19
18
  }
20
19
  function formatValue(key, value) {
@@ -1,23 +1,54 @@
1
- import chalk from "chalk";
2
- import figlet from "figlet";
1
+ // Brand logo + helpers used by `--help` output.
2
+ //
3
+ // IMPORTANT: nothing in this module is allowed to top-level-import `figlet`
4
+ // or any other heavy dep. The whole point of moving the logo onto a lazy path
5
+ // is to keep figlet (~200KB) off the cold-start of every `ay <command>`
6
+ // invocation. Help-mode users pay the cost; everyone else doesn't.
7
+ //
8
+ // `getLogoSync()` / `brandHighlight()` / `dim()` are all called from the
9
+ // commander help-text thunks in `createProgram.ts`. They use sync `require`
10
+ // because commander's `formatHelp` and `addHelpText` callbacks are sync.
11
+ import { createRequire } from "node:module";
12
+ const require = createRequire(import.meta.url);
3
13
  export const BRAND_PURPLE = "#6B3FA0";
4
14
  export const BRAND_BLUE = "#2B8DC6";
5
15
  export const BRAND_PINK = "#E91E8C";
6
- export function getLogo() {
16
+ let cachedLogo;
17
+ /**
18
+ * Render the colorized aYOUne ASCII logo for help output. Cached after first
19
+ * call so repeated `--help` invocations don't re-run figlet.
20
+ *
21
+ * Sync rather than async because commander's help-text API is sync.
22
+ */
23
+ export function getLogoSync() {
24
+ if (cachedLogo !== undefined)
25
+ return cachedLogo;
26
+ let chalk;
27
+ let figlet;
7
28
  try {
8
- const raw = figlet.textSync("aYOUne", { font: "Slant" });
9
- return colorizeByColumns(raw);
29
+ chalk = require("chalk").default;
30
+ figlet = require("figlet");
10
31
  }
11
32
  catch (_a) {
12
- // Fallback: simple colored text
13
- return ("\n " +
14
- chalk.hex(BRAND_PURPLE).bold("a") +
15
- chalk.hex(BRAND_BLUE).bold("YOU") +
16
- chalk.hex(BRAND_PINK).bold("ne") +
17
- "\n");
33
+ // figlet missing or chalk missing — fall back to a plain coloured string
34
+ cachedLogo = "\n aYOUne\n";
35
+ return cachedLogo;
36
+ }
37
+ try {
38
+ const raw = figlet.textSync("aYOUne", { font: "Slant" });
39
+ cachedLogo = "\n" + colorizeByColumns(raw, chalk) + "\n";
40
+ }
41
+ catch (_b) {
42
+ cachedLogo =
43
+ "\n " +
44
+ chalk.hex(BRAND_PURPLE).bold("a") +
45
+ chalk.hex(BRAND_BLUE).bold("YOU") +
46
+ chalk.hex(BRAND_PINK).bold("ne") +
47
+ "\n";
18
48
  }
49
+ return cachedLogo;
19
50
  }
20
- function colorizeByColumns(text) {
51
+ function colorizeByColumns(text, chalk) {
21
52
  const lines = text.split("\n");
22
53
  const colored = lines.map((line) => {
23
54
  let result = "";
@@ -38,11 +69,53 @@ function colorizeByColumns(text) {
38
69
  }
39
70
  return result;
40
71
  });
41
- return "\n" + colored.join("\n") + "\n";
72
+ return colored.join("\n");
42
73
  }
74
+ /**
75
+ * One-line tagline shown beneath the logo. Sync; lazy chalk.
76
+ */
43
77
  export function getDescription() {
78
+ let chalk;
79
+ try {
80
+ chalk = require("chalk").default;
81
+ }
82
+ catch (_a) {
83
+ return "aYOUne — Business as a Service CLI";
84
+ }
44
85
  return (chalk.hex(BRAND_PURPLE)("a") +
45
86
  chalk.hex(BRAND_BLUE)("YOU") +
46
87
  chalk.hex(BRAND_PINK)("ne") +
47
88
  chalk.dim(" — Business as a Service CLI"));
48
89
  }
90
+ /**
91
+ * Highlight a string in the brand blue + bold. Used by the help-text custom
92
+ * formatter to colour section headers (`Usage:`, `Commands:`, etc.).
93
+ */
94
+ export function brandHighlight(text) {
95
+ try {
96
+ const chalk = require("chalk").default;
97
+ return chalk.hex(BRAND_BLUE).bold(text);
98
+ }
99
+ catch (_a) {
100
+ return text;
101
+ }
102
+ }
103
+ /**
104
+ * Dimmed text helper for the help footer. Lazy chalk so this module stays
105
+ * cheap to import.
106
+ */
107
+ export function dim(text) {
108
+ try {
109
+ const chalk = require("chalk").default;
110
+ return chalk.dim(text);
111
+ }
112
+ catch (_a) {
113
+ return text;
114
+ }
115
+ }
116
+ // Back-compat: callers that previously imported `getLogo` / `getDescription`
117
+ // from this module still work — getLogo is the async equivalent of
118
+ // getLogoSync, kept around so older call sites (and tests) don't break.
119
+ export async function getLogo() {
120
+ return getLogoSync();
121
+ }
@@ -1,18 +1,13 @@
1
- import { mkdirp } from "mkdirp";
2
- import moment from "moment";
3
1
  import path from "path";
4
- import { writeFile } from "fs/promises";
2
+ import { mkdir, writeFile } from "fs/promises";
5
3
  import { spinner } from "../../index.js";
4
+ import { timestampForFilename } from "./dateFormat.js";
6
5
  export async function saveFile(type, opts, result) {
7
6
  if (opts.save) {
8
- await mkdirp(opts.outPath);
7
+ await mkdir(opts.outPath, { recursive: true });
9
8
  const fileName = opts.name
10
9
  ? opts.name
11
- : type +
12
- "_page_" +
13
- result.meta.pageInfo.page +
14
- "_" +
15
- moment().format("YYYY_DD_MM_HH_mm_ss");
10
+ : type + "_page_" + result.meta.pageInfo.page + "_" + timestampForFilename();
16
11
  const pathToWrite = path.join(opts.outPath, `${fileName}`).toString();
17
12
  console.log(pathToWrite);
18
13
  spinner.start({
@@ -1,13 +1,11 @@
1
- import _ from "lodash";
2
1
  import { addSpacesToCamelCase } from "../helpers/addSpacesToCamelCase.js";
3
2
  import { modelsAndRights } from "../../data/modelsAndRights.js";
4
3
  const getModelsInModules = (module) => {
5
- const m = _.filter(modelsAndRights, { module });
6
- return m.map((el) => {
7
- return {
8
- name: addSpacesToCamelCase(el.plural),
9
- value: el.plural.toLowerCase(),
10
- };
11
- });
4
+ return modelsAndRights
5
+ .filter((el) => el.module === module)
6
+ .map((el) => ({
7
+ name: addSpacesToCamelCase(el.plural),
8
+ value: el.plural.toLowerCase(),
9
+ }));
12
10
  };
13
11
  export { getModelsInModules };
@@ -1,7 +1,7 @@
1
- import _ from "lodash";
2
1
  import { modelsAndRights } from "../../data/modelsAndRights.js";
3
2
  const getModuleFromCollection = (collection) => {
4
- const m = _.find(modelsAndRights, (el) => el.plural.toLowerCase() === collection.toLowerCase());
3
+ const target = collection.toLowerCase();
4
+ const m = modelsAndRights.find((el) => el.plural.toLowerCase() === target);
5
5
  if (!m) {
6
6
  throw new Error(`Unknown collection: "${collection}". Use "ay modules" to browse available collections.`);
7
7
  }
@@ -11,8 +11,7 @@ import { formatDocument } from "../helpers/formatDocument.js";
11
11
  import { promptFilePath } from "../prompts/promptFilePath.js";
12
12
  import { promptFileName } from "../prompts/promptFileName.js";
13
13
  import path from "path";
14
- import { writeFile } from "fs/promises";
15
- import { mkdirp } from "mkdirp";
14
+ import { writeFile, mkdir } from "fs/promises";
16
15
  function getEntryName(res) {
17
16
  const p = res === null || res === void 0 ? void 0 : res.payload;
18
17
  if (!p || typeof p === "string")
@@ -66,7 +65,7 @@ export async function handleCollectionOperation(module, collection, entry, opts)
66
65
  spinner.stop();
67
66
  editContent = res.payload;
68
67
  const folder = await promptFilePath(collection);
69
- await mkdirp(folder);
68
+ await mkdir(folder, { recursive: true });
70
69
  const fileName = await promptFileName(`${collection}_${editContent._id || entry}.${opts.responseFormat}`);
71
70
  const contentToWrite = opts.responseFormat === "yaml"
72
71
  ? (typeof editContent === "string" ? editContent : yaml.dump(editContent))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tolinax/ayoune-cli",
3
- "version": "2026.8.2",
3
+ "version": "2026.9.0",
4
4
  "description": "CLI for the aYOUne Business-as-a-Service platform",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -113,14 +113,11 @@
113
113
  }
114
114
  },
115
115
  "dependencies": {
116
+ "@tolinax/ayoune-core": "^2026.10.0",
116
117
  "@types/jmespath": "^0.15.2",
117
- "axios": "^1.6.7",
118
- "axios-retry": "^4.0.0",
119
118
  "chalk": "^5.3.0",
120
- "chalk-animation": "^2.0.3",
121
119
  "commander": "^12.0.0",
122
120
  "figlet": "^1.7.0",
123
- "gradient-string": "^2.0.2",
124
121
  "inquirer": "^9.2.14",
125
122
  "inquirer-autocomplete-prompt": "^3.0.1",
126
123
  "inquirer-file-tree-selection-prompt": "^2.0.5",
@@ -131,25 +128,18 @@
131
128
  "jmespath": "^0.16.0",
132
129
  "js-yaml": "^4.1.0",
133
130
  "jsonwebtoken": "^9.0.2",
134
- "lodash": "^4.17.21",
135
- "mkdirp": "^3.0.1",
136
- "moment": "^2.30.1",
137
131
  "nanospinner": "^1.1.0",
138
132
  "node-localstorage": "^3.0.5",
139
- "os": "^0.1.2",
140
133
  "socket.io-client": "^4.7.4"
141
134
  },
142
135
  "devDependencies": {
143
136
  "@release-it/conventional-changelog": "^5.0.0",
144
137
  "@types/chalk": "^2.2.0",
145
- "@types/chalk-animation": "^1.6.3",
146
138
  "@types/commander": "^2.12.2",
147
139
  "@types/figlet": "^1.5.8",
148
- "@types/gradient-string": "^1.1.5",
149
140
  "@types/inquirer": "^9.0.7",
150
141
  "@types/js-yaml": "^4.0.9",
151
142
  "@types/jsonwebtoken": "^9.0.5",
152
- "@types/lodash": "^4.14.202",
153
143
  "@types/node": "^20.11.16",
154
144
  "@types/node-localstorage": "^1.3.3",
155
145
  "@typescript-eslint/eslint-plugin": "^8.55.0",