@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
@@ -6,6 +6,7 @@ import { saveFile } from "../helpers/saveFile.js";
6
6
  import { localStorage } from "../helpers/localStorage.js";
7
7
  import { spinner } from "../../index.js";
8
8
  import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
9
+ import { cliError } from "../helpers/cliError.js";
9
10
  export function createUpdateCommand(program) {
10
11
  program
11
12
  .command("update")
@@ -29,12 +30,10 @@ Examples:
29
30
  .action(async (collection, id, options) => {
30
31
  try {
31
32
  if (!collection) {
32
- spinner.error({ text: "Missing required argument: collection" });
33
- process.exit(EXIT_MISUSE);
33
+ cliError("Missing required argument: collection", EXIT_MISUSE);
34
34
  }
35
35
  if (!id) {
36
- spinner.error({ text: "Missing required argument: id" });
37
- process.exit(EXIT_MISUSE);
36
+ cliError("Missing required argument: id", EXIT_MISUSE);
38
37
  }
39
38
  const opts = { ...program.opts(), ...options };
40
39
  const module = getModuleFromCollection(collection);
@@ -45,8 +44,7 @@ Examples:
45
44
  body = JSON.parse(opts.body);
46
45
  }
47
46
  catch (_a) {
48
- spinner.error({ text: "Invalid JSON in --body" });
49
- process.exit(EXIT_MISUSE);
47
+ cliError("Invalid JSON in --body", EXIT_MISUSE);
50
48
  }
51
49
  }
52
50
  if (opts.bodyFile) {
@@ -56,8 +54,7 @@ Examples:
56
54
  body = JSON.parse(content);
57
55
  }
58
56
  catch (_b) {
59
- spinner.error({ text: `Invalid JSON in file: ${opts.bodyFile}` });
60
- process.exit(EXIT_MISUSE);
57
+ cliError(`Invalid JSON in file: ${opts.bodyFile}`, EXIT_MISUSE);
61
58
  }
62
59
  }
63
60
  if (opts.bodyStdin && !process.stdin.isTTY) {
@@ -71,14 +68,12 @@ Examples:
71
68
  body = JSON.parse(stdinContent);
72
69
  }
73
70
  catch (_c) {
74
- spinner.error({ text: "Invalid JSON from stdin" });
75
- process.exit(EXIT_MISUSE);
71
+ cliError("Invalid JSON from stdin", EXIT_MISUSE);
76
72
  }
77
73
  }
78
74
  }
79
75
  if (!body) {
80
- spinner.error({ text: "No update data provided. Use --body, --body-file, or --body-stdin." });
81
- process.exit(EXIT_MISUSE);
76
+ cliError("No update data provided. Use --body, --body-file, or --body-stdin.", EXIT_MISUSE);
82
77
  }
83
78
  // Merge mode: GET existing → merge → PUT
84
79
  if (opts.merge) {
@@ -98,7 +93,10 @@ Examples:
98
93
  verbosity: opts.verbosity,
99
94
  hideMeta: opts.hideMeta,
100
95
  });
101
- handleResponseFormatOptions(opts, res);
96
+ // Dry-run returns null payload — skip output to avoid printing "null"
97
+ if ((res === null || res === void 0 ? void 0 : res.payload) !== null && (res === null || res === void 0 ? void 0 : res.payload) !== undefined) {
98
+ handleResponseFormatOptions(opts, res);
99
+ }
102
100
  spinner.success({ text: `Updated entry ${id} in ${collection}` });
103
101
  spinner.stop();
104
102
  localStorage.setItem("lastModule", module.module);
@@ -108,8 +106,7 @@ Examples:
108
106
  await saveFile("update", opts, res);
109
107
  }
110
108
  catch (e) {
111
- spinner.error({ text: e.message || "An unexpected error occurred" });
112
- process.exit(EXIT_GENERAL_ERROR);
109
+ cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
113
110
  }
114
111
  });
115
112
  }
