@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.
Files changed (70) hide show
  1. package/data/contextSlots.js +189 -0
  2. package/data/modelsAndRights.js +56 -0
  3. package/data/modules.js +16 -0
  4. package/lib/api/apiCallHandler.js +6 -2
  5. package/lib/api/apiClient.js +9 -1
  6. package/lib/api/auditCallHandler.js +2 -2
  7. package/lib/api/handleAPIError.js +20 -18
  8. package/lib/api/login.js +3 -3
  9. package/lib/api/searchClient.js +119 -0
  10. package/lib/commands/createAccessCommand.js +126 -0
  11. package/lib/commands/createActionsCommand.js +40 -9
  12. package/lib/commands/createAiCommand.js +17 -17
  13. package/lib/commands/createAliasCommand.js +4 -6
  14. package/lib/commands/createAuditCommand.js +5 -9
  15. package/lib/commands/createBatchCommand.js +15 -28
  16. package/lib/commands/createCompletionsCommand.js +6 -3
  17. package/lib/commands/createConfigCommand.js +8 -14
  18. package/lib/commands/createContextCommand.js +163 -0
  19. package/lib/commands/createCopyCommand.js +4 -7
  20. package/lib/commands/createCreateCommand.js +4 -7
  21. package/lib/commands/createDeleteCommand.js +4 -6
  22. package/lib/commands/createDeployCommand.js +31 -55
  23. package/lib/commands/createDescribeCommand.js +12 -10
  24. package/lib/commands/createEditCommand.js +13 -8
  25. package/lib/commands/createEventsCommand.js +4 -4
  26. package/lib/commands/createExecCommand.js +65 -35
  27. package/lib/commands/createExportCommand.js +21 -24
  28. package/lib/commands/createGetCommand.js +13 -14
  29. package/lib/commands/createJobsCommand.js +8 -13
  30. package/lib/commands/createListCommand.js +13 -14
  31. package/lib/commands/createLoginCommand.js +16 -4
  32. package/lib/commands/createLogoutCommand.js +2 -2
  33. package/lib/commands/createModulesCommand.js +16 -19
  34. package/lib/commands/createMonitorCommand.js +9 -16
  35. package/lib/commands/createPermissionsCommand.js +10 -18
  36. package/lib/commands/createProgram.js +47 -21
  37. package/lib/commands/createSearchCommand.js +219 -69
  38. package/lib/commands/createSelfHostUpdateCommand.js +166 -0
  39. package/lib/commands/createServicesCommand.js +5 -8
  40. package/lib/commands/createSetupCommand.js +305 -0
  41. package/lib/commands/createStatusCommand.js +147 -0
  42. package/lib/commands/createStorageCommand.js +2 -3
  43. package/lib/commands/createStreamCommand.js +4 -4
  44. package/lib/commands/createSyncCommand.js +5 -8
  45. package/lib/commands/createTemplateCommand.js +9 -16
  46. package/lib/commands/createUpdateCommand.js +12 -15
  47. package/lib/commands/createUsersCommand.js +21 -31
  48. package/lib/commands/createWebhooksCommand.js +15 -22
  49. package/lib/commands/createWhoAmICommand.js +8 -6
  50. package/lib/helpers/cliError.js +24 -0
  51. package/lib/helpers/config.js +1 -0
  52. package/lib/helpers/configLoader.js +6 -0
  53. package/lib/helpers/contextInjector.js +65 -0
  54. package/lib/helpers/contextResolver.js +70 -0
  55. package/lib/helpers/contextStore.js +46 -0
  56. package/lib/helpers/handleResponseFormatOptions.js +59 -10
  57. package/lib/helpers/logo.js +48 -0
  58. package/lib/helpers/resolveCollectionArgs.js +36 -0
  59. package/lib/helpers/sanitizeFields.js +18 -0
  60. package/lib/helpers/secureStorage.js +72 -0
  61. package/lib/helpers/tokenPayload.js +21 -0
  62. package/lib/helpers/updateNotifier.js +49 -0
  63. package/lib/models/getModuleFromCollection.js +4 -1
  64. package/lib/operations/handleCopySingleOperation.js +10 -2
  65. package/lib/operations/handleCreateSingleOperation.js +3 -0
  66. package/lib/operations/handleDescribeSingleOperation.js +23 -0
  67. package/lib/operations/handleGetOperation.js +9 -3
  68. package/lib/operations/handleListOperation.js +14 -10
  69. package/lib/prompts/promptModule.js +9 -6
  70. package/package.json +163 -158
