@tolinax/ayoune-cli 2026.2.1 → 2026.2.3

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 (84) hide show
  1. package/data/defaultActions.js +9 -0
  2. package/data/modelsAndRights.js +3189 -0
  3. package/data/modules.js +111 -0
  4. package/data/operations.js +5 -0
  5. package/data/services.js +139 -0
  6. package/lib/api/apiCallHandler.js +68 -0
  7. package/lib/api/apiClient.js +100 -0
  8. package/lib/api/auditCallHandler.js +21 -0
  9. package/lib/api/decodeToken.js +4 -0
  10. package/lib/api/handleAPIError.js +59 -0
  11. package/lib/api/login.js +45 -0
  12. package/lib/commands/createActionsCommand.js +109 -0
  13. package/lib/commands/createAiCommand.js +188 -0
  14. package/lib/commands/createAliasCommand.js +106 -0
  15. package/lib/commands/createAuditCommand.js +49 -0
  16. package/lib/commands/createBatchCommand.js +304 -0
  17. package/lib/commands/createCompletionsCommand.js +154 -0
  18. package/lib/commands/createConfigCommand.js +208 -0
  19. package/lib/commands/createCopyCommand.js +39 -0
  20. package/lib/commands/createCreateCommand.js +50 -0
  21. package/lib/commands/createDeleteCommand.js +98 -0
  22. package/lib/commands/createDeployCommand.js +666 -0
  23. package/lib/commands/createDescribeCommand.js +42 -0
  24. package/lib/commands/createEditCommand.js +43 -0
  25. package/lib/commands/createEventsCommand.js +60 -0
  26. package/lib/commands/createExecCommand.js +182 -0
  27. package/lib/commands/createGetCommand.js +47 -0
  28. package/lib/commands/createJobsCommand.js +168 -0
  29. package/lib/commands/createListCommand.js +49 -0
  30. package/lib/commands/createLoginCommand.js +18 -0
  31. package/lib/commands/createLogoutCommand.js +21 -0
  32. package/lib/commands/createModulesCommand.js +89 -0
  33. package/lib/commands/createMonitorCommand.js +283 -0
  34. package/lib/commands/createProgram.js +175 -0
  35. package/lib/commands/createSearchCommand.js +101 -0
  36. package/lib/commands/createServicesCommand.js +228 -0
  37. package/lib/commands/createStorageCommand.js +54 -0
  38. package/lib/commands/createStreamCommand.js +50 -0
  39. package/lib/commands/createUpdateCommand.js +115 -0
  40. package/lib/commands/createWebhooksCommand.js +156 -0
  41. package/lib/commands/createWhoAmICommand.js +88 -0
  42. package/lib/exitCodes.js +6 -0
  43. package/lib/helpers/addSpacesToCamelCase.js +5 -0
  44. package/lib/helpers/config.js +6 -0
  45. package/lib/helpers/configLoader.js +60 -0
  46. package/lib/helpers/formatDocument.js +176 -0
  47. package/lib/helpers/handleResponseFormatOptions.js +85 -0
  48. package/lib/helpers/initializeSettings.js +14 -0
  49. package/lib/helpers/localStorage.js +4 -0
  50. package/lib/helpers/makeRandomToken.js +27 -0
  51. package/lib/helpers/parseInt.js +7 -0
  52. package/lib/helpers/requireArg.js +9 -0
  53. package/lib/helpers/saveFile.js +39 -0
  54. package/lib/models/getCollections.js +15 -0
  55. package/lib/models/getModelsInModules.js +13 -0
  56. package/lib/models/getModuleFromCollection.js +7 -0
  57. package/lib/operations/handleAuditOperation.js +22 -0
  58. package/lib/operations/handleCollectionOperation.js +91 -0
  59. package/lib/operations/handleCopySingleOperation.js +22 -0
  60. package/lib/operations/handleCreateSingleOperation.js +35 -0
  61. package/lib/operations/handleDeleteSingleOperation.js +14 -0
  62. package/lib/operations/handleDescribeSingleOperation.js +22 -0
  63. package/lib/operations/handleEditOperation.js +51 -0
  64. package/lib/operations/handleEditRawOperation.js +35 -0
  65. package/lib/operations/handleGetOperation.js +29 -0
  66. package/lib/operations/handleGetSingleOperation.js +20 -0
  67. package/lib/operations/handleListOperation.js +63 -0
  68. package/lib/operations/handleSingleAuditOperation.js +27 -0
  69. package/lib/prompts/promptAudits.js +15 -0
  70. package/lib/prompts/promptCollection.js +13 -0
  71. package/lib/prompts/promptCollectionInModule.js +13 -0
  72. package/lib/prompts/promptCollectionWithModule.js +15 -0
  73. package/lib/prompts/promptConfirm.js +12 -0
  74. package/lib/prompts/promptDefaultAction.js +13 -0
  75. package/lib/prompts/promptEntry.js +19 -0
  76. package/lib/prompts/promptFileName.js +12 -0
  77. package/lib/prompts/promptFilePath.js +18 -0
  78. package/lib/prompts/promptModule.js +19 -0
  79. package/lib/prompts/promptName.js +11 -0
  80. package/lib/prompts/promptOperation.js +13 -0
  81. package/lib/socket/customerSocketClient.js +13 -0
  82. package/lib/socket/socketClient.js +12 -0
  83. package/lib/types.js +1 -0
  84. package/package.json +2 -2