@@ -4,6 +4,7 @@ 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, EXIT_MISUSE } from "../exitCodes.js";
7
+ import { cliError } from "../helpers/cliError.js";
7
8
  export function createUsersCommand(program) {
8
9
  const users = program
9
10
  .command("users")
@@ -36,7 +37,7 @@ export function createUsersCommand(program) {
36
37
  params.role = opts.role;
37
38
  if (opts.active)
38
39
  params.active = "true";
39
- const res = await apiCallHandler("config", "users", "get", null, params);
40
+ const res = await apiCallHandler("su", "users", "get", null, params);
40
41
  handleResponseFormatOptions(opts, res);
41
42
  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;
42
43
  spinner.success({ text: `Found ${total} users` });
@@ -45,8 +46,7 @@ export function createUsersCommand(program) {
45
46
  await saveFile("users-list", opts, res);
46
47
  }
47
48
  catch (e) {
48
- spinner.error({ text: e.message || "Failed to list users" });
49
- process.exit(EXIT_GENERAL_ERROR);
49
+ cliError(e.message || "Failed to list users", EXIT_GENERAL_ERROR);
50
50
  }
51
51
  });
52
52
  // ay users get <id>
@@ -57,7 +57,7 @@ export function createUsersCommand(program) {
57
57
  try {
58
58
  const opts = { ...program.opts(), ...options };
59
59
  spinner.start({ text: `Fetching user ${id}...`, color: "magenta" });
60
- const res = await apiCallHandler("config", `users/${id}`, "get", null, {
60
+ const res = await apiCallHandler("su", `users/${id}`, "get", null, {
61
61
  responseFormat: opts.responseFormat,
62
62
  verbosity: opts.verbosity,
63
63
  });
@@ -66,8 +66,7 @@ export function createUsersCommand(program) {
66
66
  spinner.stop();
67
67
  }
68
68
  catch (e) {
69
- spinner.error({ text: e.message || "Failed to get user" });
70
- process.exit(EXIT_GENERAL_ERROR);
69
+ cliError(e.message || "Failed to get user", EXIT_GENERAL_ERROR);
71
70
  }
72
71
  });
73
72
  // ay users invite --email <email> --role <roleId>
@@ -89,7 +88,7 @@ export function createUsersCommand(program) {
89
88
  body.firstName = opts.firstName;
90
89
  if (opts.lastName)
91
90
  body.lastName = opts.lastName;
92
- const res = await apiCallHandler("config", "users", "post", body, {
91
+ const res = await apiCallHandler("su", "users", "post", body, {
93
92
  responseFormat: opts.responseFormat,
94
93
  });
95
94
  handleResponseFormatOptions(opts, res);
@@ -97,8 +96,7 @@ export function createUsersCommand(program) {
97
96
  spinner.stop();
98
97
  }
99
98
  catch (e) {
100
- spinner.error({ text: e.message || "Failed to invite user" });
101
- process.exit(EXIT_GENERAL_ERROR);
99
+ cliError(e.message || "Failed to invite user", EXIT_GENERAL_ERROR);
102
100
  }
103
101
  });
104
102
  // ay users deactivate <id>
@@ -119,7 +117,7 @@ export function createUsersCommand(program) {
119
117
  }
120
118
  }
121
119
  spinner.start({ text: `Deactivating user ${id}...`, color: "magenta" });
122
- const res = await apiCallHandler("config", "users", "put", { _id: id, active: false }, {
120
+ const res = await apiCallHandler("su", "users", "put", { _id: id, active: false }, {
123
121
  responseFormat: opts.responseFormat,
124
122
  });
125
123
  handleResponseFormatOptions(opts, res);
@@ -127,8 +125,7 @@ export function createUsersCommand(program) {
127
125
  spinner.stop();
128
126
  }
129
127
  catch (e) {
130
- spinner.error({ text: e.message || "Failed to deactivate user" });
131
- process.exit(EXIT_GENERAL_ERROR);
128
+ cliError(e.message || "Failed to deactivate user", EXIT_GENERAL_ERROR);
132
129
  }
133
130
  });
134
131
  // ── TEAMS ──────────────────────────────────────────────
@@ -156,7 +153,7 @@ export function createUsersCommand(program) {
156
153
  };
157
154
  if (opts.search)
158
155
  params.q = opts.search;
159
- const res = await apiCallHandler("config", "teams", "get", null, params);
156
+ const res = await apiCallHandler("su", "teams", "get", null, params);
160
157
  handleResponseFormatOptions(opts, res);
161
158
  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;
162
159
  spinner.success({ text: `Found ${total} teams` });
@@ -165,8 +162,7 @@ export function createUsersCommand(program) {
165
162
  await saveFile("teams-list", opts, res);
166
163
  }
167
164
  catch (e) {
168
- spinner.error({ text: e.message || "Failed to list teams" });
169
- process.exit(EXIT_GENERAL_ERROR);
165
+ cliError(e.message || "Failed to list teams", EXIT_GENERAL_ERROR);
170
166
  }
171
167
  });
