@intranefr/superbackend 1.7.7 → 1.7.9

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 (119) hide show
  1. package/.beads/.br_history/issues.20260314_212352_900045509.jsonl +0 -0
  2. package/.beads/.br_history/issues.20260314_212352_900045509.jsonl.meta.json +1 -0
  3. package/.beads/.br_history/issues.20260314_212353_087140743.jsonl +1 -0
  4. package/.beads/.br_history/issues.20260314_212353_087140743.jsonl.meta.json +1 -0
  5. package/.beads/.br_history/issues.20260314_212353_285881504.jsonl +2 -0
  6. package/.beads/.br_history/issues.20260314_212353_285881504.jsonl.meta.json +1 -0
  7. package/.beads/.br_history/issues.20260314_212353_473915419.jsonl +3 -0
  8. package/.beads/.br_history/issues.20260314_212353_473915419.jsonl.meta.json +1 -0
  9. package/.beads/.br_history/issues.20260314_212353_659476307.jsonl +4 -0
  10. package/.beads/.br_history/issues.20260314_212353_659476307.jsonl.meta.json +1 -0
  11. package/.beads/.br_history/issues.20260314_212353_869998925.jsonl +5 -0
  12. package/.beads/.br_history/issues.20260314_212353_869998925.jsonl.meta.json +1 -0
  13. package/.beads/.br_history/issues.20260314_212354_054785029.jsonl +6 -0
  14. package/.beads/.br_history/issues.20260314_212354_054785029.jsonl.meta.json +1 -0
  15. package/.beads/.br_history/issues.20260314_213336_175893691.jsonl +7 -0
  16. package/.beads/.br_history/issues.20260314_213336_175893691.jsonl.meta.json +1 -0
  17. package/.beads/.br_history/issues.20260314_213336_338509797.jsonl +7 -0
  18. package/.beads/.br_history/issues.20260314_213336_338509797.jsonl.meta.json +1 -0
  19. package/.beads/.br_history/issues.20260314_213336_515443192.jsonl +7 -0
  20. package/.beads/.br_history/issues.20260314_213336_515443192.jsonl.meta.json +1 -0
  21. package/.beads/.br_history/issues.20260314_213336_676417592.jsonl +7 -0
  22. package/.beads/.br_history/issues.20260314_213336_676417592.jsonl.meta.json +1 -0
  23. package/.beads/.br_history/issues.20260314_213336_839182422.jsonl +7 -0
  24. package/.beads/.br_history/issues.20260314_213336_839182422.jsonl.meta.json +1 -0
  25. package/.beads/.br_history/issues.20260314_213337_004349113.jsonl +7 -0
  26. package/.beads/.br_history/issues.20260314_213337_004349113.jsonl.meta.json +1 -0
  27. package/.beads/.br_history/issues.20260314_213337_179824080.jsonl +7 -0
  28. package/.beads/.br_history/issues.20260314_213337_179824080.jsonl.meta.json +1 -0
  29. package/.beads/.br_history/issues.20260314_213701_705075332.jsonl +7 -0
  30. package/.beads/.br_history/issues.20260314_213701_705075332.jsonl.meta.json +1 -0
  31. package/.beads/.br_history/issues.20260314_213706_783128702.jsonl +8 -0
  32. package/.beads/.br_history/issues.20260314_213706_783128702.jsonl.meta.json +1 -0
  33. package/.beads/config.yaml +4 -0
  34. package/.beads/issues.jsonl +8 -0
  35. package/.beads/metadata.json +4 -0
  36. package/.env.example +8 -0
  37. package/autochangelog/.env.example +36 -0
  38. package/autochangelog/README.md +412 -0
  39. package/autochangelog/config/database.js +27 -0
  40. package/autochangelog/package.json +47 -0
  41. package/autochangelog/public/landing.html +581 -0
  42. package/autochangelog/server.js +104 -0
  43. package/autochangelog/src/app.js +181 -0
  44. package/autochangelog/src/config/database.js +26 -0
  45. package/autochangelog/src/controllers/auth.js +488 -0
  46. package/autochangelog/src/controllers/changelog.js +682 -0
  47. package/autochangelog/src/controllers/project.js +580 -0
  48. package/autochangelog/src/controllers/repository.js +780 -0
  49. package/autochangelog/src/middleware/auth.js +386 -0
  50. package/autochangelog/src/models/Changelog.js +443 -0
  51. package/autochangelog/src/models/Project.js +226 -0
  52. package/autochangelog/src/models/Repository.js +366 -0
  53. package/autochangelog/src/models/User.js +223 -0
  54. package/autochangelog/src/routes/auth.routes.js +32 -0
  55. package/autochangelog/src/routes/changelog.routes.js +42 -0
  56. package/autochangelog/src/routes/github-auth.routes.js +102 -0
  57. package/autochangelog/src/routes/project.routes.js +50 -0
  58. package/autochangelog/src/routes/repository.routes.js +54 -0
  59. package/autochangelog/src/services/changelog.js +722 -0
  60. package/autochangelog/src/services/github.js +243 -0
  61. package/autochangelog/utils/logger.js +77 -0
  62. package/autochangelog/views/404.ejs +18 -0
  63. package/autochangelog/views/dashboard.ejs +596 -0
  64. package/autochangelog/views/index.ejs +231 -0
  65. package/autochangelog/views/layouts/main.ejs +44 -0
  66. package/autochangelog/views/login.ejs +104 -0
  67. package/autochangelog/views/partials/footer.ejs +20 -0
  68. package/autochangelog/views/partials/navbar.ejs +51 -0
  69. package/autochangelog/views/register.ejs +109 -0
  70. package/autochangelog-cli/README.md +266 -0
  71. package/autochangelog-cli/bin/autochangelog +120 -0
  72. package/autochangelog-cli/package.json +46 -0
  73. package/autochangelog-cli/src/cli/commands/auth.js +291 -0
  74. package/autochangelog-cli/src/cli/commands/changelog.js +619 -0
  75. package/autochangelog-cli/src/cli/commands/project.js +427 -0
  76. package/autochangelog-cli/src/cli/commands/repo.js +557 -0
  77. package/autochangelog-cli/src/cli/commands/stats.js +706 -0
  78. package/autochangelog-cli/src/cli/utils/config.js +277 -0
  79. package/autochangelog-cli/src/cli/utils/errors.js +307 -0
  80. package/autochangelog-cli/src/cli/utils/logger.js +75 -0
  81. package/autochangelog-cli/src/cli/utils/output.js +357 -0
  82. package/package.json +8 -3
  83. package/plugins/supercli/README.md +108 -0
  84. package/plugins/supercli/plugin.json +123 -0
  85. package/server.js +1 -1
  86. package/src/cli/api.js +380 -0
  87. package/src/cli/direct/agent-utils.js +61 -0
  88. package/src/cli/direct/cli-utils.js +112 -0
  89. package/src/cli/direct/data-seeding.js +307 -0
  90. package/src/cli/direct/db-admin.js +84 -0
  91. package/src/cli/direct/db-advanced.js +372 -0
  92. package/src/cli/direct/db-utils.js +558 -0
  93. package/src/cli/direct/help.js +195 -0
  94. package/src/cli/direct/migration.js +107 -0
  95. package/src/cli/direct/rbac-advanced.js +132 -0
  96. package/src/cli/direct/resources-additional.js +400 -0
  97. package/src/cli/direct/resources-cms-advanced.js +173 -0
  98. package/src/cli/direct/resources-cms.js +247 -0
  99. package/src/cli/direct/resources-core.js +253 -0
  100. package/src/cli/direct/resources-execution.js +367 -0
  101. package/src/cli/direct/resources-health.js +152 -0
  102. package/src/cli/direct/resources-integrations.js +182 -0
  103. package/src/cli/direct/resources-logs.js +204 -0
  104. package/src/cli/direct/resources-org-rbac.js +187 -0
  105. package/src/cli/direct/resources-system.js +236 -0
  106. package/src/cli/direct.js +556 -0
  107. package/src/controllers/admin.controller.js +4 -0
  108. package/src/controllers/auth.controller.js +148 -1
  109. package/src/controllers/waitingList.controller.js +130 -1
  110. package/src/models/RbacRole.js +1 -1
  111. package/src/models/User.js +39 -5
  112. package/src/routes/auth.routes.js +6 -0
  113. package/src/routes/waitingList.routes.js +12 -2
  114. package/src/routes/waitingListAdmin.routes.js +3 -0
  115. package/src/services/email.service.js +1 -0
  116. package/src/services/github.service.js +255 -0
  117. package/src/services/rateLimiter.service.js +29 -1
  118. package/src/services/waitingListJson.service.js +32 -3
  119. package/views/admin-waiting-list.ejs +386 -3
