@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.
- package/lib/api/apiCallHandler.js +1 -1
- package/lib/api/apiClient.js +74 -62
- package/lib/commands/_registry.js +279 -0
- package/lib/commands/aggregate/_shared.js +21 -0
- package/lib/commands/aggregate/_stageBuilders.js +295 -0
- package/lib/commands/aggregate/exec.js +51 -0
- package/lib/commands/aggregate/index.js +38 -0
- package/lib/commands/aggregate/list.js +43 -0
- package/lib/commands/aggregate/models.js +43 -0
- package/lib/commands/aggregate/run.js +53 -0
- package/lib/commands/aggregate/save.js +53 -0
- package/lib/commands/aggregate/validate.js +47 -0
- package/lib/commands/aggregate/wizard.js +174 -0
- package/lib/commands/createAggregateCommand.js +5 -658
- package/lib/commands/createDeployCommand.js +5 -642
- package/lib/commands/createProgram.js +251 -161
- package/lib/commands/createServicesCommand.js +4 -5
- package/lib/commands/createWhoAmICommand.js +5 -5
- package/lib/commands/deploy/_token.js +8 -0
- package/lib/commands/deploy/alerts.js +43 -0
- package/lib/commands/deploy/clusters.js +62 -0
- package/lib/commands/deploy/dashboard.js +31 -0
- package/lib/commands/deploy/deployments.js +216 -0
- package/lib/commands/deploy/index.js +31 -0
- package/lib/commands/deploy/pipelines.js +82 -0
- package/lib/commands/deploy/plans.js +147 -0
- package/lib/commands/deploy/pods.js +70 -0
- package/lib/commands/deploy/repos.js +63 -0
- package/lib/helpers/dateFormat.js +119 -0
- package/lib/helpers/formatDocument.js +4 -5
- package/lib/helpers/logo.js +86 -13
- package/lib/helpers/saveFile.js +4 -9
- package/lib/models/getModelsInModules.js +6 -8
- package/lib/models/getModuleFromCollection.js +2 -2
- package/lib/operations/handleCollectionOperation.js +2 -3
- 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
|
|
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
|
-
|
|
14
|
-
if (!m.isValid())
|
|
13
|
+
if (!isValidDate(value))
|
|
15
14
|
return String(value);
|
|
16
|
-
const formatted =
|
|
17
|
-
const relative =
|
|
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) {
|
package/lib/helpers/logo.js
CHANGED
|
@@ -1,23 +1,54 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9
|
-
|
|
29
|
+
chalk = require("chalk").default;
|
|
30
|
+
figlet = require("figlet");
|
|
10
31
|
}
|
|
11
32
|
catch (_a) {
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
|
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
|
+
}
|
package/lib/helpers/saveFile.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
|
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
|
|
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.
|
|
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",
|