@@ -1,10 +1,11 @@
1
1
  import { Option } from "commander";
2
- import { localStorage } from "../helpers/localStorage.js";
2
+ import { secureStorage } from "../helpers/secureStorage.js";
3
3
  import { decodeToken } from "../api/decodeToken.js";
4
4
  import { customerSocket } from "../socket/customerSocketClient.js";
5
5
  import { spinner } from "../../index.js";
6
6
  import yaml from "js-yaml";
7
7
  import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
8
+ import { cliError } from "../helpers/cliError.js";
8
9
  export function createEventsCommand(program) {
9
10
  program
10
11
  .command("events")
@@ -24,7 +25,7 @@ Examples:
24
25
  .addOption(new Option("-V, --value <value>", "Set the value to listen").default("*"))
25
26
  .action(async (options) => {
26
27
  try {
27
- const tokenPayload = decodeToken(localStorage.getItem("token"));
28
+ const tokenPayload = decodeToken(secureStorage.getItem("token"));
28
29
  const user = tokenPayload.payload;
29
30
  spinner.start({ text: `Starting stream with [${user._customerID}]` });
30
31
  const socket = customerSocket(user);
@@ -53,8 +54,7 @@ Examples:
53
54
  });
54
55
  }
55
56
  catch (e) {
56
- spinner.error({ text: e.message || "An unexpected error occurred" });
57
- process.exit(EXIT_GENERAL_ERROR);
57
+ cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
58
58
  }
59
59
  });
60
60
  }
@@ -1,35 +1,67 @@
1
1
  import chalk from "chalk";
2
- import { api } from "../api/apiClient.js";
3
- import { apiCallHandler } from "../api/apiCallHandler.js";
2
+ import { api, getModuleBaseUrl } from "../api/apiClient.js";
4
3
  import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
5
4
  import { saveFile } from "../helpers/saveFile.js";
6
- import { localStorage } from "../helpers/localStorage.js";
5
+ import { secureStorage } from "../helpers/secureStorage.js";
7
6
  import { spinner } from "../../index.js";
8
- import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
7
+ import { EXIT_GENERAL_ERROR, EXIT_MISUSE, EXIT_PERMISSION_DENIED } from "../exitCodes.js";
8
+ import { cliError } from "../helpers/cliError.js";
9
9
  async function resolveAction(searchTerm) {
10
- // Try exact operationId match first
11
- const res = await apiCallHandler("config", "ayouneapiactions", "get", null, {
12
- q: searchTerm,
13
- limit: 50,
14
- responseFormat: "json",
15
- });
16
- if (!(res === null || res === void 0 ? void 0 : res.payload) || !Array.isArray(res.payload) || res.payload.length === 0) {
17
- return null;
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.");
18
55
  }
19
- const actions = res.payload.filter((a) => !a.deprecated);
20
- // Exact operationId match
21
- const exact = actions.find((a) => a.operationId === searchTerm);
22
- if (exact)
23
- return mapAction(exact);
24
- // Partial operationId match
25
- const partial = actions.find((a) => { var _a; return (_a = a.operationId) === null || _a === void 0 ? void 0 : _a.includes(searchTerm); });
26
- if (partial)
27
- return mapAction(partial);
28
- // If multiple results, return first match
29
- if (actions.length > 0)
30
- return mapAction(actions[0]);
31
56
  return null;
32
57
  }
