@tolinax/ayoune-cli 2026.2.1 → 2026.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/data/defaultActions.js +9 -0
  2. package/data/modelsAndRights.js +3189 -0
  3. package/data/modules.js +111 -0
  4. package/data/operations.js +5 -0
  5. package/data/services.js +139 -0
  6. package/lib/api/apiCallHandler.js +68 -0
  7. package/lib/api/apiClient.js +100 -0
  8. package/lib/api/auditCallHandler.js +21 -0
  9. package/lib/api/decodeToken.js +4 -0
  10. package/lib/api/handleAPIError.js +59 -0
  11. package/lib/api/login.js +45 -0
  12. package/lib/commands/createActionsCommand.js +109 -0
  13. package/lib/commands/createAiCommand.js +188 -0
  14. package/lib/commands/createAliasCommand.js +106 -0
  15. package/lib/commands/createAuditCommand.js +49 -0
  16. package/lib/commands/createCompletionsCommand.js +136 -0
  17. package/lib/commands/createConfigCommand.js +208 -0
  18. package/lib/commands/createCopyCommand.js +39 -0
  19. package/lib/commands/createCreateCommand.js +50 -0
  20. package/lib/commands/createDeployCommand.js +666 -0
  21. package/lib/commands/createDescribeCommand.js +42 -0
  22. package/lib/commands/createEditCommand.js +43 -0
  23. package/lib/commands/createEventsCommand.js +60 -0
  24. package/lib/commands/createExecCommand.js +182 -0
  25. package/lib/commands/createGetCommand.js +47 -0
  26. package/lib/commands/createListCommand.js +49 -0
  27. package/lib/commands/createLoginCommand.js +18 -0
  28. package/lib/commands/createLogoutCommand.js +21 -0
  29. package/lib/commands/createModulesCommand.js +89 -0
  30. package/lib/commands/createMonitorCommand.js +283 -0
  31. package/lib/commands/createProgram.js +163 -0
  32. package/lib/commands/createServicesCommand.js +228 -0
  33. package/lib/commands/createStorageCommand.js +54 -0
  34. package/lib/commands/createStreamCommand.js +50 -0
  35. package/lib/commands/createWhoAmICommand.js +88 -0
  36. package/lib/exitCodes.js +6 -0
  37. package/lib/helpers/addSpacesToCamelCase.js +5 -0
  38. package/lib/helpers/config.js +6 -0
  39. package/lib/helpers/configLoader.js +60 -0
  40. package/lib/helpers/formatDocument.js +176 -0
  41. package/lib/helpers/handleResponseFormatOptions.js +85 -0
  42. package/lib/helpers/initializeSettings.js +14 -0
  43. package/lib/helpers/localStorage.js +4 -0
  44. package/lib/helpers/makeRandomToken.js +27 -0
  45. package/lib/helpers/parseInt.js +7 -0
  46. package/lib/helpers/requireArg.js +9 -0
  47. package/lib/helpers/saveFile.js +39 -0
  48. package/lib/models/getCollections.js +15 -0
  49. package/lib/models/getModelsInModules.js +13 -0
  50. package/lib/models/getModuleFromCollection.js +7 -0
  51. package/lib/operations/handleAuditOperation.js +22 -0
  52. package/lib/operations/handleCollectionOperation.js +91 -0
  53. package/lib/operations/handleCopySingleOperation.js +22 -0
  54. package/lib/operations/handleCreateSingleOperation.js +35 -0
  55. package/lib/operations/handleDeleteSingleOperation.js +14 -0
  56. package/lib/operations/handleDescribeSingleOperation.js +22 -0
  57. package/lib/operations/handleEditOperation.js +51 -0
  58. package/lib/operations/handleEditRawOperation.js +35 -0
  59. package/lib/operations/handleGetOperation.js +29 -0
  60. package/lib/operations/handleGetSingleOperation.js +20 -0
  61. package/lib/operations/handleListOperation.js +63 -0
  62. package/lib/operations/handleSingleAuditOperation.js +27 -0
  63. package/lib/prompts/promptAudits.js +15 -0
  64. package/lib/prompts/promptCollection.js +13 -0
  65. package/lib/prompts/promptCollectionInModule.js +13 -0
  66. package/lib/prompts/promptCollectionWithModule.js +15 -0
  67. package/lib/prompts/promptConfirm.js +12 -0
  68. package/lib/prompts/promptDefaultAction.js +13 -0
  69. package/lib/prompts/promptEntry.js +19 -0
  70. package/lib/prompts/promptFileName.js +12 -0
  71. package/lib/prompts/promptFilePath.js +18 -0
  72. package/lib/prompts/promptModule.js +19 -0
  73. package/lib/prompts/promptName.js +11 -0
  74. package/lib/prompts/promptOperation.js +13 -0
  75. package/lib/socket/customerSocketClient.js +13 -0
  76. package/lib/socket/socketClient.js +12 -0
  77. package/lib/types.js +1 -0
  78. package/package.json +2 -2
