@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.
Files changed (107) hide show
  1. package/data/contextSlots.js +189 -0
  2. package/data/defaultActions.js +9 -0
  3. package/data/modelsAndRights.js +3245 -0
  4. package/data/modules.js +127 -0
  5. package/data/operations.js +5 -0
  6. package/data/services.js +139 -0
  7. package/index.js +11 -0
  8. package/lib/api/apiCallHandler.js +72 -0
  9. package/lib/api/apiClient.js +108 -0
  10. package/lib/api/auditCallHandler.js +21 -0
  11. package/lib/api/decodeToken.js +4 -0
  12. package/lib/api/handleAPIError.js +61 -0
  13. package/lib/api/login.js +45 -0
  14. package/lib/api/searchClient.js +119 -0
  15. package/lib/commands/createAccessCommand.js +126 -0
  16. package/lib/commands/createActionsCommand.js +140 -0
  17. package/lib/commands/createAiCommand.js +188 -0
  18. package/lib/commands/createAliasCommand.js +104 -0
  19. package/lib/commands/createAuditCommand.js +45 -0
  20. package/lib/commands/createBatchCommand.js +291 -0
  21. package/lib/commands/createCompletionsCommand.js +172 -0
  22. package/lib/commands/createConfigCommand.js +202 -0
  23. package/lib/commands/createContextCommand.js +163 -0
  24. package/lib/commands/createCopyCommand.js +36 -0
  25. package/lib/commands/createCreateCommand.js +47 -0
  26. package/lib/commands/createDeleteCommand.js +96 -0
  27. package/lib/commands/createDeployCommand.js +642 -0
  28. package/lib/commands/createDescribeCommand.js +44 -0
  29. package/lib/commands/createEditCommand.js +48 -0
  30. package/lib/commands/createEventsCommand.js +60 -0
  31. package/lib/commands/createExecCommand.js +212 -0
  32. package/lib/commands/createExportCommand.js +216 -0
  33. package/lib/commands/createGetCommand.js +46 -0
  34. package/lib/commands/createJobsCommand.js +163 -0
  35. package/lib/commands/createListCommand.js +48 -0
  36. package/lib/commands/createLoginCommand.js +30 -0
  37. package/lib/commands/createLogoutCommand.js +21 -0
  38. package/lib/commands/createModulesCommand.js +147 -0
  39. package/lib/commands/createMonitorCommand.js +276 -0
  40. package/lib/commands/createPermissionsCommand.js +233 -0
  41. package/lib/commands/createProgram.js +211 -0
  42. package/lib/commands/createSearchCommand.js +251 -0
  43. package/lib/commands/createSelfHostUpdateCommand.js +166 -0
  44. package/lib/commands/createServicesCommand.js +225 -0
  45. package/lib/commands/createSetupCommand.js +305 -0
  46. package/lib/commands/createStatusCommand.js +147 -0
  47. package/lib/commands/createStorageCommand.js +53 -0
  48. package/lib/commands/createStreamCommand.js +50 -0
  49. package/lib/commands/createSyncCommand.js +174 -0
  50. package/lib/commands/createTemplateCommand.js +231 -0
  51. package/lib/commands/createUpdateCommand.js +112 -0
  52. package/lib/commands/createUsersCommand.js +275 -0
  53. package/lib/commands/createWebhooksCommand.js +149 -0
  54. package/lib/commands/createWhoAmICommand.js +90 -0
  55. package/lib/exitCodes.js +6 -0
  56. package/lib/helpers/addSpacesToCamelCase.js +5 -0
  57. package/lib/helpers/cliError.js +24 -0
  58. package/lib/helpers/config.js +7 -0
  59. package/lib/helpers/configLoader.js +66 -0
  60. package/lib/helpers/contextInjector.js +65 -0
  61. package/lib/helpers/contextResolver.js +70 -0
  62. package/lib/helpers/contextStore.js +46 -0
  63. package/lib/helpers/formatDocument.js +176 -0
  64. package/lib/helpers/handleResponseFormatOptions.js +134 -0
  65. package/lib/helpers/initializeSettings.js +14 -0
  66. package/lib/helpers/localStorage.js +4 -0
  67. package/lib/helpers/logo.js +48 -0
  68. package/lib/helpers/makeRandomToken.js +27 -0
  69. package/lib/helpers/parseInt.js +7 -0
  70. package/lib/helpers/requireArg.js +9 -0
  71. package/lib/helpers/resolveCollectionArgs.js +36 -0
  72. package/lib/helpers/sanitizeFields.js +18 -0
  73. package/lib/helpers/saveFile.js +39 -0
  74. package/lib/helpers/secureStorage.js +72 -0
  75. package/lib/helpers/tokenPayload.js +21 -0
  76. package/lib/helpers/updateNotifier.js +49 -0
  77. package/lib/models/getCollections.js +15 -0
  78. package/lib/models/getModelsInModules.js +13 -0
  79. package/lib/models/getModuleFromCollection.js +10 -0
  80. package/lib/operations/handleAuditOperation.js +22 -0
  81. package/lib/operations/handleCollectionOperation.js +91 -0
  82. package/lib/operations/handleCopySingleOperation.js +30 -0
  83. package/lib/operations/handleCreateSingleOperation.js +38 -0
  84. package/lib/operations/handleDeleteSingleOperation.js +14 -0
  85. package/lib/operations/handleDescribeSingleOperation.js +45 -0
  86. package/lib/operations/handleEditOperation.js +51 -0
  87. package/lib/operations/handleEditRawOperation.js +35 -0
  88. package/lib/operations/handleGetOperation.js +35 -0
  89. package/lib/operations/handleGetSingleOperation.js +20 -0
  90. package/lib/operations/handleListOperation.js +67 -0
  91. package/lib/operations/handleSingleAuditOperation.js +27 -0
  92. package/lib/prompts/promptAudits.js +15 -0
  93. package/lib/prompts/promptCollection.js +13 -0
  94. package/lib/prompts/promptCollectionInModule.js +13 -0
  95. package/lib/prompts/promptCollectionWithModule.js +15 -0
  96. package/lib/prompts/promptConfirm.js +12 -0
  97. package/lib/prompts/promptDefaultAction.js +13 -0
  98. package/lib/prompts/promptEntry.js +19 -0
  99. package/lib/prompts/promptFileName.js +12 -0
  100. package/lib/prompts/promptFilePath.js +18 -0
  101. package/lib/prompts/promptModule.js +22 -0
  102. package/lib/prompts/promptName.js +11 -0
  103. package/lib/prompts/promptOperation.js +13 -0
  104. package/lib/socket/customerSocketClient.js +13 -0
  105. package/lib/socket/socketClient.js +12 -0
  106. package/lib/types.js +1 -0
  107. 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
+ }