58
+ class PermissionError extends Error {
59
+ constructor(message) {
60
+ super(message);
61
+ this.isPermissionError = true;
62
+ this.name = "PermissionError";
63
+ }
64
+ }
33
65
  function mapAction(a) {
34
66
  return {
35
67
  host: a.host || "",
@@ -73,8 +105,7 @@ Examples:
73
105
  spinner.start({ text: `Resolving action: ${operationId}`, color: "magenta" });
74
106
  const action = await resolveAction(operationId);
75
107
  if (!action) {
76
- spinner.error({ text: `No API action found for: ${operationId}` });
77
- process.exit(EXIT_MISUSE);
108
+ cliError(`No API action found for: ${operationId}`, EXIT_MISUSE);
78
109
  }
79
110
  spinner.update({ text: `Executing: ${action.method} https://${action.host}${action.endpoint}` });
80
111
  // Parse path params
@@ -105,8 +136,7 @@ Examples:
105
136
  body = JSON.parse(opts.body);
106
137
  }
107
138
  catch (_a) {
108
- spinner.error({ text: "Invalid JSON in --body" });
109
- process.exit(EXIT_MISUSE);
139
+ cliError("Invalid JSON in --body", EXIT_MISUSE);
110
140
  }
111
141
  }
112
142
  if (opts.bodyFile) {
@@ -116,8 +146,7 @@ Examples:
116
146
  body = JSON.parse(content);
117
147
  }
118
148
  catch (_b) {
119
- spinner.error({ text: `Invalid JSON in file: ${opts.bodyFile}` });
120
- process.exit(EXIT_MISUSE);
149
+ cliError(`Invalid JSON in file: ${opts.bodyFile}`, EXIT_MISUSE);
121
150
  }
122
151
  }
123
152
  if (opts.bodyStdin && !process.stdin.isTTY) {
@@ -131,8 +160,7 @@ Examples:
131
160
  body = JSON.parse(stdinContent);
132
161
  }
133
162
  catch (_c) {
134
- spinner.error({ text: "Invalid JSON from stdin" });
135
- process.exit(EXIT_MISUSE);
163
+ cliError("Invalid JSON from stdin", EXIT_MISUSE);
136
164
  }
137
165
  }
138
166
  }
@@ -160,7 +188,7 @@ Examples:
160
188
  data: body,
161
189
  params: queryParams,
162
190
  headers: {
163
- Authorization: `Bearer ${localStorage.getItem("token")}`,
191
+ Authorization: `Bearer ${secureStorage.getItem("token")}`,
164
192
  },
165
193
  });
166
194
  const res = response.data;
@@ -175,8 +203,10 @@ Examples:
175
203
  }
176
204
  }
177
205
  catch (e) {
178
- spinner.error({ text: e.message || "Failed to execute API action" });
179
- process.exit(EXIT_GENERAL_ERROR);
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);
180
210
  }
181
211
  });
182
212
  }
@@ -4,6 +4,8 @@ import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOpti
4
4
  import { saveFile } from "../helpers/saveFile.js";
5
5
  import { spinner } from "../../index.js";
6
6
  import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