@@ -0,0 +1,42 @@
1
+ import { Argument } from "commander";
2
+ import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
3
+ import { handleDescribeSingleOperation } from "../operations/handleDescribeSingleOperation.js";
4
+ import { localStorage } from "../helpers/localStorage.js";
5
+ import { spinner } from "../../index.js";
6
+ import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
7
+ export function createDescribeCommand(program) {
8
+ program
9
+ .command("describe")
10
+ .alias("d")
11
+ .description("Show detailed YAML description of an entry")
12
+ .addHelpText("after", `
13
+ Examples:
14
+ ay describe contacts 64a1b2c3d4e5 Describe a contact entry
15
+ ay d Describe last used entry`)
16
+ .addArgument(new Argument("[collection]", "The collection to use").default(localStorage.getItem("lastCollection"), `The last used collection (${localStorage.getItem("lastCollection")})`))
17
+ .addArgument(new Argument("[id]", "The ID of the entry to describe").default(localStorage.getItem("lastId"), `The last used id (${localStorage.getItem("lastId")})`))
18
+ .action(async (collection, id, options) => {
19
+ try {
20
+ if (!collection) {
21
+ spinner.error({ text: "Missing required argument: collection. Run a list or get command first, or provide it explicitly." });
22
+ process.exit(EXIT_MISUSE);
23
+ }
24
+ if (!id) {
25
+ spinner.error({ text: "Missing required argument: id. Provide an entry ID explicitly." });
26
+ process.exit(EXIT_MISUSE);
27
+ }
28
+ const opts = { ...program.opts(), ...options };
29
+ const module = getModuleFromCollection(collection);
30
+ localStorage.setItem("lastModule", module.module);
31
+ localStorage.setItem("lastCollection", collection);
32
+ localStorage.setItem("lastId", id);
33
+ await handleDescribeSingleOperation(module.module, collection, id, {
34
+ ...opts,
35
+ });
36
+ }
37
+ catch (e) {
38
+ spinner.error({ text: e.message || "An unexpected error occurred" });
39
+ process.exit(EXIT_GENERAL_ERROR);
40
+ }
41
+ });
42
+ }
@@ -0,0 +1,43 @@
1
+ import { Argument } from "commander";
2
+ import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
3
+ import { handleGetSingleOperation } from "../operations/handleGetSingleOperation.js";
4
+ import { handleEditOperation } from "../operations/handleEditOperation.js";
5
+ import { localStorage } from "../helpers/localStorage.js";
6
+ import { spinner } from "../../index.js";
7
+ import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
8
+ export function createEditCommand(program) {
9
+ program
10
+ .command("edit")
11
+ .alias("e")
12
+ .description("Edit an entry by ID in a collection")
13
+ .addHelpText("after", `
14
+ Examples:
15
+ ay edit contacts 64a1b2c3d4e5 Edit contact by ID
16
+ ay edit Edit last used entry`)
17
+ .addArgument(new Argument("[collection]", "The collection to use").default(localStorage.getItem("lastCollection"), `The last used collection (${localStorage.getItem("lastCollection")})`))
18
+ .addArgument(new Argument("[id]", "The ID of the entry to edit").default(localStorage.getItem("lastId"), `The last used id (${localStorage.getItem("lastId")})`))
19
+ .action(async (collection, id, options) => {
20
+ try {
21
+ if (!collection) {
22
+ spinner.error({ text: "Missing required argument: collection. Run a list or get command first, or provide it explicitly." });
23
+ process.exit(EXIT_MISUSE);
24
+ }
25
+ if (!id) {
26
+ spinner.error({ text: "Missing required argument: id. Provide an entry ID explicitly." });
27
+ process.exit(EXIT_MISUSE);
28
+ }
29
+ const opts = { ...program.opts(), ...options };
30
+ const module = getModuleFromCollection(collection);
31
+ localStorage.setItem("lastModule", module.module);
32
+ localStorage.setItem("lastCollection", collection);
33
+ localStorage.setItem("lastId", id);
34
+ let result = {};
35
+ result = await handleGetSingleOperation(module.module, collection, id, opts);
36
+ await handleEditOperation(module.module, collection, result.content);
37
+ }
38
+ catch (e) {
39
+ spinner.error({ text: e.message || "An unexpected error occurred" });
40
+ process.exit(EXIT_GENERAL_ERROR);
41
+ }
42
+ });
43
+ }
@@ -0,0 +1,60 @@
1
+ import { Option } from "commander";
2
+ import { localStorage } from "../helpers/localStorage.js";
3
+ import { decodeToken } from "../api/decodeToken.js";
4
+ import { customerSocket } from "../socket/customerSocketClient.js";
5
+ import { spinner } from "../../index.js";
6
+ import yaml from "js-yaml";
7
+ import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
8
+ export function createEventsCommand(program) {
9
+ program
10
+ .command("events")
11
+ .alias("sub")
12
+ .description("Subscribe to filtered events via WebSocket")
13
+ .addHelpText("after", `
14
+ Examples:
15
+ ay events Listen to all events
16
+ ay events -c sales -a create Filter by category and action
17
+ ay sub -f yaml -c crm Subscribe to CRM events as YAML`)
18
+ .addOption(new Option("-f, --format <format>", "Set the output format")
19
+ .choices(["json", "yaml", "table"])
20
+ .default("json"))
21
+ .addOption(new Option("-c, --category <category>", "Set the category to listen").default("*"))
22
+ .addOption(new Option("-a, --action <action>", "Set the action to listen").default("*"))
23
+ .addOption(new Option("-l, --label <label>", "Set the label to listen").default("*"))
24
+ .addOption(new Option("-V, --value <value>", "Set the value to listen").default("*"))
25
+ .action(async (options) => {
26
+ try {
27
+ const tokenPayload = decodeToken(localStorage.getItem("token"));
28
+ const user = tokenPayload.payload;
29
+ spinner.start({ text: `Starting stream with [${user._customerID}]` });
30
+ const socket = customerSocket(user);
31
+ spinner.update({ text: "Stream active" });
32
+ socket.on("event", (data) => {
33
+ if (options.category !== "*" && options.category !== data.category)
34
+ return;
35
+ if (options.action !== "*" && options.action !== data.action)
36
+ return;
37
+ if (options.label !== "*" && options.label !== data.label)
38
+ return;
39
+ if (options.value !== "*" && options.value !== data.value)
40
+ return;
41
+ spinner.update({
42
+ text: `Received [${data.category}.${data.action}.${data.label}.${data.value}]`,
43
+ });
44
+ if (options.format === "table") {
45
+ console.table(data.evt_data);
46
+ }
47
+ if (options.format === "yaml") {
48
+ console.log(yaml.dump(data.evt_data));
49
+ }
50
+ if (options.format === "json") {
51
+ console.log(JSON.stringify(data.evt_data, null, 2));
52
+ }
53
+ });
54
+ }
55
+ catch (e) {
56
+ spinner.error({ text: e.message || "An unexpected error occurred" });
57
+ process.exit(EXIT_GENERAL_ERROR);
58
+ }
59
+ });
60
+ }
@@ -0,0 +1,182 @@
1
+ import chalk from "chalk";
2
+ import { api } from "../api/apiClient.js";
3
+ import { apiCallHandler } from "../api/apiCallHandler.js";
4
+ import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
5
+ import { saveFile } from "../helpers/saveFile.js";
6
+ import { localStorage } from "../helpers/localStorage.js";
7
+ import { spinner } from "../../index.js";
8
+ import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
9
+ async function resolveAction(searchTerm) {
10
+ // Try exact operationId match first
11
+ const res = await apiCallHandler("config", "ayouneapiactions", "get", null, {
12
+ q: searchTerm,
13
+ limit: 50,
14
+ responseFormat: "json",
15
+ });
16
+ if (!(res === null || res === void 0 ? void 0 : res.payload) || !Array.isArray(res.payload) || res.payload.length === 0) {
17
+ return null;
18
+ }
19
+ const actions = res.payload.filter((a) => !a.deprecated);
20
+ // Exact operationId match
21
+ const exact = actions.find((a) => a.operationId === searchTerm);
22
+ if (exact)
23
+ return mapAction(exact);
24
+ // Partial operationId match
25
+ const partial = actions.find((a) => { var _a; return (_a = a.operationId) === null || _a === void 0 ? void 0 : _a.includes(searchTerm); });
26
+ if (partial)
27
+ return mapAction(partial);
28
+ // If multiple results, return first match
29
+ if (actions.length > 0)
30
+ return mapAction(actions[0]);
31
+ return null;
32
+ }
33
+ function mapAction(a) {
34
+ return {
35
+ host: a.host || "",
36
+ endpoint: a.endpoint || "",
37
+ method: (a.method || "GET").toUpperCase(),
38
+ operationId: a.operationId || "",
39
+ nameSpace: a.nameSpace || "",
40
+ capability: a.capability || "",
41
+ params: a.params || [],
42
+ query: a.query || [],
43
+ description: a.shortDescription || a.description || "",
44
+ };
45
+ }
46
+ function buildUrl(endpoint, paramValues) {
47
+ let url = endpoint;
48
+ for (const [key, value] of Object.entries(paramValues)) {
49
+ url = url.replace(`:${key}`, encodeURIComponent(value));
50
+ }
51
+ return url;
52
+ }
53
+ export function createExecCommand(program) {
54
+ program
55
+ .command("exec <operationId>")
56
+ .alias("x")
57
+ .description("Execute any registered API action by operationId")
58
+ .addHelpText("after", `
59
+ Examples:
60
+ ay exec ayoune.ai.conversations.create.one --body '{"prompt":"Hello"}'
61
+ ay exec ayoune.crm.contacts.list
62
+ ay exec ayoune.pm.projects.create.one --body '{"subject":"Migration"}'
63
+ ay exec ayoune.ai.prompts.list -r table
64
+ ay exec ayoune.sale.orders.get.one --param id=507f1f77bcf86cd799439011`)
65
+ .option("--body <json>", "Request body as JSON string")
66
+ .option("--body-file <path>", "Read request body from file")
67
+ .option("--body-stdin", "Read request body from stdin (for piping)")
68
+ .option("--param <kv...>", "Path parameters as key=value pairs")
69
+ .option("--query <kv...>", "Query parameters as key=value pairs")
70
+ .action(async (operationId, options) => {
71
+ try {
72
+ const opts = { ...program.opts(), ...options };
73
+ spinner.start({ text: `Resolving action: ${operationId}`, color: "magenta" });
74
+ const action = await resolveAction(operationId);
75
+ if (!action) {
76
+ spinner.error({ text: `No API action found for: ${operationId}` });
77
+ process.exit(EXIT_MISUSE);
78
+ }
79
+ spinner.update({ text: `Executing: ${action.method} https://${action.host}${action.endpoint}` });
80
+ // Parse path params
81
+ const paramValues = {};
82
+ if (opts.param) {
83
+ for (const p of opts.param) {
84
+ const [key, ...rest] = p.split("=");
85
+ paramValues[key] = rest.join("=");
86
+ }
87
+ }
88
+ // Parse query params
89
+ const queryParams = {
90
+ responseFormat: opts.responseFormat,
91
+ verbosity: opts.verbosity,
92
+ };
93
+ if (opts.hideMeta)
94
+ queryParams.hideMeta = "true";
95
+ if (opts.query) {
96
+ for (const q of opts.query) {
97
+ const [key, ...rest] = q.split("=");
98
+ queryParams[key] = rest.join("=");
99
+ }
100
+ }
101
+ // Parse body
102
+ let body = null;
103
+ if (opts.body) {
104
+ try {
105
+ body = JSON.parse(opts.body);
106
+ }
107
+ catch (_a) {
108
+ spinner.error({ text: "Invalid JSON in --body" });
109
+ process.exit(EXIT_MISUSE);
110
+ }
111
+ }
112
+ if (opts.bodyFile) {
113
+ const fs = await import("fs");
114
+ const content = fs.readFileSync(opts.bodyFile, "utf-8");
115
+ try {
116
+ body = JSON.parse(content);
117
+ }
118
+ catch (_b) {
119
+ spinner.error({ text: `Invalid JSON in file: ${opts.bodyFile}` });
120
+ process.exit(EXIT_MISUSE);
121
+ }
122
+ }
123
+ if (opts.bodyStdin && !process.stdin.isTTY) {
124
+ const chunks = [];
125
+ for await (const chunk of process.stdin) {
126
+ chunks.push(chunk);
127
+ }
128
+ const stdinContent = Buffer.concat(chunks).toString("utf-8").trim();
129
+ if (stdinContent) {
130
+ try {
131
+ body = JSON.parse(stdinContent);
132
+ }
133
+ catch (_c) {
134
+ spinner.error({ text: "Invalid JSON from stdin" });
135
+ process.exit(EXIT_MISUSE);
136
+ }
137
+ }
138
+ }
139
+ // Build final URL
140
+ const url = buildUrl(action.endpoint, paramValues);
141
+ const fullUrl = `https://${action.host}${url}`;
142
+ // Dry-run support
143
+ if (opts.dryRun && action.method !== "GET") {
144
+ spinner.stop();
145
+ console.error(chalk.yellow.bold("\n [DRY RUN] Request not sent:\n"));
146
+ console.error(` ${chalk.dim("Operation:")} ${chalk.cyan(action.operationId)}`);
147
+ console.error(` ${chalk.dim("Method:")} ${chalk.cyan(action.method)}`);
148
+ console.error(` ${chalk.dim("URL:")} ${chalk.cyan(fullUrl)}`);
149
+ if (body) {
150
+ console.error(` ${chalk.dim("Body:")} ${JSON.stringify(body, null, 2).split("\n").join("\n ")}`);
151
+ }
152
+ console.error();
153
+ return;
154
+ }
155
+ // Execute the request directly against the service host
156
+ const response = await api({
157
+ baseURL: `https://${action.host}`,
158
+ method: action.method.toLowerCase(),
159
+ url,
160
+ data: body,
161
+ params: queryParams,
162
+ headers: {
163
+ Authorization: `Bearer ${localStorage.getItem("token")}`,
164
+ },
165
+ });
166
+ const res = response.data;
167
+ const { plainResult, result, content } = handleResponseFormatOptions(opts, res);
168
+ const payloadCount = Array.isArray(res === null || res === void 0 ? void 0 : res.payload) ? res.payload.length : 1;
169
+ spinner.success({
170
+ text: `${action.method} ${action.operationId} — ${payloadCount} result(s)`,
171
+ });
172
+ spinner.stop();
173
+ if (opts.save) {
174
+ await saveFile("exec", opts, res);
175
+ }
176
+ }
177
+ catch (e) {
178
+ spinner.error({ text: e.message || "Failed to execute API action" });
179
+ process.exit(EXIT_GENERAL_ERROR);
180
+ }
181
+ });
182
+ }
@@ -0,0 +1,47 @@
1
+ import { parseInteger } from "../helpers/parseInt.js";
2
+ import { promptCollectionWithModule } from "../prompts/promptCollectionWithModule.js";
3
+ import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
4
+ import { saveFile } from "../helpers/saveFile.js";
5
+ import { handleGetOperation } from "../operations/handleGetOperation.js";
6
+ import { localStorage } from "../helpers/localStorage.js";
7
+ import { spinner } from "../../index.js";
8
+ import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
9
+ export function createGetCommand(program) {
10
+ program
11
+ .command("get [collection]")
12
+ .alias("g")
13
+ .description("Retrieve entries from a collection with field selection")
14
+ .addHelpText("after", `
15
+ Examples:
16
+ ay get contacts Get contacts with default fields
17
+ ay get products -i name price Get only name and price fields
18
+ ay get orders -p 3 -l 10 -r csv Get orders page 3, 10 per page, as CSV`)
19
+ .option("-p, --page <number>", "Page", parseInteger, 1)
20
+ .option("-l, --limit <number>", "Limit", parseInteger, 20)
21
+ .option("-f, --from <date>", "From date")
22
+ .option("-i, --fields <fields...>", "Fields to get")
23
+ .action(async (collection, options) => {
24
+ try {
25
+ const opts = { ...program.opts(), ...options };
26
+ if (!collection) {
27
+ if (!process.stdin.isTTY) {
28
+ spinner.error({ text: "Missing required argument: collection" });
29
+ process.exit(EXIT_MISUSE);
30
+ }
31
+ collection = await promptCollectionWithModule();
32
+ }
33
+ const module = getModuleFromCollection(collection);
34
+ localStorage.setItem("lastModule", module.module);
35
+ localStorage.setItem("lastCollection", collection);
36
+ let result = {};
37
+ result = await handleGetOperation(module.module, collection, opts);
38
+ if (opts.save) {
39
+ await saveFile("get", opts, result);
40
+ }
41
+ }
42
+ catch (e) {
43
+ spinner.error({ text: e.message || "An unexpected error occurred" });
44
+ process.exit(EXIT_GENERAL_ERROR);
45
+ }
46
+ });
47
+ }
@@ -0,0 +1,49 @@
1
+ import { parseInteger } from "../helpers/parseInt.js";
2
+ import { promptCollectionWithModule } from "../prompts/promptCollectionWithModule.js";
3
+ import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
4
+ import { handleListOperation } from "../operations/handleListOperation.js";
5
+ import { saveFile } from "../helpers/saveFile.js";
6
+ import { localStorage } from "../helpers/localStorage.js";
7
+ import { spinner } from "../../index.js";
8
+ import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
9
+ export function createListCommand(program) {
10
+ program
11
+ .command("list [collection]")
12
+ .alias("l")
13
+ .description("List entries in a collection with pagination support")
14
+ .addHelpText("after", `
15
+ Examples:
16
+ ay list contacts List contacts (page 1, limit 20)
17
+ ay list products -p 2 -l 50 List products page 2, 50 per page
18
+ ay list orders -r yaml -s List orders as YAML and save to file
19
+ ay list contacts --all Fetch all pages
20
+ ay list contacts --jq '[].name' Extract just names using JMESPath`)
21
+ .option("-p, --page <number>", "Page", parseInteger, 1)
22
+ .option("-l, --limit <number>", "Limit", parseInteger, 20)
23
+ .option("-f, --from <date>", "From date")
24
+ .option("-a, --all", "Fetch all pages automatically")
25
+ .action(async (collection, options) => {
26
+ try {
27
+ const opts = { ...program.opts(), ...options };
28
+ if (!collection) {
29
+ if (!process.stdin.isTTY) {
30
+ spinner.error({ text: "Missing required argument: collection" });
31
+ process.exit(EXIT_MISUSE);
32
+ }
33
+ collection = await promptCollectionWithModule();
34
+ }
35
+ const module = getModuleFromCollection(collection);
36
+ localStorage.setItem("lastModule", module.module);
37
+ localStorage.setItem("lastCollection", collection);
38
+ let result = {};
39
+ result = await handleListOperation(module.module, collection, opts);
40
+ if (opts.save) {
41
+ await saveFile("list", opts, result);
42
+ }
43
+ }
44
+ catch (e) {
45
+ spinner.error({ text: e.message || "An unexpected error occurred" });
46
+ process.exit(EXIT_GENERAL_ERROR);
47
+ }
48
+ });
49
+ }
@@ -0,0 +1,18 @@
1
+ import { login } from "../api/login.js";
2
+ import { spinner } from "../../index.js";
3
+ import { EXIT_AUTH_REQUIRED } from "../exitCodes.js";
4
+ export function createLoginCommand(program) {
5
+ program
6
+ .command("login")
7
+ .alias("auth")
8
+ .description("Authenticate with your aYOUne account via browser")
9
+ .action(async () => {
10
+ try {
11
+ await login();
12
+ }
13
+ catch (e) {
14
+ spinner.error({ text: e.message || "An unexpected error occurred" });
15
+ process.exit(EXIT_AUTH_REQUIRED);
16
+ }
17
+ });
18
+ }
@@ -0,0 +1,21 @@
1
+ import { localStorage } from "../helpers/localStorage.js";
2
+ import { spinner } from "../../index.js";
3
+ import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
4
+ export function createLogoutCommand(program) {
5
+ program
6
+ .command("logout")
7
+ .alias("signout")
8
+ .description("Clear stored credentials and log out")
9
+ .action(async (options) => {
10
+ try {
11
+ spinner.start({ text: "Clearing credentials" });
12
+ localStorage.removeItem("token");
13
+ localStorage.removeItem("refreshToken");
14
+ spinner.success({ text: "Credentials cleared" });
15
+ }
16
+ catch (e) {
17
+ spinner.error({ text: e.message || "An unexpected error occurred" });
18
+ process.exit(EXIT_GENERAL_ERROR);
19
+ }
20
+ });
21
+ }
@@ -0,0 +1,89 @@
1
+ import { promptModule } from "../prompts/promptModule.js";
2
+ import { promptCollectionInModule } from "../prompts/promptCollectionInModule.js";
3
+ import { promptOperation } from "../prompts/promptOperation.js";
4
+ import { handleListOperation } from "../operations/handleListOperation.js";
5
+ import { promptEntry } from "../prompts/promptEntry.js";
6
+ import { handleCollectionOperation } from "../operations/handleCollectionOperation.js";
7
+ import { localStorage } from "../helpers/localStorage.js";
8
+ import { parseInteger } from "../helpers/parseInt.js";
9
+ import { handleGetOperation } from "../operations/handleGetOperation.js";
10
+ import { aYOUneModules } from "../../data/modules.js";
11
+ import { getModelsInModules } from "../models/getModelsInModules.js";
12
+ import { spinner } from "../../index.js";
13
+ import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
14
+ function resolveModule(input) {
15
+ const lower = input.toLowerCase();
16
+ const match = aYOUneModules.find((m) => m.module === lower || m.label.toLowerCase() === lower);
17
+ return match ? match.module : null;
18
+ }
19
+ function resolveCollection(module, input) {
20
+ const lower = input.toLowerCase();
21
+ const collections = getModelsInModules(module);
22
+ const match = collections.find((c) => c.value === lower || c.name.toLowerCase() === lower);
23
+ return match ? match.value : null;
24
+ }
25
+ export function createModulesCommand(program) {
26
+ program
27
+ .command("modules [module] [collection]")
28
+ .alias("m")
29
+ .description("Browse modules, collections, and entries interactively")
30
+ .addHelpText("after", `
31
+ Examples:
32
+ ay modules Start interactive module browser
33
+ ay m Same using alias
34
+ ay m crm Jump straight to the CRM module
35
+ ay m crm consumers Jump to CRM consumers collection`)
36
+ .option("-p, --page <number>", "Page", parseInteger, 1)
37
+ .option("-l, --limit <number>", "Limit", parseInteger, 20)
38
+ .option("-f, --from <date>", "From date")
39
+ .action(async (moduleArg, collectionArg, options) => {
40
+ try {
41
+ const opts = { ...program.opts(), ...options };
42
+ if (!process.stdin.isTTY) {
43
+ spinner.error({ text: "The modules command requires an interactive terminal" });
44
+ process.exit(EXIT_MISUSE);
45
+ }
46
+ let module;
47
+ if (moduleArg) {
48
+ const resolved = resolveModule(moduleArg);
49
+ if (!resolved) {
50
+ spinner.error({ text: `Unknown module: ${moduleArg}` });
51
+ process.exit(EXIT_MISUSE);
52
+ }
53
+ module = resolved;
54
+ }
55
+ else {
56
+ module = await promptModule();
57
+ }
58
+ let collection;
59
+ if (collectionArg) {
60
+ const resolved = resolveCollection(module, collectionArg);
61
+ if (!resolved) {
62
+ spinner.error({ text: `Unknown collection: ${collectionArg} in module ${module}` });
63
+ process.exit(EXIT_MISUSE);
64
+ }
65
+ collection = resolved;
66
+ }
67
+ else {
68
+ collection = await promptCollectionInModule(module);
69
+ }
70
+ const operation = await promptOperation();
71
+ localStorage.setItem("lastModule", module);
72
+ localStorage.setItem("lastCollection", collection);
73
+ let entry = "";
74
+ if (operation === "list") {
75
+ const { result } = await handleListOperation(module, collection, opts);
76
+ entry = await promptEntry(result);
77
+ }
78
+ if (operation === "get") {
79
+ const { result } = await handleGetOperation(module, collection, opts);
80
+ entry = await promptEntry(result);
81
+ }
82
+ await handleCollectionOperation(module, collection, entry, opts);
83
+ }
84
+ catch (e) {
85
+ spinner.error({ text: e.message || "An unexpected error occurred" });
86
+ process.exit(EXIT_GENERAL_ERROR);
87
+ }
88
+ });
89
+ }