@tolinax/ayoune-cli 2026.3.1 → 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.
Files changed (107) hide show
  1. package/data/contextSlots.js +189 -0
  2. package/data/defaultActions.js +9 -0
  3. package/data/modelsAndRights.js +3245 -0
  4. package/data/modules.js +127 -0
  5. package/data/operations.js +5 -0
  6. package/data/services.js +139 -0
  7. package/index.js +11 -0
  8. package/lib/api/apiCallHandler.js +72 -0
  9. package/lib/api/apiClient.js +108 -0
  10. package/lib/api/auditCallHandler.js +21 -0
  11. package/lib/api/decodeToken.js +4 -0
  12. package/lib/api/handleAPIError.js +61 -0
  13. package/lib/api/login.js +45 -0
  14. package/lib/api/searchClient.js +119 -0
  15. package/lib/commands/createAccessCommand.js +126 -0
  16. package/lib/commands/createActionsCommand.js +140 -0
  17. package/lib/commands/createAiCommand.js +188 -0
  18. package/lib/commands/createAliasCommand.js +104 -0
  19. package/lib/commands/createAuditCommand.js +45 -0
  20. package/lib/commands/createBatchCommand.js +291 -0
  21. package/lib/commands/createCompletionsCommand.js +172 -0
  22. package/lib/commands/createConfigCommand.js +202 -0
  23. package/lib/commands/createContextCommand.js +163 -0
  24. package/lib/commands/createCopyCommand.js +36 -0
  25. package/lib/commands/createCreateCommand.js +47 -0
  26. package/lib/commands/createDeleteCommand.js +96 -0
  27. package/lib/commands/createDeployCommand.js +642 -0
  28. package/lib/commands/createDescribeCommand.js +44 -0
  29. package/lib/commands/createEditCommand.js +48 -0
  30. package/lib/commands/createEventsCommand.js +60 -0
  31. package/lib/commands/createExecCommand.js +212 -0
  32. package/lib/commands/createExportCommand.js +216 -0
  33. package/lib/commands/createGetCommand.js +46 -0
  34. package/lib/commands/createJobsCommand.js +163 -0
  35. package/lib/commands/createListCommand.js +48 -0
  36. package/lib/commands/createLoginCommand.js +30 -0
  37. package/lib/commands/createLogoutCommand.js +21 -0
  38. package/lib/commands/createModulesCommand.js +147 -0
  39. package/lib/commands/createMonitorCommand.js +276 -0
  40. package/lib/commands/createPermissionsCommand.js +233 -0
  41. package/lib/commands/createProgram.js +211 -0
  42. package/lib/commands/createSearchCommand.js +251 -0
  43. package/lib/commands/createSelfHostUpdateCommand.js +166 -0
  44. package/lib/commands/createServicesCommand.js +225 -0
  45. package/lib/commands/createSetupCommand.js +305 -0
  46. package/lib/commands/createStatusCommand.js +147 -0
  47. package/lib/commands/createStorageCommand.js +53 -0
  48. package/lib/commands/createStreamCommand.js +50 -0
  49. package/lib/commands/createSyncCommand.js +174 -0
  50. package/lib/commands/createTemplateCommand.js +231 -0
  51. package/lib/commands/createUpdateCommand.js +112 -0
  52. package/lib/commands/createUsersCommand.js +275 -0
  53. package/lib/commands/createWebhooksCommand.js +149 -0
  54. package/lib/commands/createWhoAmICommand.js +90 -0
  55. package/lib/exitCodes.js +6 -0
  56. package/lib/helpers/addSpacesToCamelCase.js +5 -0
  57. package/lib/helpers/cliError.js +24 -0
  58. package/lib/helpers/config.js +7 -0
  59. package/lib/helpers/configLoader.js +66 -0
  60. package/lib/helpers/contextInjector.js +65 -0
  61. package/lib/helpers/contextResolver.js +70 -0
  62. package/lib/helpers/contextStore.js +46 -0
  63. package/lib/helpers/formatDocument.js +176 -0
  64. package/lib/helpers/handleResponseFormatOptions.js +134 -0
  65. package/lib/helpers/initializeSettings.js +14 -0
  66. package/lib/helpers/localStorage.js +4 -0
  67. package/lib/helpers/logo.js +48 -0
  68. package/lib/helpers/makeRandomToken.js +27 -0
  69. package/lib/helpers/parseInt.js +7 -0
  70. package/lib/helpers/requireArg.js +9 -0
  71. package/lib/helpers/resolveCollectionArgs.js +36 -0
  72. package/lib/helpers/sanitizeFields.js +18 -0
  73. package/lib/helpers/saveFile.js +39 -0
  74. package/lib/helpers/secureStorage.js +72 -0
  75. package/lib/helpers/tokenPayload.js +21 -0
  76. package/lib/helpers/updateNotifier.js +49 -0
  77. package/lib/models/getCollections.js +15 -0
  78. package/lib/models/getModelsInModules.js +13 -0
  79. package/lib/models/getModuleFromCollection.js +10 -0
  80. package/lib/operations/handleAuditOperation.js +22 -0
  81. package/lib/operations/handleCollectionOperation.js +91 -0
  82. package/lib/operations/handleCopySingleOperation.js +30 -0
  83. package/lib/operations/handleCreateSingleOperation.js +38 -0
  84. package/lib/operations/handleDeleteSingleOperation.js +14 -0
  85. package/lib/operations/handleDescribeSingleOperation.js +45 -0
  86. package/lib/operations/handleEditOperation.js +51 -0
  87. package/lib/operations/handleEditRawOperation.js +35 -0
  88. package/lib/operations/handleGetOperation.js +35 -0
  89. package/lib/operations/handleGetSingleOperation.js +20 -0
  90. package/lib/operations/handleListOperation.js +67 -0
  91. package/lib/operations/handleSingleAuditOperation.js +27 -0
  92. package/lib/prompts/promptAudits.js +15 -0
  93. package/lib/prompts/promptCollection.js +13 -0
  94. package/lib/prompts/promptCollectionInModule.js +13 -0
  95. package/lib/prompts/promptCollectionWithModule.js +15 -0
  96. package/lib/prompts/promptConfirm.js +12 -0
  97. package/lib/prompts/promptDefaultAction.js +13 -0
  98. package/lib/prompts/promptEntry.js +19 -0
  99. package/lib/prompts/promptFileName.js +12 -0
  100. package/lib/prompts/promptFilePath.js +18 -0
  101. package/lib/prompts/promptModule.js +22 -0
  102. package/lib/prompts/promptName.js +11 -0
  103. package/lib/prompts/promptOperation.js +13 -0
  104. package/lib/socket/customerSocketClient.js +13 -0
  105. package/lib/socket/socketClient.js +12 -0
  106. package/lib/types.js +1 -0
  107. package/package.json +13 -10