7
+ import { cliError } from "../helpers/cliError.js";
8
+ import { sanitizeFields } from "../helpers/sanitizeFields.js";
7
9
  export function createExportCommand(program) {
8
10
  const exp = program
9
11
  .command("export")
@@ -25,7 +27,7 @@ Examples:
25
27
  .option("-l, --limit <number>", "Limit results (0 = all)", parseInt, 0)
26
28
  .option("-p, --page <number>", "Page number", parseInt, 1)
27
29
  .action(async (collection, options) => {
28
- var _a, _b, _c, _d, _e, _f;
30
+ var _a, _b, _c, _d, _e;
29
31
  try {
30
32
  const opts = { ...program.opts(), ...options };
31
33
  const module = getModuleFromCollection(collection);
@@ -39,7 +41,7 @@ Examples:
39
41
  if (opts.limit > 0)
40
42
  params.limit = opts.limit;
41
43
  if (opts.fields)
42
- params.fields = opts.fields;
44
+ params.fields = sanitizeFields(opts.fields).join(",");
43
45
  // Parse filters
44
46
  if (opts.filter) {
45
47
  const filters = opts.filter.split(",");
@@ -96,8 +98,8 @@ Examples:
96
98
  else {
97
99
  const res = await apiCallHandler(module.module, collection.toLowerCase(), "get", null, params);
98
100
  opts.responseFormat = opts.format;
99
- handleResponseFormatOptions(opts, res);
100
- const total = (_f = (_e = (_d = res.meta) === null || _d === void 0 ? void 0 : _d.pageInfo) === null || _e === void 0 ? void 0 : _e.totalEntries) !== null && _f !== void 0 ? _f : 0;
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);
101
103
  spinner.success({ text: `Exported ${total} entries from ${collection}` });
102
104
  spinner.stop();
103
105
  if (opts.save)
@@ -105,8 +107,7 @@ Examples:
105
107
  }
106
108
  }
107
109
  catch (e) {
108
- spinner.error({ text: e.message || "Export failed" });
109
- process.exit(EXIT_GENERAL_ERROR);
110
+ cliError(e.message || "Export failed", EXIT_GENERAL_ERROR);
110
111
  }
111
112
  });
112
113
  // ay export list
@@ -117,7 +118,7 @@ Examples:
117
118
  .option("-l, --limit <number>", "Limit results", parseInt, 25)
118
119
  .option("-p, --page <number>", "Page number", parseInt, 1)
119
120
  .action(async (options) => {
120
- var _a, _b, _c;
121
+ var _a, _b;
121
122
  try {
122
123
  const opts = { ...program.opts(), ...options };
123
124
  spinner.start({ text: "Fetching exports...", color: "magenta" });
@@ -127,16 +128,15 @@ Examples:
127
128
  responseFormat: opts.responseFormat,
128
129
  verbosity: opts.verbosity,
129
130
  });
130
- handleResponseFormatOptions(opts, res);
131
- 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;
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);
132
133
  spinner.success({ text: `Found ${total} exports` });
133
134
  spinner.stop();
134
135
  if (opts.save)
135
136
  await saveFile("exports-list", opts, res);
136
137
  }
137
138
  catch (e) {
138
- spinner.error({ text: e.message || "Failed to list exports" });
139
- process.exit(EXIT_GENERAL_ERROR);
139
+ cliError(e.message || "Failed to list exports", EXIT_GENERAL_ERROR);
140
140
  }
141
141
  });
142
142
  // ay export get <id>
@@ -156,8 +156,7 @@ Examples:
156
156
  spinner.stop();
157
157
  }
158
158
  catch (e) {
159
- spinner.error({ text: e.message || "Failed to get export" });
160
- process.exit(EXIT_GENERAL_ERROR);
159
+ cliError(e.message || "Failed to get export", EXIT_GENERAL_ERROR);
161
160
  }
162
161
  });
163
162
  // ay export configs
@@ -166,7 +165,7 @@ Examples:
166
165
  .description("List export configurations")
167
166
  .option("-l, --limit <number>", "Limit results", parseInt, 50)
168
167
  .action(async (options) => {
169
- var _a, _b, _c;
168
+ var _a, _b;
170
169
  try {
171
170
  const opts = { ...program.opts(), ...options };
172
171
  spinner.start({ text: "Fetching export configs...", color: "magenta" });
@@ -175,16 +174,15 @@ Examples:
175
174
  responseFormat: opts.responseFormat,
176
175
  verbosity: opts.verbosity,
177
176
  });
178
- handleResponseFormatOptions(opts, res);
179
- 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;
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);
180
179
  spinner.success({ text: `Found ${total} export configurations` });
181
180
  spinner.stop();
182
181
  if (opts.save)
183
182
  await saveFile("export-configs", opts, res);
184
183
  }
185
184
  catch (e) {
186
- spinner.error({ text: e.message || "Failed to list export configs" });
187
- process.exit(EXIT_GENERAL_ERROR);
185
+ cliError(e.message || "Failed to list export configs", EXIT_GENERAL_ERROR);
188
186
  }
189
187
  });
190
188
  // ay export logs
@@ -194,26 +192,25 @@ Examples:
194
192
  .option("-l, --limit <number>", "Limit results", parseInt, 25)
195
193
  .option("-p, --page <number>", "Page number", parseInt, 1)
