@tolinax/ayoune-cli 2026.3.1 → 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.
- package/data/contextSlots.js +189 -0
- package/data/defaultActions.js +9 -0
- package/data/modelsAndRights.js +3245 -0
- package/data/modules.js +127 -0
- package/data/operations.js +5 -0
- package/data/services.js +139 -0
- package/index.js +11 -0
- package/lib/api/apiCallHandler.js +72 -0
- package/lib/api/apiClient.js +108 -0
- package/lib/api/auditCallHandler.js +21 -0
- package/lib/api/decodeToken.js +4 -0
- package/lib/api/handleAPIError.js +61 -0
- package/lib/api/login.js +45 -0
- package/lib/api/searchClient.js +119 -0
- package/lib/commands/createAccessCommand.js +126 -0
- package/lib/commands/createActionsCommand.js +140 -0
- package/lib/commands/createAiCommand.js +188 -0
- package/lib/commands/createAliasCommand.js +104 -0
- package/lib/commands/createAuditCommand.js +45 -0
- package/lib/commands/createBatchCommand.js +291 -0
- package/lib/commands/createCompletionsCommand.js +172 -0
- package/lib/commands/createConfigCommand.js +202 -0
- package/lib/commands/createContextCommand.js +163 -0
- package/lib/commands/createCopyCommand.js +36 -0
- package/lib/commands/createCreateCommand.js +47 -0
- package/lib/commands/createDeleteCommand.js +96 -0
- package/lib/commands/createDeployCommand.js +642 -0
- package/lib/commands/createDescribeCommand.js +44 -0
- package/lib/commands/createEditCommand.js +48 -0
- package/lib/commands/createEventsCommand.js +60 -0
- package/lib/commands/createExecCommand.js +212 -0
- package/lib/commands/createExportCommand.js +216 -0
- package/lib/commands/createGetCommand.js +46 -0
- package/lib/commands/createJobsCommand.js +163 -0
- package/lib/commands/createListCommand.js +48 -0
- package/lib/commands/createLoginCommand.js +30 -0
- package/lib/commands/createLogoutCommand.js +21 -0
- package/lib/commands/createModulesCommand.js +147 -0
- package/lib/commands/createMonitorCommand.js +276 -0
- package/lib/commands/createPermissionsCommand.js +233 -0
- package/lib/commands/createProgram.js +211 -0
- package/lib/commands/createSearchCommand.js +251 -0
- package/lib/commands/createSelfHostUpdateCommand.js +166 -0
- package/lib/commands/createServicesCommand.js +225 -0
- package/lib/commands/createSetupCommand.js +305 -0
- package/lib/commands/createStatusCommand.js +147 -0
- package/lib/commands/createStorageCommand.js +53 -0
- package/lib/commands/createStreamCommand.js +50 -0
- package/lib/commands/createSyncCommand.js +174 -0
- package/lib/commands/createTemplateCommand.js +231 -0
- package/lib/commands/createUpdateCommand.js +112 -0
- package/lib/commands/createUsersCommand.js +275 -0
- package/lib/commands/createWebhooksCommand.js +149 -0
- package/lib/commands/createWhoAmICommand.js +90 -0
- package/lib/exitCodes.js +6 -0
- package/lib/helpers/addSpacesToCamelCase.js +5 -0
- package/lib/helpers/cliError.js +24 -0
- package/lib/helpers/config.js +7 -0
- package/lib/helpers/configLoader.js +66 -0
- package/lib/helpers/contextInjector.js +65 -0
- package/lib/helpers/contextResolver.js +70 -0
- package/lib/helpers/contextStore.js +46 -0
- package/lib/helpers/formatDocument.js +176 -0
- package/lib/helpers/handleResponseFormatOptions.js +134 -0
- package/lib/helpers/initializeSettings.js +14 -0
- package/lib/helpers/localStorage.js +4 -0
- package/lib/helpers/logo.js +48 -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/resolveCollectionArgs.js +36 -0
- package/lib/helpers/sanitizeFields.js +18 -0
- package/lib/helpers/saveFile.js +39 -0
- package/lib/helpers/secureStorage.js +72 -0
- package/lib/helpers/tokenPayload.js +21 -0
- package/lib/helpers/updateNotifier.js +49 -0
- package/lib/models/getCollections.js +15 -0
- package/lib/models/getModelsInModules.js +13 -0
- package/lib/models/getModuleFromCollection.js +10 -0
- package/lib/operations/handleAuditOperation.js +22 -0
- package/lib/operations/handleCollectionOperation.js +91 -0
- package/lib/operations/handleCopySingleOperation.js +30 -0
- package/lib/operations/handleCreateSingleOperation.js +38 -0
- package/lib/operations/handleDeleteSingleOperation.js +14 -0
- package/lib/operations/handleDescribeSingleOperation.js +45 -0
- package/lib/operations/handleEditOperation.js +51 -0
- package/lib/operations/handleEditRawOperation.js +35 -0
- package/lib/operations/handleGetOperation.js +35 -0
- package/lib/operations/handleGetSingleOperation.js +20 -0
- package/lib/operations/handleListOperation.js +67 -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 +22 -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 +13 -10
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { api } from "./apiClient.js";
|
|
2
|
+
import { config } from "../helpers/config.js";
|
|
3
|
+
import { secureStorage } from "../helpers/secureStorage.js";
|
|
4
|
+
function getToken() {
|
|
5
|
+
return secureStorage.getItem("token") || process.env.AYOUNE_TOKEN || "";
|
|
6
|
+
}
|
|
7
|
+
function getSearchBaseURL() {
|
|
8
|
+
return config.searchUrl;
|
|
9
|
+
}
|
|
10
|
+
export async function searchModel(collection, params) {
|
|
11
|
+
const response = await api({
|
|
12
|
+
baseURL: getSearchBaseURL(),
|
|
13
|
+
method: "get",
|
|
14
|
+
url: `/${collection}`,
|
|
15
|
+
params,
|
|
16
|
+
headers: {
|
|
17
|
+
Authorization: `Bearer ${getToken()}`,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
return response.data;
|
|
21
|
+
}
|
|
22
|
+
export async function searchOne(collection, params) {
|
|
23
|
+
const response = await api({
|
|
24
|
+
baseURL: getSearchBaseURL(),
|
|
25
|
+
method: "get",
|
|
26
|
+
url: `/${collection}/one`,
|
|
27
|
+
params,
|
|
28
|
+
headers: {
|
|
29
|
+
Authorization: `Bearer ${getToken()}`,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
return response.data;
|
|
33
|
+
}
|
|
34
|
+
export async function searchGlobal(query, limit = 25, callbacks = {}) {
|
|
35
|
+
const results = [];
|
|
36
|
+
const response = await api({
|
|
37
|
+
baseURL: getSearchBaseURL(),
|
|
38
|
+
method: "get",
|
|
39
|
+
url: "/",
|
|
40
|
+
params: { q: query, limit },
|
|
41
|
+
headers: {
|
|
42
|
+
Authorization: `Bearer ${getToken()}`,
|
|
43
|
+
Accept: "text/event-stream",
|
|
44
|
+
},
|
|
45
|
+
responseType: "stream",
|
|
46
|
+
});
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
let buffer = "";
|
|
49
|
+
let currentCollection = "";
|
|
50
|
+
let currentEntries = [];
|
|
51
|
+
const stream = response.data;
|
|
52
|
+
stream.on("data", (chunk) => {
|
|
53
|
+
var _a, _b, _c;
|
|
54
|
+
buffer += chunk.toString();
|
|
55
|
+
const lines = buffer.split("\n");
|
|
56
|
+
buffer = lines.pop() || "";
|
|
57
|
+
for (const line of lines) {
|
|
58
|
+
if (line.startsWith("event: ")) {
|
|
59
|
+
const event = line.slice(7).trim();
|
|
60
|
+
if (event === "model_done" && currentCollection) {
|
|
61
|
+
const result = {
|
|
62
|
+
collection: currentCollection,
|
|
63
|
+
entries: currentEntries,
|
|
64
|
+
total: currentEntries.length,
|
|
65
|
+
};
|
|
66
|
+
results.push(result);
|
|
67
|
+
(_a = callbacks.onResult) === null || _a === void 0 ? void 0 : _a.call(callbacks, result);
|
|
68
|
+
currentCollection = "";
|
|
69
|
+
currentEntries = [];
|
|
70
|
+
}
|
|
71
|
+
else if (event === "error") {
|
|
72
|
+
// Error data comes in the next data: line
|
|
73
|
+
}
|
|
74
|
+
else if (event === "done") {
|
|
75
|
+
(_b = callbacks.onDone) === null || _b === void 0 ? void 0 : _b.call(callbacks);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else if (line.startsWith("data: ")) {
|
|
79
|
+
const dataStr = line.slice(6).trim();
|
|
80
|
+
try {
|
|
81
|
+
const data = JSON.parse(dataStr);
|
|
82
|
+
if (data.model) {
|
|
83
|
+
currentCollection = data.model;
|
|
84
|
+
}
|
|
85
|
+
else if (data.entry) {
|
|
86
|
+
currentEntries.push(data.entry);
|
|
87
|
+
}
|
|
88
|
+
else if (data.error) {
|
|
89
|
+
(_c = callbacks.onError) === null || _c === void 0 ? void 0 : _c.call(callbacks, data.error);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (_d) {
|
|
93
|
+
// Non-JSON data line, skip
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
stream.on("end", () => {
|
|
99
|
+
var _a, _b;
|
|
100
|
+
// Flush remaining buffer
|
|
101
|
+
if (currentCollection && currentEntries.length > 0) {
|
|
102
|
+
const result = {
|
|
103
|
+
collection: currentCollection,
|
|
104
|
+
entries: currentEntries,
|
|
105
|
+
total: currentEntries.length,
|
|
106
|
+
};
|
|
107
|
+
results.push(result);
|
|
108
|
+
(_a = callbacks.onResult) === null || _a === void 0 ? void 0 : _a.call(callbacks, result);
|
|
109
|
+
}
|
|
110
|
+
(_b = callbacks.onDone) === null || _b === void 0 ? void 0 : _b.call(callbacks);
|
|
111
|
+
resolve(results);
|
|
112
|
+
});
|
|
113
|
+
stream.on("error", (err) => {
|
|
114
|
+
var _a;
|
|
115
|
+
(_a = callbacks.onError) === null || _a === void 0 ? void 0 : _a.call(callbacks, err.message);
|
|
116
|
+
reject(err);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { modelsAndRights } from "../../data/modelsAndRights.js";
|
|
3
|
+
import { aYOUneModules } from "../../data/modules.js";
|
|
4
|
+
import { isSuperUser, getTokenPayload } from "../helpers/tokenPayload.js";
|
|
5
|
+
import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
|
|
6
|
+
import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
|
|
7
|
+
import { cliError } from "../helpers/cliError.js";
|
|
8
|
+
function getAccessibleModules(su) {
|
|
9
|
+
var _a;
|
|
10
|
+
const moduleSummaries = [];
|
|
11
|
+
for (const mod of aYOUneModules) {
|
|
12
|
+
if (mod.superuserOnly && !su)
|
|
13
|
+
continue;
|
|
14
|
+
const collections = modelsAndRights.filter((m) => m.module === mod.module);
|
|
15
|
+
moduleSummaries.push({
|
|
16
|
+
module: mod.module,
|
|
17
|
+
label: mod.label,
|
|
18
|
+
collections: collections.length,
|
|
19
|
+
superuserOnly: (_a = mod.superuserOnly) !== null && _a !== void 0 ? _a : false,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return moduleSummaries;
|
|
23
|
+
}
|
|
24
|
+
function displayAccess(modules, su) {
|
|
25
|
+
const payload = getTokenPayload();
|
|
26
|
+
const name = (payload === null || payload === void 0 ? void 0 : payload.name) || (payload === null || payload === void 0 ? void 0 : payload.username) || (payload === null || payload === void 0 ? void 0 : payload.email) || "Unknown";
|
|
27
|
+
const type = (payload === null || payload === void 0 ? void 0 : payload.type) || "user";
|
|
28
|
+
console.log();
|
|
29
|
+
console.log(chalk.bold(` Access Summary for ${chalk.cyan(name)}`));
|
|
30
|
+
console.log(chalk.dim(` Type: ${su ? chalk.yellow("superuser") : type}`));
|
|
31
|
+
console.log(chalk.dim(` ${"─".repeat(50)}`));
|
|
32
|
+
console.log();
|
|
33
|
+
const totalCollections = modules.reduce((sum, m) => sum + m.collections, 0);
|
|
34
|
+
// Column widths
|
|
35
|
+
const maxMod = Math.max(...modules.map((m) => m.module.length), 6);
|
|
36
|
+
const maxLabel = Math.max(...modules.map((m) => m.label.length), 5);
|
|
37
|
+
console.log(` ${chalk.dim("MODULE".padEnd(maxMod + 2))} ${chalk.dim("LABEL".padEnd(maxLabel + 2))} ${chalk.dim("COLLECTIONS")}`);
|
|
38
|
+
console.log(chalk.dim(` ${"─".repeat(maxMod + maxLabel + 18)}`));
|
|
39
|
+
for (const mod of modules) {
|
|
40
|
+
const modName = mod.superuserOnly
|
|
41
|
+
? chalk.yellow(mod.module.padEnd(maxMod + 2))
|
|
42
|
+
: mod.module.padEnd(maxMod + 2);
|
|
43
|
+
const label = mod.label.padEnd(maxLabel + 2);
|
|
44
|
+
const count = String(mod.collections).padStart(4);
|
|
45
|
+
console.log(` ${modName} ${label} ${count}`);
|
|
46
|
+
}
|
|
47
|
+
console.log();
|
|
48
|
+
console.log(chalk.dim(` ${modules.length} modules, ${totalCollections} collections accessible`));
|
|
49
|
+
if (su) {
|
|
50
|
+
console.log(chalk.yellow(` Superuser access: all modules including system collections`));
|
|
51
|
+
}
|
|
52
|
+
console.log();
|
|
53
|
+
}
|
|
54
|
+
export function createAccessCommand(program) {
|
|
55
|
+
program
|
|
56
|
+
.command("access")
|
|
57
|
+
.description("Show accessible modules and collections for the current user")
|
|
58
|
+
.addHelpText("after", `
|
|
59
|
+
Examples:
|
|
60
|
+
ay access Show accessible modules and collection counts
|
|
61
|
+
ay access -r json Output as JSON
|
|
62
|
+
ay access --module crm Show collections in a specific module`)
|
|
63
|
+
.option("--module <name>", "Show collections for a specific module")
|
|
64
|
+
.action(async (options) => {
|
|
65
|
+
try {
|
|
66
|
+
const opts = { ...program.opts(), ...options };
|
|
67
|
+
const su = isSuperUser();
|
|
68
|
+
const modules = getAccessibleModules(su);
|
|
69
|
+
// Detail mode: list collections in a specific module
|
|
70
|
+
if (opts.module) {
|
|
71
|
+
const modName = opts.module.toLowerCase();
|
|
72
|
+
const mod = aYOUneModules.find((m) => m.module === modName);
|
|
73
|
+
if (!mod) {
|
|
74
|
+
cliError(`Unknown module: "${opts.module}". Use "ay access" to see available modules.`, EXIT_GENERAL_ERROR);
|
|
75
|
+
}
|
|
76
|
+
if (mod.superuserOnly && !su) {
|
|
77
|
+
cliError(`Module "${opts.module}" requires superuser access.`, EXIT_GENERAL_ERROR);
|
|
78
|
+
}
|
|
79
|
+
const collections = modelsAndRights
|
|
80
|
+
.filter((m) => m.module === modName)
|
|
81
|
+
.map((m) => ({
|
|
82
|
+
collection: m.plural,
|
|
83
|
+
singular: m.singular,
|
|
84
|
+
right: m.right,
|
|
85
|
+
}));
|
|
86
|
+
if (opts.responseFormat !== "json" || opts.save) {
|
|
87
|
+
handleResponseFormatOptions(opts, collections);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
console.log();
|
|
91
|
+
console.log(chalk.bold(` Collections in ${chalk.cyan(mod.label)} (${modName})`));
|
|
92
|
+
console.log(chalk.dim(` ${"─".repeat(50)}`));
|
|
93
|
+
console.log();
|
|
94
|
+
const maxCol = Math.max(...collections.map((c) => c.collection.length), 10);
|
|
95
|
+
const maxRight = Math.max(...collections.map((c) => c.right.length), 5);
|
|
96
|
+
console.log(` ${chalk.dim("COLLECTION".padEnd(maxCol + 2))} ${chalk.dim("RIGHT")}`);
|
|
97
|
+
console.log(chalk.dim(` ${"─".repeat(maxCol + maxRight + 6)}`));
|
|
98
|
+
for (const col of collections) {
|
|
99
|
+
console.log(` ${col.collection.padEnd(maxCol + 2)} ${chalk.dim(col.right)}`);
|
|
100
|
+
}
|
|
101
|
+
console.log();
|
|
102
|
+
console.log(chalk.dim(` ${collections.length} collections`));
|
|
103
|
+
console.log();
|
|
104
|
+
}
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// Summary mode: show all accessible modules
|
|
108
|
+
if (opts.responseFormat !== "json" || opts.save) {
|
|
109
|
+
// For non-default formats or save, use standard formatting
|
|
110
|
+
const data = modules.map((m) => ({
|
|
111
|
+
module: m.module,
|
|
112
|
+
label: m.label,
|
|
113
|
+
collections: m.collections,
|
|
114
|
+
...(m.superuserOnly ? { superuserOnly: true } : {}),
|
|
115
|
+
}));
|
|
116
|
+
handleResponseFormatOptions(opts, data);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
displayAccess(modules, su);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { api, getModuleBaseUrl } from "../api/apiClient.js";
|
|
2
|
+
import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
|
|
3
|
+
import { saveFile } from "../helpers/saveFile.js";
|
|
4
|
+
import { secureStorage } from "../helpers/secureStorage.js";
|
|
5
|
+
import { spinner } from "../../index.js";
|
|
6
|
+
import { EXIT_GENERAL_ERROR, EXIT_PERMISSION_DENIED } from "../exitCodes.js";
|
|
7
|
+
import { cliError } from "../helpers/cliError.js";
|
|
8
|
+
export function createActionsCommand(program) {
|
|
9
|
+
program
|
|
10
|
+
.command("actions [search]")
|
|
11
|
+
.alias("act")
|
|
12
|
+
.description("Discover registered API actions from all services")
|
|
13
|
+
.addHelpText("after", `
|
|
14
|
+
Examples:
|
|
15
|
+
ay actions List all API actions (interactive)
|
|
16
|
+
ay actions generate Search actions matching "generate"
|
|
17
|
+
ay actions --namespace ai List all AI actions
|
|
18
|
+
ay actions --host ai.ayoune.app List actions for a specific host
|
|
19
|
+
ay actions --method POST List only POST actions
|
|
20
|
+
ay actions --list-namespaces List all available namespaces
|
|
21
|
+
ay actions -r table --columns "operationId,method,endpoint,host"`)
|
|
22
|
+
.option("--namespace <ns>", "Filter by namespace (e.g. ai, crm, pm)")
|
|
23
|
+
.option("--host <host>", "Filter by host")
|
|
24
|
+
.option("--method <method>", "Filter by HTTP method (GET, POST, PUT, DELETE)")
|
|
25
|
+
.option("--capability <cap>", "Filter by capability")
|
|
26
|
+
.option("--list-namespaces", "List all unique namespaces")
|
|
27
|
+
.option("--list-hosts", "List all unique hosts")
|
|
28
|
+
.option("-p, --page <number>", "Page", parseInt, 1)
|
|
29
|
+
.option("-l, --limit <number>", "Limit", parseInt, 100)
|
|
30
|
+
.action(async (search, options) => {
|
|
31
|
+
var _a, _b, _c;
|
|
32
|
+
try {
|
|
33
|
+
const opts = { ...program.opts(), ...options };
|
|
34
|
+
spinner.start({ text: "Fetching API actions...", color: "magenta" });
|
|
35
|
+
// Build query params
|
|
36
|
+
const params = {
|
|
37
|
+
page: opts.page,
|
|
38
|
+
limit: opts.limit,
|
|
39
|
+
responseFormat: "json",
|
|
40
|
+
verbosity: opts.verbosity,
|
|
41
|
+
hideMeta: opts.hideMeta,
|
|
42
|
+
};
|
|
43
|
+
if (search)
|
|
44
|
+
params.q = search;
|
|
45
|
+
// Try su module first (full access), fall back to config module (limited access)
|
|
46
|
+
const modules = ["su", "config"];
|
|
47
|
+
let res = null;
|
|
48
|
+
let permissionDenied = false;
|
|
49
|
+
for (const mod of modules) {
|
|
50
|
+
try {
|
|
51
|
+
const token = secureStorage.getItem("token") || "";
|
|
52
|
+
const response = await api({
|
|
53
|
+
baseURL: getModuleBaseUrl(mod),
|
|
54
|
+
method: "get",
|
|
55
|
+
url: "ayouneapiactions",
|
|
56
|
+
params,
|
|
57
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
58
|
+
});
|
|
59
|
+
res = response.data;
|
|
60
|
+
if (res === null || res === void 0 ? void 0 : res.payload)
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
// On 401/403 permission denied, try next module
|
|
65
|
+
if (((_a = e.response) === null || _a === void 0 ? void 0 : _a.status) === 403 || ((_b = e.response) === null || _b === void 0 ? void 0 : _b.status) === 401) {
|
|
66
|
+
permissionDenied = true;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
// For other errors (network, 500, etc.), only throw on last module
|
|
70
|
+
if (mod === modules[modules.length - 1])
|
|
71
|
+
throw e;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (!(res === null || res === void 0 ? void 0 : res.payload)) {
|
|
75
|
+
if (permissionDenied) {
|
|
76
|
+
cliError("Permission denied: you do not have access to query API actions. Contact your administrator for the required rights.", EXIT_PERMISSION_DENIED, "permission_denied");
|
|
77
|
+
}
|
|
78
|
+
spinner.error({ text: "No API actions found" });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
let actions = Array.isArray(res.payload) ? res.payload : [res.payload];
|
|
82
|
+
// Apply client-side filters
|
|
83
|
+
if (opts.namespace) {
|
|
84
|
+
actions = actions.filter((a) => a.nameSpace === opts.namespace);
|
|
85
|
+
}
|
|
86
|
+
if (opts.host) {
|
|
87
|
+
actions = actions.filter((a) => { var _a; return (_a = a.host) === null || _a === void 0 ? void 0 : _a.includes(opts.host); });
|
|
88
|
+
}
|
|
89
|
+
if (opts.method) {
|
|
90
|
+
actions = actions.filter((a) => { var _a; return ((_a = a.method) === null || _a === void 0 ? void 0 : _a.toUpperCase()) === opts.method.toUpperCase(); });
|
|
91
|
+
}
|
|
92
|
+
if (opts.capability) {
|
|
93
|
+
actions = actions.filter((a) => a.capability === opts.capability);
|
|
94
|
+
}
|
|
95
|
+
// Special modes: list namespaces or hosts
|
|
96
|
+
if (opts.listNamespaces) {
|
|
97
|
+
const namespaces = [...new Set(actions.map((a) => a.nameSpace).filter(Boolean))].sort();
|
|
98
|
+
spinner.success({ text: `Found ${namespaces.length} namespaces` });
|
|
99
|
+
spinner.stop();
|
|
100
|
+
console.log(namespaces.join("\n"));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (opts.listHosts) {
|
|
104
|
+
const hosts = [...new Set(actions.map((a) => a.host).filter(Boolean))].sort();
|
|
105
|
+
spinner.success({ text: `Found ${hosts.length} hosts` });
|
|
106
|
+
spinner.stop();
|
|
107
|
+
console.log(hosts.join("\n"));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
// Filter out deprecated
|
|
111
|
+
actions = actions.filter((a) => !a.deprecated);
|
|
112
|
+
// Build display-friendly response
|
|
113
|
+
const formattedRes = {
|
|
114
|
+
payload: actions.map((a) => ({
|
|
115
|
+
operationId: a.operationId || "",
|
|
116
|
+
method: (a.method || "GET").toUpperCase(),
|
|
117
|
+
endpoint: a.endpoint || "",
|
|
118
|
+
host: a.host || "",
|
|
119
|
+
nameSpace: a.nameSpace || "",
|
|
120
|
+
capability: a.capability || "",
|
|
121
|
+
action: a.action || "",
|
|
122
|
+
description: a.shortDescription || a.description || "",
|
|
123
|
+
})),
|
|
124
|
+
meta: {
|
|
125
|
+
...res.meta,
|
|
126
|
+
pageInfo: { ...(_c = res.meta) === null || _c === void 0 ? void 0 : _c.pageInfo, totalEntries: actions.length },
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
const { plainResult, result, content } = handleResponseFormatOptions(opts, formattedRes);
|
|
130
|
+
spinner.success({ text: `Found ${actions.length} API actions` });
|
|
131
|
+
spinner.stop();
|
|
132
|
+
if (opts.save) {
|
|
133
|
+
await saveFile("actions", opts, formattedRes);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (e) {
|
|
137
|
+
cliError(e.message || "Failed to fetch API actions", EXIT_GENERAL_ERROR);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { apiCallHandler } from "../api/apiCallHandler.js";
|
|
2
|
+
import { api } from "../api/apiClient.js";
|
|
3
|
+
import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
|
|
4
|
+
import { saveFile } from "../helpers/saveFile.js";
|
|
5
|
+
import { secureStorage } from "../helpers/secureStorage.js";
|
|
6
|
+
import { spinner } from "../../index.js";
|
|
7
|
+
import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
|
|
8
|
+
import { cliError } from "../helpers/cliError.js";
|
|
9
|
+
import { getContextSummary, getContextForAI, hasActiveContext } from "../helpers/contextInjector.js";
|
|
10
|
+
const AI_HOST = "ai.ayoune.app";
|
|
11
|
+
export function createAiCommand(program) {
|
|
12
|
+
const ai = program
|
|
13
|
+
.command("ai")
|
|
14
|
+
.description("Interact with aYOUne AI services");
|
|
15
|
+
// ay ai actions — list all AI endpoints
|
|
16
|
+
ai.command("actions")
|
|
17
|
+
.description("List all registered AI API actions")
|
|
18
|
+
.action(async () => {
|
|
19
|
+
try {
|
|
20
|
+
const opts = { ...program.opts() };
|
|
21
|
+
spinner.start({ text: "Fetching AI actions...", color: "magenta" });
|
|
22
|
+
const res = await apiCallHandler("su", "ayouneapiactions", "get", null, {
|
|
23
|
+
q: "ai",
|
|
24
|
+
limit: 200,
|
|
25
|
+
responseFormat: "json",
|
|
26
|
+
});
|
|
27
|
+
if (!(res === null || res === void 0 ? void 0 : res.payload)) {
|
|
28
|
+
spinner.error({ text: "No AI actions found" });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const actions = (Array.isArray(res.payload) ? res.payload : [])
|
|
32
|
+
.filter((a) => a.nameSpace === "ai" && !a.deprecated)
|
|
33
|
+
.map((a) => ({
|
|
34
|
+
operationId: a.operationId || "",
|
|
35
|
+
method: (a.method || "GET").toUpperCase(),
|
|
36
|
+
endpoint: a.endpoint || "",
|
|
37
|
+
description: a.shortDescription || a.description || "",
|
|
38
|
+
}));
|
|
39
|
+
const formattedRes = {
|
|
40
|
+
payload: actions,
|
|
41
|
+
meta: { responseTime: 0, pageInfo: { totalEntries: actions.length, page: 1, totalPages: 1 } },
|
|
42
|
+
};
|
|
43
|
+
handleResponseFormatOptions(opts, formattedRes);
|
|
44
|
+
spinner.success({ text: `Found ${actions.length} AI actions` });
|
|
45
|
+
spinner.stop();
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
cliError(e.message || "Failed to fetch AI actions", EXIT_GENERAL_ERROR);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
// ay ai conversations — list AI conversations
|
|
52
|
+
ai.command("conversations")
|
|
53
|
+
.description("List AI conversations")
|
|
54
|
+
.option("-l, --limit <number>", "Limit", parseInt, 20)
|
|
55
|
+
.option("-p, --page <number>", "Page", parseInt, 1)
|
|
56
|
+
.action(async (options) => {
|
|
57
|
+
var _a, _b, _c;
|
|
58
|
+
try {
|
|
59
|
+
const opts = { ...program.opts(), ...options };
|
|
60
|
+
spinner.start({ text: "Fetching AI conversations...", color: "magenta" });
|
|
61
|
+
const res = await apiCallHandler("ai", "aiconversations", "get", null, {
|
|
62
|
+
page: opts.page,
|
|
63
|
+
limit: opts.limit,
|
|
64
|
+
responseFormat: opts.responseFormat,
|
|
65
|
+
verbosity: opts.verbosity,
|
|
66
|
+
hideMeta: opts.hideMeta,
|
|
67
|
+
});
|
|
68
|
+
const { plainResult, result, content } = handleResponseFormatOptions(opts, res);
|
|
69
|
+
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;
|
|
70
|
+
spinner.success({ text: `Got ${total} AI conversations` });
|
|
71
|
+
spinner.stop();
|
|
72
|
+
if (opts.save)
|
|
73
|
+
await saveFile("ai-conversations", opts, res);
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
cliError(e.message || "Failed to list conversations", EXIT_GENERAL_ERROR);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
// ay ai prompts — list AI prompt templates
|
|
80
|
+
ai.command("prompts")
|
|
81
|
+
.description("List AI prompt templates")
|
|
82
|
+
.option("-l, --limit <number>", "Limit", parseInt, 20)
|
|
83
|
+
.option("-p, --page <number>", "Page", parseInt, 1)
|
|
84
|
+
.action(async (options) => {
|
|
85
|
+
var _a, _b, _c;
|
|
86
|
+
try {
|
|
87
|
+
const opts = { ...program.opts(), ...options };
|
|
88
|
+
spinner.start({ text: "Fetching AI prompts...", color: "magenta" });
|
|
89
|
+
const res = await apiCallHandler("ai", "aiprompts", "get", null, {
|
|
90
|
+
page: opts.page,
|
|
91
|
+
limit: opts.limit,
|
|
92
|
+
responseFormat: opts.responseFormat,
|
|
93
|
+
verbosity: opts.verbosity,
|
|
94
|
+
hideMeta: opts.hideMeta,
|
|
95
|
+
});
|
|
96
|
+
const { plainResult, result, content } = handleResponseFormatOptions(opts, res);
|
|
97
|
+
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;
|
|
98
|
+
spinner.success({ text: `Got ${total} AI prompts` });
|
|
99
|
+
spinner.stop();
|
|
100
|
+
if (opts.save)
|
|
101
|
+
await saveFile("ai-prompts", opts, res);
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
cliError(e.message || "Failed to list prompts", EXIT_GENERAL_ERROR);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
// ay ai ask <prompt> — send a prompt to AI
|
|
108
|
+
ai.command("ask <prompt...>")
|
|
109
|
+
.description("Send a prompt to aYOUne AI")
|
|
110
|
+
.option("--conversation <id>", "Continue an existing conversation")
|
|
111
|
+
.option("--model <model>", "AI model to use")
|
|
112
|
+
.action(async (promptWords, options) => {
|
|
113
|
+
try {
|
|
114
|
+
const opts = { ...program.opts(), ...options };
|
|
115
|
+
const prompt = promptWords.join(" ");
|
|
116
|
+
spinner.start({ text: "Sending prompt to AI...", color: "magenta" });
|
|
117
|
+
const body = { prompt };
|
|
118
|
+
if (opts.conversation)
|
|
119
|
+
body.conversationId = opts.conversation;
|
|
120
|
+
if (opts.model)
|
|
121
|
+
body.model = opts.model;
|
|
122
|
+
if (hasActiveContext()) {
|
|
123
|
+
body.context = getContextForAI();
|
|
124
|
+
body.prompt = `${getContextSummary()}\n\nUser question: ${prompt}`;
|
|
125
|
+
}
|
|
126
|
+
const res = await apiCallHandler("ai", "ask", "post", body, {
|
|
127
|
+
responseFormat: opts.responseFormat,
|
|
128
|
+
verbosity: opts.verbosity,
|
|
129
|
+
hideMeta: opts.hideMeta,
|
|
130
|
+
});
|
|
131
|
+
const { plainResult, result, content } = handleResponseFormatOptions(opts, res);
|
|
132
|
+
spinner.success({ text: "AI response received" });
|
|
133
|
+
spinner.stop();
|
|
134
|
+
if (opts.save)
|
|
135
|
+
await saveFile("ai-ask", opts, res);
|
|
136
|
+
}
|
|
137
|
+
catch (e) {
|
|
138
|
+
cliError(e.message || "AI request failed", EXIT_GENERAL_ERROR);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
// ay ai generate <type> <prompt> — generate content
|
|
142
|
+
ai.command("generate <type> <prompt...>")
|
|
143
|
+
.description("Generate content (text, image, video)")
|
|
144
|
+
.addHelpText("after", `
|
|
145
|
+
Types: text, image, video
|
|
146
|
+
|
|
147
|
+
Examples:
|
|
148
|
+
ay ai generate text "Write a product description for..."
|
|
149
|
+
ay ai generate image "A modern office building"`)
|
|
150
|
+
.action(async (type, promptWords, options) => {
|
|
151
|
+
try {
|
|
152
|
+
const opts = { ...program.opts(), ...options };
|
|
153
|
+
const prompt = promptWords.join(" ");
|
|
154
|
+
const typeHostMap = {
|
|
155
|
+
text: "text-generation",
|
|
156
|
+
image: "image-generation",
|
|
157
|
+
video: "video-generation",
|
|
158
|
+
};
|
|
159
|
+
const host = typeHostMap[type];
|
|
160
|
+
if (!host) {
|
|
161
|
+
cliError(`Unknown type: ${type}. Use: text, image, video`, EXIT_GENERAL_ERROR);
|
|
162
|
+
}
|
|
163
|
+
spinner.start({ text: `Generating ${type}...`, color: "magenta" });
|
|
164
|
+
const response = await api({
|
|
165
|
+
baseURL: `https://${host}.ayoune.app`,
|
|
166
|
+
method: "post",
|
|
167
|
+
url: "/generate",
|
|
168
|
+
data: { prompt },
|
|
169
|
+
params: {
|
|
170
|
+
responseFormat: opts.responseFormat,
|
|
171
|
+
verbosity: opts.verbosity,
|
|
172
|
+
},
|
|
173
|
+
headers: {
|
|
174
|
+
Authorization: `Bearer ${secureStorage.getItem("token")}`,
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
const res = response.data;
|
|
178
|
+
handleResponseFormatOptions(opts, res);
|
|
179
|
+
spinner.success({ text: `${type} generation complete` });
|
|
180
|
+
spinner.stop();
|
|
181
|
+
if (opts.save)
|
|
182
|
+
await saveFile(`ai-generate-${type}`, opts, res);
|
|
183
|
+
}
|
|
184
|
+
catch (e) {
|
|
185
|
+
cliError(e.message || `${type} generation failed`, EXIT_GENERAL_ERROR);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
|
|
5
|
+
import { spinner } from "../../index.js";
|
|
6
|
+
import { cliError } from "../helpers/cliError.js";
|
|
7
|
+
const ALIASES_PATH = path.join(os.homedir(), ".config", "ayoune", "aliases.json");
|
|
8
|
+
export function loadAliases() {
|
|
9
|
+
if (!existsSync(ALIASES_PATH)) {
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(readFileSync(ALIASES_PATH, "utf-8"));
|
|
14
|
+
}
|
|
15
|
+
catch (_a) {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function saveAliases(aliases) {
|
|
20
|
+
const dir = path.dirname(ALIASES_PATH);
|
|
21
|
+
if (!existsSync(dir)) {
|
|
22
|
+
mkdirSync(dir, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
writeFileSync(ALIASES_PATH, JSON.stringify(aliases, null, 2), "utf-8");
|
|
25
|
+
}
|
|
26
|
+
export function createAliasCommand(program) {
|
|
27
|
+
const alias = program
|
|
28
|
+
.command("alias")
|
|
29
|
+
.description("Manage custom command aliases")
|
|
30
|
+
.addHelpText("after", `
|
|
31
|
+
Examples:
|
|
32
|
+
ay alias set ll "list leads" Create alias 'll' for 'list leads'
|
|
33
|
+
ay alias list Show all custom aliases
|
|
34
|
+
ay alias remove ll Remove the 'll' alias`);
|
|
35
|
+
alias
|
|
36
|
+
.command("set <name> <command>")
|
|
37
|
+
.description("Create or update a custom alias")
|
|
38
|
+
.action((name, command) => {
|
|
39
|
+
try {
|
|
40
|
+
const aliases = loadAliases();
|
|
41
|
+
aliases[name] = command;
|
|
42
|
+
saveAliases(aliases);
|
|
43
|
+
spinner.success({ text: `Alias '${name}' set to '${command}'` });
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
cliError(e.message || "Failed to save alias", EXIT_GENERAL_ERROR);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
alias
|
|
50
|
+
.command("list")
|
|
51
|
+
.description("List all custom aliases")
|
|
52
|
+
.action(() => {
|
|
53
|
+
const aliases = loadAliases();
|
|
54
|
+
const entries = Object.entries(aliases);
|
|
55
|
+
if (entries.length === 0) {
|
|
56
|
+
console.log("No custom aliases defined.");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
for (const [name, command] of entries) {
|
|
60
|
+
console.log(` ${name} -> ${command}`);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
alias
|
|
64
|
+
.command("remove <name>")
|
|
65
|
+
.description("Remove a custom alias")
|
|
66
|
+
.action((name) => {
|
|
67
|
+
try {
|
|
68
|
+
const aliases = loadAliases();
|
|
69
|
+
if (!(name in aliases)) {
|
|
70
|
+
cliError(`Alias '${name}' not found`, EXIT_MISUSE);
|
|
71
|
+
}
|
|
72
|
+
delete aliases[name];
|
|
73
|
+
saveAliases(aliases);
|
|
74
|
+
spinner.success({ text: `Alias '${name}' removed` });
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
cliError(e.message || "Failed to remove alias", EXIT_GENERAL_ERROR);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
export function registerUserAliases(program) {
|
|
82
|
+
const aliases = loadAliases();
|
|
83
|
+
for (const [name, command] of Object.entries(aliases)) {
|
|
84
|
+
// Check if we'd conflict with a built-in command
|
|
85
|
+
const existing = program.commands.find((cmd) => cmd.name() === name || cmd.aliases().includes(name));
|
|
86
|
+
if (existing)
|
|
87
|
+
continue;
|
|
88
|
+
program
|
|
89
|
+
.command(name)
|
|
90
|
+
.description(`Alias for '${command}'`)
|
|
91
|
+
.allowUnknownOption()
|
|
92
|
+
.allowExcessArguments()
|
|
93
|
+
.action(async (...args) => {
|
|
94
|
+
// Re-parse with the aliased command substituted in
|
|
95
|
+
const argv = process.argv.slice(0, 2).concat(command.split(/\s+/));
|
|
96
|
+
// Append any extra args passed after the alias
|
|
97
|
+
const extraArgs = args.slice(0, -1); // last arg is the Command object
|
|
98
|
+
if (Array.isArray(extraArgs[0])) {
|
|
99
|
+
argv.push(...extraArgs[0]);
|
|
100
|
+
}
|
|
101
|
+
program.parse(argv);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|