package/src/cli/api.js ADDED
@@ -0,0 +1,380 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Non-interactive CLI to interact with a SuperBackend instance via HTTP API
5
+ *
6
+ * Usage:
7
+ * npx @intranefr/superbackend api <endpoint> [options]
8
+ * node src/cli/api.js <endpoint> [options]
9
+ *
10
+ * Examples:
11
+ * # List agents
12
+ * node src/cli/api.js /api/admin/agents --admin-basic
13
+ *
14
+ * # Create a setting
15
+ * node src/cli/api.js /api/admin/settings/MY_KEY -X POST -d '{"value":"test"}' --admin-basic
16
+ *
17
+ * # Get user info (with JWT)
18
+ * node src/cli/api.js /api/auth/me --token YOUR_JWT_TOKEN
19
+ */
20
+
21
+ require('dotenv').config(process.env.MODE ? { path: `.env.${process.env.MODE}` } : {});
22
+
23
+ const axios = require('axios');
24
+ const path = require('path');
25
+
26
+ // Default configuration
27
+ const DEFAULT_BASE_URL = process.env.SUPERBACKEND_URL || 'http://localhost:3000';
28
+ const ADMIN_USERNAME = process.env.ADMIN_USERNAME || 'admin';
29
+ const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'admin';
30
+
31
+ // Colors for terminal output
32
+ const colors = {
33
+ reset: '\x1b[0m',
34
+ red: '\x1b[31m',
35
+ green: '\x1b[32m',
36
+ yellow: '\x1b[33m',
37
+ blue: '\x1b[34m',
38
+ magenta: '\x1b[35m',
39
+ cyan: '\x1b[36m',
40
+ gray: '\x1b[90m',
41
+ bold: '\x1b[1m',
42
+ };
43
+
44
+ function colorize(color, text) {
45
+ return `${colors[color]}${text}${colors.reset}`;
46
+ }
47
+
48
+ function printHelp() {
49
+ console.log(`
50
+ ${colorize('bold', 'SuperBackend API CLI')}
51
+
52
+ ${colorize('bold', 'Usage:')}
53
+ node src/cli/api.js <endpoint> [options]
54
+
55
+ ${colorize('bold', 'Arguments:')}
56
+ endpoint API endpoint path (e.g., /api/admin/agents)
57
+
58
+ ${colorize('bold', 'Options:')}
59
+ -X, --method METHOD HTTP method (GET, POST, PUT, DELETE, PATCH). Default: GET
60
+ -d, --data DATA Request body (JSON string or file path with @prefix)
61
+ -H, --header HEADER Custom header (format: "Key: Value")
62
+ -q, --query KEY=VAL Query parameter (can be repeated)
63
+ --base-url URL Base URL of SuperBackend instance. Default: ${DEFAULT_BASE_URL}
64
+ --token TOKEN JWT token for authentication
65
+ --admin-basic Use admin basic auth (from env or defaults)
66
+ --admin-session Use admin session auth (requires --cookie)
67
+ --cookie COOKIE Session cookie for authentication
68
+ --output FORMAT Output format: json, text, table. Default: json
69
+ --silent Only output response data (no status/colors)
70
+ --verbose Show request details
71
+ --timeout MS Request timeout in ms. Default: 30000
72
+ -h, --help Show this help message
73
+
74
+ ${colorize('bold', 'Examples:')}
75
+ ${colorize('gray', '# List agents with admin auth')}
76
+ node src/cli/api.js /api/admin/agents --admin-basic
77
+
78
+ ${colorize('gray', '# Create a global setting')}
79
+ node src/cli/api.js /api/admin/settings/MY_KEY -X POST \\
80
+ -d '{"value":"my-value","description":"My setting"}' --admin-basic
81
+
82
+ ${colorize('gray', '# Get user info with JWT token')}
83
+ node src/cli/api.js /api/auth/me --token YOUR_JWT_TOKEN
84
+
85
+ ${colorize('gray', '# List blog posts with query params')}
86
+ node src/cli/api.js /api/blog/posts -q status=published -q limit=10
87
+
88
+ ${colorize('gray', '# Upload JSON data from file')}
89
+ node src/cli/api.js /api/data -X POST -d @data.json --token TOKEN
90
+
91
+ ${colorize('bold', 'Environment Variables:')}
92
+ SUPERBACKEND_URL Base URL (default: http://localhost:3000)
93
+ ADMIN_USERNAME Admin username for basic auth (default: admin)
94
+ ADMIN_PASSWORD Admin password for basic auth (default: admin)
95
+ `);
96
+ }
97
+
98
+ function parseArgs(args) {
99
+ const options = {
100
+ endpoint: null,
101
+ method: 'GET',
102
+ data: null,
103
+ headers: {},
104
+ query: {},
105
+ baseUrl: DEFAULT_BASE_URL,
106
+ token: null,
107
+ adminBasic: false,
108
+ adminSession: false,
109
+ cookie: null,
110
+ output: 'json',
111
+ silent: false,
112
+ verbose: false,
113
+ timeout: 30000,
114
+ help: false,
115
+ };
116
+
117
+ let i = 0;
118
+ while (i < args.length) {
119
+ const arg = args[i];
120
+
121
+ if (!arg.startsWith('-') && !options.endpoint) {
122
+ options.endpoint = arg;
123
+ i++;
124
+ continue;
125
+ }
126
+
127
+ switch (arg) {
128
+ case '-X':
129
+ case '--method':
130
+ options.method = args[++i].toUpperCase();
131
+ break;
132
+ case '-d':
133
+ case '--data':
134
+ options.data = args[++i];
135
+ break;
136
+ case '-H':
137
+ case '--header':
138
+ const headerParts = args[++i].split(':');
139
+ if (headerParts.length >= 2) {
140
+ const key = headerParts[0].trim();
141
+ const value = headerParts.slice(1).join(':').trim();
142
+ options.headers[key] = value;
143
+ }
144
+ break;
145
+ case '-q':
146
+ case '--query':
147
+ const queryParts = args[++i].split('=');
148
+ if (queryParts.length === 2) {
149
+ options.query[queryParts[0]] = queryParts[1];
150
+ }
151
+ break;
152
+ case '--base-url':
153
+ options.baseUrl = args[++i].replace(/\/$/, '');
154
+ break;
155
+ case '--token':
156
+ options.token = args[++i];
157
+ break;
158
+ case '--admin-basic':
159
+ options.adminBasic = true;
160
+ break;
161
+ case '--admin-session':
162
+ options.adminSession = true;
163
+ break;
164
+ case '--cookie':
165
+ options.cookie = args[++i];
166
+ break;
167
+ case '--output':
168
+ options.output = args[++i].toLowerCase();
169
+ break;
170
+ case '--silent':
171
+ options.silent = true;
172
+ break;
173
+ case '--verbose':
174
+ options.verbose = true;
175
+ break;
176
+ case '--timeout':
177
+ options.timeout = parseInt(args[++i], 10);
178
+ break;
179
+ case '-h':
180
+ case '--help':
181
+ options.help = true;
182
+ break;
183
+ default:
184
+ if (arg.startsWith('-')) {
185
+ console.error(colorize('red', `Unknown option: ${arg}`));
186
+ process.exit(1);
187
+ }
188
+ }
189
+ i++;
190
+ }
191
+
192
+ return options;
193
+ }
194
+
195
+ async function loadData(dataArg) {
196
+ if (!dataArg) return null;
197
+
198
+ if (dataArg.startsWith('@')) {
199
+ const filePath = dataArg.slice(1);
200
+ const fs = require('fs');
201
+ const absolutePath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath);
202
+
203
+ if (!fs.existsSync(absolutePath)) {
204
+ throw new Error(`File not found: ${absolutePath}`);
205
+ }
206
+
207
+ const content = fs.readFileSync(absolutePath, 'utf-8');
208
+ try {
209
+ return JSON.parse(content);
210
+ } catch (e) {
211
+ return content;
212
+ }
213
+ }
214
+
215
+ try {
216
+ return JSON.parse(dataArg);
217
+ } catch (e) {
218
+ return dataArg;
219
+ }
220
+ }
221
+
222
+ function formatOutput(data, format, silent) {
223
+ if (format === 'text') {
224
+ if (typeof data === 'string') return data;
225
+ return JSON.stringify(data, null, 2);
226
+ }
227
+
228
+ if (format === 'table') {
229
+ if (Array.isArray(data) && data.length > 0) {
230
+ const keys = Object.keys(data[0]);
231
+ const header = keys.join(' | ');
232
+ const rows = data.map(row =>
233
+ keys.map(k => {
234
+ const val = row[k];
235
+ return typeof val === 'object' ? JSON.stringify(val) : String(val);
236
+ }).join(' | ')
237
+ );
238
+ return [header, ...rows].join('\n');
239
+ }
240
+ return JSON.stringify(data, null, 2);
241
+ }
242
+
243
+ return JSON.stringify(data, null, 2);
244
+ }
245
+
246
+ async function makeRequest(options) {
247
+ const {
248
+ endpoint,
249
+ method,
250
+ data,
251
+ headers,
252
+ query,
253
+ baseUrl,
254
+ token,
255
+ adminBasic,
256
+ adminSession,
257
+ cookie,
258
+ output,
259
+ silent,
260
+ verbose,
261
+ timeout,
262
+ } = options;
263
+
264
+ if (!endpoint) {
265
+ console.error(colorize('red', 'Error: Endpoint is required'));
266
+ printHelp();
267
+ process.exit(1);
268
+ }
269
+
270
+ // Build URL
271
+ const url = new URL(endpoint, baseUrl);
272
+ Object.entries(query).forEach(([key, value]) => {
273
+ url.searchParams.append(key, value);
274
+ });
275
+
276
+ // Prepare headers
277
+ const requestHeaders = { ...headers };
278
+
279
+ if (token) {
280
+ requestHeaders['Authorization'] = `Bearer ${token}`;
281
+ } else if (adminBasic) {
282
+ const credentials = Buffer.from(`${ADMIN_USERNAME}:${ADMIN_PASSWORD}`).toString('base64');
283
+ requestHeaders['Authorization'] = `Basic ${credentials}`;
284
+ } else if (adminSession && cookie) {
285
+ requestHeaders['Cookie'] = cookie;
286
+ }
287
+
288
+ // Load data if provided
289
+ let requestData = null;
290
+ if (data) {
291
+ requestData = await loadData(data);
292
+ if (typeof requestData === 'object') {
293
+ requestHeaders['Content-Type'] = 'application/json';
294
+ }
295
+ }
296
+
297
+ if (verbose && !silent) {
298
+ console.log(colorize('cyan', '\n--- Request ---'));
299
+ console.log(`${colorize('bold', 'URL:')} ${method} ${url.toString()}`);
300
+ console.log(`${colorize('bold', 'Headers:')} ${JSON.stringify(requestHeaders, null, 2)}`);
301
+ if (requestData) {
302
+ console.log(`${colorize('bold', 'Body:')} ${JSON.stringify(requestData, null, 2)}`);
303
+ }
304
+ console.log();
305
+ }
306
+
307
+ // Make request
308
+ const startTime = Date.now();
309
+
310
+ try {
311
+ const response = await axios({
312
+ method,
313
+ url: url.toString(),
314
+ headers: requestHeaders,
315
+ data: requestData,
316
+ timeout,
317
+ validateStatus: () => true, // Don't throw on error status codes
318
+ });
319
+
320
+ const duration = Date.now() - startTime;
321
+
322
+ if (verbose && !silent) {
323
+ console.log(colorize('cyan', '\n--- Response ---'));
324
+ console.log(`${colorize('bold', 'Status:')} ${response.status} ${response.statusText}`);
325
+ console.log(`${colorize('bold', 'Duration:')} ${duration}ms`);
326
+ console.log(`${colorize('bold', 'Headers:')} ${JSON.stringify(response.headers, null, 2)}`);
327
+ console.log();
328
+ }
329
+
330
+ // Output result
331
+ let outputData = response.data;
332
+ if (typeof outputData === 'string') {
333
+ try {
334
+ outputData = JSON.parse(outputData);
335
+ } catch (e) {
336
+ // Keep as string if not JSON
337
+ }
338
+ }
339
+
340
+ if (silent) {
341
+ console.log(formatOutput(outputData, output, silent));
342
+ } else {
343
+ const statusColor = response.status >= 200 && response.status < 300 ? 'green' :
344
+ response.status >= 400 ? 'red' : 'yellow';
345
+ console.log(colorize(statusColor, `\n✓ ${method} ${endpoint}`));
346
+ console.log(colorize('gray', `Status: ${response.status} ${response.statusText} (${duration}ms)`));
347
+ console.log();
348
+ console.log(formatOutput(outputData, output, silent));
349
+ }
350
+
351
+ // Exit with error code for failed requests
352
+ if (response.status >= 400) {
353
+ process.exit(1);
354
+ }
355
+
356
+ } catch (error) {
357
+ if (!silent) {
358
+ console.error(colorize('red', '\n✗ Request failed'));
359
+ console.error(colorize('gray', `Error: ${error.message}`));
360
+ if (error.response) {
361
+ console.error(colorize('gray', `Status: ${error.response.status}`));
362
+ console.error(colorize('gray', `Data: ${JSON.stringify(error.response.data)}`));
363
+ }
364
+ } else {
365
+ console.error(colorize('red', `Error: ${error.message}`));
366
+ }
367
+ process.exit(1);
368
+ }
369
+ }
370
+
371
+ // Main execution
372
+ const args = process.argv.slice(2);
373
+ const options = parseArgs(args);
374
+
375
+ if (options.help) {
376
+ printHelp();
377
+ process.exit(0);
378
+ }
379
+
380
+ makeRequest(options);
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Agent utilities: agent-stats, agent-sessions, clear-agent-sessions
5
+ */
6
+
7
+ const mongoose = require('mongoose');
8
+
9
+ const agentStats = {
10
+ async execute(options) {
11
+ const Agent = mongoose.model('Agent');
12
+ const AgentMessage = mongoose.model('AgentMessage');
13
+
14
+ const agents = await Agent.find().lean();
15
+ const stats = [];
16
+
17
+ for (const agent of agents) {
18
+ const messageCount = await AgentMessage.countDocuments({ agentId: agent._id });
19
+ stats.push({ agentId: agent._id, name: agent.name, model: agent.model, messageCount });
20
+ }
21
+
22
+ return { totalAgents: agents.length, agents: stats };
23
+ },
24
+ };
25
+
26
+ const agentSessions = {
27
+ async execute(options) {
28
+ const JsonConfig = mongoose.model('JsonConfig');
29
+
30
+ const limit = parseInt(options.value) || 50;
31
+ const sessions = await JsonConfig.find({ alias: { $regex: /^agent-session-/ } }).sort({ updatedAt: -1 }).limit(limit).lean();
32
+
33
+ return {
34
+ total: sessions.length,
35
+ sessions: sessions.map(s => ({
36
+ alias: s.alias,
37
+ id: JSON.parse(s.jsonRaw).id,
38
+ label: JSON.parse(s.jsonRaw).label,
39
+ updatedAt: s.updatedAt,
40
+ })),
41
+ };
42
+ },
43
+ };
44
+
45
+ const clearAgentSessions = {
46
+ async execute(options) {
47
+ const JsonConfig = mongoose.model('JsonConfig');
48
+
49
+ const days = parseInt(options.value) || 7;
50
+ const cutoffDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
51
+
52
+ const result = await JsonConfig.deleteMany({
53
+ alias: { $regex: /^agent-session-/ },
54
+ updatedAt: { $lt: cutoffDate },
55
+ });
56
+
57
+ return { deletedCount: result.deletedCount, olderThan: cutoffDate.toISOString() };
58
+ },
59
+ };
60
+
61
+ module.exports = { agentStats, agentSessions, clearAgentSessions };
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * CLI utilities for the direct CLI
5
+ * - Color helpers
6
+ * - Argument parsing
7
+ * - Output formatting
8
+ */
9
+
10
+ const colors = {
11
+ reset: '\x1b[0m',
12
+ red: '\x1b[31m',
13
+ green: '\x1b[32m',
14
+ yellow: '\x1b[33m',
15
+ blue: '\x1b[34m',
16
+ magenta: '\x1b[35m',
17
+ cyan: '\x1b[36m',
18
+ gray: '\x1b[90m',
19
+ bold: '\x1b[1m',
20
+ };
21
+
22
+ function colorize(color, text) {
23
+ return `${colors[color]}${text}${colors.reset}`;
24
+ }
25
+
26
+ function parseArgs(args) {
27
+ const options = {
28
+ resource: null,
29
+ command: null,
30
+ id: null,
31
+ name: null,
32
+ model: null,
33
+ key: null,
34
+ value: null,
35
+ description: null,
36
+ email: null,
37
+ password: null,
38
+ role: null,
39
+ alias: null,
40
+ json: null,
41
+ output: 'json',
42
+ quiet: false,
43
+ verbose: false,
44
+ yes: false,
45
+ help: false,
46
+ };
47
+
48
+ let i = 0;
49
+ while (i < args.length) {
50
+ const arg = args[i];
51
+
52
+ if (!arg.startsWith('-')) {
53
+ if (!options.resource) {
54
+ options.resource = arg;
55
+ } else if (!options.command) {
56
+ options.command = arg;
57
+ } else {
58
+ options.id = arg;
59
+ }
60
+ i++;
61
+ continue;
62
+ }
63
+
64
+ switch (arg) {
65
+ case '--name': options.name = args[++i]; break;
66
+ case '--model': options.model = args[++i]; break;
67
+ case '--key': options.key = args[++i]; break;
68
+ case '--value': options.value = args[++i]; break;
69
+ case '--description': options.description = args[++i]; break;
70
+ case '--email': options.email = args[++i]; break;
71
+ case '--password': options.password = args[++i]; break;
72
+ case '--role': options.role = args[++i]; break;
73
+ case '--alias': options.alias = args[++i]; break;
74
+ case '--json': options.json = args[++i]; break;
75
+ case '--output': options.output = args[++i].toLowerCase(); break;
76
+ case '--quiet': options.quiet = true; break;
77
+ case '--verbose': options.verbose = true; break;
78
+ case '--yes':
79
+ case '-y': options.yes = true; break;
80
+ case '-h':
81
+ case '--help': options.help = true; break;
82
+ default:
83
+ if (arg.startsWith('-') && !options.id) options.id = arg;
84
+ }
85
+ i++;
86
+ }
87
+
88
+ return options;
89
+ }
90
+
91
+ function formatOutput(data, format) {
92
+ if (format === 'text') {
93
+ return typeof data === 'string' ? data : JSON.stringify(data, null, 2);
94
+ }
95
+
96
+ if (format === 'table' && Array.isArray(data) && data.length > 0) {
97
+ const keys = Object.keys(data[0]);
98
+ const header = keys.map(k => k.toUpperCase()).join(' | ');
99
+ const rows = data.map(row =>
100
+ keys.map(k => {
101
+ const val = row[k];
102
+ if (val === null || val === undefined) return '';
103
+ return typeof val === 'object' ? JSON.stringify(val).slice(0, 50) : String(val);
104
+ }).join(' | ')
105
+ );
106
+ return [header, ...rows].join('\n');
107
+ }
108
+
109
+ return JSON.stringify(data, null, 2);
110
+ }
111
+
112
+ module.exports = { colors, colorize, parseArgs, formatOutput };