@intranefr/superbackend 1.6.7 → 1.7.8
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.
- package/.beads/.br_history/issues.20260314_212352_900045509.jsonl +0 -0
- package/.beads/.br_history/issues.20260314_212352_900045509.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_212353_087140743.jsonl +1 -0
- package/.beads/.br_history/issues.20260314_212353_087140743.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_212353_285881504.jsonl +2 -0
- package/.beads/.br_history/issues.20260314_212353_285881504.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_212353_473915419.jsonl +3 -0
- package/.beads/.br_history/issues.20260314_212353_473915419.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_212353_659476307.jsonl +4 -0
- package/.beads/.br_history/issues.20260314_212353_659476307.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_212353_869998925.jsonl +5 -0
- package/.beads/.br_history/issues.20260314_212353_869998925.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_212354_054785029.jsonl +6 -0
- package/.beads/.br_history/issues.20260314_212354_054785029.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_213336_175893691.jsonl +7 -0
- package/.beads/.br_history/issues.20260314_213336_175893691.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_213336_338509797.jsonl +7 -0
- package/.beads/.br_history/issues.20260314_213336_338509797.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_213336_515443192.jsonl +7 -0
- package/.beads/.br_history/issues.20260314_213336_515443192.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_213336_676417592.jsonl +7 -0
- package/.beads/.br_history/issues.20260314_213336_676417592.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_213336_839182422.jsonl +7 -0
- package/.beads/.br_history/issues.20260314_213336_839182422.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_213337_004349113.jsonl +7 -0
- package/.beads/.br_history/issues.20260314_213337_004349113.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_213337_179824080.jsonl +7 -0
- package/.beads/.br_history/issues.20260314_213337_179824080.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_213701_705075332.jsonl +7 -0
- package/.beads/.br_history/issues.20260314_213701_705075332.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_213706_783128702.jsonl +8 -0
- package/.beads/.br_history/issues.20260314_213706_783128702.jsonl.meta.json +1 -0
- package/.beads/config.yaml +4 -0
- package/.beads/issues.jsonl +8 -0
- package/.beads/metadata.json +4 -0
- package/.env.example +8 -0
- package/autochangelog/.env.example +36 -0
- package/autochangelog/README.md +412 -0
- package/autochangelog/config/database.js +27 -0
- package/autochangelog/package.json +47 -0
- package/autochangelog/public/landing.html +581 -0
- package/autochangelog/server.js +104 -0
- package/autochangelog/src/app.js +181 -0
- package/autochangelog/src/config/database.js +26 -0
- package/autochangelog/src/controllers/auth.js +488 -0
- package/autochangelog/src/controllers/changelog.js +682 -0
- package/autochangelog/src/controllers/project.js +580 -0
- package/autochangelog/src/controllers/repository.js +780 -0
- package/autochangelog/src/middleware/auth.js +386 -0
- package/autochangelog/src/models/Changelog.js +443 -0
- package/autochangelog/src/models/Project.js +226 -0
- package/autochangelog/src/models/Repository.js +366 -0
- package/autochangelog/src/models/User.js +223 -0
- package/autochangelog/src/routes/auth.routes.js +32 -0
- package/autochangelog/src/routes/changelog.routes.js +42 -0
- package/autochangelog/src/routes/github-auth.routes.js +102 -0
- package/autochangelog/src/routes/project.routes.js +50 -0
- package/autochangelog/src/routes/repository.routes.js +54 -0
- package/autochangelog/src/services/changelog.js +722 -0
- package/autochangelog/src/services/github.js +243 -0
- package/autochangelog/utils/logger.js +77 -0
- package/autochangelog/views/404.ejs +18 -0
- package/autochangelog/views/dashboard.ejs +596 -0
- package/autochangelog/views/index.ejs +231 -0
- package/autochangelog/views/layouts/main.ejs +44 -0
- package/autochangelog/views/login.ejs +104 -0
- package/autochangelog/views/partials/footer.ejs +20 -0
- package/autochangelog/views/partials/navbar.ejs +51 -0
- package/autochangelog/views/register.ejs +109 -0
- package/autochangelog-cli/README.md +266 -0
- package/autochangelog-cli/bin/autochangelog +120 -0
- package/autochangelog-cli/package.json +46 -0
- package/autochangelog-cli/src/cli/commands/auth.js +291 -0
- package/autochangelog-cli/src/cli/commands/changelog.js +619 -0
- package/autochangelog-cli/src/cli/commands/project.js +427 -0
- package/autochangelog-cli/src/cli/commands/repo.js +557 -0
- package/autochangelog-cli/src/cli/commands/stats.js +706 -0
- package/autochangelog-cli/src/cli/utils/config.js +277 -0
- package/autochangelog-cli/src/cli/utils/errors.js +307 -0
- package/autochangelog-cli/src/cli/utils/logger.js +75 -0
- package/autochangelog-cli/src/cli/utils/output.js +357 -0
- package/package.json +9 -3
- package/plugins/supercli/README.md +108 -0
- package/plugins/supercli/plugin.json +123 -0
- package/server.js +1 -1
- package/src/cli/api.js +380 -0
- package/src/cli/direct/agent-utils.js +61 -0
- package/src/cli/direct/cli-utils.js +112 -0
- package/src/cli/direct/data-seeding.js +307 -0
- package/src/cli/direct/db-admin.js +84 -0
- package/src/cli/direct/db-advanced.js +372 -0
- package/src/cli/direct/db-utils.js +558 -0
- package/src/cli/direct/help.js +195 -0
- package/src/cli/direct/migration.js +107 -0
- package/src/cli/direct/rbac-advanced.js +132 -0
- package/src/cli/direct/resources-additional.js +400 -0
- package/src/cli/direct/resources-cms-advanced.js +173 -0
- package/src/cli/direct/resources-cms.js +247 -0
- package/src/cli/direct/resources-core.js +253 -0
- package/src/cli/direct/resources-execution.js +367 -0
- package/src/cli/direct/resources-health.js +152 -0
- package/src/cli/direct/resources-integrations.js +182 -0
- package/src/cli/direct/resources-logs.js +204 -0
- package/src/cli/direct/resources-org-rbac.js +187 -0
- package/src/cli/direct/resources-system.js +236 -0
- package/src/cli/direct.js +556 -0
- package/src/controllers/admin.controller.js +4 -0
- package/src/controllers/auth.controller.js +148 -1
- package/src/controllers/waitingList.controller.js +130 -1
- package/src/models/RbacRole.js +1 -1
- package/src/models/User.js +39 -5
- package/src/routes/auth.routes.js +6 -0
- package/src/routes/waitingList.routes.js +12 -2
- package/src/routes/waitingListAdmin.routes.js +3 -0
- package/src/services/email.service.js +1 -0
- package/src/services/github.service.js +255 -0
- package/src/services/rateLimiter.service.js +29 -1
- package/src/services/waitingListJson.service.js +32 -3
- 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 };
|