@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.
- package/data/defaultActions.js +9 -0
- package/data/modelsAndRights.js +3189 -0
- package/data/modules.js +111 -0
- package/data/operations.js +5 -0
- package/data/services.js +139 -0
- package/lib/api/apiCallHandler.js +68 -0
- package/lib/api/apiClient.js +100 -0
- package/lib/api/auditCallHandler.js +21 -0
- package/lib/api/decodeToken.js +4 -0
- package/lib/api/handleAPIError.js +59 -0
- package/lib/api/login.js +45 -0
- package/lib/commands/createActionsCommand.js +109 -0
- package/lib/commands/createAiCommand.js +188 -0
- package/lib/commands/createAliasCommand.js +106 -0
- package/lib/commands/createAuditCommand.js +49 -0
- package/lib/commands/createCompletionsCommand.js +136 -0
- package/lib/commands/createConfigCommand.js +208 -0
- package/lib/commands/createCopyCommand.js +39 -0
- package/lib/commands/createCreateCommand.js +50 -0
- package/lib/commands/createDeployCommand.js +666 -0
- package/lib/commands/createDescribeCommand.js +42 -0
- package/lib/commands/createEditCommand.js +43 -0
- package/lib/commands/createEventsCommand.js +60 -0
- package/lib/commands/createExecCommand.js +182 -0
- package/lib/commands/createGetCommand.js +47 -0
- package/lib/commands/createListCommand.js +49 -0
- package/lib/commands/createLoginCommand.js +18 -0
- package/lib/commands/createLogoutCommand.js +21 -0
- package/lib/commands/createModulesCommand.js +89 -0
- package/lib/commands/createMonitorCommand.js +283 -0
- package/lib/commands/createProgram.js +163 -0
- package/lib/commands/createServicesCommand.js +228 -0
- package/lib/commands/createStorageCommand.js +54 -0
- package/lib/commands/createStreamCommand.js +50 -0
- package/lib/commands/createWhoAmICommand.js +88 -0
- package/lib/exitCodes.js +6 -0
- package/lib/helpers/addSpacesToCamelCase.js +5 -0
- package/lib/helpers/config.js +6 -0
- package/lib/helpers/configLoader.js +60 -0
- package/lib/helpers/formatDocument.js +176 -0
- package/lib/helpers/handleResponseFormatOptions.js +85 -0
- package/lib/helpers/initializeSettings.js +14 -0
- package/lib/helpers/localStorage.js +4 -0
- package/lib/helpers/makeRandomToken.js +27 -0
- package/lib/helpers/parseInt.js +7 -0
- package/lib/helpers/requireArg.js +9 -0
- package/lib/helpers/saveFile.js +39 -0
- package/lib/models/getCollections.js +15 -0
- package/lib/models/getModelsInModules.js +13 -0
- package/lib/models/getModuleFromCollection.js +7 -0
- package/lib/operations/handleAuditOperation.js +22 -0
- package/lib/operations/handleCollectionOperation.js +91 -0
- package/lib/operations/handleCopySingleOperation.js +22 -0
- package/lib/operations/handleCreateSingleOperation.js +35 -0
- package/lib/operations/handleDeleteSingleOperation.js +14 -0
- package/lib/operations/handleDescribeSingleOperation.js +22 -0
- package/lib/operations/handleEditOperation.js +51 -0
- package/lib/operations/handleEditRawOperation.js +35 -0
- package/lib/operations/handleGetOperation.js +29 -0
- package/lib/operations/handleGetSingleOperation.js +20 -0
- package/lib/operations/handleListOperation.js +63 -0
- package/lib/operations/handleSingleAuditOperation.js +27 -0
- package/lib/prompts/promptAudits.js +15 -0
- package/lib/prompts/promptCollection.js +13 -0
- package/lib/prompts/promptCollectionInModule.js +13 -0
- package/lib/prompts/promptCollectionWithModule.js +15 -0
- package/lib/prompts/promptConfirm.js +12 -0
- package/lib/prompts/promptDefaultAction.js +13 -0
- package/lib/prompts/promptEntry.js +19 -0
- package/lib/prompts/promptFileName.js +12 -0
- package/lib/prompts/promptFilePath.js +18 -0
- package/lib/prompts/promptModule.js +19 -0
- package/lib/prompts/promptName.js +11 -0
- package/lib/prompts/promptOperation.js +13 -0
- package/lib/socket/customerSocketClient.js +13 -0
- package/lib/socket/socketClient.js +12 -0
- package/lib/types.js +1 -0
- package/package.json +2 -2
package/data/modules.js
ADDED
|
@@ -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 };
|
package/data/services.js
ADDED
|
@@ -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,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
|
+
}
|
package/lib/api/login.js
ADDED
|
@@ -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
|
+
}
|