@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,44 @@
1
+ import { Argument } from "commander";
2
+ import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
3
+ import { handleDescribeSingleOperation } from "../operations/handleDescribeSingleOperation.js";
4
+ import { localStorage } from "../helpers/localStorage.js";
5
+ import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
6
+ import { cliError } from "../helpers/cliError.js";
7
+ export function createDescribeCommand(program) {
8
+ program
9
+ .command("describe")
10
+ .alias("d")
11
+ .description("Show detailed YAML description of an entry")
12
+ .addHelpText("after", `
13
+ Examples:
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`)
19
+ .addArgument(new Argument("[collection]", "The collection to use").default(localStorage.getItem("lastCollection"), `The last used collection (${localStorage.getItem("lastCollection")})`))
20
+ .addArgument(new Argument("[id]", "The ID of the entry to describe").default(localStorage.getItem("lastId"), `The last used id (${localStorage.getItem("lastId")})`))
21
+ .addArgument(new Argument("[subResource]", "Extract a sub-resource array (e.g., comments, attachments, worklogs)"))
22
+ .action(async (collection, id, subResource, options) => {
23
+ try {
24
+ if (!collection) {
25
+ cliError("Missing required argument: collection. Run a list or get command first, or provide it explicitly.", EXIT_MISUSE);
26
+ }
27
+ if (!id) {
28
+ cliError("Missing required argument: id. Provide an entry ID explicitly.", EXIT_MISUSE);
29
+ }
30
+ const opts = { ...program.opts(), ...options };
31
+ const module = getModuleFromCollection(collection);
32
+ localStorage.setItem("lastModule", module.module);
33
+ localStorage.setItem("lastCollection", collection);
34
+ localStorage.setItem("lastId", id);
35
+ await handleDescribeSingleOperation(module.module, collection, id, {
36
+ ...opts,
37
+ subResource,
38
+ });
39
+ }
40
+ catch (e) {
41
+ cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
42
+ }
43
+ });
44
+ }
@@ -0,0 +1,48 @@
1
+ import { Argument } from "commander";
2
+ import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
3
+ import { handleGetSingleOperation } from "../operations/handleGetSingleOperation.js";
4
+ import { handleEditOperation } from "../operations/handleEditOperation.js";
5
+ import { handleEditRawOperation } from "../operations/handleEditRawOperation.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 createEditCommand(program) {
10
+ program
11
+ .command("edit")
12
+ .alias("e")
13
+ .description("Edit an entry by ID in a collection")
14
+ .addHelpText("after", `
15
+ Examples:
16
+ ay edit contacts 64a1b2c3d4e5 Edit contact by ID
17
+ ay edit Edit last used entry`)
18
+ .addArgument(new Argument("[collection]", "The collection to use").default(localStorage.getItem("lastCollection"), `The last used collection (${localStorage.getItem("lastCollection")})`))
19
+ .addArgument(new Argument("[id]", "The ID of the entry to edit").default(localStorage.getItem("lastId"), `The last used id (${localStorage.getItem("lastId")})`))
20
+ .action(async (collection, id, options) => {
21
+ try {
22
+ if (!collection) {
23
+ cliError("Missing required argument: collection. Run a list or get command first, or provide it explicitly.", EXIT_MISUSE);
24
+ }
25
+ if (!id) {
26
+ cliError("Missing required argument: id. Provide an entry ID explicitly.", EXIT_MISUSE);
27
+ }
28
+ const opts = { ...program.opts(), ...options };
29
+ const module = getModuleFromCollection(collection);
30
+ localStorage.setItem("lastModule", module.module);
31
+ localStorage.setItem("lastCollection", collection);
32
+ localStorage.setItem("lastId", id);
33
+ let result = {};
34
+ result = await handleGetSingleOperation(module.module, collection, id, opts);
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
+ }
43
+ }
44
+ catch (e) {
45
+ cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
46
+ }
47
+ });
48
+ }
@@ -0,0 +1,60 @@
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 createEventsCommand(program) {
10
+ program
11
+ .command("events")
12
+ .alias("sub")
13
+ .description("Subscribe to filtered events via WebSocket")
14
+ .addHelpText("after", `
15
+ Examples:
16
+ ay events Listen to all events
17
+ ay events -c sales -a create Filter by category and action
18
+ ay sub -f yaml -c crm Subscribe to CRM events as YAML`)
19
+ .addOption(new Option("-f, --format <format>", "Set the output format")
20
+ .choices(["json", "yaml", "table"])
21
+ .default("json"))
22
+ .addOption(new Option("-c, --category <category>", "Set the category to listen").default("*"))
23
+ .addOption(new Option("-a, --action <action>", "Set the action to listen").default("*"))
24
+ .addOption(new Option("-l, --label <label>", "Set the label to listen").default("*"))
25
+ .addOption(new Option("-V, --value <value>", "Set the value to listen").default("*"))
26
+ .action(async (options) => {
27
+ try {
28
+ const tokenPayload = decodeToken(secureStorage.getItem("token"));
29
+ const user = tokenPayload.payload;
30
+ spinner.start({ text: `Starting stream with [${user._customerID}]` });
31
+ const socket = customerSocket(user);
32
+ spinner.update({ text: "Stream active" });
33
+ socket.on("event", (data) => {
34
+ if (options.category !== "*" && options.category !== data.category)
35
+ return;
36
+ if (options.action !== "*" && options.action !== data.action)
37
+ return;
38
+ if (options.label !== "*" && options.label !== data.label)
39
+ return;
40
+ if (options.value !== "*" && options.value !== data.value)
41
+ return;
42
+ spinner.update({
43
+ text: `Received [${data.category}.${data.action}.${data.label}.${data.value}]`,
44
+ });
45
+ if (options.format === "table") {
46
+ console.table(data.evt_data);
47
+ }
48
+ if (options.format === "yaml") {
49
+ console.log(yaml.dump(data.evt_data));
50
+ }
51
+ if (options.format === "json") {
52
+ console.log(JSON.stringify(data.evt_data, null, 2));
53
+ }
54
+ });
55
+ }
56
+ catch (e) {
57
+ cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
58
+ }
59
+ });
60
+ }
@@ -0,0 +1,212 @@
1
+ import chalk from "chalk";
2
+ import { api, getModuleBaseUrl } from "../api/apiClient.js";
3
+ import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
4
+ import { saveFile } from "../helpers/saveFile.js";
5
+ import { secureStorage } from "../helpers/secureStorage.js";
6
+ import { spinner } from "../../index.js";
7
+ import { EXIT_GENERAL_ERROR, EXIT_MISUSE, EXIT_PERMISSION_DENIED } from "../exitCodes.js";
8
+ import { cliError } from "../helpers/cliError.js";
9
+ async function resolveAction(searchTerm) {
10
+ var _a, _b;
11
+ // Try su module first (full access), fall back to config module (limited access)
12
+ const modules = ["su", "config"];
13
+ let permissionDenied = false;
14
+ for (const mod of modules) {
15
+ try {
16
+ const token = secureStorage.getItem("token") || "";
17
+ const response = await api({
18
+ baseURL: getModuleBaseUrl(mod),
19
+ method: "get",
20
+ url: "ayouneapiactions",
21
+ params: { q: searchTerm, limit: 50, responseFormat: "json" },
22
+ headers: { Authorization: `Bearer ${token}` },
23
+ });
24
+ const res = response.data;
25
+ if (!(res === null || res === void 0 ? void 0 : res.payload) || !Array.isArray(res.payload) || res.payload.length === 0) {
26
+ continue;
27
+ }
28
+ const actions = res.payload.filter((a) => !a.deprecated);
29
+ // Exact operationId match
30
+ const exact = actions.find((a) => a.operationId === searchTerm);
31
+ if (exact)
32
+ return mapAction(exact);
33
+ // Partial operationId match
34
+ const partial = actions.find((a) => { var _a; return (_a = a.operationId) === null || _a === void 0 ? void 0 : _a.includes(searchTerm); });
35
+ if (partial)
36
+ return mapAction(partial);
37
+ // If multiple results, return first match
38
+ if (actions.length > 0)
39
+ return mapAction(actions[0]);
40
+ }
41
+ catch (e) {
42
+ // On 401/403 permission denied, try next module
43
+ if (((_a = e.response) === null || _a === void 0 ? void 0 : _a.status) === 403 || ((_b = e.response) === null || _b === void 0 ? void 0 : _b.status) === 401) {
44
+ permissionDenied = true;
45
+ continue;
46
+ }
47
+ // For other errors (network, 500, etc.), only throw on last module
48
+ if (mod === modules[modules.length - 1])
49
+ throw e;
50
+ }
51
+ }
52
+ // Distinguish between "not found" and "permission denied on all modules"
53
+ if (permissionDenied) {
54
+ throw new PermissionError("Permission denied: you do not have access to query API actions. Contact your administrator for the required rights.");
55
+ }
56
+ return null;
57
+ }
58
+ class PermissionError extends Error {
59
+ constructor(message) {
60
+ super(message);
61
+ this.isPermissionError = true;
62
+ this.name = "PermissionError";
63
+ }
64
+ }
65
+ function mapAction(a) {
66
+ return {
67
+ host: a.host || "",
68
+ endpoint: a.endpoint || "",
69
+ method: (a.method || "GET").toUpperCase(),
70
+ operationId: a.operationId || "",
71
+ nameSpace: a.nameSpace || "",
72
+ capability: a.capability || "",
73
+ params: a.params || [],
74
+ query: a.query || [],
75
+ description: a.shortDescription || a.description || "",
76
+ };
77
+ }
78
+ function buildUrl(endpoint, paramValues) {
79
+ let url = endpoint;
80
+ for (const [key, value] of Object.entries(paramValues)) {
81
+ url = url.replace(`:${key}`, encodeURIComponent(value));
82
+ }
83
+ return url;
84
+ }
85
+ export function createExecCommand(program) {
86
+ program
87
+ .command("exec <operationId>")
88
+ .alias("x")
89
+ .description("Execute any registered API action by operationId")
90
+ .addHelpText("after", `
91
+ Examples:
92
+ ay exec ayoune.ai.conversations.create.one --body '{"prompt":"Hello"}'
93
+ ay exec ayoune.crm.contacts.list
94
+ ay exec ayoune.pm.projects.create.one --body '{"subject":"Migration"}'
95
+ ay exec ayoune.ai.prompts.list -r table
96
+ ay exec ayoune.sale.orders.get.one --param id=507f1f77bcf86cd799439011`)
97
+ .option("--body <json>", "Request body as JSON string")
98
+ .option("--body-file <path>", "Read request body from file")
99
+ .option("--body-stdin", "Read request body from stdin (for piping)")
100
+ .option("--param <kv...>", "Path parameters as key=value pairs")
101
+ .option("--query <kv...>", "Query parameters as key=value pairs")
102
+ .action(async (operationId, options) => {
103
+ try {
104
+ const opts = { ...program.opts(), ...options };
105
+ spinner.start({ text: `Resolving action: ${operationId}`, color: "magenta" });
106
+ const action = await resolveAction(operationId);
107
+ if (!action) {
108
+ cliError(`No API action found for: ${operationId}`, EXIT_MISUSE);
109
+ }
110
+ spinner.update({ text: `Executing: ${action.method} https://${action.host}${action.endpoint}` });
111
+ // Parse path params
112
+ const paramValues = {};
113
+ if (opts.param) {
114
+ for (const p of opts.param) {
115
+ const [key, ...rest] = p.split("=");
116
+ paramValues[key] = rest.join("=");
117
+ }
118
+ }
119
+ // Parse query params
120
+ const queryParams = {
121
+ responseFormat: opts.responseFormat,
122
+ verbosity: opts.verbosity,
123
+ };
124
+ if (opts.hideMeta)
125
+ queryParams.hideMeta = "true";
126
+ if (opts.query) {
127
+ for (const q of opts.query) {
128
+ const [key, ...rest] = q.split("=");
129
+ queryParams[key] = rest.join("=");
130
+ }
131
+ }
132
+ // Parse body
133
+ let body = null;
134
+ if (opts.body) {
135
+ try {
136
+ body = JSON.parse(opts.body);
137
+ }
138
+ catch (_a) {
139
+ cliError("Invalid JSON in --body", EXIT_MISUSE);
140
+ }
141
+ }
142
+ if (opts.bodyFile) {
143
+ const fs = await import("fs");
144
+ const content = fs.readFileSync(opts.bodyFile, "utf-8");
145
+ try {
146
+ body = JSON.parse(content);
147
+ }
148
+ catch (_b) {
149
+ cliError(`Invalid JSON in file: ${opts.bodyFile}`, EXIT_MISUSE);
150
+ }
151
+ }
152
+ if (opts.bodyStdin && !process.stdin.isTTY) {
153
+ const chunks = [];
154
+ for await (const chunk of process.stdin) {
155
+ chunks.push(chunk);
156
+ }
157
+ const stdinContent = Buffer.concat(chunks).toString("utf-8").trim();
158
+ if (stdinContent) {
159
+ try {
160
+ body = JSON.parse(stdinContent);
161
+ }
162
+ catch (_c) {
163
+ cliError("Invalid JSON from stdin", EXIT_MISUSE);
164
+ }
165
+ }
166
+ }
167
+ // Build final URL
168
+ const url = buildUrl(action.endpoint, paramValues);
169
+ const fullUrl = `https://${action.host}${url}`;
170
+ // Dry-run support
171
+ if (opts.dryRun && action.method !== "GET") {
172
+ spinner.stop();
173
+ console.error(chalk.yellow.bold("\n [DRY RUN] Request not sent:\n"));
174
+ console.error(` ${chalk.dim("Operation:")} ${chalk.cyan(action.operationId)}`);
175
+ console.error(` ${chalk.dim("Method:")} ${chalk.cyan(action.method)}`);
176
+ console.error(` ${chalk.dim("URL:")} ${chalk.cyan(fullUrl)}`);
177
+ if (body) {
178
+ console.error(` ${chalk.dim("Body:")} ${JSON.stringify(body, null, 2).split("\n").join("\n ")}`);
179
+ }
180
+ console.error();
181
+ return;
182
+ }
183
+ // Execute the request directly against the service host
184
+ const response = await api({
185
+ baseURL: `https://${action.host}`,
186
+ method: action.method.toLowerCase(),
187
+ url,
188
+ data: body,
189
+ params: queryParams,
190
+ headers: {
191
+ Authorization: `Bearer ${secureStorage.getItem("token")}`,
192
+ },
193
+ });
194
+ const res = response.data;
195
+ const { plainResult, result, content } = handleResponseFormatOptions(opts, res);
196
+ const payloadCount = Array.isArray(res === null || res === void 0 ? void 0 : res.payload) ? res.payload.length : 1;
197
+ spinner.success({
198
+ text: `${action.method} ${action.operationId} — ${payloadCount} result(s)`,
199
+ });
200
+ spinner.stop();
201
+ if (opts.save) {
202
+ await saveFile("exec", opts, res);
203
+ }
204
+ }
205
+ catch (e) {
206
+ if (e instanceof PermissionError) {
207
+ cliError(e.message, EXIT_PERMISSION_DENIED);
208
+ }
209
+ cliError(e.message || "Failed to execute API action", EXIT_GENERAL_ERROR);
210
+ }
211
+ });
212
+ }
@@ -0,0 +1,216 @@
1
+ import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
2
+ import { apiCallHandler } from "../api/apiCallHandler.js";
3
+ import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
4
+ import { saveFile } from "../helpers/saveFile.js";
5
+ import { spinner } from "../../index.js";
6
+ import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
7
+ import { cliError } from "../helpers/cliError.js";
8
+ import { sanitizeFields } from "../helpers/sanitizeFields.js";
9
+ export function createExportCommand(program) {
10
+ const exp = program
11
+ .command("export")
12
+ .alias("exp")
13
+ .description("Export data from collections in various formats");
14
+ // ay export run <collection>
15
+ exp
16
+ .command("run <collection>")
17
+ .description("Export entries from a collection")
18
+ .addHelpText("after", `
19
+ Examples:
20
+ ay export run contacts --format csv --fields "firstName,lastName,email"
21
+ ay export run products --format json --filter "status=active"
22
+ ay export run invoices --format csv --filter "createdAt>2025-01-01" --save`)
23
+ .option("--format <format>", "Export format (json, csv, yaml)", "csv")
24
+ .option("--fields <fields>", "Comma-separated fields to include in export")
25
+ .option("--filter <filters>", "Comma-separated key=value filters")
26
+ .option("--sort <field>", "Sort by field (prefix with - for descending)", "-createdAt")
27
+ .option("-l, --limit <number>", "Limit results (0 = all)", parseInt, 0)
28
+ .option("-p, --page <number>", "Page number", parseInt, 1)
29
+ .action(async (collection, options) => {
30
+ var _a, _b, _c, _d, _e;
31
+ try {
32
+ const opts = { ...program.opts(), ...options };
33
+ const module = getModuleFromCollection(collection);
34
+ spinner.start({ text: `Exporting ${collection}...`, color: "magenta" });
35
+ const params = {
36
+ page: opts.page,
37
+ sort: opts.sort,
38
+ responseFormat: opts.format,
39
+ verbosity: opts.verbosity,
40
+ };
41
+ if (opts.limit > 0)
42
+ params.limit = opts.limit;
43
+ if (opts.fields)
44
+ params.fields = sanitizeFields(opts.fields).join(",");
45
+ // Parse filters
46
+ if (opts.filter) {
47
+ const filters = opts.filter.split(",");
48
+ for (const f of filters) {
49
+ const match = f.match(/^(\w+)(!=|>=|<=|>|<|=)(.+)$/);
50
+ if (match) {
51
+ const [, key, op, value] = match;
52
+ if (op === "=")
53
+ params[key] = value;
54
+ else if (op === "!=")
55
+ params[`${key}[ne]`] = value;
56
+ else if (op === ">")
57
+ params[`${key}[gt]`] = value;
58
+ else if (op === "<")
59
+ params[`${key}[lt]`] = value;
60
+ else if (op === ">=")
61
+ params[`${key}[gte]`] = value;
62
+ else if (op === "<=")
63
+ params[`${key}[lte]`] = value;
64
+ }
65
+ }
66
+ }
67
+ // If limit=0, fetch all pages
68
+ if (opts.limit === 0) {
69
+ params.limit = 500;
70
+ const allPayload = [];
71
+ let currentPage = 1;
72
+ let totalPages = 1;
73
+ do {
74
+ params.page = currentPage;
75
+ const res = await apiCallHandler(module.module, collection.toLowerCase(), "get", null, params);
76
+ if (res === null || res === void 0 ? void 0 : res.payload) {
77
+ if (Array.isArray(res.payload))
78
+ allPayload.push(...res.payload);
79
+ else
80
+ allPayload.push(res.payload);
81
+ }
82
+ totalPages = (_c = (_b = (_a = res === null || res === void 0 ? void 0 : res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo) === null || _b === void 0 ? void 0 : _b.totalPages) !== null && _c !== void 0 ? _c : 1;
83
+ spinner.update({ text: `Exporting ${collection}... page ${currentPage}/${totalPages}` });
84
+ currentPage++;
85
+ } while (currentPage <= totalPages);
86
+ const fullRes = {
87
+ payload: allPayload,
88
+ meta: { pageInfo: { totalEntries: allPayload.length, page: 1, totalPages: 1 } },
89
+ };
90
+ // Force response format to match export format
91
+ opts.responseFormat = opts.format;
92
+ handleResponseFormatOptions(opts, fullRes);
93
+ spinner.success({ text: `Exported ${allPayload.length} entries from ${collection}` });
94
+ spinner.stop();
95
+ if (opts.save)
96
+ await saveFile(`export-${collection}`, opts, fullRes);
97
+ }
98
+ else {
99
+ const res = await apiCallHandler(module.module, collection.toLowerCase(), "get", null, params);
100
+ opts.responseFormat = opts.format;
101
+ const { result: expResult, meta: expMeta } = handleResponseFormatOptions(opts, res);
102
+ const total = (_e = (_d = expMeta === null || expMeta === void 0 ? void 0 : expMeta.pageInfo) === null || _d === void 0 ? void 0 : _d.totalEntries) !== null && _e !== void 0 ? _e : (Array.isArray(expResult) ? expResult.length : 0);
103
+ spinner.success({ text: `Exported ${total} entries from ${collection}` });
104
+ spinner.stop();
105
+ if (opts.save)
106
+ await saveFile(`export-${collection}`, opts, res);
107
+ }
108
+ }
109
+ catch (e) {
110
+ cliError(e.message || "Export failed", EXIT_GENERAL_ERROR);
111
+ }
112
+ });
113
+ // ay export list
114
+ exp
115
+ .command("list")
116
+ .alias("ls")
117
+ .description("List previous exports")
118
+ .option("-l, --limit <number>", "Limit results", parseInt, 25)
119
+ .option("-p, --page <number>", "Page number", parseInt, 1)
120
+ .action(async (options) => {
121
+ var _a, _b;
122
+ try {
123
+ const opts = { ...program.opts(), ...options };
124
+ spinner.start({ text: "Fetching exports...", color: "magenta" });
125
+ const res = await apiCallHandler("export", "exports", "get", null, {
126
+ page: opts.page,
127
+ limit: opts.limit,
128
+ responseFormat: opts.responseFormat,
129
+ verbosity: opts.verbosity,
130
+ });
131
+ const { result: listResult, meta: listMeta } = handleResponseFormatOptions(opts, res);
132
+ const total = (_b = (_a = listMeta === null || listMeta === void 0 ? void 0 : listMeta.pageInfo) === null || _a === void 0 ? void 0 : _a.totalEntries) !== null && _b !== void 0 ? _b : (Array.isArray(listResult) ? listResult.length : 0);
133
+ spinner.success({ text: `Found ${total} exports` });
134
+ spinner.stop();
135
+ if (opts.save)
136
+ await saveFile("exports-list", opts, res);
137
+ }
138
+ catch (e) {
139
+ cliError(e.message || "Failed to list exports", EXIT_GENERAL_ERROR);
140
+ }
141
+ });
142
+ // ay export get <id>
143
+ exp
144
+ .command("get <id>")
145
+ .description("Get export details and download URL")
146
+ .action(async (id, options) => {
147
+ try {
148
+ const opts = { ...program.opts(), ...options };
149
+ spinner.start({ text: `Fetching export ${id}...`, color: "magenta" });
150
+ const res = await apiCallHandler("export", `exports/${id}`, "get", null, {
151
+ responseFormat: opts.responseFormat,
152
+ verbosity: opts.verbosity,
153
+ });
154
+ handleResponseFormatOptions(opts, res);
155
+ spinner.success({ text: `Export ${id} loaded` });
156
+ spinner.stop();
157
+ }
158
+ catch (e) {
159
+ cliError(e.message || "Failed to get export", EXIT_GENERAL_ERROR);
160
+ }
161
+ });
162
+ // ay export configs
163
+ exp
164
+ .command("configs")
165
+ .description("List export configurations")
166
+ .option("-l, --limit <number>", "Limit results", parseInt, 50)
167
+ .action(async (options) => {
168
+ var _a, _b;
169
+ try {
170
+ const opts = { ...program.opts(), ...options };
171
+ spinner.start({ text: "Fetching export configs...", color: "magenta" });
172
+ const res = await apiCallHandler("export", "exportconfigs", "get", null, {
173
+ limit: opts.limit,
174
+ responseFormat: opts.responseFormat,
175
+ verbosity: opts.verbosity,
176
+ });
177
+ const { result: cfgResult, meta: cfgMeta } = handleResponseFormatOptions(opts, res);
178
+ const total = (_b = (_a = cfgMeta === null || cfgMeta === void 0 ? void 0 : cfgMeta.pageInfo) === null || _a === void 0 ? void 0 : _a.totalEntries) !== null && _b !== void 0 ? _b : (Array.isArray(cfgResult) ? cfgResult.length : 0);
179
+ spinner.success({ text: `Found ${total} export configurations` });
180
+ spinner.stop();
181
+ if (opts.save)
182
+ await saveFile("export-configs", opts, res);
183
+ }
184
+ catch (e) {
185
+ cliError(e.message || "Failed to list export configs", EXIT_GENERAL_ERROR);
186
+ }
187
+ });
188
+ // ay export logs
189
+ exp
190
+ .command("logs")
191
+ .description("List export logs")
192
+ .option("-l, --limit <number>", "Limit results", parseInt, 25)
193
+ .option("-p, --page <number>", "Page number", parseInt, 1)
194
+ .action(async (options) => {
195
+ var _a, _b;
196
+ try {
197
+ const opts = { ...program.opts(), ...options };
198
+ spinner.start({ text: "Fetching export logs...", color: "magenta" });
199
+ const res = await apiCallHandler("export", "exportlogs", "get", null, {
200
+ page: opts.page,
201
+ limit: opts.limit,
202
+ responseFormat: opts.responseFormat,
203
+ verbosity: opts.verbosity,
204
+ });
205
+ const { result: logResult, meta: logMeta } = handleResponseFormatOptions(opts, res);
206
+ const total = (_b = (_a = logMeta === null || logMeta === void 0 ? void 0 : logMeta.pageInfo) === null || _a === void 0 ? void 0 : _a.totalEntries) !== null && _b !== void 0 ? _b : (Array.isArray(logResult) ? logResult.length : 0);
207
+ spinner.success({ text: `Found ${total} export log entries` });
208
+ spinner.stop();
209
+ if (opts.save)
210
+ await saveFile("export-logs", opts, res);
211
+ }
212
+ catch (e) {
213
+ cliError(e.message || "Failed to list export logs", EXIT_GENERAL_ERROR);
214
+ }
215
+ });
216
+ }
@@ -0,0 +1,46 @@
1
+ import { parseInteger } from "../helpers/parseInt.js";
2
+ import { promptCollectionWithModule } from "../prompts/promptCollectionWithModule.js";
3
+ import { resolveCollectionArgs } from "../helpers/resolveCollectionArgs.js";
4
+ import { saveFile } from "../helpers/saveFile.js";
5
+ import { handleGetOperation } from "../operations/handleGetOperation.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 createGetCommand(program) {
10
+ program
11
+ .command("get [collectionOrModule] [collection]")
12
+ .alias("g")
13
+ .description("Retrieve entries from a collection with field selection")
14
+ .addHelpText("after", `
15
+ Examples:
16
+ ay get contacts Get contacts with default fields
17
+ ay get crm consumers Get consumers (explicit module)
18
+ ay get products -i name price Get only name and price fields
19
+ ay get orders -p 3 -l 10 -r csv Get orders page 3, 10 per page, as CSV`)
20
+ .option("-p, --page <number>", "Page", parseInteger, 1)
21
+ .option("-l, --limit <number>", "Limit", parseInteger, 20)
22
+ .option("-f, --from <date>", "From date")
23
+ .option("-i, --fields <fields...>", "Fields to get")
24
+ .action(async (collectionOrModule, collection, options) => {
25
+ try {
26
+ const opts = { ...program.opts(), ...options };
27
+ if (!collectionOrModule) {
28
+ if (!process.stdin.isTTY) {
29
+ cliError("Missing required argument: collection", EXIT_MISUSE);
30
+ }
31
+ collectionOrModule = await promptCollectionWithModule();
32
+ }
33
+ const resolved = resolveCollectionArgs(collectionOrModule, collection);
34
+ localStorage.setItem("lastModule", resolved.module);
35
+ localStorage.setItem("lastCollection", resolved.collection);
36
+ let result = {};
37
+ result = await handleGetOperation(resolved.module, resolved.collection, opts);
38
+ if (opts.save) {
39
+ await saveFile("get", opts, result);
40
+ }
41
+ }
42
+ catch (e) {
43
+ cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
44
+ }
45
+ });
46
+ }