@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.
@@ -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