172
168
  // ay users teams get <id>
@@ -177,7 +173,7 @@ export function createUsersCommand(program) {
177
173
  try {
178
174
  const opts = { ...program.opts(), ...options };
179
175
  spinner.start({ text: `Fetching team ${id}...`, color: "magenta" });
180
- const res = await apiCallHandler("config", `teams/${id}`, "get", null, {
176
+ const res = await apiCallHandler("su", `teams/${id}`, "get", null, {
181
177
  responseFormat: opts.responseFormat,
182
178
  verbosity: opts.verbosity,
183
179
  });
@@ -186,8 +182,7 @@ export function createUsersCommand(program) {
186
182
  spinner.stop();
187
183
  }
188
184
  catch (e) {
189
- spinner.error({ text: e.message || "Failed to get team" });
190
- process.exit(EXIT_GENERAL_ERROR);
185
+ cliError(e.message || "Failed to get team", EXIT_GENERAL_ERROR);
191
186
  }
192
187
  });
193
188
  // ay users teams create --body '{...}'
@@ -205,19 +200,17 @@ export function createUsersCommand(program) {
205
200
  body = JSON.parse(opts.body);
206
201
  }
207
202
  catch (_a) {
208
- spinner.error({ text: "Invalid JSON in --body" });
209
- process.exit(EXIT_MISUSE);
203
+ cliError("Invalid JSON in --body", EXIT_MISUSE);
210
204
  }
211
205
  }
212
206
  else if (opts.name) {
213
207
  body = { name: opts.name };
214
208
  }
215
209
  if (!body) {
216
- spinner.error({ text: "Provide team definition via --name or --body" });
217
- process.exit(EXIT_MISUSE);
210
+ cliError("Provide team definition via --name or --body", EXIT_MISUSE);
218
211
  }
219
212
  spinner.start({ text: "Creating team...", color: "magenta" });
220
- const res = await apiCallHandler("config", "teams", "post", body, {
213
+ const res = await apiCallHandler("su", "teams", "post", body, {
221
214
  responseFormat: opts.responseFormat,
222
215
  });
223
216
  handleResponseFormatOptions(opts, res);
@@ -225,8 +218,7 @@ export function createUsersCommand(program) {
225
218
  spinner.stop();
226
219
  }
227
220
  catch (e) {
228
- spinner.error({ text: e.message || "Failed to create team" });
229
- process.exit(EXIT_GENERAL_ERROR);
221
+ cliError(e.message || "Failed to create team", EXIT_GENERAL_ERROR);
230
222
  }
231
223
  });
232
224
  // ── ROLES ──────────────────────────────────────────────
@@ -244,7 +236,7 @@ export function createUsersCommand(program) {
244
236
  try {
245
237
  const opts = { ...program.opts(), ...options };
246
238
  spinner.start({ text: "Fetching roles...", color: "magenta" });
247
- const res = await apiCallHandler("config", "roles", "get", null, {
239
+ const res = await apiCallHandler("su", "roles", "get", null, {
248
240
  limit: opts.limit,
249
241
  responseFormat: opts.responseFormat,
250
242
  verbosity: opts.verbosity,
@@ -257,8 +249,7 @@ export function createUsersCommand(program) {
257
249
  await saveFile("roles-list", opts, res);
258
250
  }
259
251
  catch (e) {
260
- spinner.error({ text: e.message || "Failed to list roles" });
261
- process.exit(EXIT_GENERAL_ERROR);
252
+ cliError(e.message || "Failed to list roles", EXIT_GENERAL_ERROR);
262
253
  }
263
254
  });
264
255
  // ay users roles get <id>
@@ -269,7 +260,7 @@ export function createUsersCommand(program) {
269
260
  try {
270
261
  const opts = { ...program.opts(), ...options };
271
262
  spinner.start({ text: `Fetching role ${id}...`, color: "magenta" });
272
- const res = await apiCallHandler("config", `roles/${id}`, "get", null, {
263
+ const res = await apiCallHandler("su", `roles/${id}`, "get", null, {
273
264
  responseFormat: opts.responseFormat,
274
265
  verbosity: opts.verbosity,
275
266
  });
@@ -278,8 +269,7 @@ export function createUsersCommand(program) {
278
269
  spinner.stop();
279
270
  }
280
271
  catch (e) {
281
- spinner.error({ text: e.message || "Failed to get role" });
282
- process.exit(EXIT_GENERAL_ERROR);
272
+ cliError(e.message || "Failed to get role", EXIT_GENERAL_ERROR);
283
273
  }
284
274
  });
285
275
  }
@@ -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, EXIT_MISUSE } from "../exitCodes.js";
6
+ import { cliError } from "../helpers/cliError.js";
6
7
  export function createWebhooksCommand(program) {
7
8
  const hooks = program
8
9
  .command("webhooks")
@@ -16,7 +17,7 @@ export function createWebhooksCommand(program) {
16
17
  .option("-l, --limit <number>", "Limit results", parseInt, 50)
17
18
  .option("-p, --page <number>", "Page number", parseInt, 1)
18
19
  .action(async (options) => {
19
- var _a, _b, _c;
20
+ var _a, _b;
20
21
  try {
21
22
  const opts = { ...program.opts(), ...options };
22
23
  spinner.start({ text: "Fetching webhooks...", color: "magenta" });
@@ -26,16 +27,15 @@ export function createWebhooksCommand(program) {
26
27
  responseFormat: opts.responseFormat,
27
28
  verbosity: opts.verbosity,
28
29
  });
29
- handleResponseFormatOptions(opts, res);
30
- 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;
30
+ const { result: fmtResult, meta: fmtMeta } = handleResponseFormatOptions(opts, res);
31
+ 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);
31
32
  spinner.success({ text: `Found ${total} webhooks` });
32
33
  spinner.stop();
33
34
  if (opts.save)
34
35
  await saveFile("webhooks-list", opts, res);
35
36
  }
36
37
  catch (e) {
37
- spinner.error({ text: e.message || "Failed to list webhooks" });
38
- process.exit(EXIT_GENERAL_ERROR);
38
+ cliError(e.message || "Failed to list webhooks", EXIT_GENERAL_ERROR);
39
39
  }
40
40
  });
41
41
  // ay webhooks get <id>
@@ -55,8 +55,7 @@ export function createWebhooksCommand(program) {
55
55
  spinner.stop();
56
56
  }
57
57
  catch (e) {
58
- spinner.error({ text: e.message || "Failed to get webhook" });
59
- process.exit(EXIT_GENERAL_ERROR);
58
+ cliError(e.message || "Failed to get webhook", EXIT_GENERAL_ERROR);
60
59
  }
61
60
  });
62
61
  // ay webhooks create --body '{...}'
@@ -74,8 +73,7 @@ export function createWebhooksCommand(program) {
74
73
  body = JSON.parse(opts.body);
75
74
  }
76
75
  catch (_a) {
77
- spinner.error({ text: "Invalid JSON in --body" });
78
- process.exit(EXIT_MISUSE);
76
+ cliError("Invalid JSON in --body", EXIT_MISUSE);
79
77
  }
80
78
  }
81
79
  else if (opts.bodyFile) {
@@ -84,13 +82,11 @@ export function createWebhooksCommand(program) {
84
82
  body = JSON.parse(fs.readFileSync(opts.bodyFile, "utf-8"));
85
83
  }
86
84
  catch (_b) {
87
- spinner.error({ text: `Invalid JSON in file: ${opts.bodyFile}` });
88
- process.exit(EXIT_MISUSE);
85
+ cliError(`Invalid JSON in file: ${opts.bodyFile}`, EXIT_MISUSE);
89
86
  }
90
87
  }
91
88
  if (!body) {
92
- spinner.error({ text: "Provide webhook definition via --body or --body-file" });
93
- process.exit(EXIT_MISUSE);
89
+ cliError("Provide webhook definition via --body or --body-file", EXIT_MISUSE);
94
90
  }
95
91
  spinner.start({ text: "Creating webhook...", color: "magenta" });
96
92
  const res = await apiCallHandler("config", "hooks", "post", body, {
@@ -101,8 +97,7 @@ export function createWebhooksCommand(program) {
101
97
  spinner.stop();
102
98
  }
103
99
  catch (e) {
104
- spinner.error({ text: e.message || "Failed to create webhook" });
105
- process.exit(EXIT_GENERAL_ERROR);
100
+ cliError(e.message || "Failed to create webhook", EXIT_GENERAL_ERROR);
106
101
  }
107
102
  });
108
103
  // ay webhooks delete <id>
@@ -122,8 +117,7 @@ export function createWebhooksCommand(program) {
122
117
  spinner.stop();
123
118
  }
124
119
  catch (e) {
125
- spinner.error({ text: e.message || "Failed to delete webhook" });
126
- process.exit(EXIT_GENERAL_ERROR);
120
+ cliError(e.message || "Failed to delete webhook", EXIT_GENERAL_ERROR);
127
121
  }
128
122
  });
