@nebulaos/cli 0.1.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.
- package/README.md +298 -0
- package/dist/bin/nebulaos.js +2629 -0
- package/dist/bin/nebulaos.js.map +1 -0
- package/dist/index.d.ts +117 -0
- package/dist/index.js +2752 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,2629 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/bin/nebulaos.ts
|
|
4
|
+
import { CommanderError } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/program.ts
|
|
7
|
+
import { Command as Command67 } from "commander";
|
|
8
|
+
|
|
9
|
+
// src/lib/flags.ts
|
|
10
|
+
import { Option } from "commander";
|
|
11
|
+
function addGlobalFlags(program) {
|
|
12
|
+
program.addOption(
|
|
13
|
+
new Option("-o, --output <format>", "Output format").choices(["table", "json", "yaml", "quiet"]).env("NEBULAOS_OUTPUT")
|
|
14
|
+
).addOption(
|
|
15
|
+
new Option("--context <name>", "Use a specific context").env("NEBULAOS_CONTEXT")
|
|
16
|
+
).addOption(
|
|
17
|
+
new Option("--api-url <url>", "Override API URL").env("NEBULAOS_API_URL")
|
|
18
|
+
).addOption(
|
|
19
|
+
new Option("--token <token>", "Override authentication token").env("NEBULAOS_TOKEN")
|
|
20
|
+
).addOption(
|
|
21
|
+
new Option("--org-id <id>", "Override organization ID").env("NEBULAOS_ORG_ID")
|
|
22
|
+
).addOption(
|
|
23
|
+
new Option("--no-color", "Disable colored output")
|
|
24
|
+
).addOption(
|
|
25
|
+
new Option("--verbose", "Enable verbose output")
|
|
26
|
+
);
|
|
27
|
+
return program;
|
|
28
|
+
}
|
|
29
|
+
function addPaginationFlags(command) {
|
|
30
|
+
command.addOption(
|
|
31
|
+
new Option("--page <number>", "Page number").argParser(parseInt)
|
|
32
|
+
).addOption(
|
|
33
|
+
new Option("--page-size <number>", "Items per page").argParser(parseInt)
|
|
34
|
+
);
|
|
35
|
+
return command;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/lib/errors.ts
|
|
39
|
+
var CLIError = class extends Error {
|
|
40
|
+
constructor(message, exitCode = 1 /* GeneralError */, hint) {
|
|
41
|
+
super(message);
|
|
42
|
+
this.exitCode = exitCode;
|
|
43
|
+
this.hint = hint;
|
|
44
|
+
this.name = "CLIError";
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var AuthenticationError = class extends CLIError {
|
|
48
|
+
constructor(message = "Authentication required. Run `nebulaos auth login` first.") {
|
|
49
|
+
super(message, 3 /* AuthenticationError */);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var AuthorizationError = class extends CLIError {
|
|
53
|
+
constructor(message = "Insufficient permissions for this operation.") {
|
|
54
|
+
super(message, 4 /* AuthorizationError */);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
var NotFoundError = class extends CLIError {
|
|
58
|
+
constructor(resource, identifier) {
|
|
59
|
+
super(`${resource} '${identifier}' not found.`, 5 /* NotFound */);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
var NetworkError = class extends CLIError {
|
|
63
|
+
constructor(message = "Unable to connect to NebulaOS API. Check your network and API URL.") {
|
|
64
|
+
super(message, 8 /* NetworkError */);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var ConfigError = class extends CLIError {
|
|
68
|
+
constructor(message) {
|
|
69
|
+
super(message, 11 /* ConfigError */);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
function handleError(error) {
|
|
73
|
+
if (error instanceof CLIError) {
|
|
74
|
+
console.error(`Error: ${error.message}`);
|
|
75
|
+
if (error.hint) {
|
|
76
|
+
console.error(`Hint: ${error.hint}`);
|
|
77
|
+
}
|
|
78
|
+
process.exit(error.exitCode);
|
|
79
|
+
}
|
|
80
|
+
if (error instanceof Error) {
|
|
81
|
+
console.error(`Error: ${error.message}`);
|
|
82
|
+
process.exit(1 /* GeneralError */);
|
|
83
|
+
}
|
|
84
|
+
console.error("An unexpected error occurred.");
|
|
85
|
+
process.exit(1 /* GeneralError */);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/commands/auth/index.ts
|
|
89
|
+
import { Command as Command4 } from "commander";
|
|
90
|
+
|
|
91
|
+
// src/commands/auth/login.ts
|
|
92
|
+
import { Command } from "commander";
|
|
93
|
+
import axios from "axios";
|
|
94
|
+
import chalk from "chalk";
|
|
95
|
+
import { password, input } from "@inquirer/prompts";
|
|
96
|
+
|
|
97
|
+
// src/lib/config.ts
|
|
98
|
+
import Conf from "conf";
|
|
99
|
+
var schema = {
|
|
100
|
+
contexts: {
|
|
101
|
+
type: "object",
|
|
102
|
+
default: {}
|
|
103
|
+
},
|
|
104
|
+
currentContext: {
|
|
105
|
+
type: ["string", "null"],
|
|
106
|
+
default: null
|
|
107
|
+
},
|
|
108
|
+
defaults: {
|
|
109
|
+
type: "object",
|
|
110
|
+
default: {
|
|
111
|
+
output: "table",
|
|
112
|
+
pageSize: 20
|
|
113
|
+
},
|
|
114
|
+
properties: {
|
|
115
|
+
output: {
|
|
116
|
+
type: "string",
|
|
117
|
+
enum: ["table", "json", "yaml", "quiet"],
|
|
118
|
+
default: "table"
|
|
119
|
+
},
|
|
120
|
+
pageSize: {
|
|
121
|
+
type: "number",
|
|
122
|
+
default: 20,
|
|
123
|
+
minimum: 1,
|
|
124
|
+
maximum: 100
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
var config = new Conf({
|
|
130
|
+
projectName: "nebulaos",
|
|
131
|
+
schema
|
|
132
|
+
});
|
|
133
|
+
function getConfig() {
|
|
134
|
+
return {
|
|
135
|
+
contexts: config.get("contexts"),
|
|
136
|
+
currentContext: config.get("currentContext"),
|
|
137
|
+
defaults: config.get("defaults")
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function getCurrentContext() {
|
|
141
|
+
const currentName = config.get("currentContext");
|
|
142
|
+
if (!currentName) {
|
|
143
|
+
throw new ConfigError(
|
|
144
|
+
"No active context. Run `nebulaos auth login` or `nebulaos config use-context <name>` first."
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
const contexts = config.get("contexts");
|
|
148
|
+
const ctx = contexts[currentName];
|
|
149
|
+
if (!ctx) {
|
|
150
|
+
throw new ConfigError(
|
|
151
|
+
`Context '${currentName}' not found. Run \`nebulaos config get-contexts\` to see available contexts.`
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
return ctx;
|
|
155
|
+
}
|
|
156
|
+
function setContext(name, context) {
|
|
157
|
+
const contexts = config.get("contexts");
|
|
158
|
+
contexts[name] = context;
|
|
159
|
+
config.set("contexts", contexts);
|
|
160
|
+
}
|
|
161
|
+
function removeContext(name) {
|
|
162
|
+
const contexts = config.get("contexts");
|
|
163
|
+
if (!contexts[name]) {
|
|
164
|
+
throw new ConfigError(`Context '${name}' not found.`);
|
|
165
|
+
}
|
|
166
|
+
delete contexts[name];
|
|
167
|
+
config.set("contexts", contexts);
|
|
168
|
+
if (config.get("currentContext") === name) {
|
|
169
|
+
config.set("currentContext", null);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function useContext(name) {
|
|
173
|
+
const contexts = config.get("contexts");
|
|
174
|
+
if (!contexts[name]) {
|
|
175
|
+
throw new ConfigError(
|
|
176
|
+
`Context '${name}' not found. Run \`nebulaos config get-contexts\` to see available contexts.`
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
config.set("currentContext", name);
|
|
180
|
+
}
|
|
181
|
+
function setDefault(key, value) {
|
|
182
|
+
const defaults = config.get("defaults");
|
|
183
|
+
defaults[key] = value;
|
|
184
|
+
config.set("defaults", defaults);
|
|
185
|
+
}
|
|
186
|
+
function getConfigPath() {
|
|
187
|
+
return config.path;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// src/commands/auth/login.ts
|
|
191
|
+
async function verifyToken(apiUrl, token) {
|
|
192
|
+
try {
|
|
193
|
+
const response = await axios.post(
|
|
194
|
+
`${apiUrl}/auth/personal-tokens/verify`,
|
|
195
|
+
{ token },
|
|
196
|
+
{ timeout: 1e4 }
|
|
197
|
+
);
|
|
198
|
+
return response.data;
|
|
199
|
+
} catch (error) {
|
|
200
|
+
if (axios.isAxiosError(error)) {
|
|
201
|
+
if (!error.response) {
|
|
202
|
+
throw new CLIError(
|
|
203
|
+
`Unable to connect to ${apiUrl}. Check the URL and your network.`,
|
|
204
|
+
8 /* NetworkError */
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
throw new CLIError(
|
|
208
|
+
`API returned ${error.response.status}: ${error.response.data?.message || "Unknown error"}`,
|
|
209
|
+
10 /* ServerError */
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function createLoginCommand() {
|
|
216
|
+
const cmd = new Command("login").description("Authenticate with NebulaOS using a Personal Access Token").option("-t, --token <token>", "Personal Access Token (PAT)").option("-u, --api-url <url>", "API URL (default: http://localhost:4100)").option("-n, --context-name <name>", "Name for this context").addHelpText("after", `
|
|
217
|
+
Examples:
|
|
218
|
+
$ nebulaos auth login Interactive login (prompts for token)
|
|
219
|
+
$ nebulaos auth login -t neb_pat_abc123 Login with a token directly
|
|
220
|
+
$ nebulaos auth login -u https://api.starya.com Login to a custom API URL
|
|
221
|
+
$ nebulaos auth login -t neb_pat_abc123 -n production Login and name the context "production"
|
|
222
|
+
|
|
223
|
+
Note: Personal Access Tokens are tied to a specific workspace.
|
|
224
|
+
Create tokens in the NebulaOS dashboard for the workspace you want to access.
|
|
225
|
+
`).action(async (options) => {
|
|
226
|
+
try {
|
|
227
|
+
console.log(chalk.bold("NebulaOS Login\n"));
|
|
228
|
+
let apiUrl = options.apiUrl || options.parent?.opts()?.apiUrl;
|
|
229
|
+
if (!apiUrl) {
|
|
230
|
+
apiUrl = await input({
|
|
231
|
+
message: "NebulaOS API URL:",
|
|
232
|
+
default: "http://localhost:4100",
|
|
233
|
+
validate: (value) => {
|
|
234
|
+
if (!value) return "API URL is required";
|
|
235
|
+
try {
|
|
236
|
+
new URL(value);
|
|
237
|
+
return true;
|
|
238
|
+
} catch {
|
|
239
|
+
return "Invalid URL format. Example: https://api.starya.com";
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
let token = options.token;
|
|
245
|
+
if (!token) {
|
|
246
|
+
console.log("\nGenerate a Personal Access Token at your NebulaOS dashboard.\n");
|
|
247
|
+
token = await password({
|
|
248
|
+
message: "Personal Access Token (PAT):",
|
|
249
|
+
mask: "*",
|
|
250
|
+
validate: (value) => {
|
|
251
|
+
if (!value) return "Token is required";
|
|
252
|
+
if (!value.startsWith("neb_pat_")) return "Invalid token format. Tokens start with 'neb_pat_'";
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
if (!token.startsWith("neb_pat_")) {
|
|
258
|
+
throw new CLIError(
|
|
259
|
+
"Invalid token format. Personal Access Tokens start with 'neb_pat_'.",
|
|
260
|
+
7 /* ValidationError */
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
console.log("\nVerifying token...");
|
|
264
|
+
const result = await verifyToken(apiUrl, token);
|
|
265
|
+
if (!result.valid) {
|
|
266
|
+
throw new CLIError(
|
|
267
|
+
`Token verification failed: ${result.error || "Unknown error"}`,
|
|
268
|
+
3 /* AuthenticationError */
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
const contextName = options.contextName || result.organization?.slug || "default";
|
|
272
|
+
setContext(contextName, {
|
|
273
|
+
name: contextName,
|
|
274
|
+
apiUrl,
|
|
275
|
+
token,
|
|
276
|
+
organizationId: result.organization?.id
|
|
277
|
+
});
|
|
278
|
+
useContext(contextName);
|
|
279
|
+
console.log(chalk.green("\nAuthenticated successfully!"));
|
|
280
|
+
console.log(` Admin: ${result.admin?.fullName} (${result.admin?.email})`);
|
|
281
|
+
console.log(` Organization: ${result.organization?.name} (${result.organization?.slug})`);
|
|
282
|
+
if (result.workspaceId) {
|
|
283
|
+
console.log(` Workspace: ${result.workspaceId}`);
|
|
284
|
+
}
|
|
285
|
+
console.log(` Context: ${contextName}`);
|
|
286
|
+
if (result.scopes && result.scopes.length > 0) {
|
|
287
|
+
console.log(` Scopes: ${result.scopes.join(", ")}`);
|
|
288
|
+
}
|
|
289
|
+
} catch (error) {
|
|
290
|
+
handleError(error);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
return cmd;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/commands/auth/logout.ts
|
|
297
|
+
import { Command as Command2 } from "commander";
|
|
298
|
+
import chalk2 from "chalk";
|
|
299
|
+
import { confirm } from "@inquirer/prompts";
|
|
300
|
+
function createLogoutCommand() {
|
|
301
|
+
const cmd = new Command2("logout").description("Remove authentication credentials for the current or specified context").option("-y, --yes", "Skip confirmation prompt").option("--context <name>", "Context to log out from (default: current)").addHelpText("after", `
|
|
302
|
+
Examples:
|
|
303
|
+
$ nebulaos auth logout Log out from the current context
|
|
304
|
+
$ nebulaos auth logout -y Log out without confirmation
|
|
305
|
+
$ nebulaos auth logout --context staging Log out from a specific context
|
|
306
|
+
`).action(async (options) => {
|
|
307
|
+
try {
|
|
308
|
+
const config2 = getConfig();
|
|
309
|
+
const contextName = options.context || config2.currentContext;
|
|
310
|
+
if (!contextName) {
|
|
311
|
+
throw new CLIError("No active context. Nothing to log out from.", 11 /* ConfigError */);
|
|
312
|
+
}
|
|
313
|
+
if (!config2.contexts[contextName]) {
|
|
314
|
+
throw new CLIError(`Context '${contextName}' not found.`, 5 /* NotFound */);
|
|
315
|
+
}
|
|
316
|
+
if (!options.yes) {
|
|
317
|
+
const shouldProceed = await confirm({
|
|
318
|
+
message: `Remove credentials for context '${contextName}'?`,
|
|
319
|
+
default: false
|
|
320
|
+
});
|
|
321
|
+
if (!shouldProceed) {
|
|
322
|
+
console.log("Cancelled.");
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
removeContext(contextName);
|
|
327
|
+
console.log(chalk2.green(`Logged out from context '${contextName}'.`));
|
|
328
|
+
} catch (error) {
|
|
329
|
+
handleError(error);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
return cmd;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// src/commands/auth/status.ts
|
|
336
|
+
import { Command as Command3 } from "commander";
|
|
337
|
+
import chalk4 from "chalk";
|
|
338
|
+
|
|
339
|
+
// src/lib/output.ts
|
|
340
|
+
import Table from "cli-table3";
|
|
341
|
+
import chalk3 from "chalk";
|
|
342
|
+
import { stringify as yamlStringify } from "yaml";
|
|
343
|
+
function resolveFormat(flagValue) {
|
|
344
|
+
if (flagValue) return flagValue;
|
|
345
|
+
return getConfig().defaults.output;
|
|
346
|
+
}
|
|
347
|
+
function printTable({ headers, rows }) {
|
|
348
|
+
const table = new Table({
|
|
349
|
+
head: headers.map((h) => chalk3.bold(h)),
|
|
350
|
+
style: { head: [], border: [] }
|
|
351
|
+
});
|
|
352
|
+
for (const row of rows) {
|
|
353
|
+
table.push(row);
|
|
354
|
+
}
|
|
355
|
+
console.log(table.toString());
|
|
356
|
+
}
|
|
357
|
+
function printJson(data) {
|
|
358
|
+
console.log(JSON.stringify(data, null, 2));
|
|
359
|
+
}
|
|
360
|
+
function printYaml(data) {
|
|
361
|
+
console.log(yamlStringify(data));
|
|
362
|
+
}
|
|
363
|
+
function printQuiet(ids) {
|
|
364
|
+
for (const id of ids) {
|
|
365
|
+
console.log(id);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
function output(format, outputData) {
|
|
369
|
+
switch (format) {
|
|
370
|
+
case "table":
|
|
371
|
+
printTable(outputData.table);
|
|
372
|
+
break;
|
|
373
|
+
case "json":
|
|
374
|
+
printJson(outputData.data);
|
|
375
|
+
break;
|
|
376
|
+
case "yaml":
|
|
377
|
+
printYaml(outputData.data);
|
|
378
|
+
break;
|
|
379
|
+
case "quiet":
|
|
380
|
+
printQuiet(outputData.ids);
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function printSuccess(message) {
|
|
385
|
+
console.log(chalk3.green(message));
|
|
386
|
+
}
|
|
387
|
+
function printDetail(label, value) {
|
|
388
|
+
console.log(`${chalk3.bold(label)}: ${value}`);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// src/commands/auth/status.ts
|
|
392
|
+
function createStatusCommand() {
|
|
393
|
+
const cmd = new Command3("status").description("Show current authentication status and context information").addHelpText("after", `
|
|
394
|
+
Examples:
|
|
395
|
+
$ nebulaos auth status Show current auth status
|
|
396
|
+
$ nebulaos auth status -o json Show auth status as JSON
|
|
397
|
+
`).action(async (_options, command) => {
|
|
398
|
+
try {
|
|
399
|
+
const globalOpts = command.optsWithGlobals();
|
|
400
|
+
const format = resolveFormat(globalOpts.output);
|
|
401
|
+
const config2 = getConfig();
|
|
402
|
+
if (!config2.currentContext) {
|
|
403
|
+
if (format === "json") {
|
|
404
|
+
console.log(JSON.stringify({ authenticated: false }));
|
|
405
|
+
} else if (format === "yaml") {
|
|
406
|
+
console.log("authenticated: false");
|
|
407
|
+
} else {
|
|
408
|
+
console.log(chalk4.yellow("Not authenticated. Run `nebulaos auth login` to get started."));
|
|
409
|
+
}
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
let context;
|
|
413
|
+
try {
|
|
414
|
+
context = getCurrentContext();
|
|
415
|
+
} catch (e) {
|
|
416
|
+
if (e instanceof ConfigError) {
|
|
417
|
+
console.log(chalk4.yellow(e.message));
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
throw e;
|
|
421
|
+
}
|
|
422
|
+
const statusData = {
|
|
423
|
+
authenticated: true,
|
|
424
|
+
context: config2.currentContext,
|
|
425
|
+
apiUrl: context.apiUrl,
|
|
426
|
+
organizationId: context.organizationId || null,
|
|
427
|
+
hasToken: !!context.token
|
|
428
|
+
};
|
|
429
|
+
if (format === "table") {
|
|
430
|
+
console.log(chalk4.bold("Authentication Status\n"));
|
|
431
|
+
printDetail("Context", config2.currentContext);
|
|
432
|
+
printDetail("API URL", context.apiUrl);
|
|
433
|
+
printDetail("Authenticated", context.token ? chalk4.green("Yes") : chalk4.red("No"));
|
|
434
|
+
if (context.organizationId) {
|
|
435
|
+
printDetail("Organization ID", context.organizationId);
|
|
436
|
+
}
|
|
437
|
+
} else {
|
|
438
|
+
output(format, {
|
|
439
|
+
table: { headers: [], rows: [] },
|
|
440
|
+
data: statusData,
|
|
441
|
+
ids: [config2.currentContext]
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
} catch (error) {
|
|
445
|
+
handleError(error);
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
return cmd;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// src/commands/auth/index.ts
|
|
452
|
+
function createAuthCommand() {
|
|
453
|
+
const auth = new Command4("auth").description("Authenticate with NebulaOS and manage credentials");
|
|
454
|
+
auth.addCommand(createLoginCommand());
|
|
455
|
+
auth.addCommand(createLogoutCommand());
|
|
456
|
+
auth.addCommand(createStatusCommand());
|
|
457
|
+
return auth;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// src/commands/orgs/index.ts
|
|
461
|
+
import { Command as Command8 } from "commander";
|
|
462
|
+
|
|
463
|
+
// src/commands/orgs/list.ts
|
|
464
|
+
import { Command as Command5 } from "commander";
|
|
465
|
+
|
|
466
|
+
// src/lib/api.ts
|
|
467
|
+
import axios2 from "axios";
|
|
468
|
+
var client = null;
|
|
469
|
+
function getApiClient() {
|
|
470
|
+
if (client) return client;
|
|
471
|
+
const context = getCurrentContext();
|
|
472
|
+
client = axios2.create({
|
|
473
|
+
baseURL: context.apiUrl,
|
|
474
|
+
timeout: 3e4,
|
|
475
|
+
headers: {
|
|
476
|
+
"Content-Type": "application/json"
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
client.interceptors.request.use((config2) => {
|
|
480
|
+
const ctx = getCurrentContext();
|
|
481
|
+
if (ctx.token) {
|
|
482
|
+
config2.headers.Authorization = `Bearer ${ctx.token}`;
|
|
483
|
+
}
|
|
484
|
+
if (ctx.organizationId) {
|
|
485
|
+
config2.headers["X-Organization-Id"] = ctx.organizationId;
|
|
486
|
+
}
|
|
487
|
+
return config2;
|
|
488
|
+
});
|
|
489
|
+
client.interceptors.response.use(
|
|
490
|
+
(response) => response,
|
|
491
|
+
(error) => {
|
|
492
|
+
if (!error.response) {
|
|
493
|
+
if (error.code === "ECONNREFUSED" || error.code === "ENOTFOUND") {
|
|
494
|
+
throw new NetworkError();
|
|
495
|
+
}
|
|
496
|
+
if (error.code === "ECONNABORTED" || error.code === "ETIMEDOUT") {
|
|
497
|
+
throw new CLIError(
|
|
498
|
+
"Request timed out. The server may be unavailable.",
|
|
499
|
+
9 /* TimeoutError */
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
throw new NetworkError(error.message);
|
|
503
|
+
}
|
|
504
|
+
const { status, data } = error.response;
|
|
505
|
+
const message = data?.message || error.message;
|
|
506
|
+
switch (status) {
|
|
507
|
+
case 401:
|
|
508
|
+
throw new AuthenticationError();
|
|
509
|
+
case 403:
|
|
510
|
+
throw new AuthorizationError();
|
|
511
|
+
case 404:
|
|
512
|
+
throw new NotFoundError("Resource", message);
|
|
513
|
+
case 409:
|
|
514
|
+
throw new CLIError(message, 6 /* ConflictError */);
|
|
515
|
+
case 422:
|
|
516
|
+
throw new CLIError(message, 7 /* ValidationError */);
|
|
517
|
+
default:
|
|
518
|
+
if (status >= 500) {
|
|
519
|
+
throw new CLIError(
|
|
520
|
+
`Server error (${status}): ${message}`,
|
|
521
|
+
10 /* ServerError */
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
throw new CLIError(message, 1 /* GeneralError */);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
);
|
|
528
|
+
return client;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// src/commands/orgs/list.ts
|
|
532
|
+
function createOrgsListCommand() {
|
|
533
|
+
const cmd = new Command5("list").description("List organizations accessible to the current admin").addHelpText("after", `
|
|
534
|
+
Examples:
|
|
535
|
+
$ nebulaos orgs list List all accessible organizations
|
|
536
|
+
$ nebulaos orgs list -o json Output as JSON
|
|
537
|
+
$ nebulaos orgs list --page 2 Show page 2 of results
|
|
538
|
+
`).action(async (_options, command) => {
|
|
539
|
+
try {
|
|
540
|
+
const globalOpts = command.optsWithGlobals();
|
|
541
|
+
const format = resolveFormat(globalOpts.output);
|
|
542
|
+
const api = getApiClient();
|
|
543
|
+
const params = {};
|
|
544
|
+
if (globalOpts.page) params.page = globalOpts.page;
|
|
545
|
+
if (globalOpts.pageSize) params.pageSize = globalOpts.pageSize;
|
|
546
|
+
const { data } = await api.get("/organizations", { params });
|
|
547
|
+
const orgs = Array.isArray(data) ? data : [];
|
|
548
|
+
output(format, {
|
|
549
|
+
table: {
|
|
550
|
+
headers: ["ID", "Name", "Slug", "Active", "Created At"],
|
|
551
|
+
rows: orgs.map((o) => [
|
|
552
|
+
o.id,
|
|
553
|
+
o.name,
|
|
554
|
+
o.slug,
|
|
555
|
+
o.isActive ? "Yes" : "No",
|
|
556
|
+
new Date(o.createdAt).toLocaleDateString()
|
|
557
|
+
])
|
|
558
|
+
},
|
|
559
|
+
data: orgs,
|
|
560
|
+
ids: orgs.map((o) => o.id)
|
|
561
|
+
});
|
|
562
|
+
} catch (error) {
|
|
563
|
+
handleError(error);
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
addPaginationFlags(cmd);
|
|
567
|
+
return cmd;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// src/commands/orgs/get.ts
|
|
571
|
+
import { Command as Command6 } from "commander";
|
|
572
|
+
import chalk5 from "chalk";
|
|
573
|
+
function createOrgsGetCommand() {
|
|
574
|
+
const cmd = new Command6("get").description("Get details of an organization by ID or slug").argument("<id>", "Organization ID or slug").addHelpText("after", `
|
|
575
|
+
Examples:
|
|
576
|
+
$ nebulaos orgs get my-org Get organization by slug
|
|
577
|
+
$ nebulaos orgs get 550e8400-e29b-41d4... Get organization by ID
|
|
578
|
+
$ nebulaos orgs get my-org -o json Output as JSON
|
|
579
|
+
`).action(async (id, _options, command) => {
|
|
580
|
+
try {
|
|
581
|
+
const globalOpts = command.optsWithGlobals();
|
|
582
|
+
const format = resolveFormat(globalOpts.output);
|
|
583
|
+
const api = getApiClient();
|
|
584
|
+
const { data: org } = await api.get(`/organizations/${id}`);
|
|
585
|
+
if (format === "table") {
|
|
586
|
+
console.log(chalk5.bold("Organization Details\n"));
|
|
587
|
+
printDetail("ID", org.id);
|
|
588
|
+
printDetail("Name", org.name);
|
|
589
|
+
printDetail("Slug", org.slug);
|
|
590
|
+
printDetail("Active", org.isActive ? chalk5.green("Yes") : chalk5.red("No"));
|
|
591
|
+
printDetail("Created", new Date(org.createdAt).toLocaleString());
|
|
592
|
+
printDetail("Updated", new Date(org.updatedAt).toLocaleString());
|
|
593
|
+
} else {
|
|
594
|
+
output(format, {
|
|
595
|
+
table: { headers: [], rows: [] },
|
|
596
|
+
data: org,
|
|
597
|
+
ids: [org.id]
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
} catch (error) {
|
|
601
|
+
handleError(error);
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
return cmd;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// src/commands/orgs/switch.ts
|
|
608
|
+
import { Command as Command7 } from "commander";
|
|
609
|
+
import chalk6 from "chalk";
|
|
610
|
+
function createOrgsSwitchCommand() {
|
|
611
|
+
const cmd = new Command7("switch").description("Switch the active organization context to a different organization").argument("<slug>", "Organization slug to switch to").addHelpText("after", `
|
|
612
|
+
Examples:
|
|
613
|
+
$ nebulaos orgs switch my-org Switch to the "my-org" organization
|
|
614
|
+
$ nebulaos orgs switch production Switch to the "production" organization
|
|
615
|
+
`).action(async (slug) => {
|
|
616
|
+
try {
|
|
617
|
+
const api = getApiClient();
|
|
618
|
+
const currentContext = getCurrentContext();
|
|
619
|
+
const { data: org } = await api.get(`/organizations/${slug}`);
|
|
620
|
+
const contextName = org.slug;
|
|
621
|
+
setContext(contextName, {
|
|
622
|
+
name: contextName,
|
|
623
|
+
apiUrl: currentContext.apiUrl,
|
|
624
|
+
token: currentContext.token,
|
|
625
|
+
organizationId: org.id
|
|
626
|
+
});
|
|
627
|
+
useContext(contextName);
|
|
628
|
+
console.log(chalk6.green(`Switched to organization '${org.name}' (${org.slug}).`));
|
|
629
|
+
console.log(` Context: ${contextName}`);
|
|
630
|
+
console.log(` Organization ID: ${org.id}`);
|
|
631
|
+
} catch (error) {
|
|
632
|
+
handleError(error);
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
return cmd;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// src/commands/orgs/index.ts
|
|
639
|
+
function createOrgsCommand() {
|
|
640
|
+
const orgs = new Command8("orgs").description("List, inspect, and switch between organizations");
|
|
641
|
+
orgs.addCommand(createOrgsListCommand());
|
|
642
|
+
orgs.addCommand(createOrgsGetCommand());
|
|
643
|
+
orgs.addCommand(createOrgsSwitchCommand());
|
|
644
|
+
return orgs;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// src/commands/clients/index.ts
|
|
648
|
+
import { Command as Command16 } from "commander";
|
|
649
|
+
|
|
650
|
+
// src/commands/clients/list.ts
|
|
651
|
+
import { Command as Command9 } from "commander";
|
|
652
|
+
function createClientsListCommand() {
|
|
653
|
+
const cmd = new Command9("list").description("List registered clients in the current organization").addHelpText("after", `
|
|
654
|
+
Examples:
|
|
655
|
+
$ nebulaos clients list List all clients
|
|
656
|
+
$ nebulaos clients list -o json Output as JSON
|
|
657
|
+
$ nebulaos clients list --page-size 5 Show 5 clients per page
|
|
658
|
+
`).action(async (_options, command) => {
|
|
659
|
+
try {
|
|
660
|
+
const globalOpts = command.optsWithGlobals();
|
|
661
|
+
const format = resolveFormat(globalOpts.output);
|
|
662
|
+
const api = getApiClient();
|
|
663
|
+
const params = {};
|
|
664
|
+
if (globalOpts.page) params.page = globalOpts.page;
|
|
665
|
+
if (globalOpts.pageSize) params.pageSize = globalOpts.pageSize;
|
|
666
|
+
const { data } = await api.get("/clients", { params });
|
|
667
|
+
const clients = Array.isArray(data) ? data : [];
|
|
668
|
+
output(format, {
|
|
669
|
+
table: {
|
|
670
|
+
headers: ["ID", "Client ID", "Status", "Last Seen", "Registered At"],
|
|
671
|
+
rows: clients.map((c) => [
|
|
672
|
+
c.id,
|
|
673
|
+
c.clientId,
|
|
674
|
+
c.isOnline ? "Online" : "Offline",
|
|
675
|
+
c.lastSeenAt ? new Date(c.lastSeenAt).toLocaleString() : "-",
|
|
676
|
+
c.registeredAt ? new Date(c.registeredAt).toLocaleDateString() : "-"
|
|
677
|
+
])
|
|
678
|
+
},
|
|
679
|
+
data: clients,
|
|
680
|
+
ids: clients.map((c) => c.id)
|
|
681
|
+
});
|
|
682
|
+
} catch (error) {
|
|
683
|
+
handleError(error);
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
addPaginationFlags(cmd);
|
|
687
|
+
return cmd;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// src/commands/clients/get.ts
|
|
691
|
+
import { Command as Command10 } from "commander";
|
|
692
|
+
import chalk7 from "chalk";
|
|
693
|
+
function createClientsGetCommand() {
|
|
694
|
+
const cmd = new Command10("get").description("Get details of a client, including its registered agents and workflows").argument("<id>", "Client ID").addHelpText("after", `
|
|
695
|
+
Examples:
|
|
696
|
+
$ nebulaos clients get 550e8400-e29b-41d4... Get client details by ID
|
|
697
|
+
$ nebulaos clients get my-client -o json Output as JSON
|
|
698
|
+
`).action(async (id, _options, command) => {
|
|
699
|
+
try {
|
|
700
|
+
const globalOpts = command.optsWithGlobals();
|
|
701
|
+
const format = resolveFormat(globalOpts.output);
|
|
702
|
+
const api = getApiClient();
|
|
703
|
+
const { data: client2 } = await api.get(`/clients/${id}`);
|
|
704
|
+
if (format === "table") {
|
|
705
|
+
console.log(chalk7.bold("Client Details\n"));
|
|
706
|
+
printDetail("ID", client2.id);
|
|
707
|
+
printDetail("Client ID", client2.clientId);
|
|
708
|
+
printDetail("Status", client2.isOnline ? "Online" : "Offline");
|
|
709
|
+
printDetail("Last Seen", client2.lastSeenAt ? new Date(client2.lastSeenAt).toLocaleString() : "-");
|
|
710
|
+
printDetail("Registered", client2.registeredAt ? new Date(client2.registeredAt).toLocaleString() : "-");
|
|
711
|
+
printDetail("Updated", client2.updatedAt ? new Date(client2.updatedAt).toLocaleString() : "-");
|
|
712
|
+
if (client2.agents && client2.agents.length > 0) {
|
|
713
|
+
console.log(chalk7.bold("\nAgents:"));
|
|
714
|
+
for (const agent of client2.agents) {
|
|
715
|
+
console.log(` - ${agent.name} (${agent.kind})`);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
if (client2.workflows && client2.workflows.length > 0) {
|
|
719
|
+
console.log(chalk7.bold("\nWorkflows:"));
|
|
720
|
+
for (const wf of client2.workflows) {
|
|
721
|
+
console.log(` - ${wf.name} (${wf.id})`);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
} else {
|
|
725
|
+
output(format, {
|
|
726
|
+
table: { headers: [], rows: [] },
|
|
727
|
+
data: client2,
|
|
728
|
+
ids: [client2.id]
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
} catch (error) {
|
|
732
|
+
handleError(error);
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
return cmd;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// src/commands/clients/create.ts
|
|
739
|
+
import { Command as Command11 } from "commander";
|
|
740
|
+
import chalk8 from "chalk";
|
|
741
|
+
function createClientsCreateCommand() {
|
|
742
|
+
const cmd = new Command11("create").description("Register a new client in the current organization").requiredOption("--client-id <clientId>", "Unique client identifier").addHelpText("after", `
|
|
743
|
+
Examples:
|
|
744
|
+
$ nebulaos clients create --client-id my-agent-server Create a new client
|
|
745
|
+
$ nebulaos clients create --client-id worker-1 -o json Create and output as JSON
|
|
746
|
+
`).action(async (options, command) => {
|
|
747
|
+
try {
|
|
748
|
+
const globalOpts = command.optsWithGlobals();
|
|
749
|
+
const format = resolveFormat(globalOpts.output);
|
|
750
|
+
const api = getApiClient();
|
|
751
|
+
const { data: client2 } = await api.post("/clients", {
|
|
752
|
+
clientId: options.clientId
|
|
753
|
+
});
|
|
754
|
+
if (format === "table") {
|
|
755
|
+
console.log(chalk8.green("Client created successfully.\n"));
|
|
756
|
+
printDetail("ID", client2.id);
|
|
757
|
+
printDetail("Client ID", client2.clientId);
|
|
758
|
+
printDetail("Status", client2.isOnline ? "Online" : "Offline");
|
|
759
|
+
printDetail("Registered", client2.registeredAt ? new Date(client2.registeredAt).toLocaleString() : "-");
|
|
760
|
+
} else {
|
|
761
|
+
output(format, {
|
|
762
|
+
table: { headers: [], rows: [] },
|
|
763
|
+
data: client2,
|
|
764
|
+
ids: [client2.id]
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
} catch (error) {
|
|
768
|
+
handleError(error);
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
return cmd;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// src/commands/clients/api-keys/index.ts
|
|
775
|
+
import { Command as Command15 } from "commander";
|
|
776
|
+
|
|
777
|
+
// src/commands/clients/api-keys/list.ts
|
|
778
|
+
import { Command as Command12 } from "commander";
|
|
779
|
+
function createApiKeysListCommand() {
|
|
780
|
+
const cmd = new Command12("list").description("List all API keys for a specific client").argument("<clientId>", "Client ID").addHelpText("after", `
|
|
781
|
+
Examples:
|
|
782
|
+
$ nebulaos clients api-keys list 550e8400-e29b... List keys for a client
|
|
783
|
+
$ nebulaos clients api-keys list my-client -o json Output as JSON
|
|
784
|
+
`).action(async (clientId, _options, command) => {
|
|
785
|
+
try {
|
|
786
|
+
const globalOpts = command.optsWithGlobals();
|
|
787
|
+
const format = resolveFormat(globalOpts.output);
|
|
788
|
+
const api = getApiClient();
|
|
789
|
+
const { data } = await api.get(`/clients/${clientId}/api-keys`);
|
|
790
|
+
const keys = Array.isArray(data) ? data : [];
|
|
791
|
+
output(format, {
|
|
792
|
+
table: {
|
|
793
|
+
headers: ["ID", "Key", "Active", "Expires At", "Last Used", "Created At"],
|
|
794
|
+
rows: keys.map((k) => [
|
|
795
|
+
k.id,
|
|
796
|
+
k.key,
|
|
797
|
+
k.isActive ? "Yes" : "No",
|
|
798
|
+
k.expiresAt ? new Date(k.expiresAt).toLocaleString() : "-",
|
|
799
|
+
k.lastUsedAt ? new Date(k.lastUsedAt).toLocaleString() : "-",
|
|
800
|
+
new Date(k.createdAt).toLocaleDateString()
|
|
801
|
+
])
|
|
802
|
+
},
|
|
803
|
+
data: keys,
|
|
804
|
+
ids: keys.map((k) => k.id)
|
|
805
|
+
});
|
|
806
|
+
} catch (error) {
|
|
807
|
+
handleError(error);
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
return cmd;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// src/commands/clients/api-keys/create.ts
|
|
814
|
+
import { Command as Command13 } from "commander";
|
|
815
|
+
import chalk9 from "chalk";
|
|
816
|
+
function createApiKeysCreateCommand() {
|
|
817
|
+
const cmd = new Command13("create").description("Generate a new API key for a client (shown only once)").argument("<clientId>", "Client ID").addHelpText("after", `
|
|
818
|
+
Examples:
|
|
819
|
+
$ nebulaos clients api-keys create 550e8400-e29b... Create a new API key
|
|
820
|
+
$ nebulaos clients api-keys create my-client -o json Output as JSON
|
|
821
|
+
|
|
822
|
+
Note: The API key is displayed only once after creation. Save it securely.
|
|
823
|
+
`).action(async (clientId, _options, command) => {
|
|
824
|
+
try {
|
|
825
|
+
const globalOpts = command.optsWithGlobals();
|
|
826
|
+
const format = resolveFormat(globalOpts.output);
|
|
827
|
+
const api = getApiClient();
|
|
828
|
+
const { data: apiKey } = await api.post(`/clients/${clientId}/api-keys`);
|
|
829
|
+
if (format === "table") {
|
|
830
|
+
console.log(chalk9.green("API key created successfully.\n"));
|
|
831
|
+
printDetail("ID", apiKey.id);
|
|
832
|
+
printDetail("Key", apiKey.key);
|
|
833
|
+
printDetail("Active", apiKey.isActive ? "Yes" : "No");
|
|
834
|
+
printDetail("Expires At", apiKey.expiresAt ? new Date(apiKey.expiresAt).toLocaleString() : "-");
|
|
835
|
+
printDetail("Created", new Date(apiKey.createdAt).toLocaleString());
|
|
836
|
+
console.log(chalk9.yellow("\nSave this key now. It will not be shown again."));
|
|
837
|
+
} else {
|
|
838
|
+
output(format, {
|
|
839
|
+
table: { headers: [], rows: [] },
|
|
840
|
+
data: apiKey,
|
|
841
|
+
ids: [apiKey.id]
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
} catch (error) {
|
|
845
|
+
handleError(error);
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
return cmd;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// src/commands/clients/api-keys/revoke.ts
|
|
852
|
+
import { Command as Command14 } from "commander";
|
|
853
|
+
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
854
|
+
function createApiKeysRevokeCommand() {
|
|
855
|
+
const cmd = new Command14("revoke").description("Permanently revoke an API key for a client").argument("<clientId>", "Client ID").argument("<keyId>", "API Key ID to revoke").option("-y, --yes", "Skip confirmation prompt").addHelpText("after", `
|
|
856
|
+
Examples:
|
|
857
|
+
$ nebulaos clients api-keys revoke my-client key-123 Revoke with confirmation
|
|
858
|
+
$ nebulaos clients api-keys revoke my-client key-123 -y Revoke without confirmation
|
|
859
|
+
`).action(async (clientId, keyId, options) => {
|
|
860
|
+
try {
|
|
861
|
+
if (!options.yes) {
|
|
862
|
+
const shouldProceed = await confirm2({
|
|
863
|
+
message: `Revoke API key '${keyId}' for client '${clientId}'?`,
|
|
864
|
+
default: false
|
|
865
|
+
});
|
|
866
|
+
if (!shouldProceed) {
|
|
867
|
+
console.log("Cancelled.");
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
const api = getApiClient();
|
|
872
|
+
await api.delete(`/clients/${clientId}/api-keys/${keyId}`);
|
|
873
|
+
printSuccess(`API key '${keyId}' revoked successfully.`);
|
|
874
|
+
} catch (error) {
|
|
875
|
+
handleError(error);
|
|
876
|
+
}
|
|
877
|
+
});
|
|
878
|
+
return cmd;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
// src/commands/clients/api-keys/index.ts
|
|
882
|
+
function createApiKeysCommand() {
|
|
883
|
+
const apiKeys = new Command15("api-keys").description("Create, list, and revoke API keys for clients");
|
|
884
|
+
apiKeys.addCommand(createApiKeysListCommand());
|
|
885
|
+
apiKeys.addCommand(createApiKeysCreateCommand());
|
|
886
|
+
apiKeys.addCommand(createApiKeysRevokeCommand());
|
|
887
|
+
return apiKeys;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// src/commands/clients/index.ts
|
|
891
|
+
function createClientsCommand() {
|
|
892
|
+
const clients = new Command16("clients").description("Register and manage clients and their API keys");
|
|
893
|
+
clients.addCommand(createClientsListCommand());
|
|
894
|
+
clients.addCommand(createClientsGetCommand());
|
|
895
|
+
clients.addCommand(createClientsCreateCommand());
|
|
896
|
+
clients.addCommand(createApiKeysCommand());
|
|
897
|
+
return clients;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// src/commands/resources/index.ts
|
|
901
|
+
import { Command as Command22 } from "commander";
|
|
902
|
+
|
|
903
|
+
// src/commands/resources/list.ts
|
|
904
|
+
import { Command as Command17, Option as Option2 } from "commander";
|
|
905
|
+
function createResourcesListCommand() {
|
|
906
|
+
const cmd = new Command17("list").description("List registered resources (agents, workflows, tools) with optional filters").addHelpText("after", `
|
|
907
|
+
Examples:
|
|
908
|
+
$ nebulaos resources list List all resources
|
|
909
|
+
$ nebulaos resources list --type agent List only agents
|
|
910
|
+
$ nebulaos resources list --status official List official resources
|
|
911
|
+
$ nebulaos resources list --type workflow -o json List workflows as JSON
|
|
912
|
+
`).addOption(
|
|
913
|
+
new Option2("--type <type>", "Filter by resource type").choices(["agent", "workflow", "tool"])
|
|
914
|
+
).addOption(
|
|
915
|
+
new Option2("--status <status>", "Filter by resource status").choices(["discovered", "official"])
|
|
916
|
+
).action(async (options, command) => {
|
|
917
|
+
try {
|
|
918
|
+
const globalOpts = command.optsWithGlobals();
|
|
919
|
+
const format = resolveFormat(globalOpts.output);
|
|
920
|
+
const api = getApiClient();
|
|
921
|
+
const params = {};
|
|
922
|
+
if (options.type) params.type = options.type;
|
|
923
|
+
if (options.status) params.status = options.status;
|
|
924
|
+
if (globalOpts.page) params.page = String(globalOpts.page);
|
|
925
|
+
if (globalOpts.pageSize) params.pageSize = String(globalOpts.pageSize);
|
|
926
|
+
const { data } = await api.get("/resources", { params });
|
|
927
|
+
const resources = Array.isArray(data) ? data : data.data ?? [];
|
|
928
|
+
output(format, {
|
|
929
|
+
table: {
|
|
930
|
+
headers: ["ID", "Name", "Type", "Status", "Online", "Created At"],
|
|
931
|
+
rows: resources.map((r) => [
|
|
932
|
+
r.id,
|
|
933
|
+
r.displayName || r.name || "-",
|
|
934
|
+
r.type,
|
|
935
|
+
r.lifecycleStatus || r.status || "-",
|
|
936
|
+
r.isOnline ? "Yes" : "No",
|
|
937
|
+
r.firstSeenAt || r.createdAt ? new Date(r.firstSeenAt || r.createdAt).toLocaleString() : "-"
|
|
938
|
+
])
|
|
939
|
+
},
|
|
940
|
+
data: resources,
|
|
941
|
+
ids: resources.map((r) => r.id)
|
|
942
|
+
});
|
|
943
|
+
} catch (error) {
|
|
944
|
+
handleError(error);
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
addPaginationFlags(cmd);
|
|
948
|
+
return cmd;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
// src/commands/resources/get.ts
|
|
952
|
+
import { Command as Command18 } from "commander";
|
|
953
|
+
import chalk10 from "chalk";
|
|
954
|
+
function createResourcesGetCommand() {
|
|
955
|
+
const cmd = new Command18("get").description("Get detailed information about a specific resource").argument("<id>", "Resource ID").addHelpText("after", `
|
|
956
|
+
Examples:
|
|
957
|
+
$ nebulaos resources get 550e8400-e29b-41d4... View resource details
|
|
958
|
+
$ nebulaos resources get my-resource -o json Output as JSON
|
|
959
|
+
`).action(async (id, _options, command) => {
|
|
960
|
+
try {
|
|
961
|
+
const globalOpts = command.optsWithGlobals();
|
|
962
|
+
const format = resolveFormat(globalOpts.output);
|
|
963
|
+
const api = getApiClient();
|
|
964
|
+
const { data: resource } = await api.get(`/resources/${id}`);
|
|
965
|
+
if (format === "table") {
|
|
966
|
+
console.log(chalk10.bold("Resource Details\n"));
|
|
967
|
+
printDetail("ID", resource.id);
|
|
968
|
+
printDetail("Name", resource.displayName || resource.name || "-");
|
|
969
|
+
printDetail("Type", resource.type);
|
|
970
|
+
printDetail("Status", resource.lifecycleStatus || resource.status || "-");
|
|
971
|
+
if (resource.kind) printDetail("Kind", resource.kind);
|
|
972
|
+
if (resource.description) printDetail("Description", resource.description);
|
|
973
|
+
if (resource.clientId) printDetail("Client ID", resource.clientId);
|
|
974
|
+
if (resource.runtimeResourceId) printDetail("Runtime ID", resource.runtimeResourceId);
|
|
975
|
+
printDetail("Online", resource.isOnline ? "Yes" : "No");
|
|
976
|
+
printDetail("Created At", resource.createdAt ? new Date(resource.createdAt).toLocaleString() : "-");
|
|
977
|
+
printDetail("Updated At", resource.updatedAt ? new Date(resource.updatedAt).toLocaleString() : "-");
|
|
978
|
+
} else {
|
|
979
|
+
output(format, {
|
|
980
|
+
table: { headers: [], rows: [] },
|
|
981
|
+
data: resource,
|
|
982
|
+
ids: [resource.id]
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
} catch (error) {
|
|
986
|
+
handleError(error);
|
|
987
|
+
}
|
|
988
|
+
});
|
|
989
|
+
return cmd;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// src/commands/resources/archive.ts
|
|
993
|
+
import { Command as Command19 } from "commander";
|
|
994
|
+
function createResourcesArchiveCommand() {
|
|
995
|
+
const cmd = new Command19("archive").description("Archive a resource to hide it from active listings").argument("<id>", "Resource ID").addHelpText("after", `
|
|
996
|
+
Examples:
|
|
997
|
+
$ nebulaos resources archive 550e8400-e29b... Archive a resource by ID
|
|
998
|
+
|
|
999
|
+
Archived resources can be restored with the 'unarchive' command.
|
|
1000
|
+
`).action(async (id) => {
|
|
1001
|
+
try {
|
|
1002
|
+
const api = getApiClient();
|
|
1003
|
+
await api.patch(`/resources/${id}/archive`);
|
|
1004
|
+
printSuccess(`Resource '${id}' archived successfully.`);
|
|
1005
|
+
} catch (error) {
|
|
1006
|
+
handleError(error);
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
1009
|
+
return cmd;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
// src/commands/resources/unarchive.ts
|
|
1013
|
+
import { Command as Command20 } from "commander";
|
|
1014
|
+
function createResourcesUnarchiveCommand() {
|
|
1015
|
+
const cmd = new Command20("unarchive").description("Restore an archived resource back to active status").argument("<id>", "Resource ID").addHelpText("after", `
|
|
1016
|
+
Examples:
|
|
1017
|
+
$ nebulaos resources unarchive 550e8400-e29b... Restore an archived resource
|
|
1018
|
+
`).action(async (id) => {
|
|
1019
|
+
try {
|
|
1020
|
+
const api = getApiClient();
|
|
1021
|
+
await api.patch(`/resources/${id}/unarchive`);
|
|
1022
|
+
printSuccess(`Resource '${id}' unarchived successfully.`);
|
|
1023
|
+
} catch (error) {
|
|
1024
|
+
handleError(error);
|
|
1025
|
+
}
|
|
1026
|
+
});
|
|
1027
|
+
return cmd;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
// src/commands/resources/promote.ts
|
|
1031
|
+
import { Command as Command21 } from "commander";
|
|
1032
|
+
function createResourcesPromoteCommand() {
|
|
1033
|
+
const cmd = new Command21("promote").description("Promote a discovered resource to official status").argument("<id>", "Resource ID").addHelpText("after", `
|
|
1034
|
+
Examples:
|
|
1035
|
+
$ nebulaos resources promote 550e8400-e29b... Promote a resource to official
|
|
1036
|
+
|
|
1037
|
+
Discovered resources are auto-registered by clients. Promoting makes them official.
|
|
1038
|
+
`).action(async (id) => {
|
|
1039
|
+
try {
|
|
1040
|
+
const api = getApiClient();
|
|
1041
|
+
await api.patch(`/resources/${id}/promote`);
|
|
1042
|
+
printSuccess(`Resource '${id}' promoted to official.`);
|
|
1043
|
+
} catch (error) {
|
|
1044
|
+
handleError(error);
|
|
1045
|
+
}
|
|
1046
|
+
});
|
|
1047
|
+
return cmd;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
// src/commands/resources/index.ts
|
|
1051
|
+
function createResourcesCommand() {
|
|
1052
|
+
const resources = new Command22("resources").description("List, inspect, archive, and promote resources (agents, workflows, tools)");
|
|
1053
|
+
resources.addCommand(createResourcesListCommand());
|
|
1054
|
+
resources.addCommand(createResourcesGetCommand());
|
|
1055
|
+
resources.addCommand(createResourcesArchiveCommand());
|
|
1056
|
+
resources.addCommand(createResourcesUnarchiveCommand());
|
|
1057
|
+
resources.addCommand(createResourcesPromoteCommand());
|
|
1058
|
+
return resources;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
// src/commands/execution/index.ts
|
|
1062
|
+
import { Command as Command27 } from "commander";
|
|
1063
|
+
|
|
1064
|
+
// src/commands/execution/run.ts
|
|
1065
|
+
import { Command as Command23 } from "commander";
|
|
1066
|
+
import ora from "ora";
|
|
1067
|
+
import chalk11 from "chalk";
|
|
1068
|
+
function createExecutionRunCommand() {
|
|
1069
|
+
const cmd = new Command23("run").description("Trigger execution of an agent or workflow by resource ID").argument("<resourceId>", "Resource ID to execute").option("-i, --input <json>", "Input data as JSON string").addHelpText("after", `
|
|
1070
|
+
Examples:
|
|
1071
|
+
$ nebulaos exec run 550e8400-e29b... Run a resource
|
|
1072
|
+
$ nebulaos exec run my-agent -i '{"prompt":"Hello"}' Run with JSON input
|
|
1073
|
+
$ nebulaos exec run my-workflow -i '{"data":[1,2,3]}' -o json Run and output as JSON
|
|
1074
|
+
`).action(async (resourceId, options, command) => {
|
|
1075
|
+
try {
|
|
1076
|
+
const globalOpts = command.optsWithGlobals();
|
|
1077
|
+
const format = resolveFormat(globalOpts.output);
|
|
1078
|
+
const api = getApiClient();
|
|
1079
|
+
let input4;
|
|
1080
|
+
if (options.input) {
|
|
1081
|
+
try {
|
|
1082
|
+
input4 = JSON.parse(options.input);
|
|
1083
|
+
} catch {
|
|
1084
|
+
throw new CLIError(
|
|
1085
|
+
"Invalid JSON input. Provide valid JSON with --input.",
|
|
1086
|
+
7 /* ValidationError */
|
|
1087
|
+
);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
const spinner = ora("Starting execution...").start();
|
|
1091
|
+
const { data: execution } = await api.post("/execution", {
|
|
1092
|
+
resourceId,
|
|
1093
|
+
input: input4
|
|
1094
|
+
});
|
|
1095
|
+
spinner.succeed("Execution started.");
|
|
1096
|
+
if (format === "table") {
|
|
1097
|
+
console.log(chalk11.bold("\nExecution Details\n"));
|
|
1098
|
+
printDetail("Execution ID", execution.id);
|
|
1099
|
+
printDetail("Resource ID", execution.resourceId || resourceId);
|
|
1100
|
+
printDetail("Status", execution.status);
|
|
1101
|
+
if (execution.result !== void 0) {
|
|
1102
|
+
printDetail("Result", JSON.stringify(execution.result));
|
|
1103
|
+
}
|
|
1104
|
+
} else {
|
|
1105
|
+
output(format, {
|
|
1106
|
+
table: { headers: [], rows: [] },
|
|
1107
|
+
data: execution,
|
|
1108
|
+
ids: [execution.id]
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
} catch (error) {
|
|
1112
|
+
handleError(error);
|
|
1113
|
+
}
|
|
1114
|
+
});
|
|
1115
|
+
return cmd;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// src/commands/execution/list.ts
|
|
1119
|
+
import { Command as Command24, Option as Option3 } from "commander";
|
|
1120
|
+
function createExecutionListCommand() {
|
|
1121
|
+
const cmd = new Command24("list").description("List past and ongoing executions with optional status filter").addHelpText("after", `
|
|
1122
|
+
Examples:
|
|
1123
|
+
$ nebulaos exec list List recent executions
|
|
1124
|
+
$ nebulaos exec list --status running Show only running executions
|
|
1125
|
+
$ nebulaos exec list --limit 5 Show last 5 executions
|
|
1126
|
+
$ nebulaos exec list -o json Output as JSON
|
|
1127
|
+
`).addOption(
|
|
1128
|
+
new Option3("--status <status>", "Filter by execution status")
|
|
1129
|
+
).addOption(
|
|
1130
|
+
new Option3("--limit <number>", "Limit number of results").argParser(parseInt)
|
|
1131
|
+
).action(async (options, command) => {
|
|
1132
|
+
try {
|
|
1133
|
+
const globalOpts = command.optsWithGlobals();
|
|
1134
|
+
const format = resolveFormat(globalOpts.output);
|
|
1135
|
+
const api = getApiClient();
|
|
1136
|
+
const params = {};
|
|
1137
|
+
if (options.status) params.status = options.status;
|
|
1138
|
+
if (options.limit) params.limit = String(options.limit);
|
|
1139
|
+
if (globalOpts.page) params.page = String(globalOpts.page);
|
|
1140
|
+
if (globalOpts.pageSize) params.pageSize = String(globalOpts.pageSize);
|
|
1141
|
+
const { data } = await api.get("/execution", { params });
|
|
1142
|
+
const executions = Array.isArray(data) ? data : data.items ?? data.data ?? [];
|
|
1143
|
+
output(format, {
|
|
1144
|
+
table: {
|
|
1145
|
+
headers: ["ID", "Resource", "Status", "Started At", "Duration"],
|
|
1146
|
+
rows: executions.map((e) => [
|
|
1147
|
+
e.id,
|
|
1148
|
+
e.targetName,
|
|
1149
|
+
e.status,
|
|
1150
|
+
e.startedAt ? new Date(e.startedAt).toLocaleString() : "-",
|
|
1151
|
+
e.completedAt && e.startedAt ? `${new Date(e.completedAt).getTime() - new Date(e.startedAt).getTime()}ms` : "-"
|
|
1152
|
+
])
|
|
1153
|
+
},
|
|
1154
|
+
data: executions,
|
|
1155
|
+
ids: executions.map((e) => e.id)
|
|
1156
|
+
});
|
|
1157
|
+
} catch (error) {
|
|
1158
|
+
handleError(error);
|
|
1159
|
+
}
|
|
1160
|
+
});
|
|
1161
|
+
addPaginationFlags(cmd);
|
|
1162
|
+
return cmd;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
// src/commands/execution/get.ts
|
|
1166
|
+
import { Command as Command25 } from "commander";
|
|
1167
|
+
import chalk12 from "chalk";
|
|
1168
|
+
function createExecutionGetCommand() {
|
|
1169
|
+
const cmd = new Command25("get").description("Get detailed status and result of a specific execution").argument("<id>", "Execution ID").addHelpText("after", `
|
|
1170
|
+
Examples:
|
|
1171
|
+
$ nebulaos exec get 550e8400-e29b-41d4... View execution details
|
|
1172
|
+
$ nebulaos exec get my-exec-id -o json Output as JSON
|
|
1173
|
+
`).action(async (id, _options, command) => {
|
|
1174
|
+
try {
|
|
1175
|
+
const globalOpts = command.optsWithGlobals();
|
|
1176
|
+
const format = resolveFormat(globalOpts.output);
|
|
1177
|
+
const api = getApiClient();
|
|
1178
|
+
const { data: execution } = await api.get(`/execution/${id}`);
|
|
1179
|
+
if (format === "table") {
|
|
1180
|
+
console.log(chalk12.bold("Execution Details\n"));
|
|
1181
|
+
printDetail("ID", execution.id);
|
|
1182
|
+
if (execution.resourceId) printDetail("Resource ID", execution.resourceId);
|
|
1183
|
+
if (execution.resourceName) printDetail("Resource", execution.resourceName);
|
|
1184
|
+
printDetail("Status", execution.status);
|
|
1185
|
+
if (execution.startedAt) printDetail("Started At", new Date(execution.startedAt).toLocaleString());
|
|
1186
|
+
if (execution.completedAt) printDetail("Completed At", new Date(execution.completedAt).toLocaleString());
|
|
1187
|
+
if (execution.duration) printDetail("Duration", `${execution.duration}ms`);
|
|
1188
|
+
if (execution.error) printDetail("Error", execution.error);
|
|
1189
|
+
if (execution.result !== void 0) {
|
|
1190
|
+
printDetail("Result", JSON.stringify(execution.result, null, 2));
|
|
1191
|
+
}
|
|
1192
|
+
} else {
|
|
1193
|
+
output(format, {
|
|
1194
|
+
table: { headers: [], rows: [] },
|
|
1195
|
+
data: execution,
|
|
1196
|
+
ids: [execution.id]
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
} catch (error) {
|
|
1200
|
+
handleError(error);
|
|
1201
|
+
}
|
|
1202
|
+
});
|
|
1203
|
+
return cmd;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
// src/commands/execution/logs.ts
|
|
1207
|
+
import { Command as Command26 } from "commander";
|
|
1208
|
+
import chalk13 from "chalk";
|
|
1209
|
+
function createExecutionLogsCommand() {
|
|
1210
|
+
const cmd = new Command26("logs").description("View execution cost items and LLM calls for a specific execution").argument("<id>", "Execution ID").option("-l, --limit <n>", "Limit number of items", "50").addHelpText("after", `
|
|
1211
|
+
Examples:
|
|
1212
|
+
$ nebulaos exec logs 550e8400-e29b... View execution cost items
|
|
1213
|
+
$ nebulaos exec logs my-exec-id --limit 10 Show last 10 items
|
|
1214
|
+
$ nebulaos exec logs my-exec-id -o json Output as JSON
|
|
1215
|
+
`).action(async (id, options, command) => {
|
|
1216
|
+
try {
|
|
1217
|
+
const globalOpts = command.optsWithGlobals();
|
|
1218
|
+
const format = resolveFormat(globalOpts.output);
|
|
1219
|
+
const api = getApiClient();
|
|
1220
|
+
const { data } = await api.get(`/execution/${id}/cost-items`, {
|
|
1221
|
+
params: { limit: options.limit }
|
|
1222
|
+
});
|
|
1223
|
+
const items = Array.isArray(data) ? data : data.data ?? [];
|
|
1224
|
+
if (format === "table") {
|
|
1225
|
+
if (items.length === 0) {
|
|
1226
|
+
console.log(chalk13.gray("No cost items available for this execution."));
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
console.log(chalk13.bold("\nExecution Cost Items\n"));
|
|
1230
|
+
for (const item of items) {
|
|
1231
|
+
const timestamp = item.createdAt ? chalk13.gray(new Date(item.createdAt).toLocaleTimeString()) : "";
|
|
1232
|
+
const type = formatType(item.type);
|
|
1233
|
+
const model = item.model ? chalk13.cyan(`[${item.model}]`) : "";
|
|
1234
|
+
const tokens = item.inputTokens || item.outputTokens ? chalk13.yellow(`(${item.inputTokens || 0}\u2192${item.outputTokens || 0} tokens)`) : "";
|
|
1235
|
+
const cost = item.totalCost ? chalk13.green(`$${item.totalCost.toFixed(6)}`) : "";
|
|
1236
|
+
console.log(`${timestamp} ${type} ${model} ${tokens} ${cost}`);
|
|
1237
|
+
if (item.input && globalOpts.verbose) {
|
|
1238
|
+
console.log(chalk13.gray(` Input: ${truncate(JSON.stringify(item.input), 100)}`));
|
|
1239
|
+
}
|
|
1240
|
+
if (item.output && globalOpts.verbose) {
|
|
1241
|
+
console.log(chalk13.gray(` Output: ${truncate(JSON.stringify(item.output), 100)}`));
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
console.log(chalk13.gray(`
|
|
1245
|
+
Showing ${items.length} items. Use --limit to see more.`));
|
|
1246
|
+
} else {
|
|
1247
|
+
output(format, {
|
|
1248
|
+
table: { headers: [], rows: [] },
|
|
1249
|
+
data: items,
|
|
1250
|
+
ids: items.map((i) => i.id ?? "")
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
} catch (error) {
|
|
1254
|
+
handleError(error);
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
return cmd;
|
|
1258
|
+
}
|
|
1259
|
+
function formatType(type) {
|
|
1260
|
+
switch (type?.toLowerCase()) {
|
|
1261
|
+
case "llm_call":
|
|
1262
|
+
case "llm":
|
|
1263
|
+
return chalk13.blue("[LLM] ");
|
|
1264
|
+
case "tool_call":
|
|
1265
|
+
case "tool":
|
|
1266
|
+
return chalk13.magenta("[TOOL] ");
|
|
1267
|
+
case "embedding":
|
|
1268
|
+
return chalk13.cyan("[EMBED]");
|
|
1269
|
+
default:
|
|
1270
|
+
return chalk13.gray("[ITEM] ");
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
function truncate(str, maxLen) {
|
|
1274
|
+
if (str.length <= maxLen) return str;
|
|
1275
|
+
return str.slice(0, maxLen - 3) + "...";
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
// src/commands/execution/index.ts
|
|
1279
|
+
function createExecutionCommand() {
|
|
1280
|
+
const execution = new Command27("execution").alias("exec").description("Run agents/workflows and inspect execution results and logs");
|
|
1281
|
+
execution.addCommand(createExecutionRunCommand());
|
|
1282
|
+
execution.addCommand(createExecutionListCommand());
|
|
1283
|
+
execution.addCommand(createExecutionGetCommand());
|
|
1284
|
+
execution.addCommand(createExecutionLogsCommand());
|
|
1285
|
+
return execution;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
// src/commands/rag/index.ts
|
|
1289
|
+
import { Command as Command36 } from "commander";
|
|
1290
|
+
|
|
1291
|
+
// src/commands/rag/connections/index.ts
|
|
1292
|
+
import { Command as Command34 } from "commander";
|
|
1293
|
+
|
|
1294
|
+
// src/commands/rag/connections/list.ts
|
|
1295
|
+
import { Command as Command28 } from "commander";
|
|
1296
|
+
function createConnectionsListCommand() {
|
|
1297
|
+
const cmd = new Command28("list").description("List all configured RAG connections").addHelpText("after", `
|
|
1298
|
+
Examples:
|
|
1299
|
+
$ nebulaos rag connections list List all connections
|
|
1300
|
+
$ nebulaos rag connections list -o json Output as JSON
|
|
1301
|
+
$ nebulaos rag connections list --page 2 Show page 2
|
|
1302
|
+
`).action(async (_options, command) => {
|
|
1303
|
+
try {
|
|
1304
|
+
const globalOpts = command.optsWithGlobals();
|
|
1305
|
+
const format = resolveFormat(globalOpts.output);
|
|
1306
|
+
const api = getApiClient();
|
|
1307
|
+
const params = {};
|
|
1308
|
+
if (globalOpts.page) params.page = globalOpts.page;
|
|
1309
|
+
if (globalOpts.pageSize) params.pageSize = globalOpts.pageSize;
|
|
1310
|
+
const { data } = await api.get("/rag/openai-connections", { params });
|
|
1311
|
+
const connections = Array.isArray(data) ? data : data.data || [];
|
|
1312
|
+
output(format, {
|
|
1313
|
+
table: {
|
|
1314
|
+
headers: ["ID", "Name", "Type", "Status", "Created At"],
|
|
1315
|
+
rows: connections.map((c) => [
|
|
1316
|
+
c.id,
|
|
1317
|
+
c.name || "-",
|
|
1318
|
+
c.type || "-",
|
|
1319
|
+
c.status || "-",
|
|
1320
|
+
c.createdAt || "-"
|
|
1321
|
+
])
|
|
1322
|
+
},
|
|
1323
|
+
data: connections,
|
|
1324
|
+
ids: connections.map((c) => c.id)
|
|
1325
|
+
});
|
|
1326
|
+
} catch (error) {
|
|
1327
|
+
handleError(error);
|
|
1328
|
+
}
|
|
1329
|
+
});
|
|
1330
|
+
addPaginationFlags(cmd);
|
|
1331
|
+
return cmd;
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
// src/commands/rag/connections/get.ts
|
|
1335
|
+
import { Command as Command29 } from "commander";
|
|
1336
|
+
import chalk14 from "chalk";
|
|
1337
|
+
function createConnectionsGetCommand() {
|
|
1338
|
+
const cmd = new Command29("get").description("Get detailed information about a RAG connection").argument("<id>", "Connection ID").addHelpText("after", `
|
|
1339
|
+
Examples:
|
|
1340
|
+
$ nebulaos rag connections get conn-123 View connection details
|
|
1341
|
+
$ nebulaos rag connections get conn-123 -o json Output as JSON
|
|
1342
|
+
`).action(async (id, _options, command) => {
|
|
1343
|
+
try {
|
|
1344
|
+
const globalOpts = command.optsWithGlobals();
|
|
1345
|
+
const format = resolveFormat(globalOpts.output);
|
|
1346
|
+
const api = getApiClient();
|
|
1347
|
+
const { data } = await api.get(`/rag/openai-connections/${id}`);
|
|
1348
|
+
if (format === "table") {
|
|
1349
|
+
console.log(chalk14.bold("RAG Connection\n"));
|
|
1350
|
+
printDetail("ID", data.id);
|
|
1351
|
+
printDetail("Name", data.name || "-");
|
|
1352
|
+
printDetail("Type", data.type || "-");
|
|
1353
|
+
printDetail("Status", data.status || "-");
|
|
1354
|
+
if (data.baseUrl) printDetail("Base URL", data.baseUrl);
|
|
1355
|
+
if (data.model) printDetail("Model", data.model);
|
|
1356
|
+
printDetail("Created At", data.createdAt || "-");
|
|
1357
|
+
printDetail("Updated At", data.updatedAt || "-");
|
|
1358
|
+
} else {
|
|
1359
|
+
output(format, {
|
|
1360
|
+
table: { headers: [], rows: [] },
|
|
1361
|
+
data,
|
|
1362
|
+
ids: [data.id]
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
} catch (error) {
|
|
1366
|
+
handleError(error);
|
|
1367
|
+
}
|
|
1368
|
+
});
|
|
1369
|
+
return cmd;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
// src/commands/rag/connections/create.ts
|
|
1373
|
+
import { Command as Command30 } from "commander";
|
|
1374
|
+
function createConnectionsCreateCommand() {
|
|
1375
|
+
const cmd = new Command30("create").description("Create a new RAG connection to a vector store provider").requiredOption("--name <name>", "Connection name").requiredOption("--type <type>", "Connection type (e.g., openai, azure)").option("--base-url <url>", "Base URL for the connection").option("--api-key <key>", "API key for the connection").option("--model <model>", "Model name").option("--dimensions <number>", "Embedding dimensions", parseInt).addHelpText("after", `
|
|
1376
|
+
Examples:
|
|
1377
|
+
$ nebulaos rag connections create --name my-store --type openai \\
|
|
1378
|
+
--api-key sk-... --model text-embedding-3-small
|
|
1379
|
+
$ nebulaos rag connections create --name azure-store --type azure \\
|
|
1380
|
+
--base-url https://my-resource.openai.azure.com --api-key abc123 \\
|
|
1381
|
+
--dimensions 1536
|
|
1382
|
+
`).action(async (options, command) => {
|
|
1383
|
+
try {
|
|
1384
|
+
const globalOpts = command.optsWithGlobals();
|
|
1385
|
+
const format = resolveFormat(globalOpts.output);
|
|
1386
|
+
const api = getApiClient();
|
|
1387
|
+
const body = {
|
|
1388
|
+
name: options.name,
|
|
1389
|
+
type: options.type
|
|
1390
|
+
};
|
|
1391
|
+
if (options.baseUrl) body.baseUrl = options.baseUrl;
|
|
1392
|
+
if (options.apiKey) body.apiKey = options.apiKey;
|
|
1393
|
+
if (options.model) body.model = options.model;
|
|
1394
|
+
if (options.dimensions) body.dimensions = options.dimensions;
|
|
1395
|
+
const { data } = await api.post("/rag/openai-connections", body);
|
|
1396
|
+
if (format === "table") {
|
|
1397
|
+
printSuccess("RAG connection created successfully.");
|
|
1398
|
+
printDetail("ID", data.id);
|
|
1399
|
+
printDetail("Name", data.name);
|
|
1400
|
+
} else {
|
|
1401
|
+
output(format, {
|
|
1402
|
+
table: { headers: [], rows: [] },
|
|
1403
|
+
data,
|
|
1404
|
+
ids: [data.id]
|
|
1405
|
+
});
|
|
1406
|
+
}
|
|
1407
|
+
} catch (error) {
|
|
1408
|
+
handleError(error);
|
|
1409
|
+
}
|
|
1410
|
+
});
|
|
1411
|
+
return cmd;
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
// src/commands/rag/connections/update.ts
|
|
1415
|
+
import { Command as Command31 } from "commander";
|
|
1416
|
+
function createConnectionsUpdateCommand() {
|
|
1417
|
+
const cmd = new Command31("update").description("Update an existing RAG connection's configuration").argument("<id>", "Connection ID").option("--name <name>", "Connection name").option("--base-url <url>", "Base URL for the connection").option("--api-key <key>", "API key for the connection").option("--model <model>", "Model name").option("--dimensions <number>", "Embedding dimensions", parseInt).addHelpText("after", `
|
|
1418
|
+
Examples:
|
|
1419
|
+
$ nebulaos rag connections update conn-123 --name new-name Rename connection
|
|
1420
|
+
$ nebulaos rag connections update conn-123 --api-key sk-new... Rotate API key
|
|
1421
|
+
$ nebulaos rag connections update conn-123 --model text-embedding-3-large
|
|
1422
|
+
`).action(async (id, options, command) => {
|
|
1423
|
+
try {
|
|
1424
|
+
const globalOpts = command.optsWithGlobals();
|
|
1425
|
+
const format = resolveFormat(globalOpts.output);
|
|
1426
|
+
const api = getApiClient();
|
|
1427
|
+
const body = {};
|
|
1428
|
+
if (options.name) body.name = options.name;
|
|
1429
|
+
if (options.baseUrl) body.baseUrl = options.baseUrl;
|
|
1430
|
+
if (options.apiKey) body.apiKey = options.apiKey;
|
|
1431
|
+
if (options.model) body.model = options.model;
|
|
1432
|
+
if (options.dimensions) body.dimensions = options.dimensions;
|
|
1433
|
+
const { data } = await api.patch(`/rag/openai-connections/${id}`, body);
|
|
1434
|
+
if (format === "table") {
|
|
1435
|
+
printSuccess("RAG connection updated successfully.");
|
|
1436
|
+
printDetail("ID", data.id);
|
|
1437
|
+
printDetail("Name", data.name);
|
|
1438
|
+
} else {
|
|
1439
|
+
output(format, {
|
|
1440
|
+
table: { headers: [], rows: [] },
|
|
1441
|
+
data,
|
|
1442
|
+
ids: [data.id]
|
|
1443
|
+
});
|
|
1444
|
+
}
|
|
1445
|
+
} catch (error) {
|
|
1446
|
+
handleError(error);
|
|
1447
|
+
}
|
|
1448
|
+
});
|
|
1449
|
+
return cmd;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
// src/commands/rag/connections/delete.ts
|
|
1453
|
+
import { Command as Command32 } from "commander";
|
|
1454
|
+
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
1455
|
+
function createConnectionsDeleteCommand() {
|
|
1456
|
+
const cmd = new Command32("delete").description("Permanently delete a RAG connection").argument("<id>", "Connection ID").option("-y, --yes", "Skip confirmation prompt").addHelpText("after", `
|
|
1457
|
+
Examples:
|
|
1458
|
+
$ nebulaos rag connections delete conn-123 Delete with confirmation
|
|
1459
|
+
$ nebulaos rag connections delete conn-123 -y Delete without confirmation
|
|
1460
|
+
`).action(async (id, options) => {
|
|
1461
|
+
try {
|
|
1462
|
+
if (!options.yes) {
|
|
1463
|
+
const shouldProceed = await confirm3({
|
|
1464
|
+
message: `Delete RAG connection '${id}'?`,
|
|
1465
|
+
default: false
|
|
1466
|
+
});
|
|
1467
|
+
if (!shouldProceed) {
|
|
1468
|
+
console.log("Cancelled.");
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
const api = getApiClient();
|
|
1473
|
+
await api.delete(`/rag/openai-connections/${id}`);
|
|
1474
|
+
printSuccess(`RAG connection '${id}' deleted.`);
|
|
1475
|
+
} catch (error) {
|
|
1476
|
+
handleError(error);
|
|
1477
|
+
}
|
|
1478
|
+
});
|
|
1479
|
+
return cmd;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
// src/commands/rag/connections/test.ts
|
|
1483
|
+
import { Command as Command33 } from "commander";
|
|
1484
|
+
import chalk15 from "chalk";
|
|
1485
|
+
function createConnectionsTestCommand() {
|
|
1486
|
+
const cmd = new Command33("test").description("Test connectivity and authentication of a RAG connection").argument("<id>", "Connection ID").addHelpText("after", `
|
|
1487
|
+
Examples:
|
|
1488
|
+
$ nebulaos rag connections test conn-123 Test a connection
|
|
1489
|
+
$ nebulaos rag connections test conn-123 -o json Output result as JSON
|
|
1490
|
+
`).action(async (id, _options, command) => {
|
|
1491
|
+
try {
|
|
1492
|
+
const globalOpts = command.optsWithGlobals();
|
|
1493
|
+
const format = resolveFormat(globalOpts.output);
|
|
1494
|
+
const api = getApiClient();
|
|
1495
|
+
console.log("Testing connection...");
|
|
1496
|
+
const { data } = await api.post(`/rag/openai-connections/${id}/test`);
|
|
1497
|
+
if (format === "table") {
|
|
1498
|
+
if (data.success) {
|
|
1499
|
+
console.log(chalk15.green("Connection test passed."));
|
|
1500
|
+
} else {
|
|
1501
|
+
console.log(chalk15.red(`Connection test failed: ${data.error || "Unknown error"}`));
|
|
1502
|
+
}
|
|
1503
|
+
} else {
|
|
1504
|
+
output(format, {
|
|
1505
|
+
table: { headers: [], rows: [] },
|
|
1506
|
+
data,
|
|
1507
|
+
ids: [id]
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1510
|
+
} catch (error) {
|
|
1511
|
+
handleError(error);
|
|
1512
|
+
}
|
|
1513
|
+
});
|
|
1514
|
+
return cmd;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
// src/commands/rag/connections/index.ts
|
|
1518
|
+
function createConnectionsCommand() {
|
|
1519
|
+
const connections = new Command34("connections").description("Create, configure, and test RAG vector store connections");
|
|
1520
|
+
connections.addCommand(createConnectionsListCommand());
|
|
1521
|
+
connections.addCommand(createConnectionsGetCommand());
|
|
1522
|
+
connections.addCommand(createConnectionsCreateCommand());
|
|
1523
|
+
connections.addCommand(createConnectionsUpdateCommand());
|
|
1524
|
+
connections.addCommand(createConnectionsDeleteCommand());
|
|
1525
|
+
connections.addCommand(createConnectionsTestCommand());
|
|
1526
|
+
return connections;
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
// src/commands/rag/search.ts
|
|
1530
|
+
import { Command as Command35 } from "commander";
|
|
1531
|
+
function createRagSearchCommand() {
|
|
1532
|
+
const cmd = new Command35("search").description("Perform a semantic search against a RAG connection").requiredOption("--connection <id>", "Connection ID to search against").requiredOption("--query <query>", "Search query").option("--limit <number>", "Maximum number of results", parseInt).addHelpText("after", `
|
|
1533
|
+
Examples:
|
|
1534
|
+
$ nebulaos rag search --connection conn-123 --query "patient intake process"
|
|
1535
|
+
$ nebulaos rag search --connection conn-123 --query "billing codes" --limit 5
|
|
1536
|
+
$ nebulaos rag search --connection conn-123 --query "diagnosis" -o json
|
|
1537
|
+
`).action(async (options, command) => {
|
|
1538
|
+
try {
|
|
1539
|
+
const globalOpts = command.optsWithGlobals();
|
|
1540
|
+
const format = resolveFormat(globalOpts.output);
|
|
1541
|
+
const api = getApiClient();
|
|
1542
|
+
const body = {
|
|
1543
|
+
connectionId: options.connection,
|
|
1544
|
+
query: options.query
|
|
1545
|
+
};
|
|
1546
|
+
if (options.limit) body.limit = options.limit;
|
|
1547
|
+
const { data } = await api.post("/rag/search", body);
|
|
1548
|
+
const results = Array.isArray(data) ? data : data.results || [];
|
|
1549
|
+
output(format, {
|
|
1550
|
+
table: {
|
|
1551
|
+
headers: ["#", "Score", "Content"],
|
|
1552
|
+
rows: results.map((r, i) => [
|
|
1553
|
+
String(i + 1),
|
|
1554
|
+
String(r.score ?? "-"),
|
|
1555
|
+
String(r.content || r.text || "-").slice(0, 100)
|
|
1556
|
+
])
|
|
1557
|
+
},
|
|
1558
|
+
data: results,
|
|
1559
|
+
ids: results.map((_, i) => String(i + 1))
|
|
1560
|
+
});
|
|
1561
|
+
} catch (error) {
|
|
1562
|
+
handleError(error);
|
|
1563
|
+
}
|
|
1564
|
+
});
|
|
1565
|
+
return cmd;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
// src/commands/rag/index.ts
|
|
1569
|
+
function createRagCommand() {
|
|
1570
|
+
const rag = new Command36("rag").description("Manage RAG connections and perform semantic searches");
|
|
1571
|
+
rag.addCommand(createConnectionsCommand());
|
|
1572
|
+
rag.addCommand(createRagSearchCommand());
|
|
1573
|
+
return rag;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
// src/commands/features/index.ts
|
|
1577
|
+
import { Command as Command43 } from "commander";
|
|
1578
|
+
|
|
1579
|
+
// src/commands/features/catalog.ts
|
|
1580
|
+
import { Command as Command37 } from "commander";
|
|
1581
|
+
function createFeaturesCatalogCommand() {
|
|
1582
|
+
const cmd = new Command37("catalog").description("List all available features in the platform catalog").addHelpText("after", `
|
|
1583
|
+
Examples:
|
|
1584
|
+
$ nebulaos features catalog Browse all available features
|
|
1585
|
+
$ nebulaos features catalog -o json Output as JSON
|
|
1586
|
+
`).action(async (_options, command) => {
|
|
1587
|
+
try {
|
|
1588
|
+
const globalOpts = command.optsWithGlobals();
|
|
1589
|
+
const format = resolveFormat(globalOpts.output);
|
|
1590
|
+
const api = getApiClient();
|
|
1591
|
+
const { data } = await api.get("/features/catalog");
|
|
1592
|
+
const features = Array.isArray(data) ? data : data.data || [];
|
|
1593
|
+
output(format, {
|
|
1594
|
+
table: {
|
|
1595
|
+
headers: ["Key", "Name", "Description", "Category"],
|
|
1596
|
+
rows: features.map((f) => [
|
|
1597
|
+
f.key,
|
|
1598
|
+
f.name || "-",
|
|
1599
|
+
f.description || "-",
|
|
1600
|
+
f.category || "-"
|
|
1601
|
+
])
|
|
1602
|
+
},
|
|
1603
|
+
data: features,
|
|
1604
|
+
ids: features.map((f) => f.key)
|
|
1605
|
+
});
|
|
1606
|
+
} catch (error) {
|
|
1607
|
+
handleError(error);
|
|
1608
|
+
}
|
|
1609
|
+
});
|
|
1610
|
+
return cmd;
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
// src/commands/features/list.ts
|
|
1614
|
+
import { Command as Command38 } from "commander";
|
|
1615
|
+
function createFeaturesListCommand() {
|
|
1616
|
+
const cmd = new Command38("list").description("List features enabled for the current organization").addHelpText("after", `
|
|
1617
|
+
Examples:
|
|
1618
|
+
$ nebulaos features list List enabled features
|
|
1619
|
+
$ nebulaos features list -o json Output as JSON
|
|
1620
|
+
`).action(async (_options, command) => {
|
|
1621
|
+
try {
|
|
1622
|
+
const globalOpts = command.optsWithGlobals();
|
|
1623
|
+
const format = resolveFormat(globalOpts.output);
|
|
1624
|
+
const api = getApiClient();
|
|
1625
|
+
const { data } = await api.get("/features");
|
|
1626
|
+
const features = Array.isArray(data) ? data : data.data || [];
|
|
1627
|
+
output(format, {
|
|
1628
|
+
table: {
|
|
1629
|
+
headers: ["Key", "Name", "Enabled", "Updated At"],
|
|
1630
|
+
rows: features.map((f) => [
|
|
1631
|
+
String(f.key),
|
|
1632
|
+
String(f.name || "-"),
|
|
1633
|
+
String(f.enabled ?? f.isEnabled ?? "-"),
|
|
1634
|
+
String(f.updatedAt || "-")
|
|
1635
|
+
])
|
|
1636
|
+
},
|
|
1637
|
+
data: features,
|
|
1638
|
+
ids: features.map((f) => f.key)
|
|
1639
|
+
});
|
|
1640
|
+
} catch (error) {
|
|
1641
|
+
handleError(error);
|
|
1642
|
+
}
|
|
1643
|
+
});
|
|
1644
|
+
return cmd;
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
// src/commands/features/get.ts
|
|
1648
|
+
import { Command as Command39 } from "commander";
|
|
1649
|
+
import chalk16 from "chalk";
|
|
1650
|
+
function createFeaturesGetCommand() {
|
|
1651
|
+
const cmd = new Command39("get").description("Get details and configuration of a specific feature").argument("<key>", "Feature key").addHelpText("after", `
|
|
1652
|
+
Examples:
|
|
1653
|
+
$ nebulaos features get llm-gateway View feature details
|
|
1654
|
+
$ nebulaos features get rag -o json Output as JSON
|
|
1655
|
+
`).action(async (key, _options, command) => {
|
|
1656
|
+
try {
|
|
1657
|
+
const globalOpts = command.optsWithGlobals();
|
|
1658
|
+
const format = resolveFormat(globalOpts.output);
|
|
1659
|
+
const api = getApiClient();
|
|
1660
|
+
const { data } = await api.get(`/features/${key}`);
|
|
1661
|
+
if (format === "table") {
|
|
1662
|
+
console.log(chalk16.bold("Feature\n"));
|
|
1663
|
+
printDetail("Key", data.key);
|
|
1664
|
+
printDetail("Name", data.name || "-");
|
|
1665
|
+
printDetail("Description", data.description || "-");
|
|
1666
|
+
printDetail("Enabled", data.enabled ?? data.isEnabled ? "true" : "false");
|
|
1667
|
+
if (data.config) {
|
|
1668
|
+
printDetail("Config", JSON.stringify(data.config, null, 2));
|
|
1669
|
+
}
|
|
1670
|
+
printDetail("Updated At", data.updatedAt || "-");
|
|
1671
|
+
} else {
|
|
1672
|
+
output(format, {
|
|
1673
|
+
table: { headers: [], rows: [] },
|
|
1674
|
+
data,
|
|
1675
|
+
ids: [data.key]
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1678
|
+
} catch (error) {
|
|
1679
|
+
handleError(error);
|
|
1680
|
+
}
|
|
1681
|
+
});
|
|
1682
|
+
return cmd;
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
// src/commands/features/enable.ts
|
|
1686
|
+
import { Command as Command40 } from "commander";
|
|
1687
|
+
function createFeaturesEnableCommand() {
|
|
1688
|
+
const cmd = new Command40("enable").description("Enable a feature for the current organization").argument("<key>", "Feature key").addHelpText("after", `
|
|
1689
|
+
Examples:
|
|
1690
|
+
$ nebulaos features enable llm-gateway Enable the LLM gateway feature
|
|
1691
|
+
$ nebulaos features enable rag Enable the RAG feature
|
|
1692
|
+
`).action(async (key) => {
|
|
1693
|
+
try {
|
|
1694
|
+
const api = getApiClient();
|
|
1695
|
+
await api.post(`/features/${key}/enable`);
|
|
1696
|
+
printSuccess(`Feature '${key}' enabled.`);
|
|
1697
|
+
} catch (error) {
|
|
1698
|
+
handleError(error);
|
|
1699
|
+
}
|
|
1700
|
+
});
|
|
1701
|
+
return cmd;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
// src/commands/features/disable.ts
|
|
1705
|
+
import { Command as Command41 } from "commander";
|
|
1706
|
+
function createFeaturesDisableCommand() {
|
|
1707
|
+
const cmd = new Command41("disable").description("Disable a feature for the current organization").argument("<key>", "Feature key").addHelpText("after", `
|
|
1708
|
+
Examples:
|
|
1709
|
+
$ nebulaos features disable llm-gateway Disable the LLM gateway feature
|
|
1710
|
+
$ nebulaos features disable rag Disable the RAG feature
|
|
1711
|
+
`).action(async (key) => {
|
|
1712
|
+
try {
|
|
1713
|
+
const api = getApiClient();
|
|
1714
|
+
await api.post(`/features/${key}/disable`);
|
|
1715
|
+
printSuccess(`Feature '${key}' disabled.`);
|
|
1716
|
+
} catch (error) {
|
|
1717
|
+
handleError(error);
|
|
1718
|
+
}
|
|
1719
|
+
});
|
|
1720
|
+
return cmd;
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
// src/commands/features/config.ts
|
|
1724
|
+
import { Command as Command42 } from "commander";
|
|
1725
|
+
import chalk17 from "chalk";
|
|
1726
|
+
function createFeaturesConfigCommand() {
|
|
1727
|
+
const cmd = new Command42("config").description("View or update key-value configuration for a feature").argument("<key>", "Feature key").option("--set <entries...>", "Set config values (key=value)").addHelpText("after", `
|
|
1728
|
+
Examples:
|
|
1729
|
+
$ nebulaos features config llm-gateway View current config
|
|
1730
|
+
$ nebulaos features config llm-gateway --set max_tokens=4096 Update a config value
|
|
1731
|
+
$ nebulaos features config rag --set provider=openai model=text-embedding-3-small
|
|
1732
|
+
$ nebulaos features config llm-gateway -o json Output as JSON
|
|
1733
|
+
`).action(async (key, options, command) => {
|
|
1734
|
+
try {
|
|
1735
|
+
const globalOpts = command.optsWithGlobals();
|
|
1736
|
+
const format = resolveFormat(globalOpts.output);
|
|
1737
|
+
const api = getApiClient();
|
|
1738
|
+
if (options.set && options.set.length > 0) {
|
|
1739
|
+
const configValues = {};
|
|
1740
|
+
for (const entry of options.set) {
|
|
1741
|
+
const eqIndex = entry.indexOf("=");
|
|
1742
|
+
if (eqIndex === -1) {
|
|
1743
|
+
throw new CLIError(
|
|
1744
|
+
`Invalid config entry '${entry}'. Use key=value format.`,
|
|
1745
|
+
7 /* ValidationError */
|
|
1746
|
+
);
|
|
1747
|
+
}
|
|
1748
|
+
const k = entry.slice(0, eqIndex);
|
|
1749
|
+
const v = entry.slice(eqIndex + 1);
|
|
1750
|
+
configValues[k] = v;
|
|
1751
|
+
}
|
|
1752
|
+
await api.patch(`/features/${key}/config`, configValues);
|
|
1753
|
+
printSuccess(`Feature '${key}' configuration updated.`);
|
|
1754
|
+
} else {
|
|
1755
|
+
const { data } = await api.get(`/features/${key}`);
|
|
1756
|
+
const featureConfig = data.config || {};
|
|
1757
|
+
if (format === "table") {
|
|
1758
|
+
console.log(chalk17.bold(`Feature '${key}' Configuration
|
|
1759
|
+
`));
|
|
1760
|
+
const entries = Object.entries(featureConfig);
|
|
1761
|
+
if (entries.length === 0) {
|
|
1762
|
+
console.log("No configuration set.");
|
|
1763
|
+
} else {
|
|
1764
|
+
for (const [k, v] of entries) {
|
|
1765
|
+
console.log(` ${chalk17.bold(k)}: ${v}`);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
} else {
|
|
1769
|
+
output(format, {
|
|
1770
|
+
table: { headers: [], rows: [] },
|
|
1771
|
+
data: featureConfig,
|
|
1772
|
+
ids: Object.keys(featureConfig)
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
} catch (error) {
|
|
1777
|
+
handleError(error);
|
|
1778
|
+
}
|
|
1779
|
+
});
|
|
1780
|
+
return cmd;
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
// src/commands/features/index.ts
|
|
1784
|
+
function createFeaturesCommand() {
|
|
1785
|
+
const features = new Command43("features").description("Browse, enable, disable, and configure platform feature flags");
|
|
1786
|
+
features.addCommand(createFeaturesCatalogCommand());
|
|
1787
|
+
features.addCommand(createFeaturesListCommand());
|
|
1788
|
+
features.addCommand(createFeaturesGetCommand());
|
|
1789
|
+
features.addCommand(createFeaturesEnableCommand());
|
|
1790
|
+
features.addCommand(createFeaturesDisableCommand());
|
|
1791
|
+
features.addCommand(createFeaturesConfigCommand());
|
|
1792
|
+
return features;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
// src/commands/observability/index.ts
|
|
1796
|
+
import { Command as Command48 } from "commander";
|
|
1797
|
+
|
|
1798
|
+
// src/commands/observability/traces/index.ts
|
|
1799
|
+
import { Command as Command46 } from "commander";
|
|
1800
|
+
|
|
1801
|
+
// src/commands/observability/traces/search.ts
|
|
1802
|
+
import { Command as Command44 } from "commander";
|
|
1803
|
+
function createTracesSearchCommand() {
|
|
1804
|
+
const cmd = new Command44("search").description("Search traces by status, agent, or time range").option("--status <status>", "Filter by status").option("--limit <number>", "Maximum number of results", parseInt).option("--agent <name>", "Filter by agent name").option("--from <date>", "Start date (ISO format)").option("--to <date>", "End date (ISO format)").addHelpText("after", `
|
|
1805
|
+
Examples:
|
|
1806
|
+
$ nebulaos obs traces search List recent traces
|
|
1807
|
+
$ nebulaos obs traces search --agent my-agent Filter by agent
|
|
1808
|
+
$ nebulaos obs traces search --status error --limit 10 Find error traces
|
|
1809
|
+
$ nebulaos obs traces search --from 2026-01-01 --to 2026-01-31 -o json
|
|
1810
|
+
`).action(async (options, command) => {
|
|
1811
|
+
try {
|
|
1812
|
+
const globalOpts = command.optsWithGlobals();
|
|
1813
|
+
const format = resolveFormat(globalOpts.output);
|
|
1814
|
+
const api = getApiClient();
|
|
1815
|
+
const params = {};
|
|
1816
|
+
if (options.status) params.status = options.status;
|
|
1817
|
+
if (options.limit) params.limit = options.limit;
|
|
1818
|
+
if (options.agent) params.agent = options.agent;
|
|
1819
|
+
if (options.from) params.from = options.from;
|
|
1820
|
+
if (options.to) params.to = options.to;
|
|
1821
|
+
if (globalOpts.page) params.page = globalOpts.page;
|
|
1822
|
+
if (globalOpts.pageSize) params.pageSize = globalOpts.pageSize;
|
|
1823
|
+
const { data } = await api.get("/observability/traces/search", { params });
|
|
1824
|
+
const traces = Array.isArray(data) ? data : data.data || [];
|
|
1825
|
+
output(format, {
|
|
1826
|
+
table: {
|
|
1827
|
+
headers: ["Trace ID", "Status", "Duration", "Agent", "Started At"],
|
|
1828
|
+
rows: traces.map((t) => [
|
|
1829
|
+
t.traceId || t.id || "-",
|
|
1830
|
+
t.status || "-",
|
|
1831
|
+
t.duration ? `${t.duration}ms` : "-",
|
|
1832
|
+
t.agent || t.agentName || "-",
|
|
1833
|
+
t.startedAt || t.createdAt || "-"
|
|
1834
|
+
])
|
|
1835
|
+
},
|
|
1836
|
+
data: traces,
|
|
1837
|
+
ids: traces.map((t) => t.traceId || t.id)
|
|
1838
|
+
});
|
|
1839
|
+
} catch (error) {
|
|
1840
|
+
handleError(error);
|
|
1841
|
+
}
|
|
1842
|
+
});
|
|
1843
|
+
addPaginationFlags(cmd);
|
|
1844
|
+
return cmd;
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
// src/commands/observability/traces/get.ts
|
|
1848
|
+
import { Command as Command45 } from "commander";
|
|
1849
|
+
import chalk18 from "chalk";
|
|
1850
|
+
function createTracesGetCommand() {
|
|
1851
|
+
const cmd = new Command45("get").description("Get detailed trace information including all spans").argument("[traceId]", "Trace ID").option("--execution-id <executionId>", "Lookup trace by execution ID").addHelpText("after", `
|
|
1852
|
+
Examples:
|
|
1853
|
+
$ nebulaos obs traces get abc123-def456... View trace by trace ID
|
|
1854
|
+
$ nebulaos obs traces get --execution-id exec-789... View trace by execution ID
|
|
1855
|
+
$ nebulaos obs traces get abc123 -o json Output as JSON
|
|
1856
|
+
`).action(async (traceId, options, command) => {
|
|
1857
|
+
try {
|
|
1858
|
+
const globalOpts = command.optsWithGlobals();
|
|
1859
|
+
const format = resolveFormat(globalOpts.output);
|
|
1860
|
+
const api = getApiClient();
|
|
1861
|
+
if (!traceId && !options.executionId) {
|
|
1862
|
+
console.error("Error: provide either a <traceId> argument or --execution-id <executionId>");
|
|
1863
|
+
process.exit(1);
|
|
1864
|
+
}
|
|
1865
|
+
const url = options.executionId ? `/observability/executions/${options.executionId}/trace` : `/observability/traces/${traceId}`;
|
|
1866
|
+
const { data } = await api.get(url);
|
|
1867
|
+
if (format === "table") {
|
|
1868
|
+
console.log(chalk18.bold("Trace\n"));
|
|
1869
|
+
printDetail("Trace ID", data.traceId || data.id || "-");
|
|
1870
|
+
printDetail("Status", data.status || "-");
|
|
1871
|
+
printDetail("Duration", data.duration ? `${data.duration}ms` : "-");
|
|
1872
|
+
printDetail("Agent", data.agent || data.agentName || "-");
|
|
1873
|
+
printDetail("Started At", data.startedAt || data.createdAt || "-");
|
|
1874
|
+
printDetail("Ended At", data.endedAt || "-");
|
|
1875
|
+
if (data.spans && data.spans.length > 0) {
|
|
1876
|
+
console.log(chalk18.bold("\nSpans:"));
|
|
1877
|
+
for (const span of data.spans) {
|
|
1878
|
+
console.log(` ${span.spanId} - ${span.name || span.operationName || "unknown"} (${span.duration ? span.duration + "ms" : "-"})`);
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
} else {
|
|
1882
|
+
output(format, {
|
|
1883
|
+
table: { headers: [], rows: [] },
|
|
1884
|
+
data,
|
|
1885
|
+
ids: [data.traceId || data.id]
|
|
1886
|
+
});
|
|
1887
|
+
}
|
|
1888
|
+
} catch (error) {
|
|
1889
|
+
handleError(error);
|
|
1890
|
+
}
|
|
1891
|
+
});
|
|
1892
|
+
return cmd;
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
// src/commands/observability/traces/index.ts
|
|
1896
|
+
function createTracesCommand() {
|
|
1897
|
+
const traces = new Command46("traces").description("Search and inspect distributed traces from agent executions");
|
|
1898
|
+
traces.addCommand(createTracesSearchCommand());
|
|
1899
|
+
traces.addCommand(createTracesGetCommand());
|
|
1900
|
+
return traces;
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
// src/commands/observability/metrics.ts
|
|
1904
|
+
import { Command as Command47 } from "commander";
|
|
1905
|
+
function createMetricsCommand() {
|
|
1906
|
+
const cmd = new Command47("metrics").description("View observability metrics with optional time range and type filters").option("--type <type>", "Metric type filter").option("--from <date>", "Start date (ISO format)").option("--to <date>", "End date (ISO format)").addHelpText("after", `
|
|
1907
|
+
Examples:
|
|
1908
|
+
$ nebulaos obs metrics List all metrics
|
|
1909
|
+
$ nebulaos obs metrics --type latency Filter by metric type
|
|
1910
|
+
$ nebulaos obs metrics --from 2026-01-01 --to 2026-01-31 Filter by date range
|
|
1911
|
+
$ nebulaos obs metrics -o json Output as JSON
|
|
1912
|
+
`).action(async (options, command) => {
|
|
1913
|
+
try {
|
|
1914
|
+
const globalOpts = command.optsWithGlobals();
|
|
1915
|
+
const format = resolveFormat(globalOpts.output);
|
|
1916
|
+
const api = getApiClient();
|
|
1917
|
+
const params = {};
|
|
1918
|
+
if (options.type) params.type = options.type;
|
|
1919
|
+
if (options.from) params.from = options.from;
|
|
1920
|
+
if (options.to) params.to = options.to;
|
|
1921
|
+
const { data } = await api.get("/observability/metrics", { params });
|
|
1922
|
+
const metrics = Array.isArray(data) ? data : data.data || [];
|
|
1923
|
+
output(format, {
|
|
1924
|
+
table: {
|
|
1925
|
+
headers: ["Name", "Type", "Value", "Timestamp"],
|
|
1926
|
+
rows: metrics.map((m) => [
|
|
1927
|
+
m.name || "-",
|
|
1928
|
+
m.type || "-",
|
|
1929
|
+
String(m.value ?? "-"),
|
|
1930
|
+
m.timestamp || "-"
|
|
1931
|
+
])
|
|
1932
|
+
},
|
|
1933
|
+
data: metrics,
|
|
1934
|
+
ids: metrics.map((m) => m.name || m.id)
|
|
1935
|
+
});
|
|
1936
|
+
} catch (error) {
|
|
1937
|
+
handleError(error);
|
|
1938
|
+
}
|
|
1939
|
+
});
|
|
1940
|
+
return cmd;
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
// src/commands/observability/index.ts
|
|
1944
|
+
function createObservabilityCommand() {
|
|
1945
|
+
const observability = new Command48("observability").alias("obs").description("Inspect traces and metrics from your agents and workflows");
|
|
1946
|
+
observability.addCommand(createTracesCommand());
|
|
1947
|
+
observability.addCommand(createMetricsCommand());
|
|
1948
|
+
return observability;
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
// src/commands/config/index.ts
|
|
1952
|
+
import { Command as Command52 } from "commander";
|
|
1953
|
+
|
|
1954
|
+
// src/commands/config/get.ts
|
|
1955
|
+
import { Command as Command49 } from "commander";
|
|
1956
|
+
function createConfigGetCommand() {
|
|
1957
|
+
const cmd = new Command49("get").description("Get the current value of a configuration key").argument("<key>", "Configuration key (e.g., defaults.output, defaults.pageSize)").addHelpText("after", `
|
|
1958
|
+
Examples:
|
|
1959
|
+
$ nebulaos config get defaults.output Get default output format
|
|
1960
|
+
$ nebulaos config get defaults.pageSize Get default page size
|
|
1961
|
+
$ nebulaos config get currentContext Get active context name
|
|
1962
|
+
`).action(async (key, _options, command) => {
|
|
1963
|
+
try {
|
|
1964
|
+
const globalOpts = command.optsWithGlobals();
|
|
1965
|
+
const format = resolveFormat(globalOpts.output);
|
|
1966
|
+
const config2 = getConfig();
|
|
1967
|
+
const parts = key.split(".");
|
|
1968
|
+
let value = config2;
|
|
1969
|
+
for (const part of parts) {
|
|
1970
|
+
if (value && typeof value === "object" && part in value) {
|
|
1971
|
+
value = value[part];
|
|
1972
|
+
} else {
|
|
1973
|
+
throw new CLIError(`Configuration key '${key}' not found.`, 5 /* NotFound */);
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
if (format === "table") {
|
|
1977
|
+
printDetail(key, String(value));
|
|
1978
|
+
} else {
|
|
1979
|
+
output(format, {
|
|
1980
|
+
table: { headers: [], rows: [] },
|
|
1981
|
+
data: { key, value },
|
|
1982
|
+
ids: [key]
|
|
1983
|
+
});
|
|
1984
|
+
}
|
|
1985
|
+
} catch (error) {
|
|
1986
|
+
handleError(error);
|
|
1987
|
+
}
|
|
1988
|
+
});
|
|
1989
|
+
return cmd;
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
// src/commands/config/set.ts
|
|
1993
|
+
import { Command as Command50 } from "commander";
|
|
1994
|
+
var VALID_KEYS = {
|
|
1995
|
+
"defaults.output": (v) => {
|
|
1996
|
+
const valid = ["table", "json", "yaml", "quiet"];
|
|
1997
|
+
if (!valid.includes(v)) {
|
|
1998
|
+
throw new CLIError(
|
|
1999
|
+
`Invalid output format '${v}'. Valid values: ${valid.join(", ")}`,
|
|
2000
|
+
7 /* ValidationError */
|
|
2001
|
+
);
|
|
2002
|
+
}
|
|
2003
|
+
return v;
|
|
2004
|
+
},
|
|
2005
|
+
"defaults.pageSize": (v) => {
|
|
2006
|
+
const n = parseInt(v, 10);
|
|
2007
|
+
if (isNaN(n) || n < 1 || n > 100) {
|
|
2008
|
+
throw new CLIError(
|
|
2009
|
+
"Page size must be a number between 1 and 100.",
|
|
2010
|
+
7 /* ValidationError */
|
|
2011
|
+
);
|
|
2012
|
+
}
|
|
2013
|
+
return n;
|
|
2014
|
+
}
|
|
2015
|
+
};
|
|
2016
|
+
function createConfigSetCommand() {
|
|
2017
|
+
const cmd = new Command50("set").description("Set a CLI configuration value").argument("<key>", "Configuration key (e.g., defaults.output, defaults.pageSize)").argument("<value>", "Configuration value").addHelpText("after", `
|
|
2018
|
+
Examples:
|
|
2019
|
+
$ nebulaos config set defaults.output json Set default output to JSON
|
|
2020
|
+
$ nebulaos config set defaults.output table Set default output to table
|
|
2021
|
+
$ nebulaos config set defaults.pageSize 25 Set default page size to 25
|
|
2022
|
+
|
|
2023
|
+
Valid keys:
|
|
2024
|
+
defaults.output Output format (table, json, yaml, quiet)
|
|
2025
|
+
defaults.pageSize Number of items per page (1-100)
|
|
2026
|
+
`).action(async (key, value, _options, command) => {
|
|
2027
|
+
try {
|
|
2028
|
+
const globalOpts = command.optsWithGlobals();
|
|
2029
|
+
const format = resolveFormat(globalOpts.output);
|
|
2030
|
+
const parser = VALID_KEYS[key];
|
|
2031
|
+
if (!parser) {
|
|
2032
|
+
throw new CLIError(
|
|
2033
|
+
`Unknown configuration key '${key}'. Valid keys: ${Object.keys(VALID_KEYS).join(", ")}`,
|
|
2034
|
+
7 /* ValidationError */
|
|
2035
|
+
);
|
|
2036
|
+
}
|
|
2037
|
+
const parsed = parser(value);
|
|
2038
|
+
const parts = key.split(".");
|
|
2039
|
+
if (parts[0] === "defaults" && parts.length === 2) {
|
|
2040
|
+
setDefault(parts[1], parsed);
|
|
2041
|
+
}
|
|
2042
|
+
output(format, {
|
|
2043
|
+
table: {
|
|
2044
|
+
headers: ["Key", "Value"],
|
|
2045
|
+
rows: [[key, String(parsed)]]
|
|
2046
|
+
},
|
|
2047
|
+
data: { key, value: parsed },
|
|
2048
|
+
ids: [key]
|
|
2049
|
+
});
|
|
2050
|
+
} catch (error) {
|
|
2051
|
+
handleError(error);
|
|
2052
|
+
}
|
|
2053
|
+
});
|
|
2054
|
+
return cmd;
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
// src/commands/config/list.ts
|
|
2058
|
+
import { Command as Command51 } from "commander";
|
|
2059
|
+
import chalk19 from "chalk";
|
|
2060
|
+
function createConfigListCommand() {
|
|
2061
|
+
const cmd = new Command51("list").description("Display all configuration values and registered contexts").addHelpText("after", `
|
|
2062
|
+
Examples:
|
|
2063
|
+
$ nebulaos config list Show all config and contexts
|
|
2064
|
+
$ nebulaos config list -o json Output as JSON
|
|
2065
|
+
`).action(async (_options, command) => {
|
|
2066
|
+
try {
|
|
2067
|
+
const globalOpts = command.optsWithGlobals();
|
|
2068
|
+
const format = resolveFormat(globalOpts.output);
|
|
2069
|
+
const config2 = getConfig();
|
|
2070
|
+
if (format === "table") {
|
|
2071
|
+
console.log(chalk19.bold("Configuration\n"));
|
|
2072
|
+
printDetail("Config File", getConfigPath());
|
|
2073
|
+
printDetail("Current Context", config2.currentContext || "(none)");
|
|
2074
|
+
printDetail("Output Format", config2.defaults.output);
|
|
2075
|
+
printDetail("Page Size", String(config2.defaults.pageSize));
|
|
2076
|
+
const contextNames = Object.keys(config2.contexts);
|
|
2077
|
+
if (contextNames.length > 0) {
|
|
2078
|
+
console.log(chalk19.bold("\nContexts:"));
|
|
2079
|
+
for (const name of contextNames) {
|
|
2080
|
+
const ctx = config2.contexts[name];
|
|
2081
|
+
const active = name === config2.currentContext ? chalk19.green(" (active)") : "";
|
|
2082
|
+
console.log(` ${name}${active} - ${ctx.apiUrl}`);
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
} else {
|
|
2086
|
+
output(format, {
|
|
2087
|
+
table: { headers: [], rows: [] },
|
|
2088
|
+
data: config2,
|
|
2089
|
+
ids: Object.keys(config2.contexts)
|
|
2090
|
+
});
|
|
2091
|
+
}
|
|
2092
|
+
} catch (error) {
|
|
2093
|
+
handleError(error);
|
|
2094
|
+
}
|
|
2095
|
+
});
|
|
2096
|
+
return cmd;
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
// src/commands/config/index.ts
|
|
2100
|
+
function createConfigCommand() {
|
|
2101
|
+
const config2 = new Command52("config").description("View and modify CLI configuration (output format, page size, contexts)");
|
|
2102
|
+
config2.addCommand(createConfigGetCommand());
|
|
2103
|
+
config2.addCommand(createConfigSetCommand());
|
|
2104
|
+
config2.addCommand(createConfigListCommand());
|
|
2105
|
+
return config2;
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
// src/commands/llm-gateway/index.ts
|
|
2109
|
+
import { Command as Command66 } from "commander";
|
|
2110
|
+
|
|
2111
|
+
// src/commands/llm-gateway/routes/index.ts
|
|
2112
|
+
import { Command as Command58 } from "commander";
|
|
2113
|
+
|
|
2114
|
+
// src/commands/llm-gateway/routes/list.ts
|
|
2115
|
+
import { Command as Command53 } from "commander";
|
|
2116
|
+
function createRoutesListCommand() {
|
|
2117
|
+
const cmd = new Command53("list").description("List all configured LLM gateway routes").addHelpText("after", `
|
|
2118
|
+
Examples:
|
|
2119
|
+
$ nebulaos llm routes list List all routes
|
|
2120
|
+
$ nebulaos llm routes list -o json Output as JSON
|
|
2121
|
+
`).action(async (_options, command) => {
|
|
2122
|
+
try {
|
|
2123
|
+
const globalOpts = command.optsWithGlobals();
|
|
2124
|
+
const format = resolveFormat(globalOpts.output);
|
|
2125
|
+
const api = getApiClient();
|
|
2126
|
+
const { data } = await api.get("/llm-gateway/routes");
|
|
2127
|
+
output(format, {
|
|
2128
|
+
table: {
|
|
2129
|
+
headers: ["Alias", "Description", "Model", "Status", "Provisioning", "Updated"],
|
|
2130
|
+
rows: data.map((r) => [
|
|
2131
|
+
r.name,
|
|
2132
|
+
r.description || "-",
|
|
2133
|
+
r.primaryModel,
|
|
2134
|
+
r.status,
|
|
2135
|
+
r.provisioningStatus,
|
|
2136
|
+
r.updatedAt ? new Date(r.updatedAt).toLocaleString() : "-"
|
|
2137
|
+
])
|
|
2138
|
+
},
|
|
2139
|
+
data,
|
|
2140
|
+
ids: data.map((r) => r.name)
|
|
2141
|
+
});
|
|
2142
|
+
} catch (error) {
|
|
2143
|
+
handleError(error);
|
|
2144
|
+
}
|
|
2145
|
+
});
|
|
2146
|
+
return cmd;
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
// src/commands/llm-gateway/routes/get.ts
|
|
2150
|
+
import { Command as Command54 } from "commander";
|
|
2151
|
+
import chalk20 from "chalk";
|
|
2152
|
+
function createRoutesGetCommand() {
|
|
2153
|
+
const cmd = new Command54("get").description("Get full details of an LLM gateway route including fallback chain").argument("<alias>", "Route alias").addHelpText("after", `
|
|
2154
|
+
Examples:
|
|
2155
|
+
$ nebulaos llm routes get my-route View route details
|
|
2156
|
+
$ nebulaos llm routes get my-route -o json Output as JSON
|
|
2157
|
+
`).action(async (alias, _options, command) => {
|
|
2158
|
+
try {
|
|
2159
|
+
const globalOpts = command.optsWithGlobals();
|
|
2160
|
+
const format = resolveFormat(globalOpts.output);
|
|
2161
|
+
const api = getApiClient();
|
|
2162
|
+
const { data } = await api.get(`/llm-gateway/routes/${alias}`);
|
|
2163
|
+
if (format === "table") {
|
|
2164
|
+
console.log(chalk20.bold(`Route: ${data.name}
|
|
2165
|
+
`));
|
|
2166
|
+
printDetail("Alias", data.name);
|
|
2167
|
+
printDetail("Description", data.description || "-");
|
|
2168
|
+
printDetail("Provider", data.provider || "-");
|
|
2169
|
+
printDetail("Model", data.modelName || data.primaryModel);
|
|
2170
|
+
printDetail("Status", data.status);
|
|
2171
|
+
printDetail("Provisioning", data.provisioningStatus);
|
|
2172
|
+
if (data.maxTokens) printDetail("Max Tokens", String(data.maxTokens));
|
|
2173
|
+
if (data.timeoutMs) printDetail("Timeout (ms)", String(data.timeoutMs));
|
|
2174
|
+
if (data.retries) printDetail("Retries", String(data.retries));
|
|
2175
|
+
if (data.fallbackChain?.length) {
|
|
2176
|
+
printDetail("Fallback Chain", data.fallbackChain.join(" -> "));
|
|
2177
|
+
}
|
|
2178
|
+
printDetail("Updated", data.updatedAt ? new Date(data.updatedAt).toLocaleString() : "-");
|
|
2179
|
+
} else {
|
|
2180
|
+
output(format, {
|
|
2181
|
+
table: { headers: [], rows: [] },
|
|
2182
|
+
data,
|
|
2183
|
+
ids: [data.name]
|
|
2184
|
+
});
|
|
2185
|
+
}
|
|
2186
|
+
} catch (error) {
|
|
2187
|
+
handleError(error);
|
|
2188
|
+
}
|
|
2189
|
+
});
|
|
2190
|
+
return cmd;
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
// src/commands/llm-gateway/routes/create.ts
|
|
2194
|
+
import { Command as Command55 } from "commander";
|
|
2195
|
+
import { input as input2 } from "@inquirer/prompts";
|
|
2196
|
+
function createRoutesCreateCommand() {
|
|
2197
|
+
const cmd = new Command55("create").description("Create a new LLM gateway route (interactive or via flags)").option("--alias <alias>", "Route alias").option("--provider <provider>", "LLM provider (e.g. openai, anthropic, azure)").option("--model <model>", "Model name (e.g. gpt-4o, claude-3-opus)").option("--description <description>", "Route description").option("--max-tokens <number>", "Max tokens limit", parseInt).option("--timeout <ms>", "Timeout in milliseconds", parseInt).option("--retries <count>", "Retry count", parseInt).option("--credential <key=value>", "Credential key=value pair (repeatable)", collectKeyValue, {}).addHelpText("after", `
|
|
2198
|
+
Examples:
|
|
2199
|
+
$ nebulaos llm routes create Interactive mode
|
|
2200
|
+
$ nebulaos llm routes create --alias gpt4 --provider openai --model gpt-4o
|
|
2201
|
+
$ nebulaos llm routes create --alias claude --provider anthropic --model claude-3-opus \\
|
|
2202
|
+
--credential api_key=sk-...
|
|
2203
|
+
$ nebulaos llm routes create --alias fast --provider openai --model gpt-4o-mini \\
|
|
2204
|
+
--max-tokens 4096 --timeout 30000 --retries 2
|
|
2205
|
+
`).action(async (options, command) => {
|
|
2206
|
+
try {
|
|
2207
|
+
const api = getApiClient();
|
|
2208
|
+
const alias = options.alias || await input2({ message: "Route alias:" });
|
|
2209
|
+
const provider = options.provider || await input2({ message: "Provider (e.g. openai, anthropic, azure):" });
|
|
2210
|
+
const modelName = options.model || await input2({ message: "Model name:" });
|
|
2211
|
+
const description = options.description || await input2({ message: "Description (optional):", default: "" });
|
|
2212
|
+
let credentials = options.credential;
|
|
2213
|
+
if (Object.keys(credentials).length === 0) {
|
|
2214
|
+
const apiKey = await input2({ message: "API key for provider:" });
|
|
2215
|
+
credentials = { api_key: apiKey };
|
|
2216
|
+
}
|
|
2217
|
+
const body = {
|
|
2218
|
+
alias,
|
|
2219
|
+
provider,
|
|
2220
|
+
modelName,
|
|
2221
|
+
credentials
|
|
2222
|
+
};
|
|
2223
|
+
if (description) body.description = description;
|
|
2224
|
+
if (options.maxTokens) body.maxTokens = options.maxTokens;
|
|
2225
|
+
if (options.timeout) body.timeoutMs = options.timeout;
|
|
2226
|
+
if (options.retries) body.retryCount = options.retries;
|
|
2227
|
+
await api.post("/llm-gateway/routes", body);
|
|
2228
|
+
printSuccess(`Route '${alias}' created successfully.`);
|
|
2229
|
+
} catch (error) {
|
|
2230
|
+
handleError(error);
|
|
2231
|
+
}
|
|
2232
|
+
});
|
|
2233
|
+
return cmd;
|
|
2234
|
+
}
|
|
2235
|
+
function collectKeyValue(value, previous) {
|
|
2236
|
+
const [k, ...rest] = value.split("=");
|
|
2237
|
+
if (k && rest.length > 0) {
|
|
2238
|
+
previous[k] = rest.join("=");
|
|
2239
|
+
}
|
|
2240
|
+
return previous;
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
// src/commands/llm-gateway/routes/update.ts
|
|
2244
|
+
import { Command as Command56 } from "commander";
|
|
2245
|
+
function createRoutesUpdateCommand() {
|
|
2246
|
+
const cmd = new Command56("update").description("Update an existing LLM gateway route configuration").argument("<alias>", "Route alias").option("--description <description>", "Route description").option("--provider <provider>", "LLM provider").option("--model <model>", "Model name").option("--max-tokens <number>", "Max tokens limit", parseInt).option("--timeout <ms>", "Timeout in milliseconds", parseInt).option("--retries <count>", "Retry count", parseInt).option("--credential <key=value>", "Credential key=value pair (repeatable)", collectKeyValue2, {}).addHelpText("after", `
|
|
2247
|
+
Examples:
|
|
2248
|
+
$ nebulaos llm routes update my-route --model gpt-4o-mini Change model
|
|
2249
|
+
$ nebulaos llm routes update my-route --max-tokens 8192 Update token limit
|
|
2250
|
+
$ nebulaos llm routes update my-route --credential api_key=sk-new... Update credentials
|
|
2251
|
+
$ nebulaos llm routes update my-route --timeout 60000 --retries 3 Update timeouts
|
|
2252
|
+
`).action(async (alias, options, command) => {
|
|
2253
|
+
try {
|
|
2254
|
+
const api = getApiClient();
|
|
2255
|
+
const body = {};
|
|
2256
|
+
if (options.description !== void 0) body.description = options.description;
|
|
2257
|
+
if (options.provider) body.provider = options.provider;
|
|
2258
|
+
if (options.model) body.modelName = options.model;
|
|
2259
|
+
if (options.maxTokens) body.maxTokens = options.maxTokens;
|
|
2260
|
+
if (options.timeout) body.timeoutMs = options.timeout;
|
|
2261
|
+
if (options.retries) body.retryCount = options.retries;
|
|
2262
|
+
if (Object.keys(options.credential).length > 0) body.credentials = options.credential;
|
|
2263
|
+
await api.put(`/llm-gateway/routes/${alias}`, body);
|
|
2264
|
+
printSuccess(`Route '${alias}' updated successfully.`);
|
|
2265
|
+
} catch (error) {
|
|
2266
|
+
handleError(error);
|
|
2267
|
+
}
|
|
2268
|
+
});
|
|
2269
|
+
return cmd;
|
|
2270
|
+
}
|
|
2271
|
+
function collectKeyValue2(value, previous) {
|
|
2272
|
+
const [k, ...rest] = value.split("=");
|
|
2273
|
+
if (k && rest.length > 0) {
|
|
2274
|
+
previous[k] = rest.join("=");
|
|
2275
|
+
}
|
|
2276
|
+
return previous;
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
// src/commands/llm-gateway/routes/delete.ts
|
|
2280
|
+
import { Command as Command57 } from "commander";
|
|
2281
|
+
import { confirm as confirm4 } from "@inquirer/prompts";
|
|
2282
|
+
function createRoutesDeleteCommand() {
|
|
2283
|
+
const cmd = new Command57("delete").description("Permanently delete an LLM gateway route").argument("<alias>", "Route alias").option("-y, --yes", "Skip confirmation prompt").addHelpText("after", `
|
|
2284
|
+
Examples:
|
|
2285
|
+
$ nebulaos llm routes delete my-route Delete with confirmation prompt
|
|
2286
|
+
$ nebulaos llm routes delete my-route -y Delete without confirmation
|
|
2287
|
+
`).action(async (alias, options) => {
|
|
2288
|
+
try {
|
|
2289
|
+
if (!options.yes) {
|
|
2290
|
+
const shouldProceed = await confirm4({
|
|
2291
|
+
message: `Delete route '${alias}'? This cannot be undone.`,
|
|
2292
|
+
default: false
|
|
2293
|
+
});
|
|
2294
|
+
if (!shouldProceed) {
|
|
2295
|
+
console.log("Cancelled.");
|
|
2296
|
+
return;
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
const api = getApiClient();
|
|
2300
|
+
await api.delete(`/llm-gateway/routes/${alias}`);
|
|
2301
|
+
printSuccess(`Route '${alias}' deleted successfully.`);
|
|
2302
|
+
} catch (error) {
|
|
2303
|
+
handleError(error);
|
|
2304
|
+
}
|
|
2305
|
+
});
|
|
2306
|
+
return cmd;
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
// src/commands/llm-gateway/routes/index.ts
|
|
2310
|
+
function createRoutesCommand() {
|
|
2311
|
+
const routes = new Command58("routes").description("Create, update, and manage LLM gateway routes (model configurations)");
|
|
2312
|
+
routes.addCommand(createRoutesListCommand());
|
|
2313
|
+
routes.addCommand(createRoutesGetCommand());
|
|
2314
|
+
routes.addCommand(createRoutesCreateCommand());
|
|
2315
|
+
routes.addCommand(createRoutesUpdateCommand());
|
|
2316
|
+
routes.addCommand(createRoutesDeleteCommand());
|
|
2317
|
+
return routes;
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
// src/commands/llm-gateway/keys/index.ts
|
|
2321
|
+
import { Command as Command62 } from "commander";
|
|
2322
|
+
|
|
2323
|
+
// src/commands/llm-gateway/keys/list.ts
|
|
2324
|
+
import { Command as Command59 } from "commander";
|
|
2325
|
+
function createKeysListCommand() {
|
|
2326
|
+
const cmd = new Command59("list").description("List all LLM gateway API keys and their allowed routes").addHelpText("after", `
|
|
2327
|
+
Examples:
|
|
2328
|
+
$ nebulaos llm keys list List all gateway API keys
|
|
2329
|
+
$ nebulaos llm keys list -o json Output as JSON
|
|
2330
|
+
`).action(async (_options, command) => {
|
|
2331
|
+
try {
|
|
2332
|
+
const globalOpts = command.optsWithGlobals();
|
|
2333
|
+
const format = resolveFormat(globalOpts.output);
|
|
2334
|
+
const api = getApiClient();
|
|
2335
|
+
const { data } = await api.get("/llm-gateway/keys");
|
|
2336
|
+
output(format, {
|
|
2337
|
+
table: {
|
|
2338
|
+
headers: ["ID", "Name", "User", "Status", "Allowed Routes", "Last Used"],
|
|
2339
|
+
rows: data.map((k) => [
|
|
2340
|
+
k.id,
|
|
2341
|
+
k.name,
|
|
2342
|
+
k.user,
|
|
2343
|
+
k.status,
|
|
2344
|
+
k.allowedRoutes?.length ? k.allowedRoutes.join(", ") : "all",
|
|
2345
|
+
k.lastUsedAt ? new Date(k.lastUsedAt).toLocaleString() : "never"
|
|
2346
|
+
])
|
|
2347
|
+
},
|
|
2348
|
+
data,
|
|
2349
|
+
ids: data.map((k) => k.id)
|
|
2350
|
+
});
|
|
2351
|
+
} catch (error) {
|
|
2352
|
+
handleError(error);
|
|
2353
|
+
}
|
|
2354
|
+
});
|
|
2355
|
+
return cmd;
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
// src/commands/llm-gateway/keys/create.ts
|
|
2359
|
+
import { Command as Command60 } from "commander";
|
|
2360
|
+
import { input as input3, select } from "@inquirer/prompts";
|
|
2361
|
+
import chalk21 from "chalk";
|
|
2362
|
+
function createKeysCreateCommand() {
|
|
2363
|
+
const cmd = new Command60("create").description("Create a new LLM gateway API key (interactive or via flags)").option("--name <name>", "Key name").option("--type <type>", "Key type (personal or service)").option("--allowed-routes <routes>", "Comma-separated list of allowed route aliases").addHelpText("after", `
|
|
2364
|
+
Examples:
|
|
2365
|
+
$ nebulaos llm keys create Interactive mode
|
|
2366
|
+
$ nebulaos llm keys create --name my-key --type personal Create personal key
|
|
2367
|
+
$ nebulaos llm keys create --name svc-key --type service \\
|
|
2368
|
+
--allowed-routes gpt4,claude Restrict to specific routes
|
|
2369
|
+
|
|
2370
|
+
Note: The API key is displayed only once after creation. Save it securely.
|
|
2371
|
+
`).action(async (options) => {
|
|
2372
|
+
try {
|
|
2373
|
+
const api = getApiClient();
|
|
2374
|
+
const name = options.name || await input3({ message: "Key name:" });
|
|
2375
|
+
const type = options.type || await select({
|
|
2376
|
+
message: "Key type:",
|
|
2377
|
+
choices: [
|
|
2378
|
+
{ name: "personal", value: "personal" },
|
|
2379
|
+
{ name: "service", value: "service" }
|
|
2380
|
+
]
|
|
2381
|
+
});
|
|
2382
|
+
const body = { name, type };
|
|
2383
|
+
if (options.allowedRoutes) {
|
|
2384
|
+
body.allowedRoutes = options.allowedRoutes.split(",").map((s) => s.trim());
|
|
2385
|
+
}
|
|
2386
|
+
const { data } = await api.post("/llm-gateway/keys", body);
|
|
2387
|
+
printSuccess(`API key '${name}' created successfully.`);
|
|
2388
|
+
console.log(`
|
|
2389
|
+
${chalk21.bold("Key:")} ${data.key}`);
|
|
2390
|
+
console.log(chalk21.yellow("\nSave this key now. It will not be shown again."));
|
|
2391
|
+
} catch (error) {
|
|
2392
|
+
handleError(error);
|
|
2393
|
+
}
|
|
2394
|
+
});
|
|
2395
|
+
return cmd;
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2398
|
+
// src/commands/llm-gateway/keys/revoke.ts
|
|
2399
|
+
import { Command as Command61 } from "commander";
|
|
2400
|
+
import { confirm as confirm5 } from "@inquirer/prompts";
|
|
2401
|
+
function createKeysRevokeCommand() {
|
|
2402
|
+
const cmd = new Command61("revoke").description("Permanently revoke an LLM gateway API key").argument("<keyId>", "API key ID").option("-y, --yes", "Skip confirmation prompt").addHelpText("after", `
|
|
2403
|
+
Examples:
|
|
2404
|
+
$ nebulaos llm keys revoke key-123 Revoke with confirmation prompt
|
|
2405
|
+
$ nebulaos llm keys revoke key-123 -y Revoke without confirmation
|
|
2406
|
+
`).action(async (keyId, options) => {
|
|
2407
|
+
try {
|
|
2408
|
+
if (!options.yes) {
|
|
2409
|
+
const shouldProceed = await confirm5({
|
|
2410
|
+
message: `Revoke API key '${keyId}'? This cannot be undone.`,
|
|
2411
|
+
default: false
|
|
2412
|
+
});
|
|
2413
|
+
if (!shouldProceed) {
|
|
2414
|
+
console.log("Cancelled.");
|
|
2415
|
+
return;
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
const api = getApiClient();
|
|
2419
|
+
await api.delete(`/llm-gateway/keys/${keyId}`);
|
|
2420
|
+
printSuccess(`API key '${keyId}' revoked successfully.`);
|
|
2421
|
+
} catch (error) {
|
|
2422
|
+
handleError(error);
|
|
2423
|
+
}
|
|
2424
|
+
});
|
|
2425
|
+
return cmd;
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
// src/commands/llm-gateway/keys/index.ts
|
|
2429
|
+
function createKeysCommand() {
|
|
2430
|
+
const keys = new Command62("keys").description("Create, list, and revoke LLM gateway API keys");
|
|
2431
|
+
keys.addCommand(createKeysListCommand());
|
|
2432
|
+
keys.addCommand(createKeysCreateCommand());
|
|
2433
|
+
keys.addCommand(createKeysRevokeCommand());
|
|
2434
|
+
return keys;
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
// src/commands/llm-gateway/requests.ts
|
|
2438
|
+
import { Command as Command63 } from "commander";
|
|
2439
|
+
function createRequestsCommand() {
|
|
2440
|
+
const cmd = new Command63("requests").description("List recent LLM gateway requests with token usage and cost details").option("--limit <number>", "Maximum number of requests to return", parseInt).option("--route <alias>", "Filter by route alias").option("--status <status>", "Filter by status (success or error)").addHelpText("after", `
|
|
2441
|
+
Examples:
|
|
2442
|
+
$ nebulaos llm requests List recent requests
|
|
2443
|
+
$ nebulaos llm requests --limit 10 Show last 10 requests
|
|
2444
|
+
$ nebulaos llm requests --route my-route Filter by route
|
|
2445
|
+
$ nebulaos llm requests --status error Show only failed requests
|
|
2446
|
+
$ nebulaos llm requests -o json Output as JSON
|
|
2447
|
+
`).action(async (options, command) => {
|
|
2448
|
+
try {
|
|
2449
|
+
const globalOpts = command.optsWithGlobals();
|
|
2450
|
+
const format = resolveFormat(globalOpts.output);
|
|
2451
|
+
const api = getApiClient();
|
|
2452
|
+
const params = {};
|
|
2453
|
+
if (options.limit) params.limit = String(options.limit);
|
|
2454
|
+
if (options.route) params.routeAlias = options.route;
|
|
2455
|
+
if (options.status) params.status = options.status;
|
|
2456
|
+
const { data } = await api.get("/llm-gateway/requests", { params });
|
|
2457
|
+
output(format, {
|
|
2458
|
+
table: {
|
|
2459
|
+
headers: ["Request ID", "Timestamp", "Route", "Model", "Tokens", "Duration (ms)", "Status", "Cost"],
|
|
2460
|
+
rows: data.map((r) => [
|
|
2461
|
+
r.requestId.substring(0, 12) + "...",
|
|
2462
|
+
new Date(r.timestamp).toLocaleString(),
|
|
2463
|
+
r.routeAlias,
|
|
2464
|
+
r.modelFinal,
|
|
2465
|
+
r.totalTokens != null ? String(r.totalTokens) : "-",
|
|
2466
|
+
r.durationMs != null ? String(r.durationMs) : "-",
|
|
2467
|
+
r.status,
|
|
2468
|
+
r.costUsd != null ? `$${r.costUsd.toFixed(4)}` : "-"
|
|
2469
|
+
])
|
|
2470
|
+
},
|
|
2471
|
+
data,
|
|
2472
|
+
ids: data.map((r) => r.requestId)
|
|
2473
|
+
});
|
|
2474
|
+
} catch (error) {
|
|
2475
|
+
handleError(error);
|
|
2476
|
+
}
|
|
2477
|
+
});
|
|
2478
|
+
return cmd;
|
|
2479
|
+
}
|
|
2480
|
+
|
|
2481
|
+
// src/commands/llm-gateway/usage.ts
|
|
2482
|
+
import { Command as Command64 } from "commander";
|
|
2483
|
+
function createUsageCommand() {
|
|
2484
|
+
const cmd = new Command64("usage").description("Show aggregated LLM gateway usage statistics (tokens and costs)").addHelpText("after", `
|
|
2485
|
+
Examples:
|
|
2486
|
+
$ nebulaos llm usage Show usage summary
|
|
2487
|
+
$ nebulaos llm usage -o json Output as JSON
|
|
2488
|
+
$ nebulaos llm usage -o yaml Output as YAML
|
|
2489
|
+
`).action(async (_options, command) => {
|
|
2490
|
+
try {
|
|
2491
|
+
const globalOpts = command.optsWithGlobals();
|
|
2492
|
+
const format = resolveFormat(globalOpts.output);
|
|
2493
|
+
const api = getApiClient();
|
|
2494
|
+
const { data } = await api.get("/llm-gateway/usage");
|
|
2495
|
+
output(format, {
|
|
2496
|
+
table: {
|
|
2497
|
+
headers: ["Date", "Route", "Model", "Tokens", "Cost", "Status"],
|
|
2498
|
+
rows: data.map((u) => [
|
|
2499
|
+
u.date,
|
|
2500
|
+
u.route,
|
|
2501
|
+
u.model,
|
|
2502
|
+
String(u.tokens),
|
|
2503
|
+
u.costUsd != null ? `$${u.costUsd.toFixed(4)}` : "-",
|
|
2504
|
+
u.status
|
|
2505
|
+
])
|
|
2506
|
+
},
|
|
2507
|
+
data,
|
|
2508
|
+
ids: data.map((u) => `${u.date}-${u.route}`)
|
|
2509
|
+
});
|
|
2510
|
+
} catch (error) {
|
|
2511
|
+
handleError(error);
|
|
2512
|
+
}
|
|
2513
|
+
});
|
|
2514
|
+
return cmd;
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
// src/commands/llm-gateway/catalog.ts
|
|
2518
|
+
import { Command as Command65 } from "commander";
|
|
2519
|
+
function createCatalogCommand() {
|
|
2520
|
+
const cmd = new Command65("catalog").description("Browse available LLM models with pricing and provider info").option("--search <query>", "Search models by name").option("--provider <provider>", "Filter by provider").option("--page <number>", "Page number", parseInt).option("--limit <number>", "Items per page", parseInt).addHelpText("after", `
|
|
2521
|
+
Examples:
|
|
2522
|
+
$ nebulaos llm catalog List all available models
|
|
2523
|
+
$ nebulaos llm catalog --search gpt-4 Search for GPT-4 models
|
|
2524
|
+
$ nebulaos llm catalog --provider openai Show only OpenAI models
|
|
2525
|
+
$ nebulaos llm catalog --provider anthropic -o json Output Anthropic models as JSON
|
|
2526
|
+
`).action(async (options, command) => {
|
|
2527
|
+
try {
|
|
2528
|
+
const globalOpts = command.optsWithGlobals();
|
|
2529
|
+
const format = resolveFormat(globalOpts.output);
|
|
2530
|
+
const api = getApiClient();
|
|
2531
|
+
const params = {};
|
|
2532
|
+
if (options.search) params.q = options.search;
|
|
2533
|
+
if (options.provider) params.provider = options.provider;
|
|
2534
|
+
if (options.page) params.page = String(options.page);
|
|
2535
|
+
if (options.limit) params.limit = String(options.limit);
|
|
2536
|
+
const { data } = await api.get("/llm-gateway/catalog", { params });
|
|
2537
|
+
if (format === "table") {
|
|
2538
|
+
output(format, {
|
|
2539
|
+
table: {
|
|
2540
|
+
headers: ["Model", "Provider", "Modality", "Input Price", "Output Price", "Status"],
|
|
2541
|
+
rows: data.items.map((m) => [
|
|
2542
|
+
m.model,
|
|
2543
|
+
m.provider,
|
|
2544
|
+
m.modality,
|
|
2545
|
+
m.priceInput != null ? `$${m.priceInput}` : "-",
|
|
2546
|
+
m.priceOutput != null ? `$${m.priceOutput}` : "-",
|
|
2547
|
+
m.status
|
|
2548
|
+
])
|
|
2549
|
+
},
|
|
2550
|
+
data: data.items,
|
|
2551
|
+
ids: data.items.map((m) => m.model)
|
|
2552
|
+
});
|
|
2553
|
+
console.log(`
|
|
2554
|
+
Page ${data.page} of ${Math.ceil(data.total / data.limit)} (${data.total} total models)`);
|
|
2555
|
+
} else {
|
|
2556
|
+
output(format, {
|
|
2557
|
+
table: { headers: [], rows: [] },
|
|
2558
|
+
data,
|
|
2559
|
+
ids: data.items.map((m) => m.model)
|
|
2560
|
+
});
|
|
2561
|
+
}
|
|
2562
|
+
} catch (error) {
|
|
2563
|
+
handleError(error);
|
|
2564
|
+
}
|
|
2565
|
+
});
|
|
2566
|
+
return cmd;
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
// src/commands/llm-gateway/index.ts
|
|
2570
|
+
function createLlmGatewayCommand() {
|
|
2571
|
+
const llmGateway = new Command66("llm-gateway").alias("llm").description("Manage LLM Gateway routes, API keys, model catalog, and usage");
|
|
2572
|
+
llmGateway.addCommand(createRoutesCommand());
|
|
2573
|
+
llmGateway.addCommand(createKeysCommand());
|
|
2574
|
+
llmGateway.addCommand(createRequestsCommand());
|
|
2575
|
+
llmGateway.addCommand(createUsageCommand());
|
|
2576
|
+
llmGateway.addCommand(createCatalogCommand());
|
|
2577
|
+
return llmGateway;
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
// src/program.ts
|
|
2581
|
+
function createProgram() {
|
|
2582
|
+
const program = new Command67();
|
|
2583
|
+
program.name("nebulaos").description("NebulaOS CLI - Manage your AI agents, workflows, and resources").version("0.1.0").configureHelp({
|
|
2584
|
+
sortSubcommands: true,
|
|
2585
|
+
sortOptions: true
|
|
2586
|
+
});
|
|
2587
|
+
addGlobalFlags(program);
|
|
2588
|
+
program.exitOverride();
|
|
2589
|
+
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
2590
|
+
const opts = actionCommand.optsWithGlobals();
|
|
2591
|
+
if (opts.noColor) {
|
|
2592
|
+
process.env.NO_COLOR = "1";
|
|
2593
|
+
}
|
|
2594
|
+
if (opts.verbose) {
|
|
2595
|
+
process.env.NEBULAOS_VERBOSE = "1";
|
|
2596
|
+
}
|
|
2597
|
+
});
|
|
2598
|
+
program.addCommand(createAuthCommand());
|
|
2599
|
+
program.addCommand(createOrgsCommand());
|
|
2600
|
+
program.addCommand(createClientsCommand());
|
|
2601
|
+
program.addCommand(createResourcesCommand());
|
|
2602
|
+
program.addCommand(createExecutionCommand());
|
|
2603
|
+
program.addCommand(createRagCommand());
|
|
2604
|
+
program.addCommand(createFeaturesCommand());
|
|
2605
|
+
program.addCommand(createObservabilityCommand());
|
|
2606
|
+
program.addCommand(createConfigCommand());
|
|
2607
|
+
program.addCommand(createLlmGatewayCommand());
|
|
2608
|
+
process.on("uncaughtException", handleError);
|
|
2609
|
+
process.on("unhandledRejection", (reason) => handleError(reason));
|
|
2610
|
+
return program;
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
// src/bin/nebulaos.ts
|
|
2614
|
+
async function main() {
|
|
2615
|
+
const program = createProgram();
|
|
2616
|
+
try {
|
|
2617
|
+
await program.parseAsync(process.argv);
|
|
2618
|
+
} catch (error) {
|
|
2619
|
+
if (error instanceof CommanderError && error.code === "commander.helpDisplayed") {
|
|
2620
|
+
process.exit(0);
|
|
2621
|
+
}
|
|
2622
|
+
if (error instanceof CommanderError && error.code === "commander.version") {
|
|
2623
|
+
process.exit(0);
|
|
2624
|
+
}
|
|
2625
|
+
handleError(error);
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
main();
|
|
2629
|
+
//# sourceMappingURL=nebulaos.js.map
|