@@ -0,0 +1,228 @@
1
+ import { apiCallHandler } from "../api/apiCallHandler.js";
2
+ import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
3
+ import { saveFile } from "../helpers/saveFile.js";
4
+ import { spinner } from "../../index.js";
5
+ import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
6
+ import { aYOUneModules } from "../../data/modules.js";
7
+ import { aYOUneServices } from "../../data/services.js";
8
+ function buildServiceRegistry() {
9
+ const services = [];
10
+ // APIs from modules
11
+ for (const m of aYOUneModules) {
12
+ services.push({
13
+ name: m.label,
14
+ type: "api",
15
+ module: m.module,
16
+ host: m.host,
17
+ });
18
+ }
19
+ // Services
20
+ for (const s of aYOUneServices) {
21
+ services.push({
22
+ name: s.label,
23
+ type: "service",
24
+ module: s.module,
25
+ host: s.host,
26
+ });
27
+ }
28
+ return services;
29
+ }
30
+ export function createServicesCommand(program) {
31
+ const svc = program
32
+ .command("services")
33
+ .alias("svc")
34
+ .description("Discover and manage aYOUne platform services");
35
+ // ay services list
36
+ svc
37
+ .command("list")
38
+ .alias("ls")
39
+ .description("List all registered services, APIs, and gateways")
40
+ .option("--type <type>", "Filter by type: api, service, gateway, worker")
41
+ .option("--module <module>", "Filter by module name")
42
+ .action(async (options) => {
43
+ try {
44
+ const opts = { ...program.opts(), ...options };
45
+ spinner.start({ text: "Loading service registry...", color: "magenta" });
46
+ let services = buildServiceRegistry();
47
+ if (opts.type) {
48
+ services = services.filter((s) => s.type === opts.type);
49
+ }
50
+ if (opts.module) {
51
+ services = services.filter((s) => s.module === opts.module);
52
+ }
53
+ const res = {
54
+ payload: services,
55
+ meta: { responseTime: 0, pageInfo: { totalEntries: services.length, page: 1, totalPages: 1 } },
56
+ };
57
+ handleResponseFormatOptions(opts, res);
58
+ spinner.success({ text: `Found ${services.length} services` });
59
+ spinner.stop();
60
+ if (opts.save)
61
+ await saveFile("services-list", opts, res);
62
+ }
63
+ catch (e) {
64
+ spinner.error({ text: e.message || "Failed to list services" });
65
+ process.exit(EXIT_GENERAL_ERROR);
66
+ }
67
+ });
68
+ // ay services endpoints <host>
69
+ svc
70
+ .command("endpoints <host>")
71
+ .alias("ep")
72
+ .description("List API endpoints for a service host")
73
+ .addHelpText("after", `
74
+ Examples:
75
+ ay services endpoints ai.ayoune.app
76
+ ay services endpoints crm-api.ayoune.app -r table`)
77
+ .action(async (host, options) => {
78
+ try {
79
+ const opts = { ...program.opts(), ...options };
80
+ spinner.start({ text: `Fetching endpoints for ${host}...`, color: "magenta" });
81
+ const res = await apiCallHandler("config", "ayouneapiactions", "get", null, {
82
+ limit: 500,
83
+ responseFormat: "json",
84
+ });
85
+ if (!(res === null || res === void 0 ? void 0 : res.payload)) {
86
+ spinner.error({ text: "No API actions found" });
87
+ return;
88
+ }
89
+ const endpoints = (Array.isArray(res.payload) ? res.payload : [])
90
+ .filter((a) => {
91
+ const h = a.host || "";
92
+ return h.includes(host) && !a.deprecated;
93
+ })
94
+ .map((a) => ({
95
+ method: (a.method || "GET").toUpperCase(),
96
+ endpoint: a.endpoint || "",
97
+ operationId: a.operationId || "",
98
+ action: a.action || "",
99
+ description: a.shortDescription || a.description || "",
100
+ }))
101
+ .sort((a, b) => a.endpoint.localeCompare(b.endpoint));
102
+ const formattedRes = {
103
+ payload: endpoints,
104
+ meta: { responseTime: 0, pageInfo: { totalEntries: endpoints.length, page: 1, totalPages: 1 } },
105
+ };
106
+ handleResponseFormatOptions(opts, formattedRes);
107
+ spinner.success({ text: `Found ${endpoints.length} endpoints on ${host}` });
108
+ spinner.stop();
109
+ if (opts.save)
110
+ await saveFile("service-endpoints", opts, formattedRes);
111
+ }
112
+ catch (e) {
113
+ spinner.error({ text: e.message || "Failed to fetch endpoints" });
114
+ process.exit(EXIT_GENERAL_ERROR);
115
+ }
116
+ });
117
+ // ay services health [host]
118
+ svc
119
+ .command("health [host]")
120
+ .description("Check health of a service or all services")
121
+ .option("--timeout <ms>", "Request timeout in ms", parseInt, 5000)
122
+ .action(async (host, options) => {
123
+ try {
124
+ const opts = { ...program.opts(), ...options };
125
+ const targets = host
126
+ ? [{ name: host, host }]
127
+ : buildServiceRegistry().map((s) => ({ name: s.name, host: s.host }));
128
+ // Deduplicate by host
129
+ const uniqueTargets = [...new Map(targets.map((t) => [t.host, t])).values()];
130
+ spinner.start({ text: `Checking ${uniqueTargets.length} service(s)...`, color: "magenta" });
131
+ const { default: axios } = await import("axios");
132
+ const https = await import("https");
133
+ const agent = new https.Agent({ rejectUnauthorized: false });
134
+ const results = await Promise.allSettled(uniqueTargets.map(async (t) => {
135
+ const start = Date.now();
136
+ try {
137
+ const resp = await axios.get(`https://${t.host}/`, {
138
+ timeout: opts.timeout,
139
+ httpsAgent: agent,
140
+ validateStatus: () => true,
141
+ });
142
+ return {
143
+ host: t.host,
144
+ name: t.name,
145
+ status: resp.status < 500 ? "healthy" : "unhealthy",
146
+ statusCode: resp.status,
147
+ responseTime: Date.now() - start,
148
+ };
149
+ }
150
+ catch (e) {
151
+ return {
152
+ host: t.host,
153
+ name: t.name,
154
+ status: "unreachable",
155
+ statusCode: 0,
156
+ responseTime: Date.now() - start,
157
+ error: e.code || e.message,
158
+ };
159
+ }
160
+ }));
161
+ const payload = results.map((r) => (r.status === "fulfilled" ? r.value : { status: "error" }));
162
+ const healthy = payload.filter((p) => p.status === "healthy").length;
163
+ const res = {
164
+ payload,
165
+ meta: {
166
+ responseTime: 0,
167
+ pageInfo: { totalEntries: payload.length, page: 1, totalPages: 1 },
168
+ },
169
+ };
170
+ handleResponseFormatOptions(opts, res);
171
+ spinner.success({ text: `${healthy}/${uniqueTargets.length} services healthy` });
172
+ spinner.stop();
173
+ if (opts.save)
174
+ await saveFile("services-health", opts, res);
175
+ }
176
+ catch (e) {
177
+ spinner.error({ text: e.message || "Health check failed" });
178
+ process.exit(EXIT_GENERAL_ERROR);
179
+ }
180
+ });
181
+ // ay services describe <module>
182
+ svc
183
+ .command("describe <module>")
184
+ .alias("desc")
185
+ .description("Show detailed info about a module/service")
186
+ .action(async (moduleName, options) => {
187
+ try {
188
+ const opts = { ...program.opts(), ...options };
189
+ spinner.start({ text: `Describing ${moduleName}...`, color: "magenta" });
190
+ // Find matching entries
191
+ const apis = aYOUneModules.filter((m) => m.module === moduleName);
192
+ const svcs = aYOUneServices.filter((s) => s.module === moduleName);
193
+ // Fetch endpoint count from apiactions
194
+ const res = await apiCallHandler("config", "ayouneapiactions", "get", null, {
195
+ limit: 500,
196
+ responseFormat: "json",
197
+ });
198
+ const allActions = Array.isArray(res === null || res === void 0 ? void 0 : res.payload) ? res.payload : [];
199
+ const moduleActions = allActions.filter((a) => a.nameSpace === moduleName && !a.deprecated);
200
+ const methods = {};
201
+ for (const a of moduleActions) {
202
+ const m = (a.method || "GET").toUpperCase();
203
+ methods[m] = (methods[m] || 0) + 1;
204
+ }
205
+ const description = {
206
+ module: moduleName,
207
+ apis: apis.map((a) => ({ label: a.label, host: a.host })),
208
+ services: svcs.map((s) => ({ label: s.label, host: s.host })),
209
+ endpoints: {
210
+ total: moduleActions.length,
211
+ byMethod: methods,
212
+ },
213
+ capabilities: [...new Set(moduleActions.map((a) => a.capability).filter(Boolean))].sort(),
214
+ };
215
+ const formattedRes = {
216
+ payload: description,
217
+ meta: { responseTime: 0 },
218
+ };
219
+ handleResponseFormatOptions(opts, formattedRes);
220
+ spinner.success({ text: `Module: ${moduleName} — ${moduleActions.length} endpoints` });
221
+ spinner.stop();
222
+ }
223
+ catch (e) {
224
+ spinner.error({ text: e.message || "Failed to describe service" });
225
+ process.exit(EXIT_GENERAL_ERROR);
226
+ }
227
+ });
228
+ }
@@ -0,0 +1,54 @@
1
+ import chalk from "chalk";
2
+ import { localStorage } from "../helpers/localStorage.js";
3
+ import { loadConfig } from "../helpers/configLoader.js";
4
+ import { spinner } from "../../index.js";
5
+ import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
6
+ const REDACT_KEYS = new Set(["token", "refreshToken"]);
7
+ function redact(value) {
8
+ if (value.length <= 8)
9
+ return "••••";
10
+ return value.slice(0, 4) + "••••" + value.slice(-4);
11
+ }
12
+ export function createStorageCommand(program) {
13
+ program
14
+ .command("storage")
15
+ .alias("s")
16
+ .description("Display stored session data and preferences")
17
+ .action(async () => {
18
+ var _a, _b;
19
+ try {
20
+ const storage = {};
21
+ for (let i = 0; i < localStorage.length; i++) {
22
+ const key = localStorage.key(i);
23
+ if (key) {
24
+ const value = (_a = localStorage.getItem(key)) !== null && _a !== void 0 ? _a : "";
25
+ storage[key] = REDACT_KEYS.has(key) ? redact(value) : value;
26
+ }
27
+ }
28
+ console.log();
29
+ console.log(chalk.bold(" Session Storage"));
30
+ console.log(chalk.dim(` ${"─".repeat(44)}`));
31
+ const maxKey = Math.max(...Object.keys(storage).map((k) => k.length), 0);
32
+ for (const [key, value] of Object.entries(storage)) {
33
+ console.log(` ${chalk.dim(key.padEnd(maxKey + 2))} ${chalk.white(value)}`);
34
+ }
35
+ const config = loadConfig();
36
+ const defaults = (_b = config.defaults) !== null && _b !== void 0 ? _b : {};
37
+ const defaultEntries = Object.entries(defaults).filter(([, v]) => v !== undefined && v !== null);
38
+ if (defaultEntries.length > 0) {
39
+ console.log();
40
+ console.log(chalk.bold(" Config Defaults"));
41
+ console.log(chalk.dim(` ${"─".repeat(44)}`));
42
+ const maxDefault = Math.max(...defaultEntries.map(([k]) => k.length));
43
+ for (const [key, value] of defaultEntries) {
44
+ console.log(` ${chalk.dim(key.padEnd(maxDefault + 2))} ${chalk.white(String(value))}`);
45
+ }
46
+ }
47
+ console.log();
48
+ }
49
+ catch (e) {
50
+ spinner.error({ text: e.message || "An unexpected error occurred" });
51
+ process.exit(EXIT_GENERAL_ERROR);
52
+ }
53
+ });
54
+ }
@@ -0,0 +1,50 @@
1
+ import { Option } from "commander";
2
+ import { localStorage } from "../helpers/localStorage.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
+ export function createStreamCommand(program) {
9
+ program
10
+ .command("stream")
11
+ .alias("listen")
12
+ .description("Subscribe to real-time data changes via WebSocket")
13
+ .addHelpText("after", `
14
+ Examples:
15
+ ay stream Stream all data changes as JSON
16
+ ay stream -f yaml Stream changes in YAML format
17
+ ay listen -l minimal Stream with minimal detail`)
18
+ .addOption(new Option("-f, --format <format>", "Set the output format")
19
+ .choices(["json", "yaml", "table"])
20
+ .default("json"))
21
+ .addOption(new Option("-l, --level <level>", "Set the detail level")
22
+ .choices(["default", "minimal", "extended"])
23
+ .default("default"))
24
+ .action(async (options) => {
25
+ try {
26
+ const tokenPayload = decodeToken(localStorage.getItem("token"));
27
+ const user = tokenPayload.payload;
28
+ console.log(`Starting stream with [${user._customerID}]`);
29
+ spinner.start({ text: `Starting stream with [${user._customerID}]` });
30
+ const socket = customerSocket(user);
31
+ spinner.update({ text: "Stream active" });
32
+ socket.onAny((channel, data) => {
33
+ spinner.update({ text: `Received [${channel}]` });
34
+ if (options.format === "table") {
35
+ console.table(data);
36
+ }
37
+ if (options.format === "yaml") {
38
+ console.log(yaml.dump(data));
39
+ }
40
+ if (options.format === "json") {
41
+ console.log(JSON.stringify(data, null, 2));
42
+ }
43
+ });
44
+ }
45
+ catch (e) {
46
+ spinner.error({ text: e.message || "An unexpected error occurred" });
47
+ process.exit(EXIT_GENERAL_ERROR);
48
+ }
49
+ });
50
+ }
@@ -0,0 +1,115 @@
1
+ import { Argument } from "commander";
2
+ import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
3
+ import { apiCallHandler } from "../api/apiCallHandler.js";
4
+ import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
5
+ import { saveFile } from "../helpers/saveFile.js";
6
+ import { localStorage } from "../helpers/localStorage.js";
7
+ import { spinner } from "../../index.js";
8
+ import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
9
+ export function createUpdateCommand(program) {
10
+ program
11
+ .command("update")
12
+ .alias("u")
13
+ .description("Update an entry non-interactively (for scripts and AI agents)")
14
+ .addHelpText("after", `
15
+ Unlike 'edit' (which opens an interactive editor), 'update' accepts changes
16
+ as JSON via --body, --body-file, or --body-stdin. Perfect for automation.
17
+
18
+ Examples:
19
+ ay update contacts 64a1b2c3 --body '{"firstName":"Jane"}'
20
+ ay update products 64a1b2c3 --body-file changes.json
21
+ echo '{"status":"active"}' | ay update contacts 64a1b2c3 --body-stdin
22
+ ay u tasks 64a1b2c3 --body '{"done":true}' --force`)
23
+ .addArgument(new Argument("[collection]", "The collection to use").default(localStorage.getItem("lastCollection"), `The last used collection (${localStorage.getItem("lastCollection")})`))
24
+ .addArgument(new Argument("[id]", "The ID of the entry to update").default(localStorage.getItem("lastId"), `The last used id (${localStorage.getItem("lastId")})`))
25
+ .option("--body <json>", "Update data as JSON string")
26
+ .option("--body-file <path>", "Read update data from JSON file")
27
+ .option("--body-stdin", "Read update data from stdin")
28
+ .option("--merge", "Merge with existing entry (GET + merge + PUT)", false)
29
+ .action(async (collection, id, options) => {
30
+ try {
31
+ if (!collection) {
32
+ spinner.error({ text: "Missing required argument: collection" });
33
+ process.exit(EXIT_MISUSE);
34
+ }
35
+ if (!id) {
36
+ spinner.error({ text: "Missing required argument: id" });
37
+ process.exit(EXIT_MISUSE);
38
+ }
39
+ const opts = { ...program.opts(), ...options };
40
+ const module = getModuleFromCollection(collection);
41
+ // Parse body from various sources
42
+ let body = null;
43
+ if (opts.body) {
44
+ try {
45
+ body = JSON.parse(opts.body);
46
+ }
47
+ catch (_a) {
48
+ spinner.error({ text: "Invalid JSON in --body" });
49
+ process.exit(EXIT_MISUSE);
50
+ }
51
+ }
52
+ if (opts.bodyFile) {
53
+ const fs = await import("fs");
54
+ const content = fs.readFileSync(opts.bodyFile, "utf-8");
55
+ try {
56
+ body = JSON.parse(content);
57
+ }
58
+ catch (_b) {
59
+ spinner.error({ text: `Invalid JSON in file: ${opts.bodyFile}` });
60
+ process.exit(EXIT_MISUSE);
61
+ }
62
+ }
63
+ if (opts.bodyStdin && !process.stdin.isTTY) {
64
+ const chunks = [];
65
+ for await (const chunk of process.stdin) {
66
+ chunks.push(chunk);
67
+ }
68
+ const stdinContent = Buffer.concat(chunks).toString("utf-8").trim();
69
+ if (stdinContent) {
70
+ try {
71
+ body = JSON.parse(stdinContent);
72
+ }
73
+ catch (_c) {
74
+ spinner.error({ text: "Invalid JSON from stdin" });
75
+ process.exit(EXIT_MISUSE);
76
+ }
77
+ }
78
+ }
79
+ if (!body) {
80
+ spinner.error({ text: "No update data provided. Use --body, --body-file, or --body-stdin." });
81
+ process.exit(EXIT_MISUSE);
82
+ }
83
+ // Merge mode: GET existing → merge → PUT
84
+ if (opts.merge) {
85
+ spinner.start({ text: `Fetching ${id} from ${collection}...`, color: "magenta" });
86
+ const existing = await apiCallHandler(module.module, `${collection.toLowerCase()}/${id}`, "get", null, { responseFormat: "json" });
87
+ const existingData = (existing === null || existing === void 0 ? void 0 : existing.payload) || {};
88
+ body = { ...existingData, ...body };
89
+ spinner.update({ text: `Updating ${id} in ${collection} (merged)...` });
90
+ }
91
+ else {
92
+ spinner.start({ text: `Updating ${id} in ${collection}...`, color: "magenta" });
93
+ }
94
+ // Ensure _id is set
95
+ body._id = id;
96
+ const res = await apiCallHandler(module.module, collection.toLowerCase(), "put", body, {
97
+ responseFormat: opts.responseFormat,
98
+ verbosity: opts.verbosity,
99
+ hideMeta: opts.hideMeta,
100
+ });
101
+ handleResponseFormatOptions(opts, res);
102
+ spinner.success({ text: `Updated entry ${id} in ${collection}` });
103
+ spinner.stop();
104
+ localStorage.setItem("lastModule", module.module);
105
+ localStorage.setItem("lastCollection", collection);
106
+ localStorage.setItem("lastId", id);
107
+ if (opts.save)
108
+ await saveFile("update", opts, res);
109
+ }
110
+ catch (e) {
111
+ spinner.error({ text: e.message || "An unexpected error occurred" });
112
+ process.exit(EXIT_GENERAL_ERROR);
113
+ }
114
+ });
115
+ }
@@ -0,0 +1,156 @@
1
+ import { apiCallHandler } from "../api/apiCallHandler.js";
2
+ import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
3
+ import { saveFile } from "../helpers/saveFile.js";
4
+ import { spinner } from "../../index.js";
5
+ import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
6
+ export function createWebhooksCommand(program) {
7
+ const hooks = program
8
+ .command("webhooks")
9
+ .alias("hooks")
10
+ .description("Manage webhooks and event subscriptions");
11
+ // ay webhooks list
12
+ hooks
13
+ .command("list")
14
+ .alias("ls")
15
+ .description("List registered webhooks")
16
+ .option("-l, --limit <number>", "Limit results", parseInt, 50)
17
+ .option("-p, --page <number>", "Page number", parseInt, 1)
18
+ .action(async (options) => {
19
+ var _a, _b, _c;
20
+ try {
21
+ const opts = { ...program.opts(), ...options };
22
+ spinner.start({ text: "Fetching webhooks...", color: "magenta" });
23
+ const res = await apiCallHandler("config", "hooks", "get", null, {
24
+ page: opts.page,
25
+ limit: opts.limit,
26
+ responseFormat: opts.responseFormat,
27
+ verbosity: opts.verbosity,
28
+ });
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;
31
+ spinner.success({ text: `Found ${total} webhooks` });
32
+ spinner.stop();
33
+ if (opts.save)
34
+ await saveFile("webhooks-list", opts, res);
35
+ }
36
+ catch (e) {
37
+ spinner.error({ text: e.message || "Failed to list webhooks" });
38
+ process.exit(EXIT_GENERAL_ERROR);
39
+ }
40
+ });
41
+ // ay webhooks get <id>
42
+ hooks
43
+ .command("get <id>")
44
+ .description("Get webhook details")
45
+ .action(async (id, options) => {
46
+ try {
47
+ const opts = { ...program.opts(), ...options };
48
+ spinner.start({ text: `Fetching webhook ${id}...`, color: "magenta" });
49
+ const res = await apiCallHandler("config", `hooks/${id}`, "get", null, {
50
+ responseFormat: opts.responseFormat,
51
+ verbosity: opts.verbosity,
52
+ });
53
+ handleResponseFormatOptions(opts, res);
54
+ spinner.success({ text: `Webhook ${id} loaded` });
55
+ spinner.stop();
56
+ }
57
+ catch (e) {
58
+ spinner.error({ text: e.message || "Failed to get webhook" });
59
+ process.exit(EXIT_GENERAL_ERROR);
60
+ }
61
+ });
62
+ // ay webhooks create --body '{...}'
63
+ hooks
64
+ .command("create")
65
+ .description("Create a new webhook")
66
+ .option("--body <json>", "Webhook definition as JSON")
67
+ .option("--body-file <path>", "Read webhook definition from file")
68
+ .action(async (options) => {
69
+ try {
70
+ const opts = { ...program.opts(), ...options };
71
+ let body = null;
72
+ if (opts.body) {
73
+ try {
74
+ body = JSON.parse(opts.body);
75
+ }
76
+ catch (_a) {
77
+ spinner.error({ text: "Invalid JSON in --body" });
78
+ process.exit(EXIT_MISUSE);
79
+ }
80
+ }
81
+ else if (opts.bodyFile) {
82
+ const fs = await import("fs");
83
+ try {
84
+ body = JSON.parse(fs.readFileSync(opts.bodyFile, "utf-8"));
85
+ }
86
+ catch (_b) {
87
+ spinner.error({ text: `Invalid JSON in file: ${opts.bodyFile}` });
88
+ process.exit(EXIT_MISUSE);
89
+ }
90
+ }
91
+ if (!body) {
92
+ spinner.error({ text: "Provide webhook definition via --body or --body-file" });
93
+ process.exit(EXIT_MISUSE);
94
+ }
95
+ spinner.start({ text: "Creating webhook...", color: "magenta" });
96
+ const res = await apiCallHandler("config", "hooks", "post", body, {
97
+ responseFormat: opts.responseFormat,
98
+ });
99
+ handleResponseFormatOptions(opts, res);
100
+ spinner.success({ text: `Webhook created` });
101
+ spinner.stop();
102
+ }
103
+ catch (e) {
104
+ spinner.error({ text: e.message || "Failed to create webhook" });
105
+ process.exit(EXIT_GENERAL_ERROR);
106
+ }
107
+ });
108
+ // ay webhooks delete <id>
109
+ hooks
110
+ .command("delete <id>")
111
+ .alias("rm")
112
+ .description("Delete a webhook")
113
+ .action(async (id, options) => {
114
+ try {
115
+ const opts = { ...program.opts(), ...options };
116
+ spinner.start({ text: `Deleting webhook ${id}...`, color: "magenta" });
117
+ const res = await apiCallHandler("config", `hooks/${id}`, "delete", null, {
118
+ responseFormat: opts.responseFormat,
119
+ });
120
+ handleResponseFormatOptions(opts, res);
121
+ spinner.success({ text: `Webhook ${id} deleted` });
122
+ spinner.stop();
123
+ }
124
+ catch (e) {
125
+ spinner.error({ text: e.message || "Failed to delete webhook" });
126
+ process.exit(EXIT_GENERAL_ERROR);
127
+ }
128
+ });
129
+ // ay webhooks templates
130
+ hooks
131
+ .command("templates")
132
+ .description("List available webhook templates")
133
+ .option("-l, --limit <number>", "Limit results", parseInt, 50)
134
+ .action(async (options) => {
135
+ var _a, _b, _c;
136
+ try {
137
+ const opts = { ...program.opts(), ...options };
138
+ spinner.start({ text: "Fetching webhook templates...", color: "magenta" });
139
+ const res = await apiCallHandler("config", "hooktemplates", "get", null, {
140
+ limit: opts.limit,
141
+ responseFormat: opts.responseFormat,
142
+ verbosity: opts.verbosity,
143
+ });
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;
146
+ spinner.success({ text: `Found ${total} webhook templates` });
147
+ spinner.stop();
148
+ if (opts.save)
149
+ await saveFile("webhook-templates", opts, res);
150
+ }
151
+ catch (e) {
152
+ spinner.error({ text: e.message || "Failed to list templates" });
153
+ process.exit(EXIT_GENERAL_ERROR);
154
+ }
155
+ });
156
+ }