@tolinax/ayoune-cli 2026.3.1 → 2026.5.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 +217 -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 +160 -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 +50 -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,217 @@
1
+ //Defines the main program structure for command line interface
2
+ import { Command, Option } from "commander";
3
+ import chalk from "chalk";
4
+ import path from "path";
5
+ import os from "os";
6
+ import { spinner } from "../../index.js";
7
+ import { createModulesCommand } from "./createModulesCommand.js";
8
+ import { createListCommand } from "./createListCommand.js";
9
+ import { createGetCommand } from "./createGetCommand.js";
10
+ import { createEditCommand } from "./createEditCommand.js";
11
+ import { createLoginCommand } from "./createLoginCommand.js";
12
+ import { createDescribeCommand } from "./createDescribeCommand.js";
13
+ import { createCopyCommand } from "./createCopyCommand.js";
14
+ import { createCreateCommand } from "./createCreateCommand.js";
15
+ import { createStorageCommand } from "./createStorageCommand.js";
16
+ import { createAuditCommand } from "./createAuditCommand.js";
17
+ import { createStreamCommand } from "./createStreamCommand.js";
18
+ import { createEventsCommand } from "./createEventsCommand.js";
19
+ import { createWhoAmICommand } from "./createWhoAmICommand.js";
20
+ import { createLogoutCommand } from "./createLogoutCommand.js";
21
+ import { createCompletionsCommand } from "./createCompletionsCommand.js";
22
+ import { createAliasCommand, registerUserAliases } from "./createAliasCommand.js";
23
+ import { createConfigCommand } from "./createConfigCommand.js";
24
+ import { createActionsCommand } from "./createActionsCommand.js";
25
+ import { createExecCommand } from "./createExecCommand.js";
26
+ import { createAiCommand } from "./createAiCommand.js";
27
+ import { enableDebug } from "../api/apiClient.js";
28
+ import { enableDryRun } from "../api/apiCallHandler.js";
29
+ import { setJsonErrorsEnabled } from "../helpers/cliError.js";
30
+ import { disableContext } from "../helpers/contextInjector.js";
31
+ import { createServicesCommand } from "./createServicesCommand.js";
32
+ import { createDeployCommand } from "./createDeployCommand.js";
33
+ import { createMonitorCommand } from "./createMonitorCommand.js";
34
+ import { createDeleteCommand } from "./createDeleteCommand.js";
35
+ import { createUpdateCommand } from "./createUpdateCommand.js";
36
+ import { createBatchCommand } from "./createBatchCommand.js";
37
+ import { createSearchCommand } from "./createSearchCommand.js";
38
+ import { createWebhooksCommand } from "./createWebhooksCommand.js";
39
+ import { createJobsCommand } from "./createJobsCommand.js";
40
+ import { createExportCommand } from "./createExportCommand.js";
41
+ import { createUsersCommand } from "./createUsersCommand.js";
42
+ import { createSyncCommand } from "./createSyncCommand.js";
43
+ import { createPermissionsCommand } from "./createPermissionsCommand.js";
44
+ import { createTemplateCommand } from "./createTemplateCommand.js";
45
+ import { createSetupCommand } from "./createSetupCommand.js";
46
+ import { createStatusCommand } from "./createStatusCommand.js";
47
+ import { createSelfHostUpdateCommand } from "./createSelfHostUpdateCommand.js";
48
+ import { createContextCommand } from "./createContextCommand.js";
49
+ import { createAccessCommand } from "./createAccessCommand.js";
50
+ import { secureStorage } from "../helpers/secureStorage.js";
51
+ import { login } from "../api/login.js";
52
+ import { loadConfig } from "../helpers/configLoader.js";
53
+ import { getLogo, getDescription, BRAND_BLUE } from "../helpers/logo.js";
54
+ import { checkForUpdates } from "../helpers/updateNotifier.js";
55
+ import { createRequire } from "module";
56
+ const require = createRequire(import.meta.url);
57
+ let pkg;
58
+ try {
59
+ pkg = require("../../package.json"); // dist/ and npm install
60
+ }
61
+ catch (_a) {
62
+ pkg = require("../../../package.json"); // source tree (tests)
63
+ }
64
+ export function createProgram(program) {
65
+ program
66
+ .version(pkg.version || "0.0.0")
67
+ .addOption(new Option("-r, --responseFormat <format>", "Set the output format")
68
+ .choices(["json", "csv", "yaml", "table"])
69
+ .default("json"))
70
+ .addOption(new Option("-v, --verbosity <level>", "Set the verbosity level of the returned meta information")
71
+ .choices(["default", "extended", "minimal"])
72
+ .default("default")
73
+ .conflicts("hideMeta"))
74
+ .addOption(new Option("-m, --hideMeta", "Returns only the payload without meta information. ")
75
+ .default(false)
76
+ .conflicts("verbosity"))
77
+ .addOption(new Option("-s, --save", "Saves the response as file"))
78
+ .addOption(new Option("-d, --debug", "Show detailed request/response information"))
79
+ .addOption(new Option("-o, --outPath [filePath]", "Lets you set a path").default(path.join(os.homedir(), "aYOUne")))
80
+ .addOption(new Option("-n, --name [fileName]", "Lets you set a filename"))
81
+ .addOption(new Option("-q, --quiet", "Suppress all output except errors and results"))
82
+ .addOption(new Option("--force", "Skip confirmation prompts for destructive actions"))
83
+ .addOption(new Option("--dry-run", "Preview what a command would do without executing"))
84
+ .addOption(new Option("--jq <expression>", "Filter JSON output using JMESPath expression"))
85
+ .addOption(new Option("--columns <fields>", "Comma-separated list of columns to display"))
86
+ .addOption(new Option("--no-color", "Disable colored output"))
87
+ .addOption(new Option("--json-errors", "Output errors as JSON to stderr (for AI agents/scripts)"))
88
+ .addOption(new Option("--token <jwt>", "Authenticate with a JWT token for this session"))
89
+ .addOption(new Option("--no-context", "Disable context injection for this command"))
90
+ .description(getDescription());
91
+ program.showHelpAfterError();
92
+ program.showSuggestionAfterError(true);
93
+ createModulesCommand(program);
94
+ createListCommand(program);
95
+ createGetCommand(program);
96
+ createEditCommand(program);
97
+ createCopyCommand(program);
98
+ createDescribeCommand(program);
99
+ createCreateCommand(program);
100
+ createStorageCommand(program);
101
+ createAuditCommand(program);
102
+ createStreamCommand(program);
103
+ createEventsCommand(program);
104
+ createWhoAmICommand(program);
105
+ createLogoutCommand(program);
106
+ createActionsCommand(program);
107
+ createExecCommand(program);
108
+ createAiCommand(program);
109
+ createServicesCommand(program);
110
+ createDeployCommand(program);
111
+ createMonitorCommand(program);
112
+ createDeleteCommand(program);
113
+ createUpdateCommand(program);
114
+ createBatchCommand(program);
115
+ createSearchCommand(program);
116
+ createWebhooksCommand(program);
117
+ createJobsCommand(program);
118
+ createExportCommand(program);
119
+ createUsersCommand(program);
120
+ createSyncCommand(program);
121
+ createPermissionsCommand(program);
122
+ createTemplateCommand(program);
123
+ createAccessCommand(program);
124
+ createSetupCommand(program);
125
+ createStatusCommand(program);
126
+ createSelfHostUpdateCommand(program);
127
+ createContextCommand(program);
128
+ createCompletionsCommand(program);
129
+ createAliasCommand(program);
130
+ createConfigCommand(program);
131
+ registerUserAliases(program);
132
+ createLoginCommand(program);
133
+ program.addHelpText("beforeAll", getLogo());
134
+ program.addHelpText("afterAll", chalk.dim("\n Run ay <command> --help for more info\n"));
135
+ program.configureHelp({
136
+ sortOptions: true,
137
+ sortSubcommands: true,
138
+ showGlobalOptions: true,
139
+ formatHelp: (cmd, helper) => {
140
+ const defaultHelp = Command.prototype.createHelp().formatHelp(cmd, helper);
141
+ return defaultHelp.replace(/^(Usage:|Commands:|Options:|Global Options:)/gm, (match) => chalk.hex(BRAND_BLUE).bold(match));
142
+ },
143
+ });
144
+ // Respect NO_COLOR env var (no-color.org standard)
145
+ if (process.env.NO_COLOR !== undefined) {
146
+ chalk.level = 0;
147
+ }
148
+ program.hook("preAction", async (thisCommand) => {
149
+ var _a;
150
+ // Apply saved defaults — only when the user didn't pass the flag explicitly
151
+ const config = loadConfig();
152
+ const defaults = (_a = config.defaults) !== null && _a !== void 0 ? _a : {};
153
+ const rawArgs = process.argv.slice(2);
154
+ if (defaults.responseFormat && !rawArgs.some(a => ["-r", "--responseFormat"].includes(a)))
155
+ program.setOptionValue("responseFormat", defaults.responseFormat);
156
+ if (defaults.verbosity && !rawArgs.some(a => ["-v", "--verbosity"].includes(a)))
157
+ program.setOptionValue("verbosity", defaults.verbosity);
158
+ if (defaults.outPath && !rawArgs.some(a => ["-o", "--outPath"].includes(a)))
159
+ program.setOptionValue("outPath", defaults.outPath);
160
+ if (defaults.hideMeta && !rawArgs.some(a => ["-m", "--hideMeta"].includes(a)))
161
+ program.setOptionValue("hideMeta", defaults.hideMeta);
162
+ if (defaults.quiet && !rawArgs.some(a => ["-q", "--quiet"].includes(a)))
163
+ program.setOptionValue("quiet", defaults.quiet);
164
+ if (defaults.force && !rawArgs.some(a => ["--force"].includes(a)))
165
+ program.setOptionValue("force", defaults.force);
166
+ if (defaults.dryRun && !rawArgs.some(a => ["--dry-run"].includes(a)))
167
+ program.setOptionValue("dryRun", defaults.dryRun);
168
+ const opts = program.opts();
169
+ if (opts.color === false) {
170
+ chalk.level = 0;
171
+ }
172
+ if (opts.debug) {
173
+ enableDebug();
174
+ }
175
+ if (opts.dryRun) {
176
+ enableDryRun();
177
+ }
178
+ if (opts.jsonErrors) {
179
+ setJsonErrorsEnabled(true);
180
+ }
181
+ if (opts.context === false) {
182
+ disableContext();
183
+ }
184
+ if (opts.quiet) {
185
+ spinner.start = () => spinner;
186
+ spinner.update = () => spinner;
187
+ spinner.success = () => spinner;
188
+ spinner.stop = () => spinner;
189
+ // spinner.error left intact
190
+ }
191
+ // Token from --token flag (highest priority), then env var, then stored token
192
+ if (opts.token) {
193
+ secureStorage.setItem("token", opts.token);
194
+ }
195
+ else {
196
+ const envToken = process.env.AYOUNE_TOKEN;
197
+ if (envToken && !secureStorage.getItem("token")) {
198
+ secureStorage.setItem("token", envToken);
199
+ }
200
+ }
201
+ // First-run onboarding: auto-login if no token stored
202
+ const cmdName = thisCommand.name();
203
+ const skipAuth = ["login", "logout", "whoami", "completions", "alias", "config", "help", "setup", "status", "self-host-update", "context"];
204
+ if (!skipAuth.includes(cmdName) && process.stdin.isTTY) {
205
+ const token = secureStorage.getItem("token");
206
+ if (!token) {
207
+ console.error(chalk.hex('#2B8DC6').bold("\n Welcome to aYOUne CLI!\n"));
208
+ console.error(chalk.dim(" You need to authenticate before using this command.\n"));
209
+ await login();
210
+ }
211
+ }
212
+ // Non-blocking update check (throttled, silent on errors)
213
+ checkForUpdates(pkg.version || "0.0.0");
214
+ });
215
+ //Parse command line arguments
216
+ program.parse(process.argv);
217
+ }
@@ -0,0 +1,251 @@
1
+ import { resolveCollectionArgs } from "../helpers/resolveCollectionArgs.js";
2
+ import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
3
+ import { apiCallHandler } from "../api/apiCallHandler.js";
4
+ import { searchModel, searchOne, searchGlobal } from "../api/searchClient.js";
5
+ import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
6
+ import { saveFile } from "../helpers/saveFile.js";
7
+ import { localStorage } from "../helpers/localStorage.js";
8
+ import { spinner } from "../../index.js";
9
+ import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
10
+ import { cliError } from "../helpers/cliError.js";
11
+ import { sanitizeFields } from "../helpers/sanitizeFields.js";
12
+ import { getContextFilterParams } from "../helpers/contextInjector.js";
13
+ export function createSearchCommand(program) {
14
+ program
15
+ .command("search [collectionOrModule] [collectionOrQuery] [query]")
16
+ .alias("find")
17
+ .description("Search entries via the aYOUne Search Service")
18
+ .addHelpText("after", `
19
+ Examples:
20
+ ay search consumers "John" Search via search service
21
+ ay search crm consumers "John" Search with explicit module
22
+ ay search -g "John" Global search across all collections (SSE)
23
+ ay search consumers "John" --one Find first match
24
+ ay search consumers "John" --field name Search specific field
25
+ ay search contacts --filter "status=active" Filter by field
26
+ ay search orders --filter "status=paid" --sort "-createdAt"
27
+ ay search consumers "John" --legacy Use legacy module API
28
+ ay find invoices --filter "total>500" --count Just count matches`)
29
+ .option("-g, --global <query>", "Global search across all collections (SSE streaming)")
30
+ .option("--field <name>", "Search in a specific field")
31
+ .option("--one", "Return only the first matching entry", false)
32
+ .option("--legacy", "Use legacy module API instead of search service", false)
33
+ .option("--filter <filters>", "Comma-separated key=value filters (supports =, !=, >, <, >=, <=)")
34
+ .option("--fields <fields>", "Comma-separated fields to return (projection)")
35
+ .option("--sort <field>", "Sort by field (prefix with - for descending)", "-createdAt")
36
+ .option("--count", "Only return count of matching entries", false)
37
+ .option("-l, --limit <number>", "Limit results", parseInt, 25)
38
+ .option("-p, --page <number>", "Page number", parseInt, 1)
39
+ .action(async (collectionOrModule, collectionOrQuery, query, options) => {
40
+ try {
41
+ const opts = { ...program.opts(), ...options };
42
+ // Mode 1: Global SSE search
43
+ if (opts.global) {
44
+ return await handleGlobalSearch(opts);
45
+ }
46
+ // For all other modes, we need a collection
47
+ if (!collectionOrModule) {
48
+ cliError("Collection name is required (or use --global for global search)", EXIT_GENERAL_ERROR);
49
+ return;
50
+ }
51
+ // Detect if first arg is a module: ay search crm consumers "John"
52
+ let resolved;
53
+ let searchQuery = query;
54
+ const m = getModuleFromCollection(collectionOrModule.toLowerCase());
55
+ if (!m && collectionOrQuery) {
56
+ resolved = resolveCollectionArgs(collectionOrModule, collectionOrQuery);
57
+ }
58
+ else {
59
+ resolved = resolveCollectionArgs(collectionOrModule);
60
+ searchQuery = collectionOrQuery;
61
+ }
62
+ const collection = resolved.collection;
63
+ // Mode 4: Legacy module API search
64
+ if (opts.legacy) {
65
+ return await handleLegacySearch(resolved, searchQuery, opts);
66
+ }
67
+ // Mode 3: FindOne via search service
68
+ if (opts.one) {
69
+ return await handleFindOneSearch(resolved, searchQuery, opts);
70
+ }
71
+ // Mode 2: Single-model search via search service
72
+ return await handleModelSearch(resolved, searchQuery, opts);
73
+ }
74
+ catch (e) {
75
+ cliError(e.message || "Search failed", EXIT_GENERAL_ERROR);
76
+ }
77
+ });
78
+ }
79
+ async function handleGlobalSearch(opts) {
80
+ const query = opts.global;
81
+ spinner.start({ text: `Global search for "${query}"...`, color: "magenta" });
82
+ const results = await searchGlobal(query, opts.limit, {
83
+ onResult: (result) => {
84
+ spinner.update({
85
+ text: `Searching... found ${result.total} in ${result.collection}`,
86
+ });
87
+ },
88
+ onError: (error) => {
89
+ spinner.update({ text: `Search warning: ${error}` });
90
+ },
91
+ });
92
+ // Flatten results into a grouped response for handleResponseFormatOptions
93
+ const totalEntries = results.reduce((sum, r) => sum + r.total, 0);
94
+ const allEntries = results.flatMap((r) => r.entries.map((e) => ({ ...e, _collection: r.collection })));
95
+ const res = {
96
+ payload: allEntries,
97
+ meta: {
98
+ responseTime: 0,
99
+ pageInfo: { totalEntries: totalEntries, page: 1, totalPages: 1 },
100
+ collections: results.map((r) => ({ name: r.collection, count: r.total })),
101
+ },
102
+ };
103
+ if (opts.count) {
104
+ spinner.success({ text: `${totalEntries} matching entries across ${results.length} collections` });
105
+ spinner.stop();
106
+ console.log(totalEntries);
107
+ return;
108
+ }
109
+ handleResponseFormatOptions(opts, res);
110
+ spinner.success({
111
+ text: `Found ${totalEntries} entries across ${results.length} collections`,
112
+ });
113
+ spinner.stop();
114
+ if (opts.save)
115
+ await saveFile("search-global", opts, res);
116
+ }
117
+ async function handleModelSearch(resolved, searchQuery, opts) {
118
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
119
+ const collection = resolved.collection;
120
+ spinner.start({ text: `Searching ${collection}...`, color: "magenta" });
121
+ const params = {
122
+ limit: opts.limit,
123
+ skip: (opts.page - 1) * opts.limit,
124
+ };
125
+ if (searchQuery)
126
+ params.q = searchQuery;
127
+ if (opts.field)
128
+ params.field = opts.field;
129
+ if (opts.fields)
130
+ params.fields = sanitizeFields(opts.fields).join(",");
131
+ if (opts.sort)
132
+ params.sort = opts.sort;
133
+ // Inject context params
134
+ const contextParams = getContextFilterParams(collection);
135
+ Object.assign(params, contextParams);
136
+ // Parse filters
137
+ applyFilters(params, opts.filter);
138
+ const res = await searchModel(collection, params);
139
+ if (opts.count) {
140
+ 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;
141
+ spinner.success({ text: `${total} matching entries in ${collection}` });
142
+ spinner.stop();
143
+ console.log(total);
144
+ return;
145
+ }
146
+ handleResponseFormatOptions(opts, res);
147
+ const total = (_f = (_e = (_d = res.meta) === null || _d === void 0 ? void 0 : _d.pageInfo) === null || _e === void 0 ? void 0 : _e.totalEntries) !== null && _f !== void 0 ? _f : 0;
148
+ const page = (_j = (_h = (_g = res.meta) === null || _g === void 0 ? void 0 : _g.pageInfo) === null || _h === void 0 ? void 0 : _h.page) !== null && _j !== void 0 ? _j : 1;
149
+ const totalPages = (_m = (_l = (_k = res.meta) === null || _k === void 0 ? void 0 : _k.pageInfo) === null || _l === void 0 ? void 0 : _l.totalPages) !== null && _m !== void 0 ? _m : 1;
150
+ spinner.success({
151
+ text: `Found ${total} entries in ${collection} (page ${page}/${totalPages})`,
152
+ });
153
+ spinner.stop();
154
+ localStorage.setItem("lastModule", resolved.module);
155
+ localStorage.setItem("lastCollection", resolved.collection);
156
+ if (opts.save)
157
+ await saveFile("search", opts, res);
158
+ }
159
+ async function handleFindOneSearch(resolved, searchQuery, opts) {
160
+ const collection = resolved.collection;
161
+ spinner.start({ text: `Finding one in ${collection}...`, color: "magenta" });
162
+ const params = {};
163
+ if (searchQuery)
164
+ params.q = searchQuery;
165
+ if (opts.field)
166
+ params.field = opts.field;
167
+ if (opts.fields)
168
+ params.fields = sanitizeFields(opts.fields).join(",");
169
+ const contextParams = getContextFilterParams(collection);
170
+ Object.assign(params, contextParams);
171
+ applyFilters(params, opts.filter);
172
+ const res = await searchOne(collection, params);
173
+ handleResponseFormatOptions(opts, res);
174
+ spinner.success({
175
+ text: `Found match in ${collection}`,
176
+ });
177
+ spinner.stop();
178
+ localStorage.setItem("lastModule", resolved.module);
179
+ localStorage.setItem("lastCollection", resolved.collection);
180
+ if (opts.save)
181
+ await saveFile("search-one", opts, res);
182
+ }
183
+ async function handleLegacySearch(resolved, searchQuery, opts) {
184
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
185
+ const collection = resolved.collection;
186
+ spinner.start({ text: `Searching ${collection} (legacy)...`, color: "magenta" });
187
+ const params = {
188
+ page: opts.page,
189
+ limit: opts.limit,
190
+ sort: opts.sort,
191
+ responseFormat: opts.responseFormat,
192
+ verbosity: opts.verbosity,
193
+ hideMeta: opts.hideMeta,
194
+ };
195
+ const contextParams = getContextFilterParams(collection);
196
+ Object.assign(params, contextParams);
197
+ if (searchQuery)
198
+ params.q = searchQuery;
199
+ if (opts.fields)
200
+ params.fields = sanitizeFields(opts.fields).join(",");
201
+ applyFilters(params, opts.filter);
202
+ const res = await apiCallHandler(resolved.module, collection, "get", null, params);
203
+ if (opts.count) {
204
+ 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;
205
+ spinner.success({ text: `${total} matching entries in ${collection}` });
206
+ spinner.stop();
207
+ console.log(total);
208
+ return;
209
+ }
210
+ handleResponseFormatOptions(opts, res);
211
+ const total = (_f = (_e = (_d = res.meta) === null || _d === void 0 ? void 0 : _d.pageInfo) === null || _e === void 0 ? void 0 : _e.totalEntries) !== null && _f !== void 0 ? _f : 0;
212
+ const page = (_j = (_h = (_g = res.meta) === null || _g === void 0 ? void 0 : _g.pageInfo) === null || _h === void 0 ? void 0 : _h.page) !== null && _j !== void 0 ? _j : 1;
213
+ const totalPages = (_m = (_l = (_k = res.meta) === null || _k === void 0 ? void 0 : _k.pageInfo) === null || _l === void 0 ? void 0 : _l.totalPages) !== null && _m !== void 0 ? _m : 1;
214
+ spinner.success({
215
+ text: `Found ${total} entries in ${collection} (page ${page}/${totalPages})`,
216
+ });
217
+ spinner.stop();
218
+ localStorage.setItem("lastModule", resolved.module);
219
+ localStorage.setItem("lastCollection", resolved.collection);
220
+ if (opts.save)
221
+ await saveFile("search", opts, res);
222
+ }
223
+ function applyFilters(params, filter) {
224
+ if (!filter)
225
+ return;
226
+ const filters = filter.split(",");
227
+ for (const f of filters) {
228
+ const match = f.match(/^(\w+)(!=|>=|<=|>|<|=)(.+)$/);
229
+ if (match) {
230
+ const [, key, op, value] = match;
231
+ if (op === "=") {
232
+ params[key] = value;
233
+ }
234
+ else if (op === "!=") {
235
+ params[`${key}[ne]`] = value;
236
+ }
237
+ else if (op === ">") {
238
+ params[`${key}[gt]`] = value;
239
+ }
240
+ else if (op === "<") {
241
+ params[`${key}[lt]`] = value;
242
+ }
243
+ else if (op === ">=") {
244
+ params[`${key}[gte]`] = value;
245
+ }
246
+ else if (op === "<=") {
247
+ params[`${key}[lte]`] = value;
248
+ }
249
+ }
250
+ }
251
+ }
@@ -0,0 +1,166 @@
1
+ import chalk from "chalk";
2
+ import { spinner } from "../../index.js";
3
+ import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
4
+ import { cliError } from "../helpers/cliError.js";
5
+ import { execSync } from "child_process";
6
+ function runCommand(cmd) {
7
+ try {
8
+ return execSync(cmd, { encoding: "utf-8", timeout: 30000 }).trim();
9
+ }
10
+ catch (_a) {
11
+ return "";
12
+ }
13
+ }
14
+ function detectRuntime() {
15
+ // Check for docker compose
16
+ const composeResult = runCommand("docker compose version 2>&1");
17
+ if (composeResult.includes("Docker Compose"))
18
+ return "compose";
19
+ // Check for kubectl
20
+ const kubectlResult = runCommand("kubectl version --client 2>&1");
21
+ if (kubectlResult.includes("Client Version"))
22
+ return "kubernetes";
23
+ return "unknown";
24
+ }
25
+ function getRunningComposeServices() {
26
+ const output = runCommand('docker compose ps --format "{{.Name}}" 2>&1');
27
+ if (!output)
28
+ return [];
29
+ return output.split("\n").filter(Boolean);
30
+ }
31
+ function getComposeImageVersions() {
32
+ const versions = new Map();
33
+ const output = runCommand('docker compose ps --format "{{.Name}}|{{.Image}}" 2>&1');
34
+ if (!output)
35
+ return versions;
36
+ for (const line of output.split("\n")) {
37
+ const [name, image] = line.split("|");
38
+ if (name && image) {
39
+ const tag = image.split(":").pop() || "unknown";
40
+ versions.set(name, tag);
41
+ }
42
+ }
43
+ return versions;
44
+ }
45
+ export function createSelfHostUpdateCommand(program) {
46
+ program
47
+ .command("self-host-update")
48
+ .alias("shu")
49
+ .description("Check for and apply updates to a self-hosted aYOUne deployment")
50
+ .addHelpText("after", `
51
+ Examples:
52
+ ay self-host-update Check for available updates
53
+ ay self-host-update --apply Pull new images and restart services
54
+ ay self-host-update --service crm Check updates for a specific service`)
55
+ .option("--apply", "Apply available updates (pull new images, restart services)")
56
+ .option("--service <name>", "Update only a specific service")
57
+ .action(async (options) => {
58
+ try {
59
+ const opts = { ...program.opts(), ...options };
60
+ const runtime = detectRuntime();
61
+ if (runtime === "unknown") {
62
+ cliError("Could not detect Docker Compose or Kubernetes. Ensure docker or kubectl is installed and accessible.", EXIT_GENERAL_ERROR);
63
+ }
64
+ console.log(chalk.cyan.bold("\n aYOUne Self-Hosted Update\n"));
65
+ console.log(chalk.dim(` Runtime: ${runtime === "compose" ? "Docker Compose" : "Kubernetes"}\n`));
66
+ if (runtime === "compose") {
67
+ await handleComposeUpdate(opts);
68
+ }
69
+ else {
70
+ await handleKubernetesUpdate(opts);
71
+ }
72
+ }
73
+ catch (e) {
74
+ cliError(e.message || "Update check failed", EXIT_GENERAL_ERROR);
75
+ }
76
+ });
77
+ }
78
+ async function handleComposeUpdate(opts) {
79
+ const services = getRunningComposeServices();
80
+ if (services.length === 0) {
81
+ console.log(chalk.yellow(" No running aYOUne services found."));
82
+ console.log(chalk.dim(" Start services first: docker compose --profile core up -d\n"));
83
+ return;
84
+ }
85
+ const versions = getComposeImageVersions();
86
+ console.log(chalk.white(` Running services: ${services.length}\n`));
87
+ for (const [name, version] of versions) {
88
+ if (opts.service && !name.includes(opts.service))
89
+ continue;
90
+ console.log(` ${chalk.dim("●")} ${name} ${chalk.dim(`(${version})`)}`);
91
+ }
92
+ console.log("");
93
+ if (opts.apply) {
94
+ spinner.start({ text: "Pulling latest images...", color: "cyan" });
95
+ const pullTarget = opts.service ? opts.service : "";
96
+ const pullResult = runCommand(`docker compose pull ${pullTarget} 2>&1`);
97
+ if (!pullResult) {
98
+ spinner.error({ text: "Failed to pull images" });
99
+ return;
100
+ }
101
+ spinner.success({ text: "Images pulled successfully" });
102
+ spinner.start({ text: "Restarting services...", color: "cyan" });
103
+ const upTarget = opts.service ? opts.service : "";
104
+ runCommand(`docker compose up -d ${upTarget} 2>&1`);
105
+ spinner.success({ text: "Services restarted" });
106
+ spinner.stop();
107
+ console.log(chalk.green("\n Update complete!"));
108
+ console.log(chalk.dim(" Run 'ay status' to verify all services are healthy.\n"));
109
+ }
110
+ else {
111
+ spinner.start({ text: "Checking for new images...", color: "cyan" });
112
+ const pullTarget = opts.service ? opts.service : "";
113
+ const dryRunResult = runCommand(`docker compose pull --dry-run ${pullTarget} 2>&1`);
114
+ spinner.stop();
115
+ if (dryRunResult) {
116
+ console.log(chalk.dim(" Image check output:"));
117
+ for (const line of dryRunResult.split("\n")) {
118
+ console.log(chalk.dim(` ${line}`));
119
+ }
120
+ }
121
+ console.log(chalk.cyan("\n To apply updates, run:"));
122
+ console.log(chalk.dim(" ay self-host-update --apply\n"));
123
+ }
124
+ }
125
+ async function handleKubernetesUpdate(opts) {
126
+ // Check for Helm releases
127
+ const helmList = runCommand("helm list -n ayoune --output json 2>&1");
128
+ if (!helmList || helmList.startsWith("[")) {
129
+ try {
130
+ const releases = JSON.parse(helmList || "[]");
131
+ if (releases.length === 0) {
132
+ console.log(chalk.yellow(" No Helm releases found in 'ayoune' namespace."));
133
+ console.log(chalk.dim(" Install: helm install ayoune tolinax/ayoune -f values.yaml\n"));
134
+ return;
135
+ }
136
+ for (const release of releases) {
137
+ console.log(` ${chalk.white(release.name)} ${chalk.dim(`v${release.app_version} (chart: ${release.chart})`)}`);
138
+ console.log(` ${chalk.dim(`Status: ${release.status} | Updated: ${release.updated}`)}`);
139
+ }
140
+ }
141
+ catch (_a) {
142
+ console.log(chalk.yellow(" Could not parse Helm releases."));
143
+ }
144
+ }
145
+ console.log("");
146
+ if (opts.apply) {
147
+ spinner.start({ text: "Updating Helm repo...", color: "cyan" });
148
+ runCommand("helm repo update 2>&1");
149
+ spinner.success({ text: "Helm repo updated" });
150
+ spinner.start({ text: "Upgrading release...", color: "cyan" });
151
+ const upgradeResult = runCommand("helm upgrade ayoune tolinax/ayoune -n ayoune --reuse-values 2>&1");
152
+ if (upgradeResult.includes("Error")) {
153
+ spinner.error({ text: `Helm upgrade failed: ${upgradeResult}` });
154
+ return;
155
+ }
156
+ spinner.success({ text: "Helm release upgraded" });
157
+ spinner.stop();
158
+ console.log(chalk.green("\n Update complete!"));
159
+ console.log(chalk.dim(" Run 'ay status' to verify all services are healthy.\n"));
160
+ }
161
+ else {
162
+ console.log(chalk.cyan(" To apply updates, run:"));
163
+ console.log(chalk.dim(" ay self-host-update --apply"));
164
+ console.log(chalk.dim(" # or manually: helm upgrade ayoune tolinax/ayoune -n ayoune --reuse-values\n"));
165
+ }
166
+ }