196
194
  .action(async (options) => {
197
- var _a, _b, _c;
195
+ var _a, _b;
198
196
  try {
199
197
  const opts = { ...program.opts(), ...options };
200
198
  spinner.start({ text: "Fetching export logs...", color: "magenta" });
201
- const res = await apiCallHandler("logs", "export", "get", null, {
199
+ const res = await apiCallHandler("export", "exportlogs", "get", null, {
202
200
  page: opts.page,
203
201
  limit: opts.limit,
204
202
  responseFormat: opts.responseFormat,
205
203
  verbosity: opts.verbosity,
206
204
  });
207
- handleResponseFormatOptions(opts, res);
208
- 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;
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);
209
207
  spinner.success({ text: `Found ${total} export log entries` });
210
208
  spinner.stop();
211
209
  if (opts.save)
212
210
  await saveFile("export-logs", opts, res);
213
211
  }
214
212
  catch (e) {
215
- spinner.error({ text: e.message || "Failed to list export logs" });
216
- process.exit(EXIT_GENERAL_ERROR);
213
+ cliError(e.message || "Failed to list export logs", EXIT_GENERAL_ERROR);
217
214
  }
218
215
  });
219
216
  }
@@ -1,47 +1,46 @@
1
1
  import { parseInteger } from "../helpers/parseInt.js";
2
2
  import { promptCollectionWithModule } from "../prompts/promptCollectionWithModule.js";
3
- import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
3
+ import { resolveCollectionArgs } from "../helpers/resolveCollectionArgs.js";
4
4
  import { saveFile } from "../helpers/saveFile.js";
5
5
  import { handleGetOperation } from "../operations/handleGetOperation.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 createGetCommand(program) {
10
10
  program
11
- .command("get [collection]")
11
+ .command("get [collectionOrModule] [collection]")
12
12
  .alias("g")
13
13
  .description("Retrieve entries from a collection with field selection")
14
14
  .addHelpText("after", `
15
15
  Examples:
16
16
  ay get contacts Get contacts with default fields
17
+ ay get crm consumers Get consumers (explicit module)
17
18
  ay get products -i name price Get only name and price fields
18
19
  ay get orders -p 3 -l 10 -r csv Get orders page 3, 10 per page, as CSV`)
19
20
  .option("-p, --page <number>", "Page", parseInteger, 1)
20
21
  .option("-l, --limit <number>", "Limit", parseInteger, 20)
21
22
  .option("-f, --from <date>", "From date")
22
23
  .option("-i, --fields <fields...>", "Fields to get")
23
- .action(async (collection, options) => {
24
+ .action(async (collectionOrModule, collection, options) => {
24
25
  try {
25
26
  const opts = { ...program.opts(), ...options };
26
- if (!collection) {
27
+ if (!collectionOrModule) {
27
28
  if (!process.stdin.isTTY) {
28
- spinner.error({ text: "Missing required argument: collection" });
29
- process.exit(EXIT_MISUSE);
29
+ cliError("Missing required argument: collection", EXIT_MISUSE);
30
30
  }
31
- collection = await promptCollectionWithModule();
31
+ collectionOrModule = await promptCollectionWithModule();
32
32
  }
33
- const module = getModuleFromCollection(collection);
34
- localStorage.setItem("lastModule", module.module);
35
- localStorage.setItem("lastCollection", collection);
33
+ const resolved = resolveCollectionArgs(collectionOrModule, collection);
34
+ localStorage.setItem("lastModule", resolved.module);
35
+ localStorage.setItem("lastCollection", resolved.collection);
36
36
  let result = {};
37
- result = await handleGetOperation(module.module, collection, opts);
37
+ result = await handleGetOperation(resolved.module, resolved.collection, opts);
38
38
  if (opts.save) {
39
39
  await saveFile("get", opts, result);
40
40
  }
41
41
  }
42
42
  catch (e) {
43
- spinner.error({ text: e.message || "An unexpected error occurred" });
44
- process.exit(EXIT_GENERAL_ERROR);
43
+ cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
45
44
  }
46
45
  });
47
46
  }
@@ -3,6 +3,7 @@ import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOpti
3
3
  import { saveFile } from "../helpers/saveFile.js";
4
4
  import { spinner } from "../../index.js";
5
5
  import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
6
+ import { cliError } from "../helpers/cliError.js";
6
7
  export function createJobsCommand(program) {
7
8
  const jobs = program
8
9
  .command("jobs")
@@ -38,8 +39,7 @@ export function createJobsCommand(program) {
38
39
  await saveFile("jobs-list", opts, res);
39
40
  }
40
41
  catch (e) {
41
- spinner.error({ text: e.message || "Failed to list jobs" });
42
- process.exit(EXIT_GENERAL_ERROR);
42
+ cliError(e.message || "Failed to list jobs", EXIT_GENERAL_ERROR);
43
43
  }
44
44
  });
45
45
  // ay jobs triggers
@@ -67,8 +67,7 @@ export function createJobsCommand(program) {
67
67
  await saveFile("jobs-triggers", opts, res);
68
68
  }
69
69
  catch (e) {
70
- spinner.error({ text: e.message || "Failed to list triggers" });
71
- process.exit(EXIT_GENERAL_ERROR);
70
+ cliError(e.message || "Failed to list triggers", EXIT_GENERAL_ERROR);
72
71
  }
73
72
  });
