@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,202 @@
1
+ import chalk from "chalk";
2
+ import inquirer from "inquirer";
3
+ import { loadConfig, saveConfig } from "../helpers/configLoader.js";
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 SETTABLE_KEYS = {
8
+ responseFormat: { type: "string", choices: ["json", "csv", "yaml", "table"] },
9
+ verbosity: { type: "string", choices: ["default", "extended", "minimal"] },
10
+ outPath: { type: "string" },
11
+ hideMeta: { type: "boolean" },
12
+ quiet: { type: "boolean" },
13
+ force: { type: "boolean" },
14
+ dryRun: { type: "boolean" },
15
+ };
16
+ function parseBoolean(value) {
17
+ const truthy = ["true", "1", "yes", "on"];
18
+ const falsy = ["false", "0", "no", "off"];
19
+ if (truthy.includes(value.toLowerCase()))
20
+ return true;
21
+ if (falsy.includes(value.toLowerCase()))
22
+ return false;
23
+ return null;
24
+ }
25
+ function displayDefaults(defaults) {
26
+ const entries = Object.entries(SETTABLE_KEYS);
27
+ const maxLabel = Math.max(...entries.map(([k]) => k.length));
28
+ console.log();
29
+ console.log(chalk.bold(" Default Options"));
30
+ console.log(chalk.dim(` ${"─".repeat(44)}`));
31
+ for (const [key] of entries) {
32
+ const value = defaults[key];
33
+ const display = value !== undefined && value !== null ? String(value) : chalk.dim("(not set)");
34
+ console.log(` ${chalk.dim(key.padEnd(maxLabel + 2))} ${chalk.white(display)}`);
35
+ }
36
+ console.log();
37
+ }
38
+ export function createConfigCommand(program) {
39
+ const config = program
40
+ .command("config")
41
+ .description("Manage default CLI options")
42
+ .addHelpText("after", `
43
+ Examples:
44
+ ay config Interactive defaults menu
45
+ ay config set responseFormat yaml Set default response format
46
+ ay config get responseFormat Show current response format default
47
+ ay config list Show all defaults
48
+ ay config reset Clear all defaults`)
49
+ .action(async () => {
50
+ var _a, _b, _c, _d, _e;
51
+ try {
52
+ const cfg = loadConfig();
53
+ const defaults = (_a = cfg.defaults) !== null && _a !== void 0 ? _a : {};
54
+ const answers = await inquirer.prompt([
55
+ {
56
+ type: "list",
57
+ name: "responseFormat",
58
+ message: "Default response format",
59
+ choices: ["json", "csv", "yaml", "table"],
60
+ default: defaults.responseFormat || "json",
61
+ },
62
+ {
63
+ type: "list",
64
+ name: "verbosity",
65
+ message: "Default verbosity level",
66
+ choices: ["default", "extended", "minimal"],
67
+ default: defaults.verbosity || "default",
68
+ },
69
+ {
70
+ type: "confirm",
71
+ name: "hideMeta",
72
+ message: "Hide meta information by default?",
73
+ default: (_b = defaults.hideMeta) !== null && _b !== void 0 ? _b : false,
74
+ },
75
+ {
76
+ type: "confirm",
77
+ name: "quiet",
78
+ message: "Quiet mode by default?",
79
+ default: (_c = defaults.quiet) !== null && _c !== void 0 ? _c : false,
80
+ },
81
+ {
82
+ type: "confirm",
83
+ name: "force",
84
+ message: "Skip confirmation prompts by default?",
85
+ default: (_d = defaults.force) !== null && _d !== void 0 ? _d : false,
86
+ },
87
+ {
88
+ type: "confirm",
89
+ name: "dryRun",
90
+ message: "Dry-run mode by default?",
91
+ default: (_e = defaults.dryRun) !== null && _e !== void 0 ? _e : false,
92
+ },
93
+ {
94
+ type: "input",
95
+ name: "outPath",
96
+ message: "Default output path (leave blank to keep current)",
97
+ default: defaults.outPath || "",
98
+ },
99
+ ]);
100
+ const newDefaults = {
101
+ responseFormat: answers.responseFormat,
102
+ verbosity: answers.verbosity,
103
+ hideMeta: answers.hideMeta,
104
+ quiet: answers.quiet,
105
+ force: answers.force,
106
+ dryRun: answers.dryRun,
107
+ };
108
+ if (answers.outPath) {
109
+ newDefaults.outPath = answers.outPath;
110
+ }
111
+ cfg.defaults = newDefaults;
112
+ saveConfig(cfg);
113
+ displayDefaults(newDefaults);
114
+ spinner.success({ text: "Defaults saved" });
115
+ }
116
+ catch (e) {
117
+ cliError(e.message || "Failed to save config", EXIT_GENERAL_ERROR);
118
+ }
119
+ });
120
+ config
121
+ .command("set <key> <value>")
122
+ .description("Set a default option value")
123
+ .action((key, value) => {
124
+ var _a;
125
+ try {
126
+ const meta = SETTABLE_KEYS[key];
127
+ if (!meta) {
128
+ cliError(`Unknown config key '${key}'. Valid keys: ${Object.keys(SETTABLE_KEYS).join(", ")}`, EXIT_MISUSE);
129
+ }
130
+ let parsed = value;
131
+ if (meta.type === "boolean") {
132
+ parsed = parseBoolean(value);
133
+ if (parsed === null) {
134
+ cliError(`Invalid boolean value '${value}'. Use true/false, yes/no, 1/0, on/off`, EXIT_MISUSE);
135
+ }
136
+ }
137
+ else if (meta.choices && !meta.choices.includes(value)) {
138
+ cliError(`Invalid value '${value}' for ${key}. Choices: ${meta.choices.join(", ")}`, EXIT_MISUSE);
139
+ }
140
+ const cfg = loadConfig();
141
+ cfg.defaults = (_a = cfg.defaults) !== null && _a !== void 0 ? _a : {};
142
+ cfg.defaults[key] = parsed;
143
+ saveConfig(cfg);
144
+ spinner.success({ text: `${key} set to '${parsed}'` });
145
+ }
146
+ catch (e) {
147
+ cliError(e.message || "Failed to save config", EXIT_GENERAL_ERROR);
148
+ }
149
+ });
150
+ config
151
+ .command("get <key>")
152
+ .description("Show a default option value")
153
+ .action((key) => {
154
+ var _a;
155
+ if (!SETTABLE_KEYS[key]) {
156
+ cliError(`Unknown config key '${key}'. Valid keys: ${Object.keys(SETTABLE_KEYS).join(", ")}`, EXIT_MISUSE);
157
+ }
158
+ const cfg = loadConfig();
159
+ const value = (_a = cfg.defaults) === null || _a === void 0 ? void 0 : _a[key];
160
+ if (value !== undefined && value !== null) {
161
+ console.log(value);
162
+ }
163
+ else {
164
+ console.log(chalk.dim("(not set)"));
165
+ }
166
+ });
167
+ config
168
+ .command("list")
169
+ .description("Show all default options")
170
+ .action(() => {
171
+ var _a;
172
+ const cfg = loadConfig();
173
+ displayDefaults((_a = cfg.defaults) !== null && _a !== void 0 ? _a : {});
174
+ });
175
+ config
176
+ .command("reset")
177
+ .description("Clear all default options")
178
+ .action(async (options) => {
179
+ try {
180
+ const opts = { ...program.opts(), ...options };
181
+ if (!opts.force) {
182
+ const { confirm } = await inquirer.prompt([
183
+ {
184
+ type: "confirm",
185
+ name: "confirm",
186
+ message: "Reset all defaults to factory settings?",
187
+ default: false,
188
+ },
189
+ ]);
190
+ if (!confirm)
191
+ return;
192
+ }
193
+ const cfg = loadConfig();
194
+ cfg.defaults = {};
195
+ saveConfig(cfg);
196
+ spinner.success({ text: "All defaults have been reset" });
197
+ }
198
+ catch (e) {
199
+ cliError(e.message || "Failed to reset config", EXIT_GENERAL_ERROR);
200
+ }
201
+ });
202
+ }
@@ -0,0 +1,163 @@
1
+ import chalk from "chalk";
2
+ import { spinner } from "../../index.js";
3
+ import { contextSlots, getSlot, getSlotsByTier } from "../../data/contextSlots.js";
4
+ import { getActiveContext, setContextEntry, unsetContextEntry, clearAllContext, } from "../helpers/contextStore.js";
5
+ import { resolveEntity, validateHierarchy, getDependentSlots } from "../helpers/contextResolver.js";
6
+ import { EXIT_MISUSE } from "../exitCodes.js";
7
+ import { cliError } from "../helpers/cliError.js";
8
+ export function createContextCommand(program) {
9
+ const ctx = program
10
+ .command("context")
11
+ .alias("ctx")
12
+ .description("Manage active entity context for scoped commands")
13
+ .addHelpText("after", `
14
+ Examples:
15
+ ay context Show active context
16
+ ay context set project "Website" Set project context by name
17
+ ay context set sprint "Sprint 14" Set sprint (auto-filtered by project)
18
+ ay context unset project Unset project (cascades to children)
19
+ ay context clear Clear all context
20
+ ay context list List all available context slots`);
21
+ // Default action: show active context
22
+ ctx.action(() => {
23
+ showContext();
24
+ });
25
+ // ay context show
26
+ ctx
27
+ .command("show")
28
+ .description("Show active context")
29
+ .action(() => {
30
+ showContext();
31
+ });
32
+ // ay context set <slot> <name>
33
+ ctx
34
+ .command("set <slot> <nameOrId...>")
35
+ .description("Set active context for a slot")
36
+ .action(async (slot, nameOrIdParts, options) => {
37
+ const opts = { ...program.opts(), ...options };
38
+ const nameOrId = nameOrIdParts.join(" ");
39
+ const slotDef = getSlot(slot);
40
+ if (!slotDef) {
41
+ const available = contextSlots.map((s) => s.slot).join(", ");
42
+ cliError(`Unknown context slot: "${slot}". Available: ${available}`, EXIT_MISUSE);
43
+ return;
44
+ }
45
+ // Hierarchy warning
46
+ const warning = validateHierarchy(slot);
47
+ if (warning) {
48
+ console.error(chalk.yellow(` ${warning}`));
49
+ }
50
+ spinner.start({ text: `Resolving ${slot} "${nameOrId}"...`, color: "magenta" });
51
+ const matches = await resolveEntity(slot, nameOrId);
52
+ if (matches.length === 0) {
53
+ spinner.error({ text: `No ${slot} found matching "${nameOrId}"` });
54
+ spinner.stop();
55
+ return;
56
+ }
57
+ let selected = matches[0];
58
+ // If multiple matches and interactive TTY, let user pick
59
+ if (matches.length > 1 && process.stdin.isTTY) {
60
+ spinner.stop();
61
+ console.error(chalk.dim(` Multiple matches for "${nameOrId}":`));
62
+ matches.forEach((m, i) => {
63
+ console.error(` ${chalk.cyan(`[${i + 1}]`)} ${m.name} ${chalk.dim(`(${m.id})`)}`);
64
+ });
65
+ console.error(chalk.dim(` Using first match. Use an ObjectId for exact selection.\n`));
66
+ selected = matches[0];
67
+ }
68
+ else if (matches.length > 1) {
69
+ spinner.stop();
70
+ }
71
+ setContextEntry(slot, {
72
+ id: selected.id,
73
+ name: selected.name,
74
+ collection: slotDef.collection,
75
+ module: slotDef.module,
76
+ setAt: new Date().toISOString(),
77
+ });
78
+ spinner.success({ text: `Context ${slot} set to "${selected.name}" (${selected.id})` });
79
+ spinner.stop();
80
+ });
81
+ // ay context unset <slot>
82
+ ctx
83
+ .command("unset <slot>")
84
+ .description("Remove context for a slot (cascades to children)")
85
+ .action((slot) => {
86
+ const slotDef = getSlot(slot);
87
+ if (!slotDef) {
88
+ cliError(`Unknown context slot: "${slot}"`, EXIT_MISUSE);
89
+ return;
90
+ }
91
+ // Cascade unset children
92
+ const children = getDependentSlots(slot);
93
+ const unset = [slot];
94
+ for (const child of children) {
95
+ const entry = getActiveContext()[child];
96
+ if (entry) {
97
+ unsetContextEntry(child);
98
+ unset.push(child);
99
+ }
100
+ }
101
+ unsetContextEntry(slot);
102
+ if (unset.length > 1) {
103
+ console.error(chalk.dim(` Cascade: also unset ${unset.slice(1).join(", ")}`));
104
+ }
105
+ spinner.success({ text: `Context "${slot}" unset` });
106
+ spinner.stop();
107
+ });
108
+ // ay context clear
109
+ ctx
110
+ .command("clear")
111
+ .description("Clear all active context")
112
+ .option("--force", "Skip confirmation")
113
+ .action((options) => {
114
+ const opts = { ...program.opts(), ...options };
115
+ const active = getActiveContext();
116
+ const count = Object.keys(active).length;
117
+ if (count === 0) {
118
+ console.error(chalk.dim(" No active context to clear."));
119
+ return;
120
+ }
121
+ clearAllContext();
122
+ spinner.success({ text: `Cleared ${count} context entries` });
123
+ spinner.stop();
124
+ });
125
+ // ay context list
126
+ ctx
127
+ .command("list")
128
+ .description("List all available context slots")
129
+ .action(() => {
130
+ const active = getActiveContext();
131
+ const tierLabels = {
132
+ 1: "Core",
133
+ 2: "Domain",
134
+ 3: "AI / Automation",
135
+ };
136
+ for (const tier of [1, 2, 3]) {
137
+ const slots = getSlotsByTier(tier);
138
+ console.log(chalk.bold(`\n ${tierLabels[tier]}`));
139
+ for (const s of slots) {
140
+ const entry = active[s.slot];
141
+ const status = entry
142
+ ? chalk.green(`= "${entry.name}" (${entry.id})`)
143
+ : chalk.dim("not set");
144
+ const parent = s.parent ? chalk.dim(` [parent: ${s.parent}]`) : "";
145
+ console.log(` ${chalk.cyan(s.slot.padEnd(14))} ${s.collection.padEnd(22)} ${status}${parent}`);
146
+ }
147
+ }
148
+ console.log();
149
+ });
150
+ }
151
+ function showContext() {
152
+ const active = getActiveContext();
153
+ const entries = Object.entries(active);
154
+ if (entries.length === 0) {
155
+ console.log(chalk.dim(" No active context. Use `ay context set <slot> <name>` to set one."));
156
+ return;
157
+ }
158
+ console.log(chalk.bold("\n Active Context"));
159
+ for (const [slot, entry] of entries) {
160
+ console.log(` ${chalk.cyan(slot.padEnd(14))} ${entry.name} ${chalk.dim(`(${entry.collection}, ${entry.id})`)}`);
161
+ }
162
+ console.log();
163
+ }
@@ -0,0 +1,36 @@
1
+ import { Argument } from "commander";
2
+ import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
3
+ import { handleCopySingleOperation } from "../operations/handleCopySingleOperation.js";
4
+ import { localStorage } from "../helpers/localStorage.js";
5
+ import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
6
+ import { cliError } from "../helpers/cliError.js";
7
+ export function createCopyCommand(program) {
8
+ program
9
+ .command("copy")
10
+ .addArgument(new Argument("[collection]", "The collection to use").default(localStorage.getItem("lastCollection"), `The last used collection (${localStorage.getItem("lastCollection")})`))
11
+ .addArgument(new Argument("[id]", "The ID of the entry to copy").default(localStorage.getItem("lastId"), `The last used id (${localStorage.getItem("lastId")})`))
12
+ .alias("cp")
13
+ .description("Duplicate an entry by ID in a collection")
14
+ .addHelpText("after", `
15
+ Examples:
16
+ ay copy contacts 64a1b2c3d4e5 Copy a contact entry
17
+ ay cp Copy last used entry`)
18
+ .action(async (collection, id, options) => {
19
+ try {
20
+ if (!collection) {
21
+ cliError("Missing required argument: collection. Run a list or get command first, or provide it explicitly.", EXIT_MISUSE);
22
+ }
23
+ if (!id) {
24
+ cliError("Missing required argument: id. Provide an entry ID explicitly.", EXIT_MISUSE);
25
+ }
26
+ const opts = { ...program.opts(), ...options };
27
+ const module = getModuleFromCollection(collection);
28
+ await handleCopySingleOperation(module.module, collection, id, {
29
+ ...opts,
30
+ });
31
+ }
32
+ catch (e) {
33
+ cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
34
+ }
35
+ });
36
+ }
@@ -0,0 +1,47 @@
1
+ import { Argument } from "commander";
2
+ import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
3
+ import { promptCollectionWithModule } from "../prompts/promptCollectionWithModule.js";
4
+ import { handleCreateSingleOperation } from "../operations/handleCreateSingleOperation.js";
5
+ import { promptName } from "../prompts/promptName.js";
6
+ import { localStorage } from "../helpers/localStorage.js";
7
+ import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
8
+ import { cliError } from "../helpers/cliError.js";
9
+ export function createCreateCommand(program) {
10
+ program
11
+ .command("create")
12
+ .alias("c")
13
+ .addArgument(new Argument("[collection]", "The collection to use").default(localStorage.getItem("lastCollection"), `The last used collection (${localStorage.getItem("lastCollection")})`))
14
+ .addArgument(new Argument("[name]", "The name of the new item"))
15
+ .description("Create a new entry in a collection")
16
+ .addHelpText("after", `
17
+ Examples:
18
+ ay create contacts "John Doe" Create a new contact
19
+ ay c products "Widget" Create a new product using alias`)
20
+ .action(async (collection, name, options) => {
21
+ try {
22
+ const opts = { ...program.opts(), ...options };
23
+ if (!collection) {
24
+ if (!process.stdin.isTTY) {
25
+ cliError("Missing required argument: collection", EXIT_MISUSE);
26
+ }
27
+ collection = await promptCollectionWithModule();
28
+ }
29
+ const module = getModuleFromCollection(collection);
30
+ let entryName = name;
31
+ if (!entryName) {
32
+ if (!process.stdin.isTTY) {
33
+ cliError("Missing required argument: name", EXIT_MISUSE);
34
+ }
35
+ entryName = await promptName();
36
+ }
37
+ await handleCreateSingleOperation(module.module, collection, entryName, {
38
+ ...opts,
39
+ });
40
+ localStorage.setItem("lastModule", module.module);
41
+ localStorage.setItem("lastCollection", collection);
42
+ }
43
+ catch (e) {
44
+ cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
45
+ }
46
+ });
47
+ }
@@ -0,0 +1,96 @@
1
+ import { Argument } from "commander";
2
+ import chalk from "chalk";
3
+ import { getModuleFromCollection } from "../models/getModuleFromCollection.js";
4
+ import { handleDeleteSingleOperation } from "../operations/handleDeleteSingleOperation.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
+ import { cliError } from "../helpers/cliError.js";
9
+ export function createDeleteCommand(program) {
10
+ program
11
+ .command("delete")
12
+ .alias("rm")
13
+ .description("Delete one or more entries from a collection")
14
+ .addHelpText("after", `
15
+ Examples:
16
+ ay delete contacts 64a1b2c3d4e5 Delete a single contact
17
+ ay rm contacts 64a1b2c3d4e5 --force Skip confirmation
18
+ ay delete contacts id1,id2,id3 Delete multiple entries
19
+ echo "id1,id2" | ay delete contacts --ids-stdin Read IDs from stdin`)
20
+ .addArgument(new Argument("[collection]", "The collection to use").default(localStorage.getItem("lastCollection"), `The last used collection (${localStorage.getItem("lastCollection")})`))
21
+ .addArgument(new Argument("[ids]", "Comma-separated ID(s) to delete").default(localStorage.getItem("lastId"), `The last used id (${localStorage.getItem("lastId")})`))
22
+ .option("--ids-stdin", "Read IDs from stdin (one per line or comma-separated)")
23
+ .action(async (collection, ids, options) => {
24
+ try {
25
+ if (!collection) {
26
+ cliError("Missing required argument: collection. Run a list or get command first, or provide it explicitly.", EXIT_MISUSE);
27
+ }
28
+ const opts = { ...program.opts(), ...options };
29
+ const module = getModuleFromCollection(collection);
30
+ // Collect IDs from argument and/or stdin
31
+ let idList = [];
32
+ if (ids) {
33
+ idList = ids.split(",").map((id) => id.trim()).filter(Boolean);
34
+ }
35
+ if (opts.idsStdin && !process.stdin.isTTY) {
36
+ const chunks = [];
37
+ for await (const chunk of process.stdin) {
38
+ chunks.push(chunk);
39
+ }
40
+ const stdinContent = Buffer.concat(chunks).toString("utf-8").trim();
41
+ if (stdinContent) {
42
+ const stdinIds = stdinContent
43
+ .split(/[,\n]/)
44
+ .map((id) => id.trim())
45
+ .filter(Boolean);
46
+ idList.push(...stdinIds);
47
+ }
48
+ }
49
+ if (idList.length === 0) {
50
+ cliError("No IDs provided. Pass IDs as argument or via --ids-stdin.", EXIT_MISUSE);
51
+ }
52
+ // Confirmation prompt (unless --force)
53
+ if (!opts.force && process.stdin.isTTY) {
54
+ const inquirer = (await import("inquirer")).default;
55
+ const { confirmed } = await inquirer.prompt([
56
+ {
57
+ type: "confirm",
58
+ name: "confirmed",
59
+ message: `Delete ${idList.length} entry/entries from ${collection}?`,
60
+ default: false,
61
+ },
62
+ ]);
63
+ if (!confirmed) {
64
+ spinner.stop();
65
+ console.error(chalk.yellow(" Aborted."));
66
+ return;
67
+ }
68
+ }
69
+ // Execute deletions
70
+ let successCount = 0;
71
+ let errorCount = 0;
72
+ for (const id of idList) {
73
+ try {
74
+ await handleDeleteSingleOperation(module.module, collection, id, opts);
75
+ successCount++;
76
+ }
77
+ catch (e) {
78
+ errorCount++;
79
+ spinner.error({ text: `Failed to delete ${id}: ${e.message}` });
80
+ }
81
+ }
82
+ if (idList.length > 1) {
83
+ spinner.success({ text: `Deleted ${successCount}/${idList.length} entries from ${collection}` });
84
+ spinner.stop();
85
+ }
86
+ localStorage.setItem("lastModule", module.module);
87
+ localStorage.setItem("lastCollection", collection);
88
+ if (errorCount > 0) {
89
+ process.exit(EXIT_GENERAL_ERROR);
90
+ }
91
+ }
92
+ catch (e) {
93
+ cliError(e.message || "An unexpected error occurred", EXIT_GENERAL_ERROR);
94
+ }
95
+ });
96
+ }