129
123
  // ay webhooks templates
@@ -132,7 +126,7 @@ export function createWebhooksCommand(program) {
132
126
  .description("List available webhook templates")
133
127
  .option("-l, --limit <number>", "Limit results", parseInt, 50)
134
128
  .action(async (options) => {
135
- var _a, _b, _c;
129
+ var _a, _b;
136
130
  try {
137
131
  const opts = { ...program.opts(), ...options };
138
132
  spinner.start({ text: "Fetching webhook templates...", color: "magenta" });
@@ -141,16 +135,15 @@ export function createWebhooksCommand(program) {
141
135
  responseFormat: opts.responseFormat,
142
136
  verbosity: opts.verbosity,
143
137
  });
144
- handleResponseFormatOptions(opts, res);
145
- 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;
138
+ const { result: fmtResult, meta: fmtMeta } = handleResponseFormatOptions(opts, res);
139
+ 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);
146
140
  spinner.success({ text: `Found ${total} webhook templates` });
147
141
  spinner.stop();
148
142
  if (opts.save)
149
143
  await saveFile("webhook-templates", opts, res);
150
144
  }
151
145
  catch (e) {
152
- spinner.error({ text: e.message || "Failed to list templates" });
153
- process.exit(EXIT_GENERAL_ERROR);
146
+ cliError(e.message || "Failed to list templates", EXIT_GENERAL_ERROR);
154
147
  }
155
148
  });
