@tolinax/ayoune-cli 2026.2.1 → 2026.2.2

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 (78) 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/createCompletionsCommand.js +136 -0
  17. package/lib/commands/createConfigCommand.js +208 -0
  18. package/lib/commands/createCopyCommand.js +39 -0
  19. package/lib/commands/createCreateCommand.js +50 -0
  20. package/lib/commands/createDeployCommand.js +666 -0
  21. package/lib/commands/createDescribeCommand.js +42 -0
  22. package/lib/commands/createEditCommand.js +43 -0
  23. package/lib/commands/createEventsCommand.js +60 -0
  24. package/lib/commands/createExecCommand.js +182 -0
  25. package/lib/commands/createGetCommand.js +47 -0
  26. package/lib/commands/createListCommand.js +49 -0
  27. package/lib/commands/createLoginCommand.js +18 -0
  28. package/lib/commands/createLogoutCommand.js +21 -0
  29. package/lib/commands/createModulesCommand.js +89 -0
  30. package/lib/commands/createMonitorCommand.js +283 -0
  31. package/lib/commands/createProgram.js +163 -0
  32. package/lib/commands/createServicesCommand.js +228 -0
  33. package/lib/commands/createStorageCommand.js +54 -0
  34. package/lib/commands/createStreamCommand.js +50 -0
  35. package/lib/commands/createWhoAmICommand.js +88 -0
  36. package/lib/exitCodes.js +6 -0
  37. package/lib/helpers/addSpacesToCamelCase.js +5 -0
  38. package/lib/helpers/config.js +6 -0
  39. package/lib/helpers/configLoader.js +60 -0
  40. package/lib/helpers/formatDocument.js +176 -0
  41. package/lib/helpers/handleResponseFormatOptions.js +85 -0
  42. package/lib/helpers/initializeSettings.js +14 -0
  43. package/lib/helpers/localStorage.js +4 -0
  44. package/lib/helpers/makeRandomToken.js +27 -0
  45. package/lib/helpers/parseInt.js +7 -0
  46. package/lib/helpers/requireArg.js +9 -0
  47. package/lib/helpers/saveFile.js +39 -0
  48. package/lib/models/getCollections.js +15 -0
  49. package/lib/models/getModelsInModules.js +13 -0
  50. package/lib/models/getModuleFromCollection.js +7 -0
  51. package/lib/operations/handleAuditOperation.js +22 -0
  52. package/lib/operations/handleCollectionOperation.js +91 -0
  53. package/lib/operations/handleCopySingleOperation.js +22 -0
  54. package/lib/operations/handleCreateSingleOperation.js +35 -0
  55. package/lib/operations/handleDeleteSingleOperation.js +14 -0
  56. package/lib/operations/handleDescribeSingleOperation.js +22 -0
  57. package/lib/operations/handleEditOperation.js +51 -0
  58. package/lib/operations/handleEditRawOperation.js +35 -0
  59. package/lib/operations/handleGetOperation.js +29 -0
  60. package/lib/operations/handleGetSingleOperation.js +20 -0
  61. package/lib/operations/handleListOperation.js +63 -0
  62. package/lib/operations/handleSingleAuditOperation.js +27 -0
  63. package/lib/prompts/promptAudits.js +15 -0
  64. package/lib/prompts/promptCollection.js +13 -0
  65. package/lib/prompts/promptCollectionInModule.js +13 -0
  66. package/lib/prompts/promptCollectionWithModule.js +15 -0
  67. package/lib/prompts/promptConfirm.js +12 -0
  68. package/lib/prompts/promptDefaultAction.js +13 -0
  69. package/lib/prompts/promptEntry.js +19 -0
  70. package/lib/prompts/promptFileName.js +12 -0
  71. package/lib/prompts/promptFilePath.js +18 -0
  72. package/lib/prompts/promptModule.js +19 -0
  73. package/lib/prompts/promptName.js +11 -0
  74. package/lib/prompts/promptOperation.js +13 -0
  75. package/lib/socket/customerSocketClient.js +13 -0
  76. package/lib/socket/socketClient.js +12 -0
  77. package/lib/types.js +1 -0
  78. package/package.json +2 -2
