@tolinax/ayoune-cli 2026.3.1 → 2026.5.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/data/contextSlots.js +189 -0
- package/data/defaultActions.js +9 -0
- package/data/modelsAndRights.js +3245 -0
- package/data/modules.js +127 -0
- package/data/operations.js +5 -0
- package/data/services.js +139 -0
- package/index.js +11 -0
- package/lib/api/apiCallHandler.js +72 -0
- package/lib/api/apiClient.js +108 -0
- package/lib/api/auditCallHandler.js +21 -0
- package/lib/api/decodeToken.js +4 -0
- package/lib/api/handleAPIError.js +61 -0
- package/lib/api/login.js +45 -0
- package/lib/api/searchClient.js +119 -0
- package/lib/commands/createAccessCommand.js +126 -0
- package/lib/commands/createActionsCommand.js +140 -0
- package/lib/commands/createAiCommand.js +188 -0
- package/lib/commands/createAliasCommand.js +104 -0
- package/lib/commands/createAuditCommand.js +45 -0
- package/lib/commands/createBatchCommand.js +291 -0
- package/lib/commands/createCompletionsCommand.js +172 -0
- package/lib/commands/createConfigCommand.js +202 -0
- package/lib/commands/createContextCommand.js +163 -0
- package/lib/commands/createCopyCommand.js +36 -0
- package/lib/commands/createCreateCommand.js +47 -0
- package/lib/commands/createDeleteCommand.js +96 -0
- package/lib/commands/createDeployCommand.js +642 -0
- package/lib/commands/createDescribeCommand.js +44 -0
- package/lib/commands/createEditCommand.js +48 -0
- package/lib/commands/createEventsCommand.js +60 -0
- package/lib/commands/createExecCommand.js +212 -0
- package/lib/commands/createExportCommand.js +216 -0
- package/lib/commands/createGetCommand.js +46 -0
- package/lib/commands/createJobsCommand.js +163 -0
- package/lib/commands/createListCommand.js +48 -0
- package/lib/commands/createLoginCommand.js +30 -0
- package/lib/commands/createLogoutCommand.js +21 -0
- package/lib/commands/createModulesCommand.js +147 -0
- package/lib/commands/createMonitorCommand.js +276 -0
- package/lib/commands/createPermissionsCommand.js +233 -0
- package/lib/commands/createProgram.js +217 -0
- package/lib/commands/createSearchCommand.js +251 -0
- package/lib/commands/createSelfHostUpdateCommand.js +166 -0
- package/lib/commands/createServicesCommand.js +225 -0
- package/lib/commands/createSetupCommand.js +305 -0
- package/lib/commands/createStatusCommand.js +160 -0
- package/lib/commands/createStorageCommand.js +53 -0
- package/lib/commands/createStreamCommand.js +50 -0
- package/lib/commands/createSyncCommand.js +174 -0
- package/lib/commands/createTemplateCommand.js +231 -0
- package/lib/commands/createUpdateCommand.js +112 -0
- package/lib/commands/createUsersCommand.js +275 -0
- package/lib/commands/createWebhooksCommand.js +149 -0
- package/lib/commands/createWhoAmICommand.js +90 -0
- package/lib/exitCodes.js +6 -0
- package/lib/helpers/addSpacesToCamelCase.js +5 -0
- package/lib/helpers/cliError.js +24 -0
- package/lib/helpers/config.js +7 -0
- package/lib/helpers/configLoader.js +66 -0
- package/lib/helpers/contextInjector.js +65 -0
- package/lib/helpers/contextResolver.js +70 -0
- package/lib/helpers/contextStore.js +46 -0
- package/lib/helpers/formatDocument.js +176 -0
- package/lib/helpers/handleResponseFormatOptions.js +134 -0
- package/lib/helpers/initializeSettings.js +14 -0
- package/lib/helpers/localStorage.js +4 -0
- package/lib/helpers/logo.js +48 -0
- package/lib/helpers/makeRandomToken.js +27 -0
- package/lib/helpers/parseInt.js +7 -0
- package/lib/helpers/requireArg.js +9 -0
- package/lib/helpers/resolveCollectionArgs.js +36 -0
- package/lib/helpers/sanitizeFields.js +18 -0
- package/lib/helpers/saveFile.js +39 -0
- package/lib/helpers/secureStorage.js +72 -0
- package/lib/helpers/tokenPayload.js +21 -0
- package/lib/helpers/updateNotifier.js +50 -0
- package/lib/models/getCollections.js +15 -0
- package/lib/models/getModelsInModules.js +13 -0
- package/lib/models/getModuleFromCollection.js +10 -0
- package/lib/operations/handleAuditOperation.js +22 -0
- package/lib/operations/handleCollectionOperation.js +91 -0
- package/lib/operations/handleCopySingleOperation.js +30 -0
- package/lib/operations/handleCreateSingleOperation.js +38 -0
- package/lib/operations/handleDeleteSingleOperation.js +14 -0
- package/lib/operations/handleDescribeSingleOperation.js +45 -0
- package/lib/operations/handleEditOperation.js +51 -0
- package/lib/operations/handleEditRawOperation.js +35 -0
- package/lib/operations/handleGetOperation.js +35 -0
- package/lib/operations/handleGetSingleOperation.js +20 -0
- package/lib/operations/handleListOperation.js +67 -0
- package/lib/operations/handleSingleAuditOperation.js +27 -0
- package/lib/prompts/promptAudits.js +15 -0
- package/lib/prompts/promptCollection.js +13 -0
- package/lib/prompts/promptCollectionInModule.js +13 -0
- package/lib/prompts/promptCollectionWithModule.js +15 -0
- package/lib/prompts/promptConfirm.js +12 -0
- package/lib/prompts/promptDefaultAction.js +13 -0
- package/lib/prompts/promptEntry.js +19 -0
- package/lib/prompts/promptFileName.js +12 -0
- package/lib/prompts/promptFilePath.js +18 -0
- package/lib/prompts/promptModule.js +22 -0
- package/lib/prompts/promptName.js +11 -0
- package/lib/prompts/promptOperation.js +13 -0
- package/lib/socket/customerSocketClient.js +13 -0
- package/lib/socket/socketClient.js +12 -0
- package/lib/types.js +1 -0
- package/package.json +13 -10
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { localStorage } from "../helpers/localStorage.js";
|
|
3
|
+
import { loadConfig } from "../helpers/configLoader.js";
|
|
4
|
+
import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
|
|
5
|
+
import { cliError } from "../helpers/cliError.js";
|
|
6
|
+
const REDACT_KEYS = new Set(["token", "refreshToken"]);
|
|
7
|
+
function redact(value) {
|
|
8
|
+
if (value.length <= 8)
|
|
9
|
+
return "••••";
|
|
10
|
+
return value.slice(0, 4) + "••••" + value.slice(-4);
|
|
11
|
+
}
|
|
12
|
+
export function createStorageCommand(program) {
|
|
13
|
+
program
|
|
14
|
+
.command("storage")
|
|
15
|
+
.alias("s")
|
|
16
|
+
.description("Display stored session data and preferences")
|
|
17
|
+
.action(async () => {
|
|
18
|
+
var _a, _b;
|
|
19
|
+
try {
|
|
20
|
+
const storage = {};
|
|
21
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
22
|
+
const key = localStorage.key(i);
|
|
23
|
+
if (key) {
|
|
24
|
+
const value = (_a = localStorage.getItem(key)) !== null && _a !== void 0 ? _a : "";
|
|
25
|
+
storage[key] = REDACT_KEYS.has(key) ? redact(value) : value;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
console.log();
|
|
29
|
+
console.log(chalk.bold(" Session Storage"));
|
|
30
|
+
console.log(chalk.dim(` ${"─".repeat(44)}`));
|
|
31
|
+
const maxKey = Math.max(...Object.keys(storage).map((k) => k.length), 0);
|
|
32
|
+
for (const [key, value] of Object.entries(storage)) {
|
|
33
|
+
console.log(` ${chalk.dim(key.padEnd(maxKey + 2))} ${chalk.white(value)}`);
|
|
34
|
+
}
|
|
35
|
+
const config = loadConfig();
|
|
36
|
+
const defaults = (_b = config.defaults) !== null && _b !== void 0 ? _b : {};
|
|
37
|
+
const defaultEntries = Object.entries(defaults).filter(([, v]) => v !== undefined && v !== null);
|
|
38
|
+
if (defaultEntries.length > 0) {
|
|
39
|
+
console.log();
|
|
40
|
+
console.log(chalk.bold(" Config Defaults"));
|
|
41
|
+
console.log(chalk.dim(` ${"─".repeat(44)}`));
|
|
42
|
+
const maxDefault = Math.max(...defaultEntries.map(([k]) => k.length));
|
|
43
|
+
for (const [key, value] of defaultEntries) {
|
|
44
|
+
console.log(` ${chalk.dim(key.padEnd(maxDefault + 2))} ${chalk.white(String(value))}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
console.log();
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Option } from "commander";
|
|
2
|
+
import { secureStorage } from "../helpers/secureStorage.js";
|
|
3
|
+
import { decodeToken } from "../api/decodeToken.js";
|
|
4
|
+
import { customerSocket } from "../socket/customerSocketClient.js";
|
|
5
|
+
import { spinner } from "../../index.js";
|
|
6
|
+
import yaml from "js-yaml";
|
|
7
|
+
import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
|
|
8
|
+
import { cliError } from "../helpers/cliError.js";
|
|
9
|
+
export function createStreamCommand(program) {
|
|
10
|
+
program
|
|
11
|
+
.command("stream")
|
|
12
|
+
.alias("listen")
|
|
13
|
+
.description("Subscribe to real-time data changes via WebSocket")
|
|
14
|
+
.addHelpText("after", `
|
|
15
|
+
Examples:
|
|
16
|
+
ay stream Stream all data changes as JSON
|
|
17
|
+
ay stream -f yaml Stream changes in YAML format
|
|
18
|
+
ay listen -l minimal Stream with minimal detail`)
|
|
19
|
+
.addOption(new Option("-f, --format <format>", "Set the output format")
|
|
20
|
+
.choices(["json", "yaml", "table"])
|
|
21
|
+
.default("json"))
|
|
22
|
+
.addOption(new Option("-l, --level <level>", "Set the detail level")
|
|
23
|
+
.choices(["default", "minimal", "extended"])
|
|
24
|
+
.default("default"))
|
|
25
|
+
.action(async (options) => {
|
|
26
|
+
try {
|
|
27
|
+
const tokenPayload = decodeToken(secureStorage.getItem("token"));
|
|
28
|
+
const user = tokenPayload.payload;
|
|
29
|
+
console.log(`Starting stream with [${user._customerID}]`);
|
|
30
|
+
spinner.start({ text: `Starting stream with [${user._customerID}]` });
|
|
31
|
+
const socket = customerSocket(user);
|
|
32
|
+
spinner.update({ text: "Stream active" });
|
|
33
|
+
socket.onAny((channel, data) => {
|
|
34
|
+
spinner.update({ text: `Received [${channel}]` });
|
|
35
|
+
if (options.format === "table") {
|
|
36
|
+
console.table(data);
|
|
37
|
+
}
|
|
38
|
+
if (options.format === "yaml") {
|
|
39
|
+
console.log(yaml.dump(data));
|
|
40
|
+
}
|
|
41
|
+
if (options.format === "json") {
|
|
42
|
+
console.log(JSON.stringify(data, null, 2));
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { apiCallHandler } from "../api/apiCallHandler.js";
|
|
2
|
+
import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
|
|
3
|
+
import { spinner } from "../../index.js";
|
|
4
|
+
import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
|
|
5
|
+
import { cliError } from "../helpers/cliError.js";
|
|
6
|
+
export function createSyncCommand(program) {
|
|
7
|
+
const sync = program
|
|
8
|
+
.command("sync")
|
|
9
|
+
.description("Synchronize platform data across systems");
|
|
10
|
+
// ay sync repos
|
|
11
|
+
sync
|
|
12
|
+
.command("repos")
|
|
13
|
+
.description("Sync repositories from connected providers")
|
|
14
|
+
.addHelpText("after", `
|
|
15
|
+
Examples:
|
|
16
|
+
ay sync repos Sync all repositories
|
|
17
|
+
ay sync repos --provider bitbucket Sync only Bitbucket repos
|
|
18
|
+
ay sync repos --id <repoId> Sync a specific repository`)
|
|
19
|
+
.option("--provider <provider>", "Filter by provider (bitbucket, github)")
|
|
20
|
+
.option("--id <repoId>", "Sync a specific repository by ID")
|
|
21
|
+
.action(async (options) => {
|
|
22
|
+
try {
|
|
23
|
+
const opts = { ...program.opts(), ...options };
|
|
24
|
+
if (opts.id) {
|
|
25
|
+
spinner.start({ text: `Syncing repository ${opts.id}...`, color: "magenta" });
|
|
26
|
+
const res = await apiCallHandler("devops", `repositories/${opts.id}/sync`, "post", null, { responseFormat: opts.responseFormat });
|
|
27
|
+
handleResponseFormatOptions(opts, res);
|
|
28
|
+
spinner.success({ text: `Repository ${opts.id} sync initiated` });
|
|
29
|
+
spinner.stop();
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
spinner.start({ text: "Syncing repositories...", color: "magenta" });
|
|
33
|
+
const params = {
|
|
34
|
+
responseFormat: opts.responseFormat,
|
|
35
|
+
verbosity: opts.verbosity,
|
|
36
|
+
};
|
|
37
|
+
if (opts.provider)
|
|
38
|
+
params.provider = opts.provider;
|
|
39
|
+
// List repos first, then trigger sync
|
|
40
|
+
const listRes = await apiCallHandler("devops", "repositories", "get", null, {
|
|
41
|
+
...params,
|
|
42
|
+
limit: 200,
|
|
43
|
+
});
|
|
44
|
+
const repos = Array.isArray(listRes === null || listRes === void 0 ? void 0 : listRes.payload) ? listRes.payload : [];
|
|
45
|
+
let syncCount = 0;
|
|
46
|
+
let errorCount = 0;
|
|
47
|
+
for (const repo of repos) {
|
|
48
|
+
if (opts.provider && repo.provider !== opts.provider)
|
|
49
|
+
continue;
|
|
50
|
+
try {
|
|
51
|
+
await apiCallHandler("devops", `repositories/${repo._id}/sync`, "post", null, { responseFormat: "json" });
|
|
52
|
+
syncCount++;
|
|
53
|
+
spinner.update({ text: `Syncing repositories... ${syncCount}/${repos.length}` });
|
|
54
|
+
}
|
|
55
|
+
catch (_a) {
|
|
56
|
+
errorCount++;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
spinner.success({ text: `Synced ${syncCount}/${repos.length} repositories` });
|
|
60
|
+
spinner.stop();
|
|
61
|
+
if (errorCount > 0) {
|
|
62
|
+
console.error(` ${errorCount} repositories failed to sync`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
cliError(e.message || "Repository sync failed", EXIT_GENERAL_ERROR);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
// ay sync clusters
|
|
71
|
+
sync
|
|
72
|
+
.command("clusters")
|
|
73
|
+
.description("Sync Kubernetes cluster state")
|
|
74
|
+
.option("--id <clusterId>", "Sync a specific cluster by ID")
|
|
75
|
+
.action(async (options) => {
|
|
76
|
+
try {
|
|
77
|
+
const opts = { ...program.opts(), ...options };
|
|
78
|
+
if (opts.id) {
|
|
79
|
+
spinner.start({ text: `Syncing cluster ${opts.id}...`, color: "magenta" });
|
|
80
|
+
const res = await apiCallHandler("devops", `clusters/${opts.id}/sync`, "post", null, { responseFormat: opts.responseFormat });
|
|
81
|
+
handleResponseFormatOptions(opts, res);
|
|
82
|
+
spinner.success({ text: `Cluster ${opts.id} sync initiated` });
|
|
83
|
+
spinner.stop();
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
spinner.start({ text: "Syncing all clusters...", color: "magenta" });
|
|
87
|
+
const listRes = await apiCallHandler("devops", "clusters", "get", null, {
|
|
88
|
+
limit: 100,
|
|
89
|
+
responseFormat: "json",
|
|
90
|
+
});
|
|
91
|
+
const clusters = Array.isArray(listRes === null || listRes === void 0 ? void 0 : listRes.payload) ? listRes.payload : [];
|
|
92
|
+
let syncCount = 0;
|
|
93
|
+
for (const cluster of clusters) {
|
|
94
|
+
try {
|
|
95
|
+
await apiCallHandler("devops", `clusters/${cluster._id}/sync`, "post", null, { responseFormat: "json" });
|
|
96
|
+
syncCount++;
|
|
97
|
+
}
|
|
98
|
+
catch (_a) {
|
|
99
|
+
// ignore individual failures
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
spinner.success({ text: `Synced ${syncCount}/${clusters.length} clusters` });
|
|
103
|
+
spinner.stop();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch (e) {
|
|
107
|
+
cliError(e.message || "Cluster sync failed", EXIT_GENERAL_ERROR);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
// ay sync pipelines
|
|
111
|
+
sync
|
|
112
|
+
.command("pipelines")
|
|
113
|
+
.description("Sync pipeline status from CI/CD providers")
|
|
114
|
+
.option("--provider <provider>", "Filter by provider (bitbucket, github)")
|
|
115
|
+
.option("-l, --limit <number>", "Limit results", parseInt, 50)
|
|
116
|
+
.action(async (options) => {
|
|
117
|
+
try {
|
|
118
|
+
const opts = { ...program.opts(), ...options };
|
|
119
|
+
spinner.start({ text: "Syncing pipeline status...", color: "magenta" });
|
|
120
|
+
const params = {
|
|
121
|
+
limit: opts.limit,
|
|
122
|
+
responseFormat: opts.responseFormat,
|
|
123
|
+
verbosity: opts.verbosity,
|
|
124
|
+
};
|
|
125
|
+
if (opts.provider)
|
|
126
|
+
params.provider = opts.provider;
|
|
127
|
+
const res = await apiCallHandler("devops", "pipelines/sync", "post", null, params);
|
|
128
|
+
handleResponseFormatOptions(opts, res);
|
|
129
|
+
spinner.success({ text: "Pipeline sync initiated" });
|
|
130
|
+
spinner.stop();
|
|
131
|
+
}
|
|
132
|
+
catch (e) {
|
|
133
|
+
cliError(e.message || "Pipeline sync failed", EXIT_GENERAL_ERROR);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
// ay sync status
|
|
137
|
+
sync
|
|
138
|
+
.command("status")
|
|
139
|
+
.description("Show sync status across all systems")
|
|
140
|
+
.action(async (options) => {
|
|
141
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
142
|
+
try {
|
|
143
|
+
const opts = { ...program.opts(), ...options };
|
|
144
|
+
spinner.start({ text: "Fetching sync status...", color: "magenta" });
|
|
145
|
+
// Gather status from multiple endpoints
|
|
146
|
+
const [reposRes, clustersRes, pipelinesRes] = await Promise.allSettled([
|
|
147
|
+
apiCallHandler("devops", "repositories", "get", null, { limit: 1, responseFormat: "json" }),
|
|
148
|
+
apiCallHandler("devops", "clusters", "get", null, { limit: 1, responseFormat: "json" }),
|
|
149
|
+
apiCallHandler("devops", "pipelines", "get", null, { limit: 1, responseFormat: "json" }),
|
|
150
|
+
]);
|
|
151
|
+
const status = {
|
|
152
|
+
repositories: {
|
|
153
|
+
total: reposRes.status === "fulfilled" ? (_d = (_c = (_b = (_a = reposRes.value) === null || _a === void 0 ? void 0 : _a.meta) === null || _b === void 0 ? void 0 : _b.pageInfo) === null || _c === void 0 ? void 0 : _c.totalEntries) !== null && _d !== void 0 ? _d : 0 : "error",
|
|
154
|
+
},
|
|
155
|
+
clusters: {
|
|
156
|
+
total: clustersRes.status === "fulfilled" ? (_h = (_g = (_f = (_e = clustersRes.value) === null || _e === void 0 ? void 0 : _e.meta) === null || _f === void 0 ? void 0 : _f.pageInfo) === null || _g === void 0 ? void 0 : _g.totalEntries) !== null && _h !== void 0 ? _h : 0 : "error",
|
|
157
|
+
},
|
|
158
|
+
pipelines: {
|
|
159
|
+
total: pipelinesRes.status === "fulfilled" ? (_m = (_l = (_k = (_j = pipelinesRes.value) === null || _j === void 0 ? void 0 : _j.meta) === null || _k === void 0 ? void 0 : _k.pageInfo) === null || _l === void 0 ? void 0 : _l.totalEntries) !== null && _m !== void 0 ? _m : 0 : "error",
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
const res = {
|
|
163
|
+
payload: status,
|
|
164
|
+
meta: { pageInfo: { totalEntries: 1, page: 1, totalPages: 1 } },
|
|
165
|
+
};
|
|
166
|
+
handleResponseFormatOptions(opts, res);
|
|
167
|
+
spinner.success({ text: "Sync status retrieved" });
|
|
168
|
+
spinner.stop();
|
|
169
|
+
}
|
|
170
|
+
catch (e) {
|
|
171
|
+
cliError(e.message || "Failed to get sync status", EXIT_GENERAL_ERROR);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { apiCallHandler } from "../api/apiCallHandler.js";
|
|
2
|
+
import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
|
|
3
|
+
import { saveFile } from "../helpers/saveFile.js";
|
|
4
|
+
import { spinner } from "../../index.js";
|
|
5
|
+
import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
|
|
6
|
+
import { cliError } from "../helpers/cliError.js";
|
|
7
|
+
export function createTemplateCommand(program) {
|
|
8
|
+
const tmpl = program
|
|
9
|
+
.command("templates")
|
|
10
|
+
.alias("tmpl")
|
|
11
|
+
.description("Manage platform templates (email, notification, report, store)");
|
|
12
|
+
// ── EMAIL TEMPLATES ────────────────────────────────────
|
|
13
|
+
const email = tmpl
|
|
14
|
+
.command("email")
|
|
15
|
+
.description("Manage email templates");
|
|
16
|
+
// ay templates email list
|
|
17
|
+
email
|
|
18
|
+
.command("list")
|
|
19
|
+
.alias("ls")
|
|
20
|
+
.description("List email templates")
|
|
21
|
+
.option("--search <query>", "Search by name")
|
|
22
|
+
.option("-l, --limit <number>", "Limit results", parseInt, 50)
|
|
23
|
+
.option("-p, --page <number>", "Page number", parseInt, 1)
|
|
24
|
+
.action(async (options) => {
|
|
25
|
+
var _a, _b, _c;
|
|
26
|
+
try {
|
|
27
|
+
const opts = { ...program.opts(), ...options };
|
|
28
|
+
spinner.start({ text: "Fetching email templates...", color: "magenta" });
|
|
29
|
+
const params = {
|
|
30
|
+
page: opts.page,
|
|
31
|
+
limit: opts.limit,
|
|
32
|
+
responseFormat: opts.responseFormat,
|
|
33
|
+
verbosity: opts.verbosity,
|
|
34
|
+
};
|
|
35
|
+
if (opts.search)
|
|
36
|
+
params.q = opts.search;
|
|
37
|
+
const res = await apiCallHandler("marketing", "emailtemplates", "get", null, params);
|
|
38
|
+
handleResponseFormatOptions(opts, res);
|
|
39
|
+
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;
|
|
40
|
+
spinner.success({ text: `Found ${total} email templates` });
|
|
41
|
+
spinner.stop();
|
|
42
|
+
if (opts.save)
|
|
43
|
+
await saveFile("email-templates", opts, res);
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
cliError(e.message || "Failed to list email templates", EXIT_GENERAL_ERROR);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
// ay templates email get <id>
|
|
50
|
+
email
|
|
51
|
+
.command("get <id>")
|
|
52
|
+
.description("Get email template details")
|
|
53
|
+
.action(async (id, options) => {
|
|
54
|
+
try {
|
|
55
|
+
const opts = { ...program.opts(), ...options };
|
|
56
|
+
spinner.start({ text: `Fetching email template ${id}...`, color: "magenta" });
|
|
57
|
+
const res = await apiCallHandler("marketing", `emailtemplates/${id}`, "get", null, {
|
|
58
|
+
responseFormat: opts.responseFormat,
|
|
59
|
+
verbosity: opts.verbosity,
|
|
60
|
+
});
|
|
61
|
+
handleResponseFormatOptions(opts, res);
|
|
62
|
+
spinner.success({ text: `Email template ${id} loaded` });
|
|
63
|
+
spinner.stop();
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
cliError(e.message || "Failed to get email template", EXIT_GENERAL_ERROR);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
// ── NOTIFICATION TEMPLATES ─────────────────────────────
|
|
70
|
+
const notify = tmpl
|
|
71
|
+
.command("notification")
|
|
72
|
+
.alias("notify")
|
|
73
|
+
.description("Manage notification templates");
|
|
74
|
+
// ay templates notification list
|
|
75
|
+
notify
|
|
76
|
+
.command("list")
|
|
77
|
+
.alias("ls")
|
|
78
|
+
.description("List notification templates")
|
|
79
|
+
.option("-l, --limit <number>", "Limit results", parseInt, 50)
|
|
80
|
+
.option("-p, --page <number>", "Page number", parseInt, 1)
|
|
81
|
+
.action(async (options) => {
|
|
82
|
+
var _a, _b, _c;
|
|
83
|
+
try {
|
|
84
|
+
const opts = { ...program.opts(), ...options };
|
|
85
|
+
spinner.start({ text: "Fetching notification templates...", color: "magenta" });
|
|
86
|
+
const res = await apiCallHandler("config", "notificationtemplates", "get", null, {
|
|
87
|
+
page: opts.page,
|
|
88
|
+
limit: opts.limit,
|
|
89
|
+
responseFormat: opts.responseFormat,
|
|
90
|
+
verbosity: opts.verbosity,
|
|
91
|
+
});
|
|
92
|
+
handleResponseFormatOptions(opts, res);
|
|
93
|
+
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;
|
|
94
|
+
spinner.success({ text: `Found ${total} notification templates` });
|
|
95
|
+
spinner.stop();
|
|
96
|
+
if (opts.save)
|
|
97
|
+
await saveFile("notification-templates", opts, res);
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
cliError(e.message || "Failed to list notification templates", EXIT_GENERAL_ERROR);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
// ay templates notification get <id>
|
|
104
|
+
notify
|
|
105
|
+
.command("get <id>")
|
|
106
|
+
.description("Get notification template details")
|
|
107
|
+
.action(async (id, options) => {
|
|
108
|
+
try {
|
|
109
|
+
const opts = { ...program.opts(), ...options };
|
|
110
|
+
spinner.start({ text: `Fetching notification template ${id}...`, color: "magenta" });
|
|
111
|
+
const res = await apiCallHandler("config", `notificationtemplates/${id}`, "get", null, {
|
|
112
|
+
responseFormat: opts.responseFormat,
|
|
113
|
+
verbosity: opts.verbosity,
|
|
114
|
+
});
|
|
115
|
+
handleResponseFormatOptions(opts, res);
|
|
116
|
+
spinner.success({ text: `Notification template ${id} loaded` });
|
|
117
|
+
spinner.stop();
|
|
118
|
+
}
|
|
119
|
+
catch (e) {
|
|
120
|
+
cliError(e.message || "Failed to get notification template", EXIT_GENERAL_ERROR);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
// ── REPORT TEMPLATES ───────────────────────────────────
|
|
124
|
+
const report = tmpl
|
|
125
|
+
.command("report")
|
|
126
|
+
.description("Manage report templates");
|
|
127
|
+
// ay templates report list
|
|
128
|
+
report
|
|
129
|
+
.command("list")
|
|
130
|
+
.alias("ls")
|
|
131
|
+
.description("List report templates")
|
|
132
|
+
.option("-l, --limit <number>", "Limit results", parseInt, 50)
|
|
133
|
+
.action(async (options) => {
|
|
134
|
+
var _a, _b, _c;
|
|
135
|
+
try {
|
|
136
|
+
const opts = { ...program.opts(), ...options };
|
|
137
|
+
spinner.start({ text: "Fetching report templates...", color: "magenta" });
|
|
138
|
+
const res = await apiCallHandler("reporting", "reporttemplates", "get", null, {
|
|
139
|
+
limit: opts.limit,
|
|
140
|
+
responseFormat: opts.responseFormat,
|
|
141
|
+
verbosity: opts.verbosity,
|
|
142
|
+
});
|
|
143
|
+
handleResponseFormatOptions(opts, res);
|
|
144
|
+
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;
|
|
145
|
+
spinner.success({ text: `Found ${total} report templates` });
|
|
146
|
+
spinner.stop();
|
|
147
|
+
if (opts.save)
|
|
148
|
+
await saveFile("report-templates", opts, res);
|
|
149
|
+
}
|
|
150
|
+
catch (e) {
|
|
151
|
+
cliError(e.message || "Failed to list report templates", EXIT_GENERAL_ERROR);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
// ── STORE TEMPLATES ────────────────────────────────────
|
|
155
|
+
const store = tmpl
|
|
156
|
+
.command("store")
|
|
157
|
+
.description("Browse template store / marketplace");
|
|
158
|
+
// ay templates store list
|
|
159
|
+
store
|
|
160
|
+
.command("list")
|
|
161
|
+
.alias("ls")
|
|
162
|
+
.description("List available store templates")
|
|
163
|
+
.option("--category <category>", "Filter by category")
|
|
164
|
+
.option("--search <query>", "Search templates")
|
|
165
|
+
.option("-l, --limit <number>", "Limit results", parseInt, 50)
|
|
166
|
+
.option("-p, --page <number>", "Page number", parseInt, 1)
|
|
167
|
+
.action(async (options) => {
|
|
168
|
+
var _a, _b, _c;
|
|
169
|
+
try {
|
|
170
|
+
const opts = { ...program.opts(), ...options };
|
|
171
|
+
spinner.start({ text: "Fetching store templates...", color: "magenta" });
|
|
172
|
+
const params = {
|
|
173
|
+
page: opts.page,
|
|
174
|
+
limit: opts.limit,
|
|
175
|
+
responseFormat: opts.responseFormat,
|
|
176
|
+
verbosity: opts.verbosity,
|
|
177
|
+
};
|
|
178
|
+
if (opts.category)
|
|
179
|
+
params.category = opts.category;
|
|
180
|
+
if (opts.search)
|
|
181
|
+
params.q = opts.search;
|
|
182
|
+
const res = await apiCallHandler("config", "templategroups", "get", null, params);
|
|
183
|
+
handleResponseFormatOptions(opts, res);
|
|
184
|
+
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;
|
|
185
|
+
spinner.success({ text: `Found ${total} store templates` });
|
|
186
|
+
spinner.stop();
|
|
187
|
+
if (opts.save)
|
|
188
|
+
await saveFile("store-templates", opts, res);
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
cliError(e.message || "Failed to list store templates", EXIT_GENERAL_ERROR);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
// ay templates store get <id>
|
|
195
|
+
store
|
|
196
|
+
.command("get <id>")
|
|
197
|
+
.description("Get store template details")
|
|
198
|
+
.action(async (id, options) => {
|
|
199
|
+
try {
|
|
200
|
+
const opts = { ...program.opts(), ...options };
|
|
201
|
+
spinner.start({ text: `Fetching store template ${id}...`, color: "magenta" });
|
|
202
|
+
const res = await apiCallHandler("config", `templategroups/${id}`, "get", null, {
|
|
203
|
+
responseFormat: opts.responseFormat,
|
|
204
|
+
verbosity: opts.verbosity,
|
|
205
|
+
});
|
|
206
|
+
handleResponseFormatOptions(opts, res);
|
|
207
|
+
spinner.success({ text: `Store template ${id} loaded` });
|
|
208
|
+
spinner.stop();
|
|
209
|
+
}
|
|
210
|
+
catch (e) {
|
|
211
|
+
cliError(e.message || "Failed to get store template", EXIT_GENERAL_ERROR);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
// ay templates store install <id>
|
|
215
|
+
store
|
|
216
|
+
.command("install <id>")
|
|
217
|
+
.description("Install a template from the store")
|
|
218
|
+
.action(async (id, options) => {
|
|
219
|
+
try {
|
|
220
|
+
const opts = { ...program.opts(), ...options };
|
|
221
|
+
spinner.start({ text: `Installing template ${id}...`, color: "magenta" });
|
|
222
|
+
const res = await apiCallHandler("config", `templategroups/${id}/install`, "post", null, { responseFormat: opts.responseFormat });
|
|
223
|
+
handleResponseFormatOptions(opts, res);
|
|
224
|
+
spinner.success({ text: `Template ${id} installed` });
|
|
225
|
+
spinner.stop();
|
|
226
|
+
}
|
|
227
|
+
catch (e) {
|
|
228
|
+
cliError(e.message || "Failed to install template", EXIT_GENERAL_ERROR);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Argument } from "commander";
|
|
2
|
+
import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
|
|
3
|
+
import { apiCallHandler } from "../api/apiCallHandler.js";
|
|
4
|
+
import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
|
|
5
|
+
import { saveFile } from "../helpers/saveFile.js";
|
|
6
|
+
import { localStorage } from "../helpers/localStorage.js";
|
|
7
|
+
import { spinner } from "../../index.js";
|
|
8
|
+
import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
|
|
9
|
+
import { cliError } from "../helpers/cliError.js";
|
|
10
|
+
export function createUpdateCommand(program) {
|
|
11
|
+
program
|
|
12
|
+
.command("update")
|
|
13
|
+
.alias("u")
|
|
14
|
+
.description("Update an entry non-interactively (for scripts and AI agents)")
|
|
15
|
+
.addHelpText("after", `
|
|
16
|
+
Unlike 'edit' (which opens an interactive editor), 'update' accepts changes
|
|
17
|
+
as JSON via --body, --body-file, or --body-stdin. Perfect for automation.
|
|
18
|
+
|
|
19
|
+
Examples:
|
|
20
|
+
ay update contacts 64a1b2c3 --body '{"firstName":"Jane"}'
|
|
21
|
+
ay update products 64a1b2c3 --body-file changes.json
|
|
22
|
+
echo '{"status":"active"}' | ay update contacts 64a1b2c3 --body-stdin
|
|
23
|
+
ay u tasks 64a1b2c3 --body '{"done":true}' --force`)
|
|
24
|
+
.addArgument(new Argument("[collection]", "The collection to use").default(localStorage.getItem("lastCollection"), `The last used collection (${localStorage.getItem("lastCollection")})`))
|
|
25
|
+
.addArgument(new Argument("[id]", "The ID of the entry to update").default(localStorage.getItem("lastId"), `The last used id (${localStorage.getItem("lastId")})`))
|
|
26
|
+
.option("--body <json>", "Update data as JSON string")
|
|
27
|
+
.option("--body-file <path>", "Read update data from JSON file")
|
|
28
|
+
.option("--body-stdin", "Read update data from stdin")
|
|
29
|
+
.option("--merge", "Merge with existing entry (GET + merge + PUT)", false)
|
|
30
|
+
.action(async (collection, id, options) => {
|
|
31
|
+
try {
|
|
32
|
+
if (!collection) {
|
|
33
|
+
cliError("Missing required argument: collection", EXIT_MISUSE);
|
|
34
|
+
}
|
|
35
|
+
if (!id) {
|
|
36
|
+
cliError("Missing required argument: id", EXIT_MISUSE);
|
|
37
|
+
}
|
|
38
|
+
const opts = { ...program.opts(), ...options };
|
|
39
|
+
const module = getModuleFromCollection(collection);
|
|
40
|
+
// Parse body from various sources
|
|
41
|
+
let body = null;
|
|
42
|
+
if (opts.body) {
|
|
43
|
+
try {
|
|
44
|
+
body = JSON.parse(opts.body);
|
|
45
|
+
}
|
|
46
|
+
catch (_a) {
|
|
47
|
+
cliError("Invalid JSON in --body", EXIT_MISUSE);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (opts.bodyFile) {
|
|
51
|
+
const fs = await import("fs");
|
|
52
|
+
const content = fs.readFileSync(opts.bodyFile, "utf-8");
|
|
53
|
+
try {
|
|
54
|
+
body = JSON.parse(content);
|
|
55
|
+
}
|
|
56
|
+
catch (_b) {
|
|
57
|
+
cliError(`Invalid JSON in file: ${opts.bodyFile}`, EXIT_MISUSE);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (opts.bodyStdin && !process.stdin.isTTY) {
|
|
61
|
+
const chunks = [];
|
|
62
|
+
for await (const chunk of process.stdin) {
|
|
63
|
+
chunks.push(chunk);
|
|
64
|
+
}
|
|
65
|
+
const stdinContent = Buffer.concat(chunks).toString("utf-8").trim();
|
|
66
|
+
if (stdinContent) {
|
|
67
|
+
try {
|
|
68
|
+
body = JSON.parse(stdinContent);
|
|
69
|
+
}
|
|
70
|
+
catch (_c) {
|
|
71
|
+
cliError("Invalid JSON from stdin", EXIT_MISUSE);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (!body) {
|
|
76
|
+
cliError("No update data provided. Use --body, --body-file, or --body-stdin.", EXIT_MISUSE);
|
|
77
|
+
}
|
|
78
|
+
// Merge mode: GET existing → merge → PUT
|
|
79
|
+
if (opts.merge) {
|
|
80
|
+
spinner.start({ text: `Fetching ${id} from ${collection}...`, color: "magenta" });
|
|
81
|
+
const existing = await apiCallHandler(module.module, `${collection.toLowerCase()}/${id}`, "get", null, { responseFormat: "json" });
|
|
82
|
+
const existingData = (existing === null || existing === void 0 ? void 0 : existing.payload) || {};
|
|
83
|
+
body = { ...existingData, ...body };
|
|
84
|
+
spinner.update({ text: `Updating ${id} in ${collection} (merged)...` });
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
spinner.start({ text: `Updating ${id} in ${collection}...`, color: "magenta" });
|
|
88
|
+
}
|
|
89
|
+
// Ensure _id is set
|
|
90
|
+
body._id = id;
|
|
91
|
+
const res = await apiCallHandler(module.module, collection.toLowerCase(), "put", body, {
|
|
92
|
+
responseFormat: opts.responseFormat,
|
|
93
|
+
verbosity: opts.verbosity,
|
|
94
|
+
hideMeta: opts.hideMeta,
|
|
95
|
+
});
|
|
96
|
+
// Dry-run returns null payload — skip output to avoid printing "null"
|
|
97
|
+
if ((res === null || res === void 0 ? void 0 : res.payload) !== null && (res === null || res === void 0 ? void 0 : res.payload) !== undefined) {
|
|
98
|
+
handleResponseFormatOptions(opts, res);
|
|
99
|
+
}
|
|
100
|
+
spinner.success({ text: `Updated entry ${id} in ${collection}` });
|
|
101
|
+
spinner.stop();
|
|
102
|
+
localStorage.setItem("lastModule", module.module);
|
|
103
|
+
localStorage.setItem("lastCollection", collection);
|
|
104
|
+
localStorage.setItem("lastId", id);
|
|
105
|
+
if (opts.save)
|
|
106
|
+
await saveFile("update", opts, res);
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|