156
149
  }
@@ -1,10 +1,10 @@
1
1
  import chalk from "chalk";
2
2
  import moment from "moment";
3
- import { localStorage } from "../helpers/localStorage.js";
3
+ import { secureStorage } from "../helpers/secureStorage.js";
4
4
  import { decodeToken } from "../api/decodeToken.js";
5
- import { spinner } from "../../index.js";
6
5
  import { login } from "../api/login.js";
7
6
  import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
7
+ import { cliError } from "../helpers/cliError.js";
8
8
  const LABEL_MAP = {
9
9
  _id: "User ID",
10
10
  _customerID: "Customer",
@@ -30,6 +30,9 @@ function formatValue(key, value) {
30
30
  }
31
31
  return `${formatted} ${chalk.dim(`(${relative})`)}`;
32
32
  }
33
+ if (key === "type" && value === "superuser") {
34
+ return chalk.yellow.bold("superuser");
35
+ }
33
36
  if (typeof value === "object" && value !== null) {
34
37
  return JSON.stringify(value);
35
38
  }
@@ -68,21 +71,20 @@ export function createWhoAmICommand(program) {
68
71
  .description("Display the currently authenticated user")
69
72
  .action(async (options) => {
70
73
  try {
71
- const tokenPayload = decodeToken(localStorage.getItem("token"));
74
+ const tokenPayload = decodeToken(secureStorage.getItem("token"));
72
75
  if (tokenPayload) {
73
76
  displayUser(tokenPayload.payload);
74
77
  }
75
78
  else {
76
79
  await login();
77
- const newPayload = decodeToken(localStorage.getItem("token"));
80
+ const newPayload = decodeToken(secureStorage.getItem("token"));
78
81
  if (newPayload) {
79
82
  displayUser(newPayload.payload);
80
83
  }
81
84
  }
82
85
  }
83
86
  catch (e) {
84
- spinner.error({ text: e.message || "An unexpected error occurred" });
85
- process.exit(EXIT_GENERAL_ERROR);
87
+ cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
86
88
  }
87
89
  });
88
90
  }