@@ -0,0 +1,111 @@
1
+ const aYOUneModules = [
2
+ {
3
+ label: "Artificial Intelligence",
4
+ module: "ai",
5
+ host: "ai.ayoune.app",
6
+ },
7
+ {
8
+ label: "Asset store",
9
+ module: "assetstore",
10
+ host: "assetstore.ayoune.app",
11
+ },
12
+ {
13
+ label: "Customer Relationship Management",
14
+ module: "crm",
15
+ host: "crm.ayoune.app",
16
+ },
17
+ {
18
+ label: "Project management",
19
+ module: "pm",
20
+ host: "pm.ayoune.app",
21
+ },
22
+ {
23
+ label: "Content management system",
24
+ module: "cms",
25
+ host: "cms.ayoune.app",
26
+ },
27
+ {
28
+ label: "Marketing",
29
+ module: "marketing",
30
+ host: "marketing.ayoune.app",
31
+ },
32
+ {
33
+ label: "Automation",
34
+ module: "automation",
35
+ host: "automation.ayoune.app",
36
+ },
37
+ { label: "Sale", module: "sale", host: "sale.ayoune.app" },
38
+ {
39
+ label: "Product information management",
40
+ module: "pim",
41
+ host: "pim.ayoune.app",
42
+ },
43
+ { label: "Hub", module: "hub", host: "hub.ayoune.app" },
44
+ {
45
+ label: "Human Resources",
46
+ module: "hr",
47
+ host: "hr.ayoune.app",
48
+ },
49
+ {
50
+ label: "Service Desk",
51
+ module: "service",
52
+ host: "service.ayoune.app",
53
+ },
54
+ {
55
+ label: "Purchase",
56
+ module: "purchase",
57
+ host: "purchase.ayoune.app",
58
+ },
59
+ {
60
+ label: "Accounting",
61
+ module: "accounting",
62
+ host: "accounting.ayoune.app",
63
+ },
64
+ {
65
+ label: "E Commerce",
66
+ module: "ecommerce",
67
+ host: "ecommerce.ayoune.app",
68
+ },
69
+ {
70
+ label: "Monitoring",
71
+ module: "monitoring",
72
+ host: "monitoring.ayoune.app",
73
+ },
74
+ {
75
+ label: "Research",
76
+ module: "research",
77
+ host: "research.ayoune.app",
78
+ },
79
+ {
80
+ label: "Production",
81
+ module: "production",
82
+ host: "production.ayoune.app",
83
+ },
84
+ {
85
+ label: "Affiliate",
86
+ module: "affiliate",
87
+ host: "affiliate.ayoune.app",
88
+ },
89
+ {
90
+ label: "Reporting",
91
+ module: "reporting",
92
+ host: "reporting.ayoune.app",
93
+ },
94
+ {
95
+ label: "Export",
96
+ module: "export",
97
+ host: "export.ayoune.app",
98
+ },
99
+ {
100
+ label: "Configuration",
101
+ module: "config",
102
+ host: "config.ayoune.app",
103
+ },
104
+ {
105
+ label: "Audit",
106
+ module: "audit",
107
+ host: "audit.ayoune.app",
108
+ },
109
+ ];
110
+ export default aYOUneModules;
111
+ export { aYOUneModules };
@@ -0,0 +1,5 @@
1
+ const operations = [
2
+ 'list',
3
+ 'get',
4
+ ];
5
+ export { operations };
@@ -0,0 +1,139 @@
1
+ const aYOUneServices = [
2
+ {
3
+ label: "Barcodes",
4
+ module: "global",
5
+ host: "bc.ayoune.app",
6
+ },
7
+ {
8
+ label: "Aggregation",
9
+ module: "global",
10
+ host: "aggregation.ayoune.app",
11
+ },
12
+ {
13
+ label: "Browser Automations",
14
+ module: "automation",
15
+ host: "browser-automations.ayoune.app",
16
+ },
17
+ {
18
+ label: "Datatables",
19
+ module: "global",
20
+ host: "datatables.ayoune.app",
21
+ },
22
+ {
23
+ label: "Exports",
24
+ module: "exports",
25
+ host: "exports.ayoune.app",
26
+ },
27
+ {
28
+ label: "Middleware",
29
+ module: "global",
30
+ host: "mw.ayoune.app",
31
+ },
32
+ {
33
+ label: "Replacer",
34
+ module: "global",
35
+ host: "replacer.ayoune.app",
36
+ },
37
+ {
38
+ label: "Services",
39
+ module: "services",
40
+ host: "services.ayoune.app",
41
+ },
42
+ {
43
+ label: "Endpoints",
44
+ module: "global",
45
+ host: "endpoints.ayoune.app",
46
+ },
47
+ {
48
+ label: "Gists",
49
+ module: "pm",
50
+ host: "gists.ayoune.app",
51
+ },
52
+ {
53
+ label: "Text to Speech",
54
+ module: "global",
55
+ host: "tts.ayoune.app",
56
+ },
57
+ {
58
+ label: "Statistics",
59
+ module: "metrics",
60
+ host: "stats.ayoune.app",
61
+ },
62
+ {
63
+ label: "Statistics",
64
+ module: "stats",
65
+ host: "stats.ayoune.app",
66
+ },
67
+ {
68
+ label: "Video Generation",
69
+ module: "cms",
70
+ host: "video-generation.ayoune.app",
71
+ },
72
+ {
73
+ label: "Image Generation",
74
+ module: "cms",
75
+ host: "image-generation.ayoune.app",
76
+ },
77
+ {
78
+ label: "Text Generation",
79
+ module: "text",
80
+ host: "text-generation.ayoune.app",
81
+ },
82
+ {
83
+ label: "Serp Html Archive",
84
+ module: "research",
85
+ host: "serp-html-archive.ayoune.app",
86
+ },
87
+ {
88
+ label: "Config files",
89
+ module: "global",
90
+ host: "configs.ayoune.app",
91
+ },
92
+ {
93
+ label: "GraphQl",
94
+ module: "global",
95
+ host: "graph.ayoune.app",
96
+ },
97
+ {
98
+ label: "Dynamic Content",
99
+ module: "cms",
100
+ host: "dc.ayoune.app",
101
+ },
102
+ {
103
+ label: "Media",
104
+ module: "cms",
105
+ host: "media.ayoune.app",
106
+ },
107
+ {
108
+ label: "Text Spinner",
109
+ module: "cms",
110
+ host: "spinner.ayoune.app",
111
+ },
112
+ {
113
+ label: "Content Snippets",
114
+ module: "cms",
115
+ host: "snippets.ayoune.app",
116
+ },
117
+ {
118
+ label: "Downloads",
119
+ module: "cms",
120
+ host: "downloads.ayoune.app",
121
+ },
122
+ {
123
+ label: "Scripts",
124
+ module: "global",
125
+ host: "scripts.ayoune.app",
126
+ },
127
+ {
128
+ label: "Downloads",
129
+ module: "cms",
130
+ host: "downloads.ayoune.app",
131
+ },
132
+ {
133
+ label: "Documents",
134
+ module: "crm",
135
+ host: "documents.ayoune.app",
136
+ },
137
+ ];
138
+ export default aYOUneServices;
139
+ export { aYOUneServices };
@@ -0,0 +1,68 @@
1
+ import { api, getModuleBaseUrl } from "./apiClient.js";
2
+ import chalk from "chalk";
3
+ import * as process from "process";
4
+ import { localStorage } from "../helpers/localStorage.js";
5
+ import { handleAPIError } from "./handleAPIError.js";
6
+ import { login } from "./login.js";
7
+ import { spinner } from "../../index.js";
8
+ let dryRunEnabled = false;
9
+ export function enableDryRun() {
10
+ dryRunEnabled = true;
11
+ }
12
+ function cleanParams(params) {
13
+ const cleaned = {};
14
+ for (const [key, value] of Object.entries(params)) {
15
+ if (value === undefined || value === null || value === false || value === "")
16
+ continue;
17
+ cleaned[key] = value;
18
+ }
19
+ const version = process.env.AYOUNE_VERSION;
20
+ if (version) {
21
+ cleaned.ref = `ayoune-cli@${version}`;
22
+ }
23
+ return cleaned;
24
+ }
25
+ function getToken() {
26
+ return localStorage.getItem("token") || process.env.AYOUNE_TOKEN || "";
27
+ }
28
+ const makeRequest = (module, url, method, data, params) => api({
29
+ baseURL: getModuleBaseUrl(module),
30
+ method,
31
+ url,
32
+ data,
33
+ params: cleanParams(params),
34
+ headers: {
35
+ Authorization: `Bearer ${getToken()}`,
36
+ },
37
+ });
38
+ export const apiCallHandler = async (module, url, method = "get", data = null, params = {}) => {
39
+ var _a;
40
+ // Dry-run: show what would happen for mutating requests
41
+ if (dryRunEnabled && method !== "get") {
42
+ const baseUrl = getModuleBaseUrl(module);
43
+ const fullUrl = `${baseUrl}/${url}`.replace(/\/+/g, "/").replace(":/", "://");
44
+ spinner.stop();
45
+ console.error(chalk.yellow.bold("\n [DRY RUN] Request not sent:\n"));
46
+ console.error(` ${chalk.dim("Method:")} ${chalk.cyan(method.toUpperCase())}`);
47
+ console.error(` ${chalk.dim("URL:")} ${chalk.cyan(fullUrl)}`);
48
+ if (data) {
49
+ console.error(` ${chalk.dim("Body:")} ${JSON.stringify(data, null, 2).split("\n").join("\n ")}`);
50
+ }
51
+ console.error();
52
+ return { payload: null, meta: { responseTime: 0, message: "Dry run - no request sent" } };
53
+ }
54
+ try {
55
+ const response = await makeRequest(module, url, method, data, params);
56
+ return response.data;
57
+ }
58
+ catch (error) {
59
+ if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 401) {
60
+ spinner.stop();
61
+ spinner.error({ text: "Session expired. Re-authenticating..." });
62
+ await login();
63
+ const response = await makeRequest(module, url, method, data, params);
64
+ return response.data;
65
+ }
66
+ handleAPIError(error);
67
+ }
68
+ };
@@ -0,0 +1,100 @@
1
+ import axios from "axios";
2
+ import https from "https";
3
+ import chalk from "chalk";
4
+ import axiosRetry from "axios-retry";
5
+ import { config } from "../helpers/config.js";
6
+ const httpsAgent = new https.Agent({ rejectUnauthorized: false });
7
+ let debugEnabled = false;
8
+ export function enableDebug() {
9
+ debugEnabled = true;
10
+ }
11
+ function debugLog(prefix, ...args) {
12
+ if (!debugEnabled)
13
+ return;
14
+ console.error(chalk.dim(` [debug] ${prefix}`), ...args);
15
+ }
16
+ const api = axios.create({
17
+ timeout: 30000,
18
+ httpsAgent,
19
+ });
20
+ const audit = axios.create({
21
+ baseURL: config.auditUrl,
22
+ httpsAgent,
23
+ });
24
+ export function getModuleBaseUrl(module) {
25
+ return `https://${module}-api.ayoune.app`;
26
+ }
27
+ function addDebugInterceptors(instance) {
28
+ instance.interceptors.request.use((req) => {
29
+ var _a, _b, _c;
30
+ const url = `${req.baseURL || ""}/${req.url || ""}`.replace(/\/+/g, "/").replace(":/", "://");
31
+ debugLog(`${chalk.yellow((_a = req.method) === null || _a === void 0 ? void 0 : _a.toUpperCase())} ${chalk.cyan(url)}`);
32
+ if (req.params && Object.keys(req.params).length) {
33
+ debugLog("params:", JSON.stringify(req.params));
34
+ }
35
+ if (req.data) {
36
+ debugLog("body:", JSON.stringify(req.data).substring(0, 200));
37
+ }
38
+ const auth = ((_b = req.headers) === null || _b === void 0 ? void 0 : _b.Authorization) || ((_c = req.headers) === null || _c === void 0 ? void 0 : _c.authorization);
39
+ if (auth) {
40
+ const token = String(auth);
41
+ debugLog("auth:", token.substring(0, 15) + "..." + token.substring(token.length - 10));
42
+ }
43
+ return req;
44
+ });
45
+ instance.interceptors.response.use((res) => {
46
+ var _a, _b;
47
+ const meta = (_a = res.data) === null || _a === void 0 ? void 0 : _a.meta;
48
+ const total = (_b = meta === null || meta === void 0 ? void 0 : meta.pageInfo) === null || _b === void 0 ? void 0 : _b.totalEntries;
49
+ const time = meta === null || meta === void 0 ? void 0 : meta.responseTime;
50
+ debugLog(`${chalk.green(res.status)} ${res.statusText}` +
51
+ (time ? ` ${chalk.dim(`(${time}ms)`)}` : "") +
52
+ (total !== undefined ? ` ${chalk.dim(`[${total} entries]`)}` : ""));
53
+ if (res.data) {
54
+ const body = JSON.stringify(res.data);
55
+ debugLog("response:", body.length > 500 ? body.substring(0, 500) + "..." : body);
56
+ }
57
+ return res;
58
+ }, (err) => {
59
+ if (err.response) {
60
+ debugLog(`${chalk.red(err.response.status)} ${err.response.statusText}`);
61
+ if (err.response.data) {
62
+ debugLog("response:", JSON.stringify(err.response.data).substring(0, 500));
63
+ }
64
+ }
65
+ else {
66
+ debugLog(chalk.red(`network error: ${err.message}`));
67
+ }
68
+ return Promise.reject(err);
69
+ });
70
+ }
71
+ addDebugInterceptors(api);
72
+ addDebugInterceptors(audit);
73
+ const retryConfig = {
74
+ retries: 3,
75
+ retryDelay: (retryCount, error) => {
76
+ var _a, _b;
77
+ // Respect Retry-After header for 429 responses
78
+ const retryAfter = (_b = (_a = error.response) === null || _a === void 0 ? void 0 : _a.headers) === null || _b === void 0 ? void 0 : _b["retry-after"];
79
+ if (retryAfter) {
80
+ const seconds = parseInt(retryAfter, 10);
81
+ if (!isNaN(seconds)) {
82
+ debugLog(chalk.yellow(`Rate limited. Retrying in ${seconds}s...`));
83
+ return seconds * 1000;
84
+ }
85
+ }
86
+ // Exponential backoff: 1s, 2s, 4s
87
+ const delay = Math.pow(2, retryCount - 1) * 1000;
88
+ debugLog(chalk.yellow(`Retrying in ${delay / 1000}s (attempt ${retryCount}/3)...`));
89
+ return delay;
90
+ },
91
+ retryCondition: (error) => {
92
+ var _a;
93
+ // Retry on network errors, 5xx, and 429 (rate limit)
94
+ return axiosRetry.isNetworkOrIdempotentRequestError(error)
95
+ || ((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 429;
96
+ },
97
+ };
98
+ axiosRetry(api, retryConfig);
99
+ axiosRetry(audit, retryConfig);
100
+ export { api, audit };
@@ -0,0 +1,21 @@
1
+ import { audit } from "./apiClient.js";
2
+ import * as process from "process";
3
+ import { localStorage } from "../helpers/localStorage.js";
4
+ import { handleAPIError } from "./handleAPIError.js";
5
+ export const auditCallHandler = async (url, method = "get", data = null, params = {}) => {
6
+ try {
7
+ const response = await audit({
8
+ method,
9
+ url,
10
+ data,
11
+ params: { ...params, ref: `ayoune-cli@${process.env.AYOUNE_VERSION}` },
12
+ headers: {
13
+ Authorization: `Bearer ${localStorage.getItem("token")}`,
14
+ },
15
+ });
16
+ return response.data;
17
+ }
18
+ catch (error) {
19
+ handleAPIError(error);
20
+ }
21
+ };
@@ -0,0 +1,4 @@
1
+ import { decode } from "jsonwebtoken";
2
+ export const decodeToken = (token) => {
3
+ return decode(token, { json: true, complete: true });
4
+ };
@@ -0,0 +1,59 @@
1
+ import { spinner } from "../../index.js";
2
+ import { EXIT_GENERAL_ERROR, EXIT_AUTH_REQUIRED, EXIT_PERMISSION_DENIED, } from "../exitCodes.js";
3
+ let jsonErrorsEnabled = false;
4
+ export function enableJsonErrors() {
5
+ jsonErrorsEnabled = true;
6
+ }
7
+ function emitJsonError(code, type, message, details) {
8
+ if (jsonErrorsEnabled) {
9
+ const err = { error: type, code, message, ...(details ? { details } : {}) };
10
+ console.error(JSON.stringify(err));
11
+ }
12
+ process.exit(code);
13
+ }
14
+ export function handleAPIError(error) {
15
+ var _a, _b, _c, _d;
16
+ const apiError = error.response;
17
+ const requestConfig = error.config;
18
+ const method = ((requestConfig === null || requestConfig === void 0 ? void 0 : requestConfig.method) || "GET").toUpperCase();
19
+ const fullUrl = requestConfig
20
+ ? `${requestConfig.baseURL || ""}/${requestConfig.url || ""}`.replace(/\/+/g, "/").replace(":/", "://")
21
+ : "unknown";
22
+ if (!apiError) {
23
+ spinner.error({ text: `Network error: ${error.message}` });
24
+ if (!jsonErrorsEnabled)
25
+ console.error(` Request: ${method} ${fullUrl}`);
26
+ emitJsonError(EXIT_GENERAL_ERROR, "network_error", error.message, { method, url: fullUrl });
27
+ }
28
+ const serverMessage = ((_b = (_a = apiError.data) === null || _a === void 0 ? void 0 : _a.meta) === null || _b === void 0 ? void 0 : _b.message) || apiError.statusText;
29
+ if (apiError.status === 400) {
30
+ spinner.error({ text: `Bad Request: ${serverMessage}` });
31
+ if (!jsonErrorsEnabled)
32
+ console.error(` Request: ${method} ${fullUrl}`);
33
+ emitJsonError(EXIT_GENERAL_ERROR, "bad_request", serverMessage, { method, url: fullUrl, status: 400 });
34
+ }
35
+ if (apiError.status === 401) {
36
+ spinner.error({ text: "Session expired. Please run: ay login" });
37
+ emitJsonError(EXIT_AUTH_REQUIRED, "auth_required", "Session expired", { status: 401 });
38
+ }
39
+ if (apiError.status === 403) {
40
+ const rights = ((_d = (_c = apiError.data) === null || _c === void 0 ? void 0 : _c.meta) === null || _d === void 0 ? void 0 : _d.requiredRights) || [];
41
+ spinner.error({
42
+ text: `Forbidden. Required rights: [${rights.join(", ")}]`,
43
+ });
44
+ if (!jsonErrorsEnabled)
45
+ console.error(` Request: ${method} ${fullUrl}`);
46
+ emitJsonError(EXIT_PERMISSION_DENIED, "permission_denied", "Forbidden", { method, url: fullUrl, status: 403, requiredRights: rights });
47
+ }
48
+ spinner.error({
49
+ text: `Error ${apiError.status}: ${serverMessage}`,
50
+ });
51
+ if (!jsonErrorsEnabled) {
52
+ console.error(` Request: ${method} ${fullUrl}`);
53
+ if (apiError.data && typeof apiError.data === "object") {
54
+ const debugInfo = apiError.data.meta || apiError.data;
55
+ console.error(` Response: ${JSON.stringify(debugInfo)}`);
56
+ }
57
+ }
58
+ emitJsonError(EXIT_GENERAL_ERROR, "api_error", serverMessage, { method, url: fullUrl, status: apiError.status });
59
+ }
@@ -0,0 +1,45 @@
1
+ import { socket } from "../socket/socketClient.js";
2
+ import { randomAsciiString } from "../helpers/makeRandomToken.js";
3
+ import { spinner } from "../../index.js";
4
+ import { localStorage } from "../helpers/localStorage.js";
5
+ import { config } from "../helpers/config.js";
6
+ import chalk from "chalk";
7
+ function terminalLink(text, url) {
8
+ // OSC 8 hyperlink escape sequence - supported by most modern terminals
9
+ return `\u001B]8;;${url}\u0007${text}\u001B]8;;\u0007`;
10
+ }
11
+ export const login = async () => {
12
+ spinner.start({ text: "Waiting for login\n" });
13
+ const token = randomAsciiString();
14
+ const url = `${config.loginUrl}/ayoune?cli=${token}`;
15
+ const clickable = terminalLink(chalk.cyan.underline(url), url);
16
+ console.log();
17
+ console.log(` ${chalk.dim("Open this URL to authenticate:")}`);
18
+ console.log(` ${clickable}`);
19
+ console.log();
20
+ const credentials = await waitForCredentials(token);
21
+ return credentials;
22
+ };
23
+ const waitForCredentials = async (token) => {
24
+ return new Promise(function (resolve, reject) {
25
+ const _socket = socket(token);
26
+ _socket.on("join:cli:confirmed", async (data) => {
27
+ spinner.update({ text: `Session active` });
28
+ });
29
+ _socket.on("cli-token", async (data) => {
30
+ spinner.success({ text: `Received auth credentials` });
31
+ localStorage.setItem("token", data.token);
32
+ localStorage.setItem("refreshToken", data.refreshToken);
33
+ _socket.disconnect();
34
+ resolve(data);
35
+ });
36
+ _socket.on("connect_error", async (err) => {
37
+ spinner.error({ text: err.message });
38
+ _socket.disconnect();
39
+ reject(err);
40
+ });
41
+ _socket.on("connect", async () => {
42
+ spinner.update({ text: "Tunnel established. Waiting for credentials" });
43
+ });
44
+ });
45
+ };
@@ -0,0 +1,109 @@
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
+ export function createActionsCommand(program) {
7
+ program
8
+ .command("actions [search]")
9
+ .alias("act")
10
+ .description("Discover registered API actions from all services")
11
+ .addHelpText("after", `
12
+ Examples:
13
+ ay actions List all API actions (interactive)
14
+ ay actions generate Search actions matching "generate"
15
+ ay actions --namespace ai List all AI actions
16
+ ay actions --host ai.ayoune.app List actions for a specific host
17
+ ay actions --method POST List only POST actions
18
+ ay actions --list-namespaces List all available namespaces
19
+ ay actions -r table --columns "operationId,method,endpoint,host"`)
20
+ .option("--namespace <ns>", "Filter by namespace (e.g. ai, crm, pm)")
21
+ .option("--host <host>", "Filter by host")
22
+ .option("--method <method>", "Filter by HTTP method (GET, POST, PUT, DELETE)")
23
+ .option("--capability <cap>", "Filter by capability")
24
+ .option("--list-namespaces", "List all unique namespaces")
25
+ .option("--list-hosts", "List all unique hosts")
26
+ .option("-p, --page <number>", "Page", parseInt, 1)
27
+ .option("-l, --limit <number>", "Limit", parseInt, 100)
28
+ .action(async (search, options) => {
29
+ var _a;
30
+ try {
31
+ const opts = { ...program.opts(), ...options };
32
+ spinner.start({ text: "Fetching API actions...", color: "magenta" });
33
+ // Build query params
34
+ const params = {
35
+ page: opts.page,
36
+ limit: opts.limit,
37
+ responseFormat: "json",
38
+ verbosity: opts.verbosity,
39
+ hideMeta: opts.hideMeta,
40
+ };
41
+ if (search)
42
+ params.q = search;
43
+ // Fetch actions from the SU config API (where apiactions are stored)
44
+ const res = await apiCallHandler("config", "ayouneapiactions", "get", null, params);
45
+ if (!(res === null || res === void 0 ? void 0 : res.payload)) {
46
+ spinner.error({ text: "No API actions found or insufficient permissions" });
47
+ return;
48
+ }
49
+ let actions = Array.isArray(res.payload) ? res.payload : [res.payload];
50
+ // Apply client-side filters
51
+ if (opts.namespace) {
52
+ actions = actions.filter((a) => a.nameSpace === opts.namespace);
53
+ }
54
+ if (opts.host) {
55
+ actions = actions.filter((a) => { var _a; return (_a = a.host) === null || _a === void 0 ? void 0 : _a.includes(opts.host); });
56
+ }
57
+ if (opts.method) {
58
+ actions = actions.filter((a) => { var _a; return ((_a = a.method) === null || _a === void 0 ? void 0 : _a.toUpperCase()) === opts.method.toUpperCase(); });
59
+ }
60
+ if (opts.capability) {
61
+ actions = actions.filter((a) => a.capability === opts.capability);
62
+ }
63
+ // Special modes: list namespaces or hosts
64
+ if (opts.listNamespaces) {
65
+ const namespaces = [...new Set(actions.map((a) => a.nameSpace).filter(Boolean))].sort();
66
+ spinner.success({ text: `Found ${namespaces.length} namespaces` });
67
+ spinner.stop();
68
+ console.log(namespaces.join("\n"));
69
+ return;
70
+ }
71
+ if (opts.listHosts) {
72
+ const hosts = [...new Set(actions.map((a) => a.host).filter(Boolean))].sort();
73
+ spinner.success({ text: `Found ${hosts.length} hosts` });
74
+ spinner.stop();
75
+ console.log(hosts.join("\n"));
76
+ return;
77
+ }
78
+ // Filter out deprecated
79
+ actions = actions.filter((a) => !a.deprecated);
80
+ // Build display-friendly response
81
+ const formattedRes = {
82
+ payload: actions.map((a) => ({
83
+ operationId: a.operationId || "",
84
+ method: (a.method || "GET").toUpperCase(),
85
+ endpoint: a.endpoint || "",
86
+ host: a.host || "",
87
+ nameSpace: a.nameSpace || "",
88
+ capability: a.capability || "",
89
+ action: a.action || "",
90
+ description: a.shortDescription || a.description || "",
91
+ })),
92
+ meta: {
93
+ ...res.meta,
94
+ pageInfo: { ...(_a = res.meta) === null || _a === void 0 ? void 0 : _a.pageInfo, totalEntries: actions.length },
95
+ },
96
+ };
97
+ const { plainResult, result, content } = handleResponseFormatOptions(opts, formattedRes);
98
+ spinner.success({ text: `Found ${actions.length} API actions` });
99
+ spinner.stop();
100
+ if (opts.save) {
101
+ await saveFile("actions", opts, formattedRes);
102
+ }
103
+ }
104
+ catch (e) {
105
+ spinner.error({ text: e.message || "Failed to fetch API actions" });
106
+ process.exit(EXIT_GENERAL_ERROR);
107
+ }
108
+ });
109
+ }