74
73
  // ay jobs automations
@@ -101,8 +100,7 @@ export function createJobsCommand(program) {
101
100
  await saveFile("jobs-automations", opts, res);
102
101
  }
103
102
  catch (e) {
104
- spinner.error({ text: e.message || "Failed to list automations" });
105
- process.exit(EXIT_GENERAL_ERROR);
103
+ cliError(e.message || "Failed to list automations", EXIT_GENERAL_ERROR);
106
104
  }
107
105
  });
108
106
  // ay jobs execute <automationId>
@@ -121,8 +119,7 @@ export function createJobsCommand(program) {
121
119
  body = JSON.parse(opts.body);
122
120
  }
123
121
  catch (_a) {
124
- spinner.error({ text: "Invalid JSON in --body" });
125
- process.exit(EXIT_GENERAL_ERROR);
122
+ cliError("Invalid JSON in --body", EXIT_GENERAL_ERROR);
126
123
  }
127
124
  }
128
125
  const res = await apiCallHandler("automation", `automations/${automationId}/execute`, "post", body, { responseFormat: opts.responseFormat });
@@ -131,8 +128,7 @@ export function createJobsCommand(program) {
131
128
  spinner.stop();
132
129
  }
133
130
  catch (e) {
134
- spinner.error({ text: e.message || "Failed to execute automation" });
135
- process.exit(EXIT_GENERAL_ERROR);
131
+ cliError(e.message || "Failed to execute automation", EXIT_GENERAL_ERROR);
136
132
  }
137
133
  });
138
134
  // ay jobs notifications
