@tolinax/ayoune-cli 2026.3.0 → 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.
- package/data/contextSlots.js +189 -0
- package/data/modelsAndRights.js +56 -0
- package/data/modules.js +16 -0
- package/lib/api/apiCallHandler.js +6 -2
- package/lib/api/apiClient.js +9 -1
- package/lib/api/auditCallHandler.js +2 -2
- package/lib/api/handleAPIError.js +20 -18
- package/lib/api/login.js +3 -3
- package/lib/api/searchClient.js +119 -0
- package/lib/commands/createAccessCommand.js +126 -0
- package/lib/commands/createActionsCommand.js +40 -9
- package/lib/commands/createAiCommand.js +17 -17
- package/lib/commands/createAliasCommand.js +4 -6
- package/lib/commands/createAuditCommand.js +5 -9
- package/lib/commands/createBatchCommand.js +15 -28
- package/lib/commands/createCompletionsCommand.js +6 -3
- package/lib/commands/createConfigCommand.js +8 -14
- package/lib/commands/createContextCommand.js +163 -0
- package/lib/commands/createCopyCommand.js +4 -7
- package/lib/commands/createCreateCommand.js +4 -7
- package/lib/commands/createDeleteCommand.js +4 -6
- package/lib/commands/createDeployCommand.js +31 -55
- package/lib/commands/createDescribeCommand.js +12 -10
- package/lib/commands/createEditCommand.js +13 -8
- package/lib/commands/createEventsCommand.js +4 -4
- package/lib/commands/createExecCommand.js +65 -35
- package/lib/commands/createExportCommand.js +21 -24
- package/lib/commands/createGetCommand.js +13 -14
- package/lib/commands/createJobsCommand.js +8 -13
- package/lib/commands/createListCommand.js +13 -14
- package/lib/commands/createLoginCommand.js +16 -4
- package/lib/commands/createLogoutCommand.js +2 -2
- package/lib/commands/createModulesCommand.js +16 -19
- package/lib/commands/createMonitorCommand.js +9 -16
- package/lib/commands/createPermissionsCommand.js +10 -18
- package/lib/commands/createProgram.js +47 -21
- package/lib/commands/createSearchCommand.js +219 -69
- package/lib/commands/createSelfHostUpdateCommand.js +166 -0
- package/lib/commands/createServicesCommand.js +5 -8
- package/lib/commands/createSetupCommand.js +305 -0
- package/lib/commands/createStatusCommand.js +147 -0
- package/lib/commands/createStorageCommand.js +2 -3
- package/lib/commands/createStreamCommand.js +4 -4
- package/lib/commands/createSyncCommand.js +5 -8
- package/lib/commands/createTemplateCommand.js +9 -16
- package/lib/commands/createUpdateCommand.js +12 -15
- package/lib/commands/createUsersCommand.js +21 -31
- package/lib/commands/createWebhooksCommand.js +15 -22
- package/lib/commands/createWhoAmICommand.js +8 -6
- package/lib/helpers/cliError.js +24 -0
- package/lib/helpers/config.js +1 -0
- package/lib/helpers/configLoader.js +6 -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/handleResponseFormatOptions.js +59 -10
- package/lib/helpers/logo.js +48 -0
- package/lib/helpers/resolveCollectionArgs.js +36 -0
- package/lib/helpers/sanitizeFields.js +18 -0
- package/lib/helpers/secureStorage.js +72 -0
- package/lib/helpers/tokenPayload.js +21 -0
- package/lib/helpers/updateNotifier.js +49 -0
- package/lib/models/getModuleFromCollection.js +4 -1
- package/lib/operations/handleCopySingleOperation.js +10 -2
- package/lib/operations/handleCreateSingleOperation.js +3 -0
- package/lib/operations/handleDescribeSingleOperation.js +23 -0
- package/lib/operations/handleGetOperation.js +9 -3
- package/lib/operations/handleListOperation.js +14 -10
- package/lib/prompts/promptModule.js +9 -6
- package/package.json +163 -158
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { spinner } from "../../index.js";
|
|
3
|
+
import { contextSlots, getSlot, getSlotsByTier } from "../../data/contextSlots.js";
|
|
4
|
+
import { getActiveContext, setContextEntry, unsetContextEntry, clearAllContext, } from "../helpers/contextStore.js";
|
|
5
|
+
import { resolveEntity, validateHierarchy, getDependentSlots } from "../helpers/contextResolver.js";
|
|
6
|
+
import { EXIT_MISUSE } from "../exitCodes.js";
|
|
7
|
+
import { cliError } from "../helpers/cliError.js";
|
|
8
|
+
export function createContextCommand(program) {
|
|
9
|
+
const ctx = program
|
|
10
|
+
.command("context")
|
|
11
|
+
.alias("ctx")
|
|
12
|
+
.description("Manage active entity context for scoped commands")
|
|
13
|
+
.addHelpText("after", `
|
|
14
|
+
Examples:
|
|
15
|
+
ay context Show active context
|
|
16
|
+
ay context set project "Website" Set project context by name
|
|
17
|
+
ay context set sprint "Sprint 14" Set sprint (auto-filtered by project)
|
|
18
|
+
ay context unset project Unset project (cascades to children)
|
|
19
|
+
ay context clear Clear all context
|
|
20
|
+
ay context list List all available context slots`);
|
|
21
|
+
// Default action: show active context
|
|
22
|
+
ctx.action(() => {
|
|
23
|
+
showContext();
|
|
24
|
+
});
|
|
25
|
+
// ay context show
|
|
26
|
+
ctx
|
|
27
|
+
.command("show")
|
|
28
|
+
.description("Show active context")
|
|
29
|
+
.action(() => {
|
|
30
|
+
showContext();
|
|
31
|
+
});
|
|
32
|
+
// ay context set <slot> <name>
|
|
33
|
+
ctx
|
|
34
|
+
.command("set <slot> <nameOrId...>")
|
|
35
|
+
.description("Set active context for a slot")
|
|
36
|
+
.action(async (slot, nameOrIdParts, options) => {
|
|
37
|
+
const opts = { ...program.opts(), ...options };
|
|
38
|
+
const nameOrId = nameOrIdParts.join(" ");
|
|
39
|
+
const slotDef = getSlot(slot);
|
|
40
|
+
if (!slotDef) {
|
|
41
|
+
const available = contextSlots.map((s) => s.slot).join(", ");
|
|
42
|
+
cliError(`Unknown context slot: "${slot}". Available: ${available}`, EXIT_MISUSE);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
// Hierarchy warning
|
|
46
|
+
const warning = validateHierarchy(slot);
|
|
47
|
+
if (warning) {
|
|
48
|
+
console.error(chalk.yellow(` ${warning}`));
|
|
49
|
+
}
|
|
50
|
+
spinner.start({ text: `Resolving ${slot} "${nameOrId}"...`, color: "magenta" });
|
|
51
|
+
const matches = await resolveEntity(slot, nameOrId);
|
|
52
|
+
if (matches.length === 0) {
|
|
53
|
+
spinner.error({ text: `No ${slot} found matching "${nameOrId}"` });
|
|
54
|
+
spinner.stop();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
let selected = matches[0];
|
|
58
|
+
// If multiple matches and interactive TTY, let user pick
|
|
59
|
+
if (matches.length > 1 && process.stdin.isTTY) {
|
|
60
|
+
spinner.stop();
|
|
61
|
+
console.error(chalk.dim(` Multiple matches for "${nameOrId}":`));
|
|
62
|
+
matches.forEach((m, i) => {
|
|
63
|
+
console.error(` ${chalk.cyan(`[${i + 1}]`)} ${m.name} ${chalk.dim(`(${m.id})`)}`);
|
|
64
|
+
});
|
|
65
|
+
console.error(chalk.dim(` Using first match. Use an ObjectId for exact selection.\n`));
|
|
66
|
+
selected = matches[0];
|
|
67
|
+
}
|
|
68
|
+
else if (matches.length > 1) {
|
|
69
|
+
spinner.stop();
|
|
70
|
+
}
|
|
71
|
+
setContextEntry(slot, {
|
|
72
|
+
id: selected.id,
|
|
73
|
+
name: selected.name,
|
|
74
|
+
collection: slotDef.collection,
|
|
75
|
+
module: slotDef.module,
|
|
76
|
+
setAt: new Date().toISOString(),
|
|
77
|
+
});
|
|
78
|
+
spinner.success({ text: `Context ${slot} set to "${selected.name}" (${selected.id})` });
|
|
79
|
+
spinner.stop();
|
|
80
|
+
});
|
|
81
|
+
// ay context unset <slot>
|
|
82
|
+
ctx
|
|
83
|
+
.command("unset <slot>")
|
|
84
|
+
.description("Remove context for a slot (cascades to children)")
|
|
85
|
+
.action((slot) => {
|
|
86
|
+
const slotDef = getSlot(slot);
|
|
87
|
+
if (!slotDef) {
|
|
88
|
+
cliError(`Unknown context slot: "${slot}"`, EXIT_MISUSE);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// Cascade unset children
|
|
92
|
+
const children = getDependentSlots(slot);
|
|
93
|
+
const unset = [slot];
|
|
94
|
+
for (const child of children) {
|
|
95
|
+
const entry = getActiveContext()[child];
|
|
96
|
+
if (entry) {
|
|
97
|
+
unsetContextEntry(child);
|
|
98
|
+
unset.push(child);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
unsetContextEntry(slot);
|
|
102
|
+
if (unset.length > 1) {
|
|
103
|
+
console.error(chalk.dim(` Cascade: also unset ${unset.slice(1).join(", ")}`));
|
|
104
|
+
}
|
|
105
|
+
spinner.success({ text: `Context "${slot}" unset` });
|
|
106
|
+
spinner.stop();
|
|
107
|
+
});
|
|
108
|
+
// ay context clear
|
|
109
|
+
ctx
|
|
110
|
+
.command("clear")
|
|
111
|
+
.description("Clear all active context")
|
|
112
|
+
.option("--force", "Skip confirmation")
|
|
113
|
+
.action((options) => {
|
|
114
|
+
const opts = { ...program.opts(), ...options };
|
|
115
|
+
const active = getActiveContext();
|
|
116
|
+
const count = Object.keys(active).length;
|
|
117
|
+
if (count === 0) {
|
|
118
|
+
console.error(chalk.dim(" No active context to clear."));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
clearAllContext();
|
|
122
|
+
spinner.success({ text: `Cleared ${count} context entries` });
|
|
123
|
+
spinner.stop();
|
|
124
|
+
});
|
|
125
|
+
// ay context list
|
|
126
|
+
ctx
|
|
127
|
+
.command("list")
|
|
128
|
+
.description("List all available context slots")
|
|
129
|
+
.action(() => {
|
|
130
|
+
const active = getActiveContext();
|
|
131
|
+
const tierLabels = {
|
|
132
|
+
1: "Core",
|
|
133
|
+
2: "Domain",
|
|
134
|
+
3: "AI / Automation",
|
|
135
|
+
};
|
|
136
|
+
for (const tier of [1, 2, 3]) {
|
|
137
|
+
const slots = getSlotsByTier(tier);
|
|
138
|
+
console.log(chalk.bold(`\n ${tierLabels[tier]}`));
|
|
139
|
+
for (const s of slots) {
|
|
140
|
+
const entry = active[s.slot];
|
|
141
|
+
const status = entry
|
|
142
|
+
? chalk.green(`= "${entry.name}" (${entry.id})`)
|
|
143
|
+
: chalk.dim("not set");
|
|
144
|
+
const parent = s.parent ? chalk.dim(` [parent: ${s.parent}]`) : "";
|
|
145
|
+
console.log(` ${chalk.cyan(s.slot.padEnd(14))} ${s.collection.padEnd(22)} ${status}${parent}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
console.log();
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function showContext() {
|
|
152
|
+
const active = getActiveContext();
|
|
153
|
+
const entries = Object.entries(active);
|
|
154
|
+
if (entries.length === 0) {
|
|
155
|
+
console.log(chalk.dim(" No active context. Use `ay context set <slot> <name>` to set one."));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
console.log(chalk.bold("\n Active Context"));
|
|
159
|
+
for (const [slot, entry] of entries) {
|
|
160
|
+
console.log(` ${chalk.cyan(slot.padEnd(14))} ${entry.name} ${chalk.dim(`(${entry.collection}, ${entry.id})`)}`);
|
|
161
|
+
}
|
|
162
|
+
console.log();
|
|
163
|
+
}
|
|
@@ -2,8 +2,8 @@ import { Argument } from "commander";
|
|
|
2
2
|
import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
|
|
3
3
|
import { handleCopySingleOperation } from "../operations/handleCopySingleOperation.js";
|
|
4
4
|
import { localStorage } from "../helpers/localStorage.js";
|
|
5
|
-
import { spinner } from "../../index.js";
|
|
6
5
|
import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
|
|
6
|
+
import { cliError } from "../helpers/cliError.js";
|
|
7
7
|
export function createCopyCommand(program) {
|
|
8
8
|
program
|
|
9
9
|
.command("copy")
|
|
@@ -18,12 +18,10 @@ Examples:
|
|
|
18
18
|
.action(async (collection, id, options) => {
|
|
19
19
|
try {
|
|
20
20
|
if (!collection) {
|
|
21
|
-
|
|
22
|
-
process.exit(EXIT_MISUSE);
|
|
21
|
+
cliError("Missing required argument: collection. Run a list or get command first, or provide it explicitly.", EXIT_MISUSE);
|
|
23
22
|
}
|
|
24
23
|
if (!id) {
|
|
25
|
-
|
|
26
|
-
process.exit(EXIT_MISUSE);
|
|
24
|
+
cliError("Missing required argument: id. Provide an entry ID explicitly.", EXIT_MISUSE);
|
|
27
25
|
}
|
|
28
26
|
const opts = { ...program.opts(), ...options };
|
|
29
27
|
const module = getModuleFromCollection(collection);
|
|
@@ -32,8 +30,7 @@ Examples:
|
|
|
32
30
|
});
|
|
33
31
|
}
|
|
34
32
|
catch (e) {
|
|
35
|
-
|
|
36
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
33
|
+
cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
|
|
37
34
|
}
|
|
38
35
|
});
|
|
39
36
|
}
|
|
@@ -4,8 +4,8 @@ import { promptCollectionWithModule } from "../prompts/promptCollectionWithModul
|
|
|
4
4
|
import { handleCreateSingleOperation } from "../operations/handleCreateSingleOperation.js";
|
|
5
5
|
import { promptName } from "../prompts/promptName.js";
|
|
6
6
|
import { localStorage } from "../helpers/localStorage.js";
|
|
7
|
-
import { spinner } from "../../index.js";
|
|
8
7
|
import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
|
|
8
|
+
import { cliError } from "../helpers/cliError.js";
|
|
9
9
|
export function createCreateCommand(program) {
|
|
10
10
|
program
|
|
11
11
|
.command("create")
|
|
@@ -22,8 +22,7 @@ Examples:
|
|
|
22
22
|
const opts = { ...program.opts(), ...options };
|
|
23
23
|
if (!collection) {
|
|
24
24
|
if (!process.stdin.isTTY) {
|
|
25
|
-
|
|
26
|
-
process.exit(EXIT_MISUSE);
|
|
25
|
+
cliError("Missing required argument: collection", EXIT_MISUSE);
|
|
27
26
|
}
|
|
28
27
|
collection = await promptCollectionWithModule();
|
|
29
28
|
}
|
|
@@ -31,8 +30,7 @@ Examples:
|
|
|
31
30
|
let entryName = name;
|
|
32
31
|
if (!entryName) {
|
|
33
32
|
if (!process.stdin.isTTY) {
|
|
34
|
-
|
|
35
|
-
process.exit(EXIT_MISUSE);
|
|
33
|
+
cliError("Missing required argument: name", EXIT_MISUSE);
|
|
36
34
|
}
|
|
37
35
|
entryName = await promptName();
|
|
38
36
|
}
|
|
@@ -43,8 +41,7 @@ Examples:
|
|
|
43
41
|
localStorage.setItem("lastCollection", collection);
|
|
44
42
|
}
|
|
45
43
|
catch (e) {
|
|
46
|
-
|
|
47
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
44
|
+
cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
|
|
48
45
|
}
|
|
49
46
|
});
|
|
50
47
|
}
|
|
@@ -5,6 +5,7 @@ import { handleDeleteSingleOperation } from "../operations/handleDeleteSingleOpe
|
|
|
5
5
|
import { localStorage } from "../helpers/localStorage.js";
|
|
6
6
|
import { spinner } from "../../index.js";
|
|
7
7
|
import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
|
|
8
|
+
import { cliError } from "../helpers/cliError.js";
|
|
8
9
|
export function createDeleteCommand(program) {
|
|
9
10
|
program
|
|
10
11
|
.command("delete")
|
|
@@ -22,8 +23,7 @@ Examples:
|
|
|
22
23
|
.action(async (collection, ids, options) => {
|
|
23
24
|
try {
|
|
24
25
|
if (!collection) {
|
|
25
|
-
|
|
26
|
-
process.exit(EXIT_MISUSE);
|
|
26
|
+
cliError("Missing required argument: collection. Run a list or get command first, or provide it explicitly.", EXIT_MISUSE);
|
|
27
27
|
}
|
|
28
28
|
const opts = { ...program.opts(), ...options };
|
|
29
29
|
const module = getModuleFromCollection(collection);
|
|
@@ -47,8 +47,7 @@ Examples:
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
if (idList.length === 0) {
|
|
50
|
-
|
|
51
|
-
process.exit(EXIT_MISUSE);
|
|
50
|
+
cliError("No IDs provided. Pass IDs as argument or via --ids-stdin.", EXIT_MISUSE);
|
|
52
51
|
}
|
|
53
52
|
// Confirmation prompt (unless --force)
|
|
54
53
|
if (!opts.force && process.stdin.isTTY) {
|
|
@@ -91,8 +90,7 @@ Examples:
|
|
|
91
90
|
}
|
|
92
91
|
}
|
|
93
92
|
catch (e) {
|
|
94
|
-
|
|
95
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
93
|
+
cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
|
|
96
94
|
}
|
|
97
95
|
});
|
|
98
96
|
}
|
|
@@ -3,11 +3,12 @@ import { getModuleBaseUrl } from "../api/apiClient.js";
|
|
|
3
3
|
import { apiCallHandler } from "../api/apiCallHandler.js";
|
|
4
4
|
import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
|
|
5
5
|
import { saveFile } from "../helpers/saveFile.js";
|
|
6
|
-
import {
|
|
6
|
+
import { secureStorage } from "../helpers/secureStorage.js";
|
|
7
7
|
import { spinner } from "../../index.js";
|
|
8
8
|
import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
|
|
9
|
+
import { cliError } from "../helpers/cliError.js";
|
|
9
10
|
function getToken() {
|
|
10
|
-
return
|
|
11
|
+
return secureStorage.getItem("token") || process.env.AYOUNE_TOKEN || "";
|
|
11
12
|
}
|
|
12
13
|
export function createDeployCommand(program) {
|
|
13
14
|
const deploy = program
|
|
@@ -27,7 +28,7 @@ export function createDeployCommand(program) {
|
|
|
27
28
|
.option("-l, --limit <number>", "Limit", parseInt, 50)
|
|
28
29
|
.option("-p, --page <number>", "Page", parseInt, 1)
|
|
29
30
|
.action(async (options) => {
|
|
30
|
-
var _a, _b
|
|
31
|
+
var _a, _b;
|
|
31
32
|
try {
|
|
32
33
|
const opts = { ...program.opts(), ...options };
|
|
33
34
|
spinner.start({ text: "Fetching deployments...", color: "magenta" });
|
|
@@ -47,16 +48,15 @@ export function createDeployCommand(program) {
|
|
|
47
48
|
if (opts.search)
|
|
48
49
|
params.q = opts.search;
|
|
49
50
|
const res = await apiCallHandler("devops", "deployments", "get", null, params);
|
|
50
|
-
handleResponseFormatOptions(opts, res);
|
|
51
|
-
const total = (
|
|
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);
|
|
52
53
|
spinner.success({ text: `Found ${total} deployments` });
|
|
53
54
|
spinner.stop();
|
|
54
55
|
if (opts.save)
|
|
55
56
|
await saveFile("deploy-list", opts, res);
|
|
56
57
|
}
|
|
57
58
|
catch (e) {
|
|
58
|
-
|
|
59
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
59
|
+
cliError(e.message || "Failed to list deployments", EXIT_GENERAL_ERROR);
|
|
60
60
|
}
|
|
61
61
|
});
|
|
62
62
|
// ay deploy get <id>
|
|
@@ -76,8 +76,7 @@ export function createDeployCommand(program) {
|
|
|
76
76
|
spinner.stop();
|
|
77
77
|
}
|
|
78
78
|
catch (e) {
|
|
79
|
-
|
|
80
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
79
|
+
cliError(e.message || "Failed to get deployment", EXIT_GENERAL_ERROR);
|
|
81
80
|
}
|
|
82
81
|
});
|
|
83
82
|
// ay deploy logs <id>
|
|
@@ -113,8 +112,7 @@ Examples:
|
|
|
113
112
|
},
|
|
114
113
|
}, (res) => {
|
|
115
114
|
if (res.statusCode !== 200) {
|
|
116
|
-
|
|
117
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
115
|
+
cliError(`Error: HTTP ${res.statusCode}`, EXIT_GENERAL_ERROR);
|
|
118
116
|
}
|
|
119
117
|
res.setEncoding("utf-8");
|
|
120
118
|
res.on("data", (chunk) => {
|
|
@@ -146,8 +144,7 @@ Examples:
|
|
|
146
144
|
});
|
|
147
145
|
});
|
|
148
146
|
req.on("error", (e) => {
|
|
149
|
-
|
|
150
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
147
|
+
cliError(`Stream error: ${e.message}`, EXIT_GENERAL_ERROR);
|
|
151
148
|
});
|
|
152
149
|
// Keep alive until Ctrl+C
|
|
153
150
|
process.on("SIGINT", () => {
|
|
@@ -173,8 +170,7 @@ Examples:
|
|
|
173
170
|
}
|
|
174
171
|
}
|
|
175
172
|
catch (e) {
|
|
176
|
-
|
|
177
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
173
|
+
cliError(e.message || "Failed to fetch logs", EXIT_GENERAL_ERROR);
|
|
178
174
|
}
|
|
179
175
|
});
|
|
180
176
|
// ay deploy scale <deployment> <replicas>
|
|
@@ -194,8 +190,7 @@ Examples:
|
|
|
194
190
|
spinner.stop();
|
|
195
191
|
}
|
|
196
192
|
catch (e) {
|
|
197
|
-
|
|
198
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
193
|
+
cliError(e.message || "Failed to scale deployment", EXIT_GENERAL_ERROR);
|
|
199
194
|
}
|
|
200
195
|
});
|
|
201
196
|
// ay deploy restart <deployment>
|
|
@@ -214,8 +209,7 @@ Examples:
|
|
|
214
209
|
spinner.stop();
|
|
215
210
|
}
|
|
216
211
|
catch (e) {
|
|
217
|
-
|
|
218
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
212
|
+
cliError(e.message || "Failed to restart deployment", EXIT_GENERAL_ERROR);
|
|
219
213
|
}
|
|
220
214
|
});
|
|
221
215
|
// ─── PODS ──────────────────────────────────────────────
|
|
@@ -255,8 +249,7 @@ Examples:
|
|
|
255
249
|
await saveFile("deploy-pods", opts, res);
|
|
256
250
|
}
|
|
257
251
|
catch (e) {
|
|
258
|
-
|
|
259
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
252
|
+
cliError(e.message || "Failed to list pods", EXIT_GENERAL_ERROR);
|
|
260
253
|
}
|
|
261
254
|
});
|
|
262
255
|
// ay deploy pod-delete <id>
|
|
@@ -275,8 +268,7 @@ Examples:
|
|
|
275
268
|
spinner.stop();
|
|
276
269
|
}
|
|
277
270
|
catch (e) {
|
|
278
|
-
|
|
279
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
271
|
+
cliError(e.message || "Failed to delete pod", EXIT_GENERAL_ERROR);
|
|
280
272
|
}
|
|
281
273
|
});
|
|
282
274
|
// ─── CLUSTERS ──────────────────────────────────────────
|
|
@@ -308,8 +300,7 @@ Examples:
|
|
|
308
300
|
await saveFile("deploy-clusters", opts, res);
|
|
309
301
|
}
|
|
310
302
|
catch (e) {
|
|
311
|
-
|
|
312
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
303
|
+
cliError(e.message || "Failed to list clusters", EXIT_GENERAL_ERROR);
|
|
313
304
|
}
|
|
314
305
|
});
|
|
315
306
|
// ay deploy cluster-sync <id>
|
|
@@ -328,8 +319,7 @@ Examples:
|
|
|
328
319
|
spinner.stop();
|
|
329
320
|
}
|
|
330
321
|
catch (e) {
|
|
331
|
-
|
|
332
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
322
|
+
cliError(e.message || "Failed to sync cluster", EXIT_GENERAL_ERROR);
|
|
333
323
|
}
|
|
334
324
|
});
|
|
335
325
|
// ─── ALERTS ────────────────────────────────────────────
|
|
@@ -363,8 +353,7 @@ Examples:
|
|
|
363
353
|
await saveFile("deploy-alerts", opts, res);
|
|
364
354
|
}
|
|
365
355
|
catch (e) {
|
|
366
|
-
|
|
367
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
356
|
+
cliError(e.message || "Failed to fetch alerts", EXIT_GENERAL_ERROR);
|
|
368
357
|
}
|
|
369
358
|
});
|
|
370
359
|
// ─── PIPELINES ─────────────────────────────────────────
|
|
@@ -399,8 +388,7 @@ Examples:
|
|
|
399
388
|
await saveFile("deploy-pipelines", opts, res);
|
|
400
389
|
}
|
|
401
390
|
catch (e) {
|
|
402
|
-
|
|
403
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
391
|
+
cliError(e.message || "Failed to fetch pipelines", EXIT_GENERAL_ERROR);
|
|
404
392
|
}
|
|
405
393
|
});
|
|
406
394
|
// ay deploy trigger <repo>
|
|
@@ -432,8 +420,7 @@ Examples:
|
|
|
432
420
|
spinner.stop();
|
|
433
421
|
}
|
|
434
422
|
catch (e) {
|
|
435
|
-
|
|
436
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
423
|
+
cliError(e.message || "Failed to trigger pipeline", EXIT_GENERAL_ERROR);
|
|
437
424
|
}
|
|
438
425
|
});
|
|
439
426
|
// ─── DEPLOYMENT PLANS ──────────────────────────────────
|
|
@@ -466,8 +453,7 @@ Examples:
|
|
|
466
453
|
await saveFile("deploy-plans", opts, res);
|
|
467
454
|
}
|
|
468
455
|
catch (e) {
|
|
469
|
-
|
|
470
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
456
|
+
cliError(e.message || "Failed to list deployment plans", EXIT_GENERAL_ERROR);
|
|
471
457
|
}
|
|
472
458
|
});
|
|
473
459
|
// ay deploy plans get <id>
|
|
@@ -487,8 +473,7 @@ Examples:
|
|
|
487
473
|
spinner.stop();
|
|
488
474
|
}
|
|
489
475
|
catch (e) {
|
|
490
|
-
|
|
491
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
476
|
+
cliError(e.message || "Failed to get plan", EXIT_GENERAL_ERROR);
|
|
492
477
|
}
|
|
493
478
|
});
|
|
494
479
|
// ay deploy plans create --body '{...}'
|
|
@@ -507,8 +492,7 @@ Examples:
|
|
|
507
492
|
body = JSON.parse(opts.body);
|
|
508
493
|
}
|
|
509
494
|
catch (_b) {
|
|
510
|
-
|
|
511
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
495
|
+
cliError("Invalid JSON in --body", EXIT_GENERAL_ERROR);
|
|
512
496
|
}
|
|
513
497
|
}
|
|
514
498
|
else if (opts.bodyFile) {
|
|
@@ -518,13 +502,11 @@ Examples:
|
|
|
518
502
|
body = JSON.parse(content);
|
|
519
503
|
}
|
|
520
504
|
catch (_c) {
|
|
521
|
-
|
|
522
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
505
|
+
cliError(`Invalid JSON in file: ${opts.bodyFile}`, EXIT_GENERAL_ERROR);
|
|
523
506
|
}
|
|
524
507
|
}
|
|
525
508
|
if (!body) {
|
|
526
|
-
|
|
527
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
509
|
+
cliError("Provide plan definition via --body or --body-file", EXIT_GENERAL_ERROR);
|
|
528
510
|
}
|
|
529
511
|
spinner.start({ text: "Creating deployment plan...", color: "magenta" });
|
|
530
512
|
const res = await apiCallHandler("devops", "deployment-plans", "post", body, {
|
|
@@ -536,8 +518,7 @@ Examples:
|
|
|
536
518
|
spinner.stop();
|
|
537
519
|
}
|
|
538
520
|
catch (e) {
|
|
539
|
-
|
|
540
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
521
|
+
cliError(e.message || "Failed to create plan", EXIT_GENERAL_ERROR);
|
|
541
522
|
}
|
|
542
523
|
});
|
|
543
524
|
// ay deploy plans execute <id>
|
|
@@ -557,8 +538,7 @@ Examples:
|
|
|
557
538
|
spinner.stop();
|
|
558
539
|
}
|
|
559
540
|
catch (e) {
|
|
560
|
-
|
|
561
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
541
|
+
cliError(e.message || "Failed to execute plan", EXIT_GENERAL_ERROR);
|
|
562
542
|
}
|
|
563
543
|
});
|
|
564
544
|
// ay deploy plans delete <id>
|
|
@@ -578,8 +558,7 @@ Examples:
|
|
|
578
558
|
spinner.stop();
|
|
579
559
|
}
|
|
580
560
|
catch (e) {
|
|
581
|
-
|
|
582
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
561
|
+
cliError(e.message || "Failed to delete plan", EXIT_GENERAL_ERROR);
|
|
583
562
|
}
|
|
584
563
|
});
|
|
585
564
|
// ─── REPOSITORIES ──────────────────────────────────────
|
|
@@ -616,8 +595,7 @@ Examples:
|
|
|
616
595
|
await saveFile("deploy-repos", opts, res);
|
|
617
596
|
}
|
|
618
597
|
catch (e) {
|
|
619
|
-
|
|
620
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
598
|
+
cliError(e.message || "Failed to list repositories", EXIT_GENERAL_ERROR);
|
|
621
599
|
}
|
|
622
600
|
});
|
|
623
601
|
// ay deploy repos sync <id>
|
|
@@ -636,8 +614,7 @@ Examples:
|
|
|
636
614
|
spinner.stop();
|
|
637
615
|
}
|
|
638
616
|
catch (e) {
|
|
639
|
-
|
|
640
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
617
|
+
cliError(e.message || "Failed to sync repository", EXIT_GENERAL_ERROR);
|
|
641
618
|
}
|
|
642
619
|
});
|
|
643
620
|
// ─── DASHBOARD ─────────────────────────────────────────
|
|
@@ -659,8 +636,7 @@ Examples:
|
|
|
659
636
|
spinner.stop();
|
|
660
637
|
}
|
|
661
638
|
catch (e) {
|
|
662
|
-
|
|
663
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
639
|
+
cliError(e.message || "Failed to load dashboard", EXIT_GENERAL_ERROR);
|
|
664
640
|
}
|
|
665
641
|
});
|
|
666
642
|
}
|
|
@@ -2,8 +2,8 @@ import { Argument } from "commander";
|
|
|
2
2
|
import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
|
|
3
3
|
import { handleDescribeSingleOperation } from "../operations/handleDescribeSingleOperation.js";
|
|
4
4
|
import { localStorage } from "../helpers/localStorage.js";
|
|
5
|
-
import { spinner } from "../../index.js";
|
|
6
5
|
import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
|
|
6
|
+
import { cliError } from "../helpers/cliError.js";
|
|
7
7
|
export function createDescribeCommand(program) {
|
|
8
8
|
program
|
|
9
9
|
.command("describe")
|
|
@@ -11,19 +11,21 @@ export function createDescribeCommand(program) {
|
|
|
11
11
|
.description("Show detailed YAML description of an entry")
|
|
12
12
|
.addHelpText("after", `
|
|
13
13
|
Examples:
|
|
14
|
-
ay describe contacts 64a1b2c3d4e5
|
|
15
|
-
ay
|
|
14
|
+
ay describe contacts 64a1b2c3d4e5 Describe a contact entry
|
|
15
|
+
ay describe tasks 64a1b2c3d4e5 comments Extract sub-resource array
|
|
16
|
+
ay describe tasks 64a1b2c3d4e5 comments -r json Sub-resource as JSON
|
|
17
|
+
ay describe tasks 64a1b2c3d4e5 --jq "subject" JMESPath on describe
|
|
18
|
+
ay d Describe last used entry`)
|
|
16
19
|
.addArgument(new Argument("[collection]", "The collection to use").default(localStorage.getItem("lastCollection"), `The last used collection (${localStorage.getItem("lastCollection")})`))
|
|
17
20
|
.addArgument(new Argument("[id]", "The ID of the entry to describe").default(localStorage.getItem("lastId"), `The last used id (${localStorage.getItem("lastId")})`))
|
|
18
|
-
.
|
|
21
|
+
.addArgument(new Argument("[subResource]", "Extract a sub-resource array (e.g., comments, attachments, worklogs)"))
|
|
22
|
+
.action(async (collection, id, subResource, options) => {
|
|
19
23
|
try {
|
|
20
24
|
if (!collection) {
|
|
21
|
-
|
|
22
|
-
process.exit(EXIT_MISUSE);
|
|
25
|
+
cliError("Missing required argument: collection. Run a list or get command first, or provide it explicitly.", EXIT_MISUSE);
|
|
23
26
|
}
|
|
24
27
|
if (!id) {
|
|
25
|
-
|
|
26
|
-
process.exit(EXIT_MISUSE);
|
|
28
|
+
cliError("Missing required argument: id. Provide an entry ID explicitly.", EXIT_MISUSE);
|
|
27
29
|
}
|
|
28
30
|
const opts = { ...program.opts(), ...options };
|
|
29
31
|
const module = getModuleFromCollection(collection);
|
|
@@ -32,11 +34,11 @@ Examples:
|
|
|
32
34
|
localStorage.setItem("lastId", id);
|
|
33
35
|
await handleDescribeSingleOperation(module.module, collection, id, {
|
|
34
36
|
...opts,
|
|
37
|
+
subResource,
|
|
35
38
|
});
|
|
36
39
|
}
|
|
37
40
|
catch (e) {
|
|
38
|
-
|
|
39
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
41
|
+
cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
|
|
40
42
|
}
|
|
41
43
|
});
|
|
42
44
|
}
|
|
@@ -2,9 +2,10 @@ import { Argument } from "commander";
|
|
|
2
2
|
import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
|
|
3
3
|
import { handleGetSingleOperation } from "../operations/handleGetSingleOperation.js";
|
|
4
4
|
import { handleEditOperation } from "../operations/handleEditOperation.js";
|
|
5
|
+
import { handleEditRawOperation } from "../operations/handleEditRawOperation.js";
|
|
5
6
|
import { localStorage } from "../helpers/localStorage.js";
|
|
6
|
-
import { spinner } from "../../index.js";
|
|
7
7
|
import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
|
|
8
|
+
import { cliError } from "../helpers/cliError.js";
|
|
8
9
|
export function createEditCommand(program) {
|
|
9
10
|
program
|
|
10
11
|
.command("edit")
|
|
@@ -19,12 +20,10 @@ Examples:
|
|
|
19
20
|
.action(async (collection, id, options) => {
|
|
20
21
|
try {
|
|
21
22
|
if (!collection) {
|
|
22
|
-
|
|
23
|
-
process.exit(EXIT_MISUSE);
|
|
23
|
+
cliError("Missing required argument: collection. Run a list or get command first, or provide it explicitly.", EXIT_MISUSE);
|
|
24
24
|
}
|
|
25
25
|
if (!id) {
|
|
26
|
-
|
|
27
|
-
process.exit(EXIT_MISUSE);
|
|
26
|
+
cliError("Missing required argument: id. Provide an entry ID explicitly.", EXIT_MISUSE);
|
|
28
27
|
}
|
|
29
28
|
const opts = { ...program.opts(), ...options };
|
|
30
29
|
const module = getModuleFromCollection(collection);
|
|
@@ -33,11 +32,17 @@ Examples:
|
|
|
33
32
|
localStorage.setItem("lastId", id);
|
|
34
33
|
let result = {};
|
|
35
34
|
result = await handleGetSingleOperation(module.module, collection, id, opts);
|
|
36
|
-
|
|
35
|
+
// handleEditOperation expects {columns, rows} table structure;
|
|
36
|
+
// fall back to raw JSON editor if content doesn't have columns
|
|
37
|
+
if (result.content && result.content.columns) {
|
|
38
|
+
await handleEditOperation(module.module, collection, result.content);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
await handleEditRawOperation(module.module, collection, result.result || result.data);
|
|
42
|
+
}
|
|
37
43
|
}
|
|
38
44
|
catch (e) {
|
|
39
|
-
|
|
40
|
-
process.exit(EXIT_GENERAL_ERROR);
|
|
45
|
+
cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
|
|
41
46
|
}
|
|
42
47
|
});
|
|
43
48
|
}
|