@@ -0,0 +1,163 @@
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 createJobsCommand(program) {
8
+ const jobs = program
9
+ .command("jobs")
10
+ .alias("j")
11
+ .description("Manage background jobs, automations, and triggers");
12
+ // ay jobs list
13
+ jobs
14
+ .command("list")
15
+ .alias("ls")
16
+ .description("List scheduled/queued jobs")
17
+ .option("--status <status>", "Filter: active, waiting, completed, failed, delayed")
18
+ .option("-l, --limit <number>", "Limit results", parseInt, 50)
19
+ .option("-p, --page <number>", "Page number", parseInt, 1)
20
+ .action(async (options) => {
21
+ var _a, _b, _c;
22
+ try {
23
+ const opts = { ...program.opts(), ...options };
24
+ spinner.start({ text: "Fetching jobs...", color: "magenta" });
25
+ const params = {
26
+ page: opts.page,
27
+ limit: opts.limit,
28
+ responseFormat: opts.responseFormat,
29
+ verbosity: opts.verbosity,
30
+ };
31
+ if (opts.status)
32
+ params.status = opts.status;
33
+ const res = await apiCallHandler("general", "agendajobs", "get", null, params);
34
+ handleResponseFormatOptions(opts, res);
35
+ 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;
36
+ spinner.success({ text: `Found ${total} jobs` });
37
+ spinner.stop();
38
+ if (opts.save)
39
+ await saveFile("jobs-list", opts, res);
40
+ }
41
+ catch (e) {
42
+ cliError(e.message || "Failed to list jobs", EXIT_GENERAL_ERROR);
43
+ }
44
+ });
45
+ // ay jobs triggers
46
+ jobs
47
+ .command("triggers")
48
+ .description("List automation triggers")
49
+ .option("-l, --limit <number>", "Limit results", parseInt, 50)
50
+ .option("-p, --page <number>", "Page number", parseInt, 1)
51
+ .action(async (options) => {
52
+ var _a, _b, _c;
53
+ try {
54
+ const opts = { ...program.opts(), ...options };
55
+ spinner.start({ text: "Fetching triggers...", color: "magenta" });
56
+ const res = await apiCallHandler("automation", "triggers", "get", null, {
57
+ page: opts.page,
58
+ limit: opts.limit,
59
+ responseFormat: opts.responseFormat,
60
+ verbosity: opts.verbosity,
61
+ });
62
+ handleResponseFormatOptions(opts, res);
63
+ 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;
64
+ spinner.success({ text: `Found ${total} triggers` });
65
+ spinner.stop();
66
+ if (opts.save)
67
+ await saveFile("jobs-triggers", opts, res);
68
+ }
69
+ catch (e) {
70
+ cliError(e.message || "Failed to list triggers", EXIT_GENERAL_ERROR);
71
+ }
72
+ });
73
+ // ay jobs automations
74
+ jobs
75
+ .command("automations")
76
+ .alias("auto")
77
+ .description("List automations")
78
+ .option("--active", "Show only active automations")
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 automations...", color: "magenta" });
86
+ const params = {
87
+ page: opts.page,
88
+ limit: opts.limit,
89
+ responseFormat: opts.responseFormat,
90
+ verbosity: opts.verbosity,
91
+ };
92
+ if (opts.active)
93
+ params.active = "true";
94
+ const res = await apiCallHandler("automation", "automations", "get", null, params);
95
+ handleResponseFormatOptions(opts, res);
96
+ 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;
97
+ spinner.success({ text: `Found ${total} automations` });
98
+ spinner.stop();
99
+ if (opts.save)
100
+ await saveFile("jobs-automations", opts, res);
101
+ }
102
+ catch (e) {
103
+ cliError(e.message || "Failed to list automations", EXIT_GENERAL_ERROR);
104
+ }
105
+ });
106
+ // ay jobs execute <automationId>
107
+ jobs
108
+ .command("execute <automationId>")
109
+ .alias("run")
110
+ .description("Execute an automation immediately")
111
+ .option("--body <json>", "Optional payload as JSON")
112
+ .action(async (automationId, options) => {
113
+ try {
114
+ const opts = { ...program.opts(), ...options };
115
+ spinner.start({ text: `Executing automation ${automationId}...`, color: "magenta" });
116
+ let body = null;
117
+ if (opts.body) {
118
+ try {
119
+ body = JSON.parse(opts.body);
120
+ }
121
+ catch (_a) {
122
+ cliError("Invalid JSON in --body", EXIT_GENERAL_ERROR);
123
+ }
124
+ }
125
+ const res = await apiCallHandler("automation", `automations/${automationId}/execute`, "post", body, { responseFormat: opts.responseFormat });
126
+ handleResponseFormatOptions(opts, res);
127
+ spinner.success({ text: `Automation ${automationId} executed` });
128
+ spinner.stop();
129
+ }
130
+ catch (e) {
131
+ cliError(e.message || "Failed to execute automation", EXIT_GENERAL_ERROR);
132
+ }
133
+ });
134
+ // ay jobs notifications
135
+ jobs
136
+ .command("notifications")
137
+ .alias("notify")
138
+ .description("List recent notifications")
139
+ .option("-l, --limit <number>", "Limit results", parseInt, 25)
140
+ .option("-p, --page <number>", "Page number", parseInt, 1)
141
+ .action(async (options) => {
142
+ var _a, _b, _c;
143
+ try {
144
+ const opts = { ...program.opts(), ...options };
145
+ spinner.start({ text: "Fetching notifications...", color: "magenta" });
146
+ const res = await apiCallHandler("crm", "notifications", "get", null, {
147
+ page: opts.page,
148
+ limit: opts.limit,
149
+ responseFormat: opts.responseFormat,
150
+ verbosity: opts.verbosity,
151
+ });
152
+ handleResponseFormatOptions(opts, res);
153
+ 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;
154
+ spinner.success({ text: `Found ${total} notifications` });
155
+ spinner.stop();
156
+ if (opts.save)
157
+ await saveFile("jobs-notifications", opts, res);
158
+ }
159
+ catch (e) {
160
+ cliError(e.message || "Failed to list notifications", EXIT_GENERAL_ERROR);
161
+ }
162
+ });
163
+ }
@@ -0,0 +1,48 @@
1
+ import { parseInteger } from "../helpers/parseInt.js";
2
+ import { promptCollectionWithModule } from "../prompts/promptCollectionWithModule.js";
3
+ import { resolveCollectionArgs } from "../helpers/resolveCollectionArgs.js";
4
+ import { handleListOperation } from "../operations/handleListOperation.js";
5
+ import { saveFile } from "../helpers/saveFile.js";
6
+ import { localStorage } from "../helpers/localStorage.js";
7
+ import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
8
+ import { cliError } from "../helpers/cliError.js";
9
+ export function createListCommand(program) {
10
+ program
11
+ .command("list [collectionOrModule] [collection]")
12
+ .alias("l")
13
+ .description("List entries in a collection with pagination support")
14
+ .addHelpText("after", `
15
+ Examples:
16
+ ay list contacts List contacts (page 1, limit 20)
17
+ ay list crm consumers List consumers (explicit module)
18
+ ay list products -p 2 -l 50 List products page 2, 50 per page
19
+ ay list orders -r yaml -s List orders as YAML and save to file
20
+ ay list contacts --all Fetch all pages
21
+ ay list contacts --jq '[].name' Extract just names using JMESPath`)
22
+ .option("-p, --page <number>", "Page", parseInteger, 1)
23
+ .option("-l, --limit <number>", "Limit", parseInteger, 20)
24
+ .option("-f, --from <date>", "From date")
25
+ .option("-a, --all", "Fetch all pages automatically")
26
+ .action(async (collectionOrModule, collection, options) => {
27
+ try {
28
+ const opts = { ...program.opts(), ...options };
29
+ if (!collectionOrModule) {
30
+ if (!process.stdin.isTTY) {
31
+ cliError("Missing required argument: collection", EXIT_MISUSE);
32
+ }
33
+ collectionOrModule = await promptCollectionWithModule();
34
+ }
35
+ const resolved = resolveCollectionArgs(collectionOrModule, collection);
36
+ localStorage.setItem("lastModule", resolved.module);
37
+ localStorage.setItem("lastCollection", resolved.collection);
38
+ let result = {};
39
+ result = await handleListOperation(resolved.module, resolved.collection, opts);
40
+ if (opts.save) {
41
+ await saveFile("list", opts, result);
42
+ }
43
+ }
44
+ catch (e) {
45
+ cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
46
+ }
47
+ });
48
+ }
@@ -0,0 +1,30 @@
1
+ import { login } from "../api/login.js";
2
+ import { spinner } from "../../index.js";
3
+ import { EXIT_AUTH_REQUIRED } from "../exitCodes.js";
4
+ import { cliError } from "../helpers/cliError.js";
5
+ import { decodeToken } from "../api/decodeToken.js";
6
+ import chalk from "chalk";
7
+ export function createLoginCommand(program) {
8
+ program
9
+ .command("login")
10
+ .alias("auth")
11
+ .description("Authenticate with your aYOUne account via browser or JWT token")
12
+ .action(async (_options, cmd) => {
13
+ var _a, _b, _c, _d;
14
+ try {
15
+ // --token is a global option, check it from the parent program
16
+ const globalToken = (_b = (_a = cmd.parent) === null || _a === void 0 ? void 0 : _a.opts()) === null || _b === void 0 ? void 0 : _b.token;
17
+ if (globalToken) {
18
+ // Token was already stored by preAction hook, just confirm
19
+ const decoded = decodeToken(globalToken);
20
+ const username = ((_c = decoded === null || decoded === void 0 ? void 0 : decoded.payload) === null || _c === void 0 ? void 0 : _c.username) || ((_d = decoded === null || decoded === void 0 ? void 0 : decoded.payload) === null || _d === void 0 ? void 0 : _d._id) || "unknown";
21
+ spinner.success({ text: `Authenticated as ${chalk.cyan(username)} via token` });
22
+ return;
23
+ }
24
+ await login();
25
+ }
26
+ catch (e) {
27
+ cliError(e.message || "An unexpected error occurred", EXIT_AUTH_REQUIRED);
28
+ }
29
+ });
30
+ }
@@ -0,0 +1,21 @@
1
+ import { localStorage } from "../helpers/localStorage.js";
2
+ import { spinner } from "../../index.js";
3
+ import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
4
+ import { cliError } from "../helpers/cliError.js";
5
+ export function createLogoutCommand(program) {
6
+ program
7
+ .command("logout")
8
+ .alias("signout")
9
+ .description("Clear stored credentials and log out")
10
+ .action(async (options) => {
11
+ try {
12
+ spinner.start({ text: "Clearing credentials" });
13
+ localStorage.removeItem("token");
14
+ localStorage.removeItem("refreshToken");
15
+ spinner.success({ text: "Credentials cleared" });
16
+ }
17
+ catch (e) {
18
+ cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
19
+ }
20
+ });
21
+ }
@@ -0,0 +1,147 @@
1
+ import { promptModule } from "../prompts/promptModule.js";
2
+ import { promptCollectionInModule } from "../prompts/promptCollectionInModule.js";
3
+ import { promptOperation } from "../prompts/promptOperation.js";
4
+ import { handleListOperation } from "../operations/handleListOperation.js";
5
+ import { promptEntry } from "../prompts/promptEntry.js";
6
+ import { handleCollectionOperation } from "../operations/handleCollectionOperation.js";
7
+ import { localStorage } from "../helpers/localStorage.js";
8
+ import { parseInteger } from "../helpers/parseInt.js";
9
+ import { handleGetOperation } from "../operations/handleGetOperation.js";
10
+ import { handleCreateSingleOperation } from "../operations/handleCreateSingleOperation.js";
11
+ import { handleDeleteSingleOperation } from "../operations/handleDeleteSingleOperation.js";
12
+ import { aYOUneModules } from "../../data/modules.js";
13
+ import { getModelsInModules } from "../models/getModelsInModules.js";
14
+ import { apiCallHandler } from "../api/apiCallHandler.js";
15
+ import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
16
+ import { isSuperUser } from "../helpers/tokenPayload.js";
17
+ import { spinner } from "../../index.js";
18
+ import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
19
+ import { cliError } from "../helpers/cliError.js";
20
+ const DIRECT_OPERATIONS = ["list", "get", "create", "delete"];
21
+ function resolveModule(input) {
22
+ const lower = input.toLowerCase();
23
+ const match = aYOUneModules.find((m) => m.module === lower || m.label.toLowerCase() === lower);
24
+ if (!match)
25
+ return null;
26
+ if (match.superuserOnly && !isSuperUser())
27
+ return null;
28
+ return match.module;
29
+ }
30
+ function resolveCollection(module, input) {
31
+ const lower = input.toLowerCase();
32
+ const collections = getModelsInModules(module);
33
+ const match = collections.find((c) => c.value === lower || c.name.toLowerCase() === lower);
34
+ return match ? match.value : null;
35
+ }
36
+ export function createModulesCommand(program) {
37
+ program
38
+ .command("modules [module] [collection] [operation] [subject]")
39
+ .alias("m")
40
+ .description("Browse modules, collections, and entries interactively")
41
+ .addHelpText("after", `
42
+ Examples:
43
+ ay modules Start interactive module browser
44
+ ay m Same using alias
45
+ ay m crm Jump straight to the CRM module
46
+ ay m crm consumers Jump to CRM consumers collection
47
+ ay m pm projects list List all projects
48
+ ay m pm projects create "Migration" Create a new project
49
+ ay m pm projects get <id> Get a project by ID
50
+ ay m pm projects delete <id> Delete a project by ID`)
51
+ .option("-p, --page <number>", "Page", parseInteger, 1)
52
+ .option("-l, --limit <number>", "Limit", parseInteger, 20)
53
+ .option("-f, --from <date>", "From date")
54
+ .option("-a, --all", "Fetch all pages (list only)")
55
+ .option("-q, --search <term>", "Search term")
56
+ .action(async (moduleArg, collectionArg, operationArg, subjectArg, options) => {
57
+ try {
58
+ const opts = { ...program.opts(), ...options };
59
+ // ─── Resolve module ──────────────────────────────────
60
+ let module;
61
+ if (moduleArg) {
62
+ const resolved = resolveModule(moduleArg);
63
+ if (!resolved) {
64
+ cliError(`Unknown module: ${moduleArg}`, EXIT_MISUSE);
65
+ }
66
+ module = resolved;
67
+ }
68
+ else {
69
+ if (!process.stdin.isTTY) {
70
+ cliError("The modules command requires an interactive terminal", EXIT_MISUSE);
71
+ }
72
+ module = await promptModule();
73
+ }
74
+ // ─── Resolve collection ──────────────────────────────
75
+ let collection;
76
+ if (collectionArg) {
77
+ const resolved = resolveCollection(module, collectionArg);
78
+ if (!resolved) {
79
+ cliError(`Unknown collection: ${collectionArg} in module ${module}`, EXIT_MISUSE);
80
+ }
81
+ collection = resolved;
82
+ }
83
+ else {
84
+ if (!process.stdin.isTTY) {
85
+ cliError("Missing required argument: collection", EXIT_MISUSE);
86
+ }
87
+ collection = await promptCollectionInModule(module);
88
+ }
89
+ localStorage.setItem("lastModule", module);
90
+ localStorage.setItem("lastCollection", collection);
91
+ // ─── Direct operation mode ───────────────────────────
92
+ if (operationArg && DIRECT_OPERATIONS.includes(operationArg.toLowerCase())) {
93
+ const op = operationArg.toLowerCase();
94
+ if (op === "list") {
95
+ const { result } = await handleListOperation(module, collection, opts);
96
+ return;
97
+ }
98
+ if (op === "get") {
99
+ if (!subjectArg) {
100
+ cliError("Missing required argument: id", EXIT_MISUSE);
101
+ }
102
+ spinner.start({ text: `Getting ${collection} ${subjectArg}...`, color: "magenta" });
103
+ const res = await apiCallHandler(module, `${collection.toLowerCase()}/${subjectArg}`, "get", null, {
104
+ responseFormat: opts.responseFormat,
105
+ verbosity: opts.verbosity,
106
+ });
107
+ handleResponseFormatOptions(opts, res);
108
+ spinner.success({ text: `Got ${collection} [${subjectArg}]` });
109
+ spinner.stop();
110
+ return;
111
+ }
112
+ if (op === "create") {
113
+ if (!subjectArg) {
114
+ cliError("Missing required argument: name/subject", EXIT_MISUSE);
115
+ }
116
+ await handleCreateSingleOperation(module, collection, subjectArg, opts);
117
+ return;
118
+ }
119
+ if (op === "delete") {
120
+ if (!subjectArg) {
121
+ cliError("Missing required argument: id", EXIT_MISUSE);
122
+ }
123
+ await handleDeleteSingleOperation(module, collection, subjectArg, opts);
124
+ return;
125
+ }
126
+ }
127
+ // ─── Interactive mode (no operation provided) ────────
128
+ if (!process.stdin.isTTY) {
129
+ cliError("Interactive mode requires a terminal. Use: ay m <module> <collection> <operation> [subject]", EXIT_MISUSE);
130
+ }
131
+ const operation = await promptOperation();
132
+ let entry = "";
133
+ if (operation === "list") {
134
+ const { result } = await handleListOperation(module, collection, opts);
135
+ entry = await promptEntry(result);
136
+ }
137
+ if (operation === "get") {
138
+ const { result } = await handleGetOperation(module, collection, opts);
139
+ entry = await promptEntry(result);
140
+ }
141
+ await handleCollectionOperation(module, collection, entry, opts);
142
+ }
143
+ catch (e) {
144
+ cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
145
+ }
146
+ });
147
+ }