@@ -0,0 +1,24 @@
1
+ import { spinner } from "../../index.js";
2
+ let jsonErrorsEnabled = false;
3
+ export function setJsonErrorsEnabled(enabled) {
4
+ jsonErrorsEnabled = enabled;
5
+ }
6
+ export function isJsonErrorsEnabled() {
7
+ return jsonErrorsEnabled;
8
+ }
9
+ /**
10
+ * Centralized CLI error handler.
11
+ * When --json-errors is active, emits structured JSON to stderr.
12
+ * Otherwise, uses spinner.error for human-readable output.
13
+ * Always calls process.exit with the given code.
14
+ */
15
+ export function cliError(message, code, type = "cli_error") {
16
+ if (jsonErrorsEnabled) {
17
+ const err = { error: type, code, message };
18
+ console.error(JSON.stringify(err));
19
+ }
20
+ else {
21
+ spinner.error({ text: message });
22
+ }
23
+ process.exit(code);
24
+ }
@@ -3,4 +3,5 @@ export const config = {
3
3
  auditUrl: process.env.AYOUNE_AUDIT_URL || "https://audit.ayoune.app",
4
4
  notifierUrl: process.env.AYOUNE_NOTIFIER_URL || "https://notifier.ayoune.app",
5
5
  loginUrl: process.env.AYOUNE_LOGIN_URL || "https://login.ayoune.app",
6
+ searchUrl: process.env.AYOUNE_SEARCH_HOST || "https://search-api.ayoune.app",
6
7
  };
@@ -58,3 +58,9 @@ export function getNotifierUrl(config) {
58
58
  profile.notifierUrl ||
59
59
  "https://notifier.ayoune.app");
60
60
  }