@@ -147,7 +143,7 @@ export function createJobsCommand(program) {
147
143
  try {
148
144
  const opts = { ...program.opts(), ...options };
149
145
  spinner.start({ text: "Fetching notifications...", color: "magenta" });
150
- const res = await apiCallHandler("general", "notifications", "get", null, {
146
+ const res = await apiCallHandler("crm", "notifications", "get", null, {
151
147
  page: opts.page,
152
148
  limit: opts.limit,
153
149
  responseFormat: opts.responseFormat,
@@ -161,8 +157,7 @@ export function createJobsCommand(program) {
161
157
  await saveFile("jobs-notifications", opts, res);
162
158
  }
163
159
  catch (e) {
164
- spinner.error({ text: e.message || "Failed to list notifications" });
165
- process.exit(EXIT_GENERAL_ERROR);
160
+ cliError(e.message || "Failed to list notifications", EXIT_GENERAL_ERROR);
166
161
  }
167
162
  });
168
163
  }
@@ -1,19 +1,20 @@
1
1
  import { parseInteger } from "../helpers/parseInt.js";
2
2
  import { promptCollectionWithModule } from "../prompts/promptCollectionWithModule.js";
3
- import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
3
+ import { resolveCollectionArgs } from "../helpers/resolveCollectionArgs.js";
4
4
  import { handleListOperation } from "../operations/handleListOperation.js";
5
5
  import { saveFile } from "../helpers/saveFile.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 createListCommand(program) {
10
10
  program
11
- .command("list [collection]")
11
+ .command("list [collectionOrModule] [collection]")
12
12
  .alias("l")
13
13
  .description("List entries in a collection with pagination support")
14
14
  .addHelpText("after", `
15
15
  Examples:
16
16
  ay list contacts List contacts (page 1, limit 20)
17
+ ay list crm consumers List consumers (explicit module)
17
18
  ay list products -p 2 -l 50 List products page 2, 50 per page
18
19
  ay list orders -r yaml -s List orders as YAML and save to file
19
20
  ay list contacts --all Fetch all pages
@@ -22,28 +23,26 @@ Examples:
22
23
  .option("-l, --limit <number>", "Limit", parseInteger, 20)
23
24
  .option("-f, --from <date>", "From date")
24
25
  .option("-a, --all", "Fetch all pages automatically")
25
- .action(async (collection, options) => {
26
+ .action(async (collectionOrModule, collection, options) => {
26
27
  try {
27
28
  const opts = { ...program.opts(), ...options };
28
- if (!collection) {
29
+ if (!collectionOrModule) {
29
30
  if (!process.stdin.isTTY) {
30
- spinner.error({ text: "Missing required argument: collection" });
31
- process.exit(EXIT_MISUSE);
31
+ cliError("Missing required argument: collection", EXIT_MISUSE);
32
32
  }
33
- collection = await promptCollectionWithModule();
33
+ collectionOrModule = await promptCollectionWithModule();
34
34
  }
35
- const module = getModuleFromCollection(collection);
36
- localStorage.setItem("lastModule", module.module);
37
- localStorage.setItem("lastCollection", collection);
35
+ const resolved = resolveCollectionArgs(collectionOrModule, collection);
36
+ localStorage.setItem("lastModule", resolved.module);
37
+ localStorage.setItem("lastCollection", resolved.collection);
38
38
  let result = {};
39
- result = await handleListOperation(module.module, collection, opts);
39
+ result = await handleListOperation(resolved.module, resolved.collection, opts);
40
40
  if (opts.save) {
41
41
  await saveFile("list", opts, result);
42
42
  }
43
43
  }
44
44
  catch (e) {
45
- spinner.error({ text: e.message || "An unexpected error occurred" });
46
- process.exit(EXIT_GENERAL_ERROR);
45
+ cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
47
46
  }
48
47
  });
49
48
  }
@@ -1,18 +1,30 @@
1
1
  import { login } from "../api/login.js";
2
2
  import { spinner } from "../../index.js";
3
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";
4
7
  export function createLoginCommand(program) {
5
8
  program
6
9
  .command("login")
7
10
  .alias("auth")
8
- .description("Authenticate with your aYOUne account via browser")
9
- .action(async () => {
11
+ .description("Authenticate with your aYOUne account via browser or JWT token")
12
+ .action(async (_options, cmd) => {
13
+ var _a, _b, _c, _d;
10
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
+ }
11
24
  await login();
12
25
  }
13
26
  catch (e) {
14
- spinner.error({ text: e.message || "An unexpected error occurred" });
15
- process.exit(EXIT_AUTH_REQUIRED);
27
+ cliError(e.message || "An unexpected error occurred", EXIT_AUTH_REQUIRED);
16
28
  }
17
29
  });
18
30
  }
@@ -1,6 +1,7 @@
1
1
  import { localStorage } from "../helpers/localStorage.js";
2
2
  import { spinner } from "../../index.js";
3
3
  import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
4
+ import { cliError } from "../helpers/cliError.js";
4
5
  export function createLogoutCommand(program) {
5
6
  program
6
7
  .command("logout")
@@ -14,8 +15,7 @@ export function createLogoutCommand(program) {
14
15
  spinner.success({ text: "Credentials cleared" });
15
16
  }
16
17
  catch (e) {
17
- spinner.error({ text: e.message || "An unexpected error occurred" });
18
- process.exit(EXIT_GENERAL_ERROR);
18
+ cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
19
19
  }
20
20
  });
21
21
  }