61
+ export function getSearchUrl(config) {
62
+ const profile = getActiveProfile(config);
63
+ return (process.env.AYOUNE_SEARCH_HOST ||
64
+ profile.searchUrl ||
65
+ "https://search-api.ayoune.app");
66
+ }
@@ -0,0 +1,65 @@
1
+ import { contextSlots } from "../../data/contextSlots.js";
2
+ import { getActiveContext } from "./contextStore.js";
3
+ let contextDisabled = false;
4
+ export function disableContext() {
5
+ contextDisabled = true;
6
+ }
7
+ export function hasActiveContext() {
8
+ if (contextDisabled)
9
+ return false;
10
+ return Object.keys(getActiveContext()).length > 0;
11
+ }
12
+ export function getContextCreateFields(collection) {
13
+ if (contextDisabled)
14
+ return {};
15
+ const active = getActiveContext();
16
+ const fields = {};
17
+ const col = collection.toLowerCase();
18
+ for (const slot of contextSlots) {
19
+ const entry = active[slot.slot];
20
+ if (!entry)
21
+ continue;
22
+ const fieldName = slot.injectAsCreateField[col];
23
+ if (fieldName) {
24
+ fields[fieldName] = entry.id;
25
+ }
26
+ }
27
+ return fields;
28
+ }
29
+ export function getContextFilterParams(collection) {
30
+ if (contextDisabled)
31
+ return {};
32
+ const active = getActiveContext();
33
+ const params = {};
34
+ const col = collection.toLowerCase();
35
+ for (const slot of contextSlots) {
36
+ const entry = active[slot.slot];
37
+ if (!entry)
38
+ continue;
39
+ const paramName = slot.injectAsFilterField[col];
40
+ if (paramName) {
41
+ params[paramName] = entry.id;
42
+ }
43
+ }
44
+ return params;
45
+ }
46
+ export function getContextSummary() {
47
+ if (contextDisabled)
48
+ return "";
49
+ const active = getActiveContext();
50
+ const entries = Object.entries(active);
51
+ if (entries.length === 0)
52
+ return "";
53
+ const lines = entries.map(([slot, entry]) => `- Active ${slot}: "${entry.name}" (${entry.collection}, ID: ${entry.id})`);
54
+ return `[Active context]\n${lines.join("\n")}`;
55
+ }
56
+ export function getContextForAI() {
57
+ if (contextDisabled)
58
+ return {};
59
+ const active = getActiveContext();
60
+ const result = {};
61
+ for (const [slot, entry] of Object.entries(active)) {
62
+ result[slot] = { id: entry.id, name: entry.name, collection: entry.collection };
63
+ }
64
+ return result;
65
+ }
@@ -0,0 +1,70 @@
1
+ import { apiCallHandler } from "../api/apiCallHandler.js";
2
+ import { getSlot, getChildSlots } from "../../data/contextSlots.js";
3
+ import { getContextEntry } from "./contextStore.js";
4
+ const OBJECT_ID_RE = /^[0-9a-fA-F]{24}$/;
5
+ export async function resolveEntity(slot, input) {
6
+ const slotDef = getSlot(slot);
7
+ if (!slotDef)
8
+ return [];
9
+ // Direct ID lookup
10
+ if (OBJECT_ID_RE.test(input)) {
11
+ try {
12
+ const res = await apiCallHandler(slotDef.module, `${slotDef.collection}/${input}`, "get", null, { fields: `_id,${slotDef.nameField}`, responseFormat: "json" });
13
+ if (res === null || res === void 0 ? void 0 : res.payload) {
14
+ const entity = Array.isArray(res.payload) ? res.payload[0] : res.payload;
15
+ if (entity === null || entity === void 0 ? void 0 : entity._id) {
16
+ return [{ id: entity._id, name: entity[slotDef.nameField] || entity._id }];
17
+ }
18
+ }
19
+ }
20
+ catch (_a) {
21
+ // Fall through to search
22
+ }
23
+ }
24
+ // Search by name
25
+ const params = {
26
+ q: input,
27
+ limit: 5,
28
+ responseFormat: "json",
29
+ fields: `_id,${slotDef.nameField}`,
30
+ };
31
+ // If parent context is active, filter by it
32
+ if (slotDef.parent) {
33
+ const parentEntry = getContextEntry(slotDef.parent);
34
+ if (parentEntry) {
35
+ const parentSlot = getSlot(slotDef.parent);
36
+ if (parentSlot) {
37
+ const filterField = parentSlot.injectAsFilterField[slotDef.collection];
38
+ if (filterField) {
39
+ params[filterField] = parentEntry.id;
40
+ }
41
+ }
42
+ }
43
+ }
44
+ try {
45
+ const res = await apiCallHandler(slotDef.module, slotDef.collection, "get", null, params);
46
+ if ((res === null || res === void 0 ? void 0 : res.payload) && Array.isArray(res.payload)) {
47
+ return res.payload.map((e) => ({
48
+ id: e._id,
49
+ name: e[slotDef.nameField] || e._id,
50
+ }));
51
+ }
52
+ }
53
+ catch (_b) {
54
+ // API error — return empty
55
+ }
56
+ return [];
57
+ }
58
+ export function validateHierarchy(slot) {
59
+ const slotDef = getSlot(slot);
60
+ if (!(slotDef === null || slotDef === void 0 ? void 0 : slotDef.parent))
61
+ return null;
62
+ const parentEntry = getContextEntry(slotDef.parent);
63
+ if (!parentEntry) {
64
+ return `Warning: Setting "${slot}" without parent "${slotDef.parent}" context. Results may not be scoped correctly.`;
65
+ }
66
+ return null;
67
+ }
68
+ export function getDependentSlots(slot) {
69
+ return getChildSlots(slot).map((s) => s.slot);
70
+ }
@@ -0,0 +1,46 @@
1
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
2
+ import path from "path";
3
+ import os from "os";
4
+ const CONTEXT_PATH = path.join(os.homedir(), ".config", "ayoune", "context.json");
5
+ function ensureDir() {
6
+ const dir = path.dirname(CONTEXT_PATH);
7
+ if (!existsSync(dir)) {
8
+ mkdirSync(dir, { recursive: true });
9
+ }
10
+ }
11
+ export function loadContext() {
12
+ if (!existsSync(CONTEXT_PATH)) {
13
+ return { active: {} };
14
+ }
15
+ try {
16
+ const raw = readFileSync(CONTEXT_PATH, "utf-8");
17
+ return JSON.parse(raw);
18
+ }
19
+ catch (_a) {
20
+ return { active: {} };
21
+ }
22
+ }
23
+ export function saveContext(data) {
24
+ ensureDir();
25
+ writeFileSync(CONTEXT_PATH, JSON.stringify(data, null, 2), "utf-8");
26
+ }
27
+ export function getContextEntry(slot) {
28
+ const ctx = loadContext();
29
+ return ctx.active[slot];
30
+ }
31
+ export function setContextEntry(slot, entry) {
32
+ const ctx = loadContext();
33
+ ctx.active[slot] = entry;
34
+ saveContext(ctx);
35
+ }
36
+ export function unsetContextEntry(slot) {
37
+ const ctx = loadContext();
38
+ delete ctx.active[slot];
39
+ saveContext(ctx);
40
+ }
41
+ export function clearAllContext() {
42
+ saveContext({ active: {} });
43
+ }
44
+ export function getActiveContext() {
45
+ return loadContext().active;
46
+ }