@burdenoff/bigconsole-cli 2026.802.1 → 2026.1110.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,2895 @@
1
- /**
2
- * @burdenoff/bigconsole-cli — public API exports.
3
- */
4
- export * from './config/index.js';
5
- export { workspaceRequest, globalRequest, resetClients } from './graphql/client.js';
6
- export * from './commands/index.js';
1
+ // src/config/index.ts
2
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ var CONFIG_DIR = join(homedir(), ".config", "bigconsole-cli");
6
+ var CONFIG_FILE = join(CONFIG_DIR, "config.json");
7
+ var DEFAULT_CLIENT_ID = "burdenoff_cli_bigconsole";
8
+ var PROD_OIDC_ISSUER = "https://graphql.burdenoff.com/global";
9
+ var PROD_WORKSPACE_ENDPOINT = "https://graphqlworkspaces.burdenoff.com/workspaces/graphql";
10
+ var PROD_GLOBAL_ENDPOINT = "https://graphql.burdenoff.com/global/graphql";
11
+ var DEFAULT_CONFIG = {
12
+ workspaceEndpoint: PROD_WORKSPACE_ENDPOINT,
13
+ globalEndpoint: PROD_GLOBAL_ENDPOINT
14
+ };
15
+ function ensureConfigDir() {
16
+ if (!existsSync(CONFIG_DIR)) {
17
+ mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
18
+ }
19
+ }
20
+ function getEnvOverrides() {
21
+ const overrides = {};
22
+ if (process.env.BIGCONSOLE_WORKSPACE_ENDPOINT) {
23
+ overrides.workspaceEndpoint = process.env.BIGCONSOLE_WORKSPACE_ENDPOINT;
24
+ }
25
+ if (process.env.BIGCONSOLE_GLOBAL_ENDPOINT) {
26
+ overrides.globalEndpoint = process.env.BIGCONSOLE_GLOBAL_ENDPOINT;
27
+ }
28
+ if (process.env.BIGCONSOLE_OIDC_ISSUER) {
29
+ overrides.oidcIssuer = process.env.BIGCONSOLE_OIDC_ISSUER;
30
+ }
31
+ if (process.env.BIGCONSOLE_CLIENT_ID) {
32
+ overrides.clientId = process.env.BIGCONSOLE_CLIENT_ID;
33
+ }
34
+ return overrides;
35
+ }
36
+ function readConfig() {
37
+ try {
38
+ const envOverrides = getEnvOverrides();
39
+ if (!existsSync(CONFIG_FILE))
40
+ return { ...DEFAULT_CONFIG, ...envOverrides };
41
+ const raw = readFileSync(CONFIG_FILE, "utf8");
42
+ const stored = JSON.parse(raw);
43
+ return { ...DEFAULT_CONFIG, ...envOverrides, ...stored };
44
+ } catch {
45
+ return { ...DEFAULT_CONFIG };
46
+ }
47
+ }
48
+ function writeConfig(config) {
49
+ ensureConfigDir();
50
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { encoding: "utf8" });
51
+ try {
52
+ chmodSync(CONFIG_FILE, 384);
53
+ } catch {}
54
+ }
55
+ function getConfigValue(key) {
56
+ const config = readConfig();
57
+ return config[key];
58
+ }
59
+ function setConfigValue(key, value) {
60
+ const config = readConfig();
61
+ config[key] = value;
62
+ writeConfig(config);
63
+ }
64
+ function getConfigPath() {
65
+ return CONFIG_FILE;
66
+ }
67
+ function getConfigDir() {
68
+ return CONFIG_DIR;
69
+ }
70
+ function resolveOIDCConfig() {
71
+ const config = readConfig();
72
+ const clientId = config.clientId || DEFAULT_CLIENT_ID;
73
+ let oidcIssuer = config.oidcIssuer;
74
+ if (!oidcIssuer) {
75
+ const globalEndpoint = config.globalEndpoint;
76
+ const isProdEndpoint = globalEndpoint.includes("graphql.burdenoff.com");
77
+ if (isProdEndpoint) {
78
+ oidcIssuer = PROD_OIDC_ISSUER;
79
+ } else {
80
+ oidcIssuer = globalEndpoint.replace(/\/graphql$/, "");
81
+ }
82
+ }
83
+ return { oidcIssuer, clientId };
84
+ }
85
+ var KNOWN_CONFIG_KEYS = [
86
+ "workspaceEndpoint",
87
+ "globalEndpoint",
88
+ "oidcIssuer",
89
+ "clientId",
90
+ "defaultFormat",
91
+ "verbose",
92
+ "authServiceEndpoint"
93
+ ];
94
+ // src/graphql/client.ts
95
+ import { GraphQLClient } from "graphql-request";
96
+
97
+ // src/auth/store.ts
98
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2, chmodSync as chmodSync2, unlinkSync } from "fs";
99
+ import { join as join2 } from "path";
100
+ import { homedir as homedir2 } from "os";
101
+ var CONFIG_DIR2 = join2(homedir2(), ".config", "bigconsole-cli");
102
+ var AUTH_FILE = join2(CONFIG_DIR2, "auth.json");
103
+ function ensureConfigDir2() {
104
+ if (!existsSync2(CONFIG_DIR2)) {
105
+ mkdirSync2(CONFIG_DIR2, { recursive: true, mode: 448 });
106
+ }
107
+ }
108
+ function readAuth() {
109
+ try {
110
+ if (!existsSync2(AUTH_FILE))
111
+ return null;
112
+ const raw = readFileSync2(AUTH_FILE, "utf8");
113
+ return JSON.parse(raw);
114
+ } catch {
115
+ return null;
116
+ }
117
+ }
118
+ function writeAuth(auth) {
119
+ ensureConfigDir2();
120
+ writeFileSync2(AUTH_FILE, JSON.stringify(auth, null, 2), { encoding: "utf8", mode: 384 });
121
+ try {
122
+ chmodSync2(AUTH_FILE, 384);
123
+ } catch {}
124
+ }
125
+ function clearAuth() {
126
+ try {
127
+ if (existsSync2(AUTH_FILE)) {
128
+ unlinkSync(AUTH_FILE);
129
+ }
130
+ } catch {}
131
+ }
132
+ function isTokenExpired(auth) {
133
+ if (!auth.expiresAt)
134
+ return false;
135
+ return Date.now() >= auth.expiresAt;
136
+ }
137
+ function isTokenExpiringSoon(auth) {
138
+ if (!auth.expiresAt)
139
+ return false;
140
+ const BUFFER_MS = 5 * 60 * 1000;
141
+ return Date.now() + BUFFER_MS >= auth.expiresAt;
142
+ }
143
+ function isWorkspaceTokenExpiringSoon(auth) {
144
+ if (!auth.workspaceTokenExpiresAt)
145
+ return false;
146
+ const BUFFER_MS = 5 * 60 * 1000;
147
+ return Date.now() + BUFFER_MS >= auth.workspaceTokenExpiresAt;
148
+ }
149
+
150
+ // src/auth/device-code.ts
151
+ import fetch from "cross-fetch";
152
+
153
+ // src/utils/sanitize.ts
154
+ var MAX_ERROR_MESSAGE_LENGTH = 500;
155
+ var MAX_HTTP_BODY_EXCERPT_LENGTH = 200;
156
+ var MAX_SCAN_LENGTH = 4000;
157
+ var REDACTED = "[REDACTED]";
158
+ var ELLIPSIS = "…";
159
+ var ESC = 27;
160
+ var BEL = 7;
161
+ var TAB = 9;
162
+ var LF = 10;
163
+ var CR = 13;
164
+ var DEL = 127;
165
+ var NEL = 133;
166
+ var C1_CSI = 155;
167
+ var C1_ST = 156;
168
+ var LINE_SEPARATOR = 8232;
169
+ var PARAGRAPH_SEPARATOR = 8233;
170
+ var C1_STRING_INTRODUCERS = new Set([144, 152, 157, 158, 159]);
171
+ var ESC_STRING_INTRODUCERS = new Set(["P", "X", "]", "^", "_"].map((c) => c.charCodeAt(0)));
172
+ var BIDI_CONTROLS = /[\u202a-\u202e\u2066-\u2069]/g;
173
+ function toUnicodeEscape(char) {
174
+ return `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`;
175
+ }
176
+ function escapeBidiControls(text) {
177
+ return text.replace(BIDI_CONTROLS, toUnicodeEscape);
178
+ }
179
+ function isInRange(code, low, high) {
180
+ return code >= low && code <= high;
181
+ }
182
+ function skipControlSequence(text, index) {
183
+ let i = index;
184
+ while (i < text.length && isInRange(text.charCodeAt(i), 48, 63))
185
+ i++;
186
+ while (i < text.length && isInRange(text.charCodeAt(i), 32, 47))
187
+ i++;
188
+ if (i < text.length && isInRange(text.charCodeAt(i), 64, 126))
189
+ i++;
190
+ return i;
191
+ }
192
+ function skipControlString(text, index) {
193
+ let i = index;
194
+ while (i < text.length) {
195
+ const code = text.charCodeAt(i);
196
+ if (code === BEL || code === C1_ST)
197
+ return i + 1;
198
+ if (code === ESC)
199
+ return text.charCodeAt(i + 1) === 92 ? i + 2 : i;
200
+ i++;
201
+ }
202
+ return i;
203
+ }
204
+ function skipEscapeSequence(text, index) {
205
+ const next = text.charCodeAt(index + 1);
206
+ if (Number.isNaN(next))
207
+ return index + 1;
208
+ if (next === 91)
209
+ return skipControlSequence(text, index + 2);
210
+ if (ESC_STRING_INTRODUCERS.has(next))
211
+ return skipControlString(text, index + 2);
212
+ let i = index + 1;
213
+ while (i < text.length && isInRange(text.charCodeAt(i), 32, 47))
214
+ i++;
215
+ if (i < text.length && isInRange(text.charCodeAt(i), 48, 126))
216
+ i++;
217
+ return i;
218
+ }
219
+ function sanitizeForTerminal(value, options = {}) {
220
+ const text = value === null || value === undefined ? "" : String(value);
221
+ const multiline = options.preserveNewlines === true;
222
+ let out = "";
223
+ let i = 0;
224
+ while (i < text.length) {
225
+ const code = text.charCodeAt(i);
226
+ if (code === ESC) {
227
+ i = skipEscapeSequence(text, i);
228
+ continue;
229
+ }
230
+ if (code === C1_CSI) {
231
+ i = skipControlSequence(text, i + 1);
232
+ continue;
233
+ }
234
+ if (C1_STRING_INTRODUCERS.has(code)) {
235
+ i = skipControlString(text, i + 1);
236
+ continue;
237
+ }
238
+ if (code === CR || code === LF) {
239
+ const crlf = code === CR && text.charCodeAt(i + 1) === LF;
240
+ out += multiline ? `
241
+ ` : " ";
242
+ i += crlf ? 2 : 1;
243
+ continue;
244
+ }
245
+ if (code === NEL || code === LINE_SEPARATOR || code === PARAGRAPH_SEPARATOR) {
246
+ out += multiline ? toUnicodeEscape(text.charAt(i)) : " ";
247
+ i++;
248
+ continue;
249
+ }
250
+ if (code === TAB) {
251
+ out += multiline ? "\t" : " ";
252
+ i++;
253
+ continue;
254
+ }
255
+ if (code < 32 || isInRange(code, DEL, 159)) {
256
+ i++;
257
+ continue;
258
+ }
259
+ out += text.charAt(i);
260
+ i++;
261
+ }
262
+ return escapeBidiControls(out);
263
+ }
264
+ var SECRET_KEY_HINT = /pass(?:word|wd|phrase)?|secret|token|api[-_]?key|private[-_]?key|access[-_]?key|credential|authori[sz]ation|cookie|session[-_]?id/i;
265
+ var KEY_VALUE_OPENING = /\b([A-Za-z_$][\w$-]{0,127})\\*["']?\s*[:=]\s*/g;
266
+ var QUOTE_OPENING = /^(\\*)(["'])/;
267
+ var UNQUOTED_VALUE = /^[^\s"'\\,;&)}\]]*/;
268
+ var AUTH_SCHEME = /^(?:Bearer|Basic|Digest|Token)\s+/i;
269
+ var BEARER_CREDENTIAL = /\bBearer\s+[A-Za-z0-9\-._~+/]+=*/g;
270
+ var JWT_LIKE = /\beyJ[\w-]{4,}\.[\w-]{4,}\.[\w-]*/g;
271
+ var URL_USERINFO = /\b([a-z][a-z0-9+.-]{1,31}:\/\/)[^\s/?#@"'\\]+@/gi;
272
+ function backslashRun(text, index, floor) {
273
+ let run = 0;
274
+ while (index - run - 1 >= floor && text.charAt(index - run - 1) === "\\")
275
+ run++;
276
+ return run;
277
+ }
278
+ function maskValue(text, start) {
279
+ if (text.startsWith(REDACTED, start)) {
280
+ return { replacement: REDACTED, end: start + REDACTED.length };
281
+ }
282
+ const quote = QUOTE_OPENING.exec(text.slice(start, start + 64));
283
+ if (quote) {
284
+ const opener = quote[0];
285
+ const depth = (quote[1] ?? "").length;
286
+ const quoteChar = quote[2] ?? '"';
287
+ let search = start + opener.length;
288
+ while (search < text.length) {
289
+ const candidate = text.indexOf(quoteChar, search);
290
+ if (candidate === -1)
291
+ break;
292
+ if (backslashRun(text, candidate, start + opener.length) === depth) {
293
+ return { replacement: `${opener}${REDACTED}${opener}`, end: candidate + 1 };
294
+ }
295
+ search = candidate + 1;
296
+ }
297
+ return { replacement: `${opener}${REDACTED}`, end: text.length };
298
+ }
299
+ const rest = text.slice(start);
300
+ const scheme = AUTH_SCHEME.exec(rest);
301
+ const offset = scheme ? scheme[0].length : 0;
302
+ const run = UNQUOTED_VALUE.exec(rest.slice(offset));
303
+ const length = offset + (run ? run[0].length : 0);
304
+ return { replacement: length > 0 ? REDACTED : "", end: start + length };
305
+ }
306
+ function redactKeyValues(text) {
307
+ let out = "";
308
+ let copiedUpTo = 0;
309
+ KEY_VALUE_OPENING.lastIndex = 0;
310
+ let match;
311
+ while ((match = KEY_VALUE_OPENING.exec(text)) !== null) {
312
+ if (!SECRET_KEY_HINT.test(match[1] ?? ""))
313
+ continue;
314
+ const start = match.index + match[0].length;
315
+ const { replacement, end } = maskValue(text, start);
316
+ if (end === start)
317
+ continue;
318
+ out += text.slice(copiedUpTo, start) + replacement;
319
+ copiedUpTo = end;
320
+ KEY_VALUE_OPENING.lastIndex = end;
321
+ }
322
+ return out + text.slice(copiedUpTo);
323
+ }
324
+ function redactSecrets(text) {
325
+ return redactKeyValues(text).replace(BEARER_CREDENTIAL, `Bearer ${REDACTED}`).replace(JWT_LIKE, REDACTED).replace(URL_USERINFO, `$1${REDACTED}@`);
326
+ }
327
+ var LINE_BREAK = /\r\n|[\r\n\u0085\u2028\u2029]/;
328
+ function toDisplayLine(value, maxLength = MAX_ERROR_MESSAGE_LENGTH) {
329
+ if (value === null || value === undefined)
330
+ return "";
331
+ const firstLine = String(value).split(LINE_BREAK, 1)[0] ?? "";
332
+ const scanned = firstLine.slice(0, MAX_SCAN_LENGTH);
333
+ const line = redactSecrets(sanitizeForTerminal(scanned)).trim();
334
+ return line.length > maxLength ? `${line.slice(0, maxLength)}${ELLIPSIS}` : line;
335
+ }
336
+ function summarizeHttpBody(body) {
337
+ return toDisplayLine(body, MAX_HTTP_BODY_EXCERPT_LENGTH) || "empty response body";
338
+ }
339
+ var ERROR_CODE = /^[A-Z][A-Z0-9_]{1,63}$/;
340
+ function asRecord(value) {
341
+ return typeof value === "object" && value !== null ? value : undefined;
342
+ }
343
+ function stringField(record, key) {
344
+ const value = record?.[key];
345
+ return typeof value === "string" ? value : undefined;
346
+ }
347
+ function isAbortError(error) {
348
+ const name = stringField(asRecord(error), "name");
349
+ return name === "AbortError" || name === "TimeoutError";
350
+ }
351
+ function describeResponseError(response) {
352
+ const status = typeof response.status === "number" ? response.status : undefined;
353
+ const errors = Array.isArray(response.errors) ? response.errors : [];
354
+ const first = asRecord(errors[0]);
355
+ const extensions = asRecord(first?.extensions);
356
+ const original = toDisplayLine(stringField(asRecord(extensions?.originalError), "message"));
357
+ const wrapper = toDisplayLine(stringField(first, "message"));
358
+ const rawCode = stringField(extensions, "code");
359
+ const code = rawCode !== undefined && ERROR_CODE.test(rawCode) ? rawCode : undefined;
360
+ const base = original || wrapper || (status !== undefined ? `Request failed (HTTP ${status})` : "Request failed");
361
+ return {
362
+ message: code ? `${base} [${code}]` : base,
363
+ ...status !== undefined ? { status } : {},
364
+ ...code ? { code } : {}
365
+ };
366
+ }
367
+ function networkErrorCode(error) {
368
+ const code = stringField(asRecord(asRecord(error)?.cause), "code");
369
+ return code !== undefined && ERROR_CODE.test(code) ? code : undefined;
370
+ }
371
+ function describeRequestError(error) {
372
+ const response = asRecord(asRecord(error)?.response);
373
+ if (response)
374
+ return describeResponseError(response);
375
+ if (isAbortError(error))
376
+ return { message: "Request timed out" };
377
+ if (error instanceof TypeError) {
378
+ const detail = toDisplayLine(error.message, MAX_HTTP_BODY_EXCERPT_LENGTH);
379
+ const code = networkErrorCode(error);
380
+ return {
381
+ message: `Network error${detail ? `: ${detail}` : ""}${code ? ` (${code})` : ""}`
382
+ };
383
+ }
384
+ return { message: toSafeErrorMessage(error) };
385
+ }
386
+ function toSafeErrorMessage(error) {
387
+ const response = asRecord(asRecord(error)?.response);
388
+ if (response)
389
+ return describeResponseError(response).message;
390
+ if (isAbortError(error))
391
+ return "Request timed out";
392
+ if (error instanceof Error || typeof error === "string") {
393
+ const text = typeof error === "string" ? error : error.message;
394
+ return toDisplayLine(text) || "Unexpected error";
395
+ }
396
+ return "Unexpected error";
397
+ }
398
+
399
+ // src/auth/device-code.ts
400
+ function serverMessage(message) {
401
+ return toDisplayLine(message, MAX_HTTP_BODY_EXCERPT_LENGTH) || "Unknown error";
402
+ }
403
+ async function httpFailure(prefix, response) {
404
+ const body = await response.text().catch(() => "");
405
+ return new Error(`${prefix} (${response.status}): ${summarizeHttpBody(body)}`);
406
+ }
407
+ async function startDeviceCodeFlow(config) {
408
+ const url = `${config.oidcIssuer}/oidc/device/code`;
409
+ const body = new URLSearchParams({
410
+ client_id: config.clientId,
411
+ scope: "openid profile email offline_access"
412
+ });
413
+ const response = await fetch(url, {
414
+ method: "POST",
415
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
416
+ body: body.toString()
417
+ });
418
+ if (!response.ok) {
419
+ throw await httpFailure("Device code request failed", response);
420
+ }
421
+ const data = await response.json();
422
+ return {
423
+ deviceCode: data["device_code"],
424
+ userCode: data["user_code"],
425
+ verificationUri: data["verification_uri"],
426
+ verificationUriComplete: data["verification_uri_complete"] ?? null,
427
+ expiresIn: data["expires_in"],
428
+ interval: data["interval"] ?? 5
429
+ };
430
+ }
431
+ async function pollForToken(deviceCode, interval, config) {
432
+ const url = `${config.oidcIssuer}/oauth/device/token`;
433
+ let pollInterval = interval;
434
+ while (true) {
435
+ await new Promise((resolve) => setTimeout(resolve, pollInterval * 1000));
436
+ const response = await fetch(url, {
437
+ method: "POST",
438
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
439
+ body: new URLSearchParams({
440
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
441
+ device_code: deviceCode,
442
+ client_id: config.clientId
443
+ }).toString()
444
+ });
445
+ const raw = await response.text();
446
+ let data;
447
+ try {
448
+ data = JSON.parse(raw);
449
+ } catch {
450
+ if (response.status >= 500) {
451
+ continue;
452
+ }
453
+ throw new Error(`Token endpoint returned non-JSON response (status ${response.status}): ${summarizeHttpBody(raw)}`);
454
+ }
455
+ if (response.ok && data["access_token"]) {
456
+ return {
457
+ accessToken: data["access_token"],
458
+ refreshToken: data["refresh_token"] ?? "",
459
+ expiresIn: data["expires_in"] ?? 3600
460
+ };
461
+ }
462
+ const errorCode = data["error"];
463
+ switch (errorCode) {
464
+ case "authorization_pending":
465
+ continue;
466
+ case "slow_down":
467
+ pollInterval += 5;
468
+ continue;
469
+ case "expired_token":
470
+ throw new Error("Device code has expired. Please try again.");
471
+ case "access_denied":
472
+ throw new Error("Authorization was denied by the user.");
473
+ default:
474
+ throw new Error(`Device code polling failed: ${serverMessage(data["error_description"] ?? errorCode)}`);
475
+ }
476
+ }
477
+ }
478
+ async function clientCredentialsFlow(globalEndpoint, clientId, clientSecret, scopes) {
479
+ const query = `
480
+ mutation AuthenticateApp($input: AuthenticateAppInput!) {
481
+ authenticateApp(input: $input) {
482
+ accessToken tokenType expiresIn scopes
483
+ }
484
+ }
485
+ `;
486
+ const response = await fetch(globalEndpoint, {
487
+ method: "POST",
488
+ headers: { "Content-Type": "application/json" },
489
+ body: JSON.stringify({
490
+ query,
491
+ variables: { input: { clientId, clientSecret, scopes } }
492
+ })
493
+ });
494
+ if (!response.ok) {
495
+ throw await httpFailure("App authentication failed", response);
496
+ }
497
+ const data = await response.json();
498
+ if (data.errors?.length) {
499
+ throw new Error(`App authentication failed: ${serverMessage(data.errors[0]?.message)}`);
500
+ }
501
+ if (!data.data?.authenticateApp?.accessToken) {
502
+ throw new Error("App authentication failed: no access token returned");
503
+ }
504
+ return data.data.authenticateApp;
505
+ }
506
+ async function issueWorkspaceToken(workspaceEndpoint, authToken, workspaceId, organizationId) {
507
+ const query = `
508
+ mutation IssueWorkspaceToken($input: IssueWorkspaceTokenInput!) {
509
+ issueWorkspaceToken(input: $input) {
510
+ success token expiresAt scopes
511
+ error { code message }
512
+ }
513
+ }
514
+ `;
515
+ const response = await fetch(workspaceEndpoint, {
516
+ method: "POST",
517
+ headers: {
518
+ "Content-Type": "application/json",
519
+ Authorization: `Bearer ${authToken}`
520
+ },
521
+ body: JSON.stringify({
522
+ query,
523
+ variables: { input: { workspaceId, organizationId } }
524
+ })
525
+ });
526
+ if (!response.ok) {
527
+ throw await httpFailure("Workspace token request failed", response);
528
+ }
529
+ const data = await response.json();
530
+ if (data.errors?.length) {
531
+ throw new Error(`Workspace token failed: ${serverMessage(data.errors[0]?.message)}`);
532
+ }
533
+ const result = data.data?.issueWorkspaceToken;
534
+ if (!result?.success || !result.token) {
535
+ throw new Error(`Workspace token failed: ${serverMessage(result?.error?.message)}`);
536
+ }
537
+ return { token: result.token, expiresAt: result.expiresAt, scopes: result.scopes ?? [] };
538
+ }
539
+ async function fetchWorkspaces(globalEndpoint, authToken) {
540
+ const query = `
541
+ query MyWorkspaces($limit: Int) {
542
+ myWorkspaces(limit: $limit) {
543
+ items { id name slug tenantId organizationId }
544
+ }
545
+ }
546
+ `;
547
+ const response = await fetch(globalEndpoint, {
548
+ method: "POST",
549
+ headers: {
550
+ "Content-Type": "application/json",
551
+ Authorization: `Bearer ${authToken}`
552
+ },
553
+ body: JSON.stringify({ query, variables: { limit: 50 } })
554
+ });
555
+ if (!response.ok) {
556
+ throw await httpFailure("Failed to fetch workspaces", response);
557
+ }
558
+ const data = await response.json();
559
+ if (data.errors?.length) {
560
+ throw new Error(`Failed to fetch workspaces: ${serverMessage(data.errors[0]?.message)}`);
561
+ }
562
+ return data.data?.myWorkspaces?.items ?? [];
563
+ }
564
+ function isValidHttpUrl(urlString) {
565
+ try {
566
+ const parsed = new URL(urlString);
567
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
568
+ } catch {
569
+ return false;
570
+ }
571
+ }
572
+ async function openBrowser(url) {
573
+ try {
574
+ if (!isValidHttpUrl(url)) {
575
+ return false;
576
+ }
577
+ const { execFile } = await import("child_process");
578
+ const platform = process.platform;
579
+ let command;
580
+ let args;
581
+ if (platform === "darwin") {
582
+ command = "open";
583
+ args = [url];
584
+ } else if (platform === "win32") {
585
+ command = "cmd";
586
+ args = ["/c", "start", "", url];
587
+ } else {
588
+ command = "xdg-open";
589
+ args = [url];
590
+ }
591
+ return new Promise((resolve) => {
592
+ execFile(command, args, (error) => {
593
+ resolve(!error);
594
+ });
595
+ });
596
+ } catch {
597
+ return false;
598
+ }
599
+ }
600
+
601
+ // src/utils/output.ts
602
+ import {
603
+ DEFAULT_SCALAR_STYLE_RULES,
604
+ JSON_SCHEMA,
605
+ SCALAR_STYLE,
606
+ dump,
607
+ strTag
608
+ } from "js-yaml";
609
+ var MAX_COLUMN_WIDTH = 50;
610
+ function isInteractive() {
611
+ return Boolean(process.stdout.isTTY && process.stdin.isTTY);
612
+ }
613
+ function getFormat(options) {
614
+ if (options.format) {
615
+ const f = options.format.toLowerCase();
616
+ if (f === "json" || f === "table" || f === "yaml")
617
+ return f;
618
+ if (f === "yml")
619
+ return "yaml";
620
+ }
621
+ return isInteractive() ? "table" : "json";
622
+ }
623
+ var JSON_DISPLAY_ESCAPES = /[\u007f-\u009f\u2028\u2029\u202a-\u202e\u2066-\u2069]/g;
624
+ function toJson(data) {
625
+ const json = JSON.stringify(data, null, 2) ?? "null";
626
+ return json.replace(JSON_DISPLAY_ESCAPES, toUnicodeEscape);
627
+ }
628
+ function printJson(data) {
629
+ console.log(toJson(data));
630
+ }
631
+ function printTable(headers, rows) {
632
+ if (headers.length === 0)
633
+ return;
634
+ const header = headers.map((h) => sanitizeForTerminal(h));
635
+ const body = rows.map((row) => headers.map((_, i) => sanitizeForTerminal(row[i])));
636
+ const colWidths = header.map((h, i) => Math.min(body.reduce((max, row) => Math.max(max, (row[i] ?? "").length), h.length), MAX_COLUMN_WIDTH));
637
+ const separator = colWidths.map((w) => "-".repeat(w + 2)).join("+");
638
+ const formatRow = (cells) => cells.map((cell, i) => ` ${cell.padEnd(colWidths[i] ?? 0)} `).join("|");
639
+ console.log(formatRow(header));
640
+ console.log(separator);
641
+ for (const row of body) {
642
+ console.log(formatRow(row));
643
+ }
644
+ }
645
+ function allowsStyle(mask, style) {
646
+ return (mask & 1 << style) !== 0;
647
+ }
648
+ var BIDI_CONTROL = /[\u202a-\u202e\u2066-\u2069]/;
649
+ var UNAMBIGUOUS_PLAIN = /^\p{L}[\p{L}\p{N} _./@+-]*$/u;
650
+ var NON_STRING_WORDS = /^(?:y|n|yes|no|on|off|true|false|null)$/i;
651
+ var doubleQuoteBidiControls = (layout) => {
652
+ if (BIDI_CONTROL.test(layout.node.value))
653
+ layout.style = SCALAR_STYLE.DOUBLE_QUOTED;
654
+ };
655
+ var quoteAmbiguousStrings = (layout) => {
656
+ if (layout.style !== SCALAR_STYLE.PLAIN || layout.node.tag !== strTag.tagName)
657
+ return;
658
+ const value = layout.node.value;
659
+ if (UNAMBIGUOUS_PLAIN.test(value) && !NON_STRING_WORDS.test(value))
660
+ return;
661
+ layout.style = allowsStyle(layout.allowedStylesMask, SCALAR_STYLE.SINGLE_QUOTED) ? SCALAR_STYLE.SINGLE_QUOTED : SCALAR_STYLE.DOUBLE_QUOTED;
662
+ };
663
+ var YAML_STYLE_RULES = [
664
+ doubleQuoteBidiControls,
665
+ ...Object.values(DEFAULT_SCALAR_STYLE_RULES),
666
+ quoteAmbiguousStrings
667
+ ];
668
+ function toYaml(data) {
669
+ const yaml = dump(data ?? null, {
670
+ schema: JSON_SCHEMA,
671
+ noRefs: true,
672
+ skipInvalid: true,
673
+ lineWidth: -1,
674
+ scalarStyleRules: YAML_STYLE_RULES
675
+ });
676
+ return escapeBidiControls(yaml).trimEnd();
677
+ }
678
+ function printYaml(data) {
679
+ console.log(toYaml(data));
680
+ }
681
+ function printSuccess(message) {
682
+ console.error(`✓ ${sanitizeForTerminal(message)}`);
683
+ }
684
+ function printError(message) {
685
+ console.error(`✗ ${redactSecrets(sanitizeForTerminal(message))}`);
686
+ }
687
+ function printWarning(message) {
688
+ console.warn(`! ${redactSecrets(sanitizeForTerminal(message))}`);
689
+ }
690
+ function printInfo(message) {
691
+ console.error(`ℹ ${sanitizeForTerminal(message)}`);
692
+ }
693
+ function formatDate(date) {
694
+ if (!date)
695
+ return "-";
696
+ try {
697
+ return new Date(date).toLocaleString();
698
+ } catch {
699
+ return date;
700
+ }
701
+ }
702
+ function formatStatus(status) {
703
+ if (!status)
704
+ return "-";
705
+ const upper = status.toUpperCase();
706
+ switch (upper) {
707
+ case "ACTIVE":
708
+ case "RUNNING":
709
+ case "HEALTHY":
710
+ return `✓ ${status}`;
711
+ case "INACTIVE":
712
+ case "STOPPED":
713
+ case "PENDING":
714
+ return `○ ${status}`;
715
+ case "ERROR":
716
+ return `✗ ${status}`;
717
+ case "ARCHIVED":
718
+ return `→ ${status}`;
719
+ case "STARTING":
720
+ return `▶ ${status}`;
721
+ case "TERMINATED":
722
+ return `■ ${status}`;
723
+ default:
724
+ return status;
725
+ }
726
+ }
727
+ function outputData(format, data, tableConfig) {
728
+ switch (format) {
729
+ case "json":
730
+ printJson(data);
731
+ break;
732
+ case "yaml":
733
+ printYaml(data);
734
+ break;
735
+ case "table":
736
+ if (tableConfig) {
737
+ printTable(tableConfig.headers, tableConfig.rows);
738
+ } else {
739
+ printJson(data);
740
+ }
741
+ break;
742
+ }
743
+ }
744
+ function truncate(str, maxLen) {
745
+ if (!str)
746
+ return "-";
747
+ const clean = sanitizeForTerminal(str);
748
+ if (clean.length <= maxLen)
749
+ return clean;
750
+ return clean.substring(0, maxLen - 3) + "...";
751
+ }
752
+
753
+ // src/graphql/client.ts
754
+ var DEFAULT_TIMEOUT = 30000;
755
+ var MAX_RETRIES = 3;
756
+ var INITIAL_RETRY_DELAY_MS = 1000;
757
+ function sleep(ms) {
758
+ return new Promise((resolve) => setTimeout(resolve, ms));
759
+ }
760
+ function isNetworkOrServerError(error) {
761
+ if (error instanceof TypeError)
762
+ return true;
763
+ if (error && typeof error === "object" && "response" in error) {
764
+ const resp = error.response;
765
+ if (resp && typeof resp.status === "number" && resp.status >= 500)
766
+ return true;
767
+ }
768
+ return false;
769
+ }
770
+
771
+ class BigConsoleRequestError extends Error {
772
+ status;
773
+ code;
774
+ constructor(description) {
775
+ super(description.message);
776
+ this.name = "BigConsoleRequestError";
777
+ this.status = description.status;
778
+ this.code = description.code;
779
+ }
780
+ }
781
+ var workspaceClient = null;
782
+ var globalClient = null;
783
+ function getHeaders(auth) {
784
+ const headers = {};
785
+ if (auth?.accessToken) {
786
+ headers["Authorization"] = `Bearer ${auth.accessToken}`;
787
+ }
788
+ return headers;
789
+ }
790
+ function getWorkspaceHeaders(auth) {
791
+ const headers = getHeaders(auth);
792
+ if (auth?.workspaceToken) {
793
+ headers["X-Workspace-Authorization"] = `Bearer ${auth.workspaceToken}`;
794
+ }
795
+ if (auth?.organizationId) {
796
+ headers["x-org-id"] = auth.organizationId;
797
+ }
798
+ return headers;
799
+ }
800
+ function initClients() {
801
+ const config = readConfig();
802
+ const auth = readAuth();
803
+ workspaceClient = new GraphQLClient(config.workspaceEndpoint);
804
+ workspaceClient.setHeaders(getWorkspaceHeaders(auth));
805
+ globalClient = new GraphQLClient(config.globalEndpoint);
806
+ globalClient.setHeaders(getHeaders(auth));
807
+ return { workspace: workspaceClient, global: globalClient };
808
+ }
809
+ async function refreshPlatformToken() {
810
+ const auth = readAuth();
811
+ if (!auth)
812
+ return;
813
+ if (auth.authMethod === "client_credentials" && auth.clientId && auth.clientSecret) {
814
+ const config = readConfig();
815
+ try {
816
+ const result = await clientCredentialsFlow(config.globalEndpoint, auth.clientId, auth.clientSecret);
817
+ writeAuth({
818
+ ...auth,
819
+ accessToken: result.accessToken,
820
+ expiresAt: Date.now() + result.expiresIn * 1000
821
+ });
822
+ workspaceClient = null;
823
+ globalClient = null;
824
+ } catch (error) {
825
+ printError(`App token refresh failed: ${toSafeErrorMessage(error)}`);
826
+ }
827
+ return;
828
+ }
829
+ if (!auth.refreshToken)
830
+ return;
831
+ const config = readConfig();
832
+ const client = new GraphQLClient(config.globalEndpoint);
833
+ client.setHeaders({ Authorization: `Bearer ${auth.accessToken}` });
834
+ const REFRESH_MUTATION = `
835
+ mutation RefreshToken($token: String!) {
836
+ refreshToken(token: $token) {
837
+ accessToken
838
+ refreshToken
839
+ expiresIn
840
+ }
841
+ }
842
+ `;
843
+ try {
844
+ const data = await client.request(REFRESH_MUTATION, {
845
+ token: auth.refreshToken
846
+ });
847
+ const result = data.refreshToken;
848
+ const expiresAt = Date.now() + result.expiresIn * 1000;
849
+ writeAuth({
850
+ ...auth,
851
+ accessToken: result.accessToken,
852
+ refreshToken: result.refreshToken,
853
+ expiresAt
854
+ });
855
+ workspaceClient = null;
856
+ globalClient = null;
857
+ } catch (error) {
858
+ printError(`Token refresh failed: ${toSafeErrorMessage(error)}`);
859
+ }
860
+ }
861
+ async function refreshWorkspaceToken() {
862
+ const auth = readAuth();
863
+ if (!auth?.accessToken || !auth.workspaceId || !auth.organizationId)
864
+ return;
865
+ const config = readConfig();
866
+ try {
867
+ const result = await issueWorkspaceToken(config.workspaceEndpoint, auth.accessToken, auth.workspaceId, auth.organizationId);
868
+ writeAuth({
869
+ ...auth,
870
+ workspaceToken: result.token,
871
+ workspaceTokenExpiresAt: new Date(result.expiresAt).getTime()
872
+ });
873
+ workspaceClient = null;
874
+ } catch (error) {
875
+ printError(`Workspace token refresh failed: ${toSafeErrorMessage(error)}`);
876
+ }
877
+ }
878
+ async function ensureFreshTokens() {
879
+ const auth = readAuth();
880
+ if (!auth)
881
+ return;
882
+ if (isTokenExpiringSoon(auth)) {
883
+ await refreshPlatformToken();
884
+ }
885
+ const freshAuth = readAuth();
886
+ if (freshAuth && freshAuth.workspaceToken && isWorkspaceTokenExpiringSoon(freshAuth)) {
887
+ await refreshWorkspaceToken();
888
+ }
889
+ }
890
+ function isRetryableDocument(document) {
891
+ const text = typeof document === "string" ? document : typeof document?.loc?.source?.body === "string" ? document.loc.source.body : "";
892
+ const m = text.match(/\b(query|mutation|subscription)\b/i);
893
+ return !m || m[1].toLowerCase() === "query";
894
+ }
895
+ async function executeWithRetry(client, document, variables) {
896
+ const retryable = isRetryableDocument(document);
897
+ const maxAttempts = retryable ? MAX_RETRIES : 1;
898
+ let lastError;
899
+ for (let attempt = 0;attempt < maxAttempts; attempt++) {
900
+ try {
901
+ const controller = new AbortController;
902
+ const timer = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT);
903
+ try {
904
+ return await client.request({
905
+ document,
906
+ variables,
907
+ signal: controller.signal
908
+ });
909
+ } finally {
910
+ clearTimeout(timer);
911
+ }
912
+ } catch (error) {
913
+ lastError = error;
914
+ if (error && typeof error === "object" && "response" in error) {
915
+ const resp = error.response;
916
+ if (resp && typeof resp.status === "number" && resp.status >= 400 && resp.status < 500) {
917
+ break;
918
+ }
919
+ }
920
+ if (retryable && isNetworkOrServerError(error) && attempt < maxAttempts - 1) {
921
+ const delay = INITIAL_RETRY_DELAY_MS * Math.pow(2, attempt);
922
+ await sleep(delay);
923
+ continue;
924
+ }
925
+ break;
926
+ }
927
+ }
928
+ throw new BigConsoleRequestError(describeRequestError(lastError));
929
+ }
930
+ async function workspaceRequest(document, variables) {
931
+ await ensureFreshTokens();
932
+ const { workspace } = workspaceClient ? { workspace: workspaceClient } : initClients();
933
+ const auth = readAuth();
934
+ workspace.setHeaders(getWorkspaceHeaders(auth));
935
+ return executeWithRetry(workspace, document, variables);
936
+ }
937
+ async function globalRequest(document, variables) {
938
+ const { global: client } = globalClient ? { global: globalClient } : initClients();
939
+ const auth = readAuth();
940
+ client.setHeaders(getHeaders(auth));
941
+ return executeWithRetry(client, document, variables);
942
+ }
943
+ function resetClients() {
944
+ workspaceClient = null;
945
+ globalClient = null;
946
+ }
947
+ // src/graphql/operations.ts
948
+ var SIGN_OUT = `
949
+ mutation SignOut {
950
+ signOut
951
+ }
952
+ `;
953
+ var REFRESH_TOKEN = `
954
+ mutation RefreshToken($token: String!) {
955
+ refreshToken(token: $token) {
956
+ accessToken
957
+ refreshToken
958
+ expiresIn
959
+ }
960
+ }
961
+ `;
962
+ var LIST_DASHBOARDS = `
963
+ query ListDashboards(
964
+ $first: Int
965
+ $after: String
966
+ $search: String
967
+ $visibility: BigConsoleDashboardVisibility
968
+ $category: BigConsoleDashboardCategory
969
+ $includeArchived: Boolean
970
+ $sortBy: BigConsoleDashboardSortField
971
+ $sortOrder: SortOrder
972
+ ) {
973
+ listDashboards(
974
+ first: $first
975
+ after: $after
976
+ search: $search
977
+ visibility: $visibility
978
+ category: $category
979
+ includeArchived: $includeArchived
980
+ sortBy: $sortBy
981
+ sortOrder: $sortOrder
982
+ ) {
983
+ edges {
984
+ node {
985
+ id
986
+ name
987
+ description
988
+ status
989
+ visibility
990
+ category
991
+ ownerId
992
+ createdAt
993
+ updatedAt
994
+ }
995
+ }
996
+ pageInfo { hasNextPage endCursor }
997
+ totalCount
998
+ }
999
+ }
1000
+ `;
1001
+ var GET_DASHBOARD = `
1002
+ query GetDashboard($id: ID!) {
1003
+ getDashboard(id: $id) {
1004
+ id
1005
+ name
1006
+ description
1007
+ status
1008
+ visibility
1009
+ category
1010
+ ownerId
1011
+ definitionVersion
1012
+ createdAt
1013
+ updatedAt
1014
+ }
1015
+ }
1016
+ `;
1017
+ var ARCHIVE_DASHBOARD = `
1018
+ mutation ArchiveDashboard($id: ID!) {
1019
+ archiveDashboard(id: $id) {
1020
+ id
1021
+ name
1022
+ status
1023
+ }
1024
+ }
1025
+ `;
1026
+ var DELETE_DASHBOARD = `
1027
+ mutation DeleteDashboard($id: ID!) {
1028
+ deleteBigConsoleDashboard(id: $id)
1029
+ }
1030
+ `;
1031
+ var LIST_WIDGETS = `
1032
+ query ListWidgets($pageId: ID!, $first: Int, $after: String, $type: BigConsoleWidgetType) {
1033
+ listWidgets(pageId: $pageId, first: $first, after: $after, type: $type) {
1034
+ edges {
1035
+ node {
1036
+ id
1037
+ pageId
1038
+ dashboardId
1039
+ type
1040
+ title
1041
+ dataSinkId
1042
+ lastRefreshedAt
1043
+ createdAt
1044
+ updatedAt
1045
+ }
1046
+ }
1047
+ pageInfo { hasNextPage endCursor }
1048
+ totalCount
1049
+ }
1050
+ }
1051
+ `;
1052
+ var GET_WIDGET = `
1053
+ query GetWidget($id: ID!) {
1054
+ getWidget(id: $id) {
1055
+ id
1056
+ pageId
1057
+ dashboardId
1058
+ type
1059
+ title
1060
+ config
1061
+ dataSinkId
1062
+ datasetId
1063
+ lastRefreshedAt
1064
+ cacheTTL
1065
+ createdAt
1066
+ updatedAt
1067
+ }
1068
+ }
1069
+ `;
1070
+ var DELETE_WIDGET = `
1071
+ mutation DeleteWidget($id: ID!) {
1072
+ deleteBigConsoleWidget(id: $id)
1073
+ }
1074
+ `;
1075
+ var LIST_DATASINKS = `
1076
+ query ListDataSinks(
1077
+ $first: Int
1078
+ $after: String
1079
+ $search: String
1080
+ $status: DataSinkStatus
1081
+ $setupState: DataSinkSetupState
1082
+ $isActive: Boolean
1083
+ ) {
1084
+ listDataSinks(
1085
+ first: $first
1086
+ after: $after
1087
+ search: $search
1088
+ status: $status
1089
+ setupState: $setupState
1090
+ isActive: $isActive
1091
+ ) {
1092
+ edges {
1093
+ node {
1094
+ id
1095
+ name
1096
+ key
1097
+ description
1098
+ status
1099
+ setupState
1100
+ isActive
1101
+ createdAt
1102
+ updatedAt
1103
+ }
1104
+ }
1105
+ pageInfo { hasNextPage endCursor }
1106
+ totalCount
1107
+ }
1108
+ }
1109
+ `;
1110
+ var GET_DATASINK = `
1111
+ query GetDataSink($id: ID, $key: String) {
1112
+ getDataSink(id: $id, key: $key) {
1113
+ id
1114
+ name
1115
+ key
1116
+ description
1117
+ status
1118
+ setupState
1119
+ isActive
1120
+ version
1121
+ createdAt
1122
+ updatedAt
1123
+ }
1124
+ }
1125
+ `;
1126
+ var GET_DATASINK_DATA = `
1127
+ query GetDataSinkData($id: ID, $key: String, $dataPath: String, $forceRefresh: Boolean) {
1128
+ getDataSinkData(id: $id, key: $key, dataPath: $dataPath, forceRefresh: $forceRefresh) {
1129
+ raw
1130
+ extracted
1131
+ cached
1132
+ fetchedAt
1133
+ version
1134
+ totalCount
1135
+ hasMore
1136
+ limit
1137
+ offset
1138
+ }
1139
+ }
1140
+ `;
1141
+ var DELETE_DATASINK = `
1142
+ mutation DeleteDataSink($id: ID!) {
1143
+ deleteDataSink(id: $id) {
1144
+ id
1145
+ name
1146
+ }
1147
+ }
1148
+ `;
1149
+ var LIST_PARSERS = `
1150
+ query ListParsers($first: Int, $after: String, $filter: ParserFilterInput) {
1151
+ listParsers(first: $first, after: $after, filter: $filter) {
1152
+ edges {
1153
+ node {
1154
+ id
1155
+ name
1156
+ description
1157
+ type
1158
+ status
1159
+ inputSinkKey
1160
+ outputKey
1161
+ outputWidgetId
1162
+ ttl
1163
+ version
1164
+ createdAt
1165
+ updatedAt
1166
+ }
1167
+ }
1168
+ pageInfo { hasNextPage endCursor }
1169
+ totalCount
1170
+ }
1171
+ }
1172
+ `;
1173
+ var GET_PARSER = `
1174
+ query GetParser($id: ID!) {
1175
+ parser(id: $id) {
1176
+ id
1177
+ name
1178
+ description
1179
+ type
1180
+ status
1181
+ expression
1182
+ inputSinkKey
1183
+ outputKey
1184
+ outputWidgetId
1185
+ ttl
1186
+ version
1187
+ createdAt
1188
+ updatedAt
1189
+ }
1190
+ }
1191
+ `;
1192
+ var DELETE_PARSER = `
1193
+ mutation DeleteParser($id: ID!) {
1194
+ deleteParser(id: $id)
1195
+ }
1196
+ `;
1197
+ var LIST_PIPELINES = `
1198
+ query ListPipelines($first: Int, $after: String, $filter: PipelineFilterInput) {
1199
+ listPipelines(first: $first, after: $after, filter: $filter) {
1200
+ edges {
1201
+ node {
1202
+ id
1203
+ name
1204
+ description
1205
+ status
1206
+ version
1207
+ lastExecutedAt
1208
+ createdAt
1209
+ updatedAt
1210
+ }
1211
+ }
1212
+ pageInfo { hasNextPage endCursor }
1213
+ totalCount
1214
+ }
1215
+ }
1216
+ `;
1217
+ var GET_PIPELINE = `
1218
+ query GetPipeline($id: ID!) {
1219
+ pipeline(id: $id) {
1220
+ id
1221
+ name
1222
+ description
1223
+ status
1224
+ version
1225
+ lastExecutedAt
1226
+ stages {
1227
+ id
1228
+ position
1229
+ inputKey
1230
+ outputKey
1231
+ enabled
1232
+ parserName
1233
+ parserType
1234
+ }
1235
+ createdAt
1236
+ updatedAt
1237
+ }
1238
+ }
1239
+ `;
1240
+ var DELETE_PIPELINE = `
1241
+ mutation DeletePipeline($id: ID!) {
1242
+ deletePipeline(id: $id)
1243
+ }
1244
+ `;
1245
+ var CREATE_DASHBOARD = `
1246
+ mutation CreateDashboard($input: BigConsoleCreateDashboardInput!) {
1247
+ createBigConsoleDashboard(input: $input) {
1248
+ id
1249
+ name
1250
+ status
1251
+ visibility
1252
+ category
1253
+ createdAt
1254
+ }
1255
+ }
1256
+ `;
1257
+ var UPDATE_DASHBOARD = `
1258
+ mutation UpdateDashboard($input: BigConsoleUpdateDashboardInput!) {
1259
+ updateBigConsoleDashboard(input: $input) {
1260
+ id
1261
+ name
1262
+ status
1263
+ visibility
1264
+ category
1265
+ updatedAt
1266
+ }
1267
+ }
1268
+ `;
1269
+ var IMPORT_DASHBOARD = `
1270
+ mutation ImportDashboard($input: BigConsoleImportDashboardInput!) {
1271
+ importDashboard(input: $input) {
1272
+ id
1273
+ name
1274
+ status
1275
+ visibility
1276
+ category
1277
+ createdAt
1278
+ }
1279
+ }
1280
+ `;
1281
+ var EXPORT_DASHBOARD = `
1282
+ query ExportDashboard($id: ID!, $options: BigConsoleExportDashboardOptions) {
1283
+ exportDashboard(id: $id, options: $options)
1284
+ }
1285
+ `;
1286
+ var CREATE_WIDGET = `
1287
+ mutation CreateWidget($input: BigConsoleCreateWidgetInput!) {
1288
+ createBigConsoleWidget(input: $input) {
1289
+ id
1290
+ pageId
1291
+ type
1292
+ title
1293
+ createdAt
1294
+ }
1295
+ }
1296
+ `;
1297
+ var UPDATE_WIDGET = `
1298
+ mutation UpdateWidget($input: BigConsoleUpdateWidgetInput!) {
1299
+ updateBigConsoleWidget(input: $input) {
1300
+ id
1301
+ pageId
1302
+ type
1303
+ title
1304
+ updatedAt
1305
+ }
1306
+ }
1307
+ `;
1308
+ var CREATE_DATASINK = `
1309
+ mutation CreateDataSink($input: CreateDataSinkInput!) {
1310
+ createDataSink(input: $input) {
1311
+ id
1312
+ name
1313
+ key
1314
+ status
1315
+ setupState
1316
+ isActive
1317
+ createdAt
1318
+ }
1319
+ }
1320
+ `;
1321
+ var UPDATE_DATASINK = `
1322
+ mutation UpdateDataSink($input: UpdateDataSinkInput!) {
1323
+ updateDataSink(input: $input) {
1324
+ id
1325
+ name
1326
+ key
1327
+ status
1328
+ isActive
1329
+ updatedAt
1330
+ }
1331
+ }
1332
+ `;
1333
+ var CREATE_PARSER = `
1334
+ mutation CreateParser($input: CreateParserInput!) {
1335
+ createParser(input: $input) {
1336
+ id
1337
+ name
1338
+ type
1339
+ status
1340
+ inputSinkKey
1341
+ outputKey
1342
+ version
1343
+ createdAt
1344
+ }
1345
+ }
1346
+ `;
1347
+ var UPDATE_PARSER = `
1348
+ mutation UpdateParser($id: ID!, $version: Int!, $input: UpdateParserInput!) {
1349
+ updateParser(id: $id, version: $version, input: $input) {
1350
+ id
1351
+ name
1352
+ type
1353
+ status
1354
+ version
1355
+ updatedAt
1356
+ }
1357
+ }
1358
+ `;
1359
+ var CREATE_PIPELINE = `
1360
+ mutation CreatePipeline($input: CreatePipelineInput!) {
1361
+ createPipeline(input: $input) {
1362
+ id
1363
+ name
1364
+ status
1365
+ version
1366
+ createdAt
1367
+ }
1368
+ }
1369
+ `;
1370
+ var UPDATE_PIPELINE = `
1371
+ mutation UpdatePipeline($id: ID!, $version: Int!, $input: UpdatePipelineInput!) {
1372
+ updatePipeline(id: $id, version: $version, input: $input) {
1373
+ id
1374
+ name
1375
+ status
1376
+ version
1377
+ updatedAt
1378
+ }
1379
+ }
1380
+ `;
1381
+ var GET_WIDGET_ACTIONS = `
1382
+ query GetWidgetActions($widgetId: ID!) {
1383
+ getWidgetActions(widgetId: $widgetId) {
1384
+ id
1385
+ widgetId
1386
+ type
1387
+ name
1388
+ label
1389
+ order
1390
+ disabled
1391
+ hidden
1392
+ createdAt
1393
+ }
1394
+ }
1395
+ `;
1396
+ var DELETE_WIDGET_DRILLDOWN = `
1397
+ mutation DeleteWidgetDrilldown($id: ID!) {
1398
+ deleteWidgetDrilldown(id: $id)
1399
+ }
1400
+ `;
1401
+ var GET_WIDGET_DRILLDOWN = `
1402
+ query GetWidgetDrilldown($id: ID!) {
1403
+ getWidget(id: $id) {
1404
+ id
1405
+ drilldown {
1406
+ id
1407
+ }
1408
+ }
1409
+ }
1410
+ `;
1411
+ var LIST_DASHBOARD_SHARES = `
1412
+ query ListDashboardShares(
1413
+ $dashboardId: ID!
1414
+ $first: Int
1415
+ $after: String
1416
+ $isActive: Boolean
1417
+ $permission: BigConsoleDashboardSharePermission
1418
+ $recipientType: BigConsoleDashboardShareRecipientType
1419
+ ) {
1420
+ listDashboardShares(
1421
+ dashboardId: $dashboardId
1422
+ first: $first
1423
+ after: $after
1424
+ isActive: $isActive
1425
+ permission: $permission
1426
+ recipientType: $recipientType
1427
+ ) {
1428
+ edges {
1429
+ node {
1430
+ id
1431
+ dashboardId
1432
+ permission
1433
+ isActive
1434
+ expiresAt
1435
+ createdAt
1436
+ }
1437
+ }
1438
+ pageInfo { hasNextPage endCursor }
1439
+ totalCount
1440
+ }
1441
+ }
1442
+ `;
1443
+ var LIST_TEMPLATES = `
1444
+ query ListTemplates(
1445
+ $first: Int
1446
+ $after: String
1447
+ $category: BigConsoleDashboardCategory
1448
+ $search: String
1449
+ $isFeatured: Boolean
1450
+ ) {
1451
+ listTemplates(
1452
+ first: $first
1453
+ after: $after
1454
+ category: $category
1455
+ search: $search
1456
+ isFeatured: $isFeatured
1457
+ ) {
1458
+ edges {
1459
+ node {
1460
+ id
1461
+ name
1462
+ description
1463
+ category
1464
+ isFeatured
1465
+ status
1466
+ createdAt
1467
+ }
1468
+ }
1469
+ pageInfo { hasNextPage endCursor }
1470
+ totalCount
1471
+ }
1472
+ }
1473
+ `;
1474
+ var GET_TEMPLATE = `
1475
+ query GetTemplate($id: ID!) {
1476
+ getTemplate(id: $id) {
1477
+ id
1478
+ name
1479
+ description
1480
+ category
1481
+ isFeatured
1482
+ status
1483
+ createdAt
1484
+ }
1485
+ }
1486
+ `;
1487
+ var GENERATE_DASHBOARD_FROM_PROMPT = `
1488
+ mutation GenerateDashboardFromPrompt($input: BigConsoleGenerateDashboardFromPromptInput!) {
1489
+ generateDashboardFromPrompt(input: $input) {
1490
+ id
1491
+ status
1492
+ prompt
1493
+ createdAt
1494
+ }
1495
+ }
1496
+ `;
1497
+ var LIST_PROMPT_GENERATIONS = `
1498
+ query ListPromptGenerations($first: Int, $after: String, $status: PromptGenerationStatus) {
1499
+ listPromptGenerations(first: $first, after: $after, status: $status) {
1500
+ edges {
1501
+ node {
1502
+ id
1503
+ status
1504
+ prompt
1505
+ createdAt
1506
+ }
1507
+ }
1508
+ pageInfo { hasNextPage endCursor }
1509
+ totalCount
1510
+ }
1511
+ }
1512
+ `;
1513
+ var GET_PROMPT_GENERATION = `
1514
+ query GetPromptGeneration($id: ID!) {
1515
+ getPromptGeneration(id: $id) {
1516
+ id
1517
+ status
1518
+ prompt
1519
+ createdAt
1520
+ }
1521
+ }
1522
+ `;
1523
+ var CANCEL_PROMPT_GENERATION = `
1524
+ mutation CancelPromptGeneration($generationId: ID!) {
1525
+ cancelPromptGeneration(generationId: $generationId) {
1526
+ id
1527
+ status
1528
+ }
1529
+ }
1530
+ `;
1531
+ var LIST_WORKFLOWS = `
1532
+ query ListWorkflows($filter: FluidGridsWorkflowFilterInput) {
1533
+ workflows(filter: $filter) {
1534
+ id
1535
+ name
1536
+ key
1537
+ description
1538
+ status
1539
+ executionCount
1540
+ linkedDataSinkCount
1541
+ lastExecutedAt
1542
+ createdAt
1543
+ updatedAt
1544
+ }
1545
+ }
1546
+ `;
1547
+ var GET_WORKFLOW = `
1548
+ query GetWorkflow($id: ID!) {
1549
+ workflow(id: $id) {
1550
+ id
1551
+ name
1552
+ key
1553
+ description
1554
+ status
1555
+ executionCount
1556
+ linkedDataSinkCount
1557
+ externalWorkflowId
1558
+ lastExecutionId
1559
+ lastExecutedAt
1560
+ webhookUrl
1561
+ createdAt
1562
+ updatedAt
1563
+ }
1564
+ }
1565
+ `;
1566
+
1567
+ // src/commands/base.ts
1568
+ import { readFileSync as readFileSync3 } from "fs";
1569
+ function loadJsonInput(file, inline) {
1570
+ if (!file && !inline)
1571
+ return;
1572
+ if (file && inline) {
1573
+ printError("Provide --file OR --json, not both");
1574
+ process.exit(1);
1575
+ }
1576
+ try {
1577
+ const raw = file ? readFileSync3(file, "utf8") : inline;
1578
+ return JSON.parse(raw);
1579
+ } catch (err) {
1580
+ const msg = err instanceof Error ? err.message : String(err);
1581
+ printError(`Failed to parse JSON input: ${msg}`);
1582
+ process.exit(1);
1583
+ }
1584
+ }
1585
+ function loadTextInput(file, inline) {
1586
+ if (!file && !inline)
1587
+ return;
1588
+ if (file && inline) {
1589
+ printError("Provide --file OR inline value, not both");
1590
+ process.exit(1);
1591
+ }
1592
+ try {
1593
+ return file ? readFileSync3(file, "utf8") : inline;
1594
+ } catch (err) {
1595
+ const msg = err instanceof Error ? err.message : String(err);
1596
+ printError(`Failed to read file: ${msg}`);
1597
+ process.exit(1);
1598
+ }
1599
+ }
1600
+ function requireAuth() {
1601
+ const auth = readAuth();
1602
+ if (!auth?.accessToken) {
1603
+ printError("Authentication required. Run: bigconsole auth login");
1604
+ process.exit(1);
1605
+ }
1606
+ }
1607
+ async function execWorkspace(document, variables) {
1608
+ requireAuth();
1609
+ return workspaceRequest(document, variables);
1610
+ }
1611
+ function handleCommandError(error) {
1612
+ printError(toSafeErrorMessage(error));
1613
+ process.exit(1);
1614
+ }
1615
+ function inheritGlobalFormat(actionCommand) {
1616
+ const declaresFormat = actionCommand.options.some((o) => o.attributeName() === "format");
1617
+ if (!declaresFormat)
1618
+ return;
1619
+ const current = actionCommand.getOptionValue("format");
1620
+ if (current !== undefined)
1621
+ return;
1622
+ const { format } = actionCommand.optsWithGlobals();
1623
+ if (format !== undefined)
1624
+ actionCommand.setOptionValue("format", format);
1625
+ }
1626
+ function formatAndOutput(options, data, tableConfig) {
1627
+ const format = getFormat(options);
1628
+ outputData(format, data, tableConfig);
1629
+ }
1630
+
1631
+ // src/commands/auth.ts
1632
+ function registerAuthCommands(program) {
1633
+ const auth = program.command("auth").description("Authentication commands");
1634
+ auth.command("login").description("Authenticate with BigConsole").option("--client-credentials", "Authenticate as an app with client ID and secret").option("--workspace-id <id>", "Workspace ID (for app auth)").action(async (options) => {
1635
+ try {
1636
+ if (options.clientCredentials) {
1637
+ await loginWithClientCredentials(options.workspaceId);
1638
+ } else {
1639
+ await loginWithDeviceCode();
1640
+ }
1641
+ } catch (error) {
1642
+ handleCommandError(error);
1643
+ }
1644
+ });
1645
+ auth.command("logout").description("Revoke tokens and clear stored auth").action(async () => {
1646
+ try {
1647
+ const authData = readAuth();
1648
+ if (authData?.accessToken) {
1649
+ try {
1650
+ await globalRequest(SIGN_OUT);
1651
+ } catch {
1652
+ printWarning("Server-side logout failed, clearing local tokens");
1653
+ }
1654
+ }
1655
+ clearAuth();
1656
+ resetClients();
1657
+ printSuccess("Logged out successfully");
1658
+ } catch (error) {
1659
+ handleCommandError(error);
1660
+ }
1661
+ });
1662
+ auth.command("status").description("Show current auth state").option("-f, --format <format>", "Output format (json, table)").action((options) => {
1663
+ const authData = readAuth();
1664
+ const config = readConfig();
1665
+ const authenticated = Boolean(authData?.accessToken);
1666
+ const expired = authData?.accessToken ? isTokenExpired(authData) : false;
1667
+ const expiresAt = authData?.expiresAt ? formatDate(new Date(authData.expiresAt).toISOString()) : "-";
1668
+ const method = authData?.authMethod === "client_credentials" ? "Client Credentials" : "OAuth2 Device Code";
1669
+ const wsTokenExpires = authData?.workspaceTokenExpiresAt ? formatDate(new Date(authData.workspaceTokenExpiresAt).toISOString()) : "-";
1670
+ formatAndOutput(options, {
1671
+ authenticated,
1672
+ method,
1673
+ expired,
1674
+ expiresAt,
1675
+ workspaceId: authData?.workspaceId ?? "-",
1676
+ organizationId: authData?.organizationId ?? "-",
1677
+ workspaceTokenExpiresAt: wsTokenExpires,
1678
+ workspaceEndpoint: config.workspaceEndpoint,
1679
+ globalEndpoint: config.globalEndpoint
1680
+ }, {
1681
+ headers: ["Key", "Value"],
1682
+ rows: [
1683
+ ["Authenticated", String(authenticated)],
1684
+ ["Method", method],
1685
+ ["Expired", String(expired)],
1686
+ ["Expires At", expiresAt],
1687
+ ["Workspace ID", authData?.workspaceId ?? "-"],
1688
+ ["Organization ID", authData?.organizationId ?? "-"],
1689
+ ["Workspace Token Expires", wsTokenExpires],
1690
+ ["Workspace Endpoint", config.workspaceEndpoint],
1691
+ ["Global Endpoint", config.globalEndpoint]
1692
+ ]
1693
+ });
1694
+ });
1695
+ auth.command("token").description("Print access token to stdout").option("--workspace", "Print workspace token instead").action((options) => {
1696
+ const authData = readAuth();
1697
+ if (options.workspace) {
1698
+ if (!authData?.workspaceToken) {
1699
+ printError("No workspace token. Log in and select a workspace: bigconsole auth login");
1700
+ process.exit(1);
1701
+ }
1702
+ process.stdout.write(authData.workspaceToken);
1703
+ return;
1704
+ }
1705
+ if (!authData?.accessToken) {
1706
+ printError("Not authenticated. Run: bigconsole auth login");
1707
+ process.exit(1);
1708
+ }
1709
+ process.stdout.write(authData.accessToken);
1710
+ });
1711
+ auth.command("refresh").description("Force token refresh").action(async () => {
1712
+ try {
1713
+ const authData = readAuth();
1714
+ if (!authData?.refreshToken && authData?.authMethod !== "client_credentials") {
1715
+ printError("No refresh token available. Run: bigconsole auth login");
1716
+ process.exit(1);
1717
+ }
1718
+ if (authData?.authMethod === "client_credentials" && authData.clientId && authData.clientSecret) {
1719
+ const config = readConfig();
1720
+ const result = await clientCredentialsFlow(config.globalEndpoint, authData.clientId, authData.clientSecret);
1721
+ writeAuth({
1722
+ ...authData,
1723
+ accessToken: result.accessToken,
1724
+ expiresAt: Date.now() + result.expiresIn * 1000
1725
+ });
1726
+ resetClients();
1727
+ printSuccess("App token refreshed successfully");
1728
+ if (authData.workspaceId && authData.organizationId) {
1729
+ await refreshWorkspaceTokenForAuth();
1730
+ }
1731
+ return;
1732
+ }
1733
+ const data = await globalRequest(REFRESH_TOKEN, {
1734
+ token: authData.refreshToken
1735
+ });
1736
+ const r = data.refreshToken;
1737
+ writeAuth({
1738
+ ...authData,
1739
+ accessToken: r.accessToken,
1740
+ refreshToken: r.refreshToken,
1741
+ expiresAt: Date.now() + r.expiresIn * 1000
1742
+ });
1743
+ resetClients();
1744
+ printSuccess("Token refreshed successfully");
1745
+ if (authData.workspaceId && authData.organizationId) {
1746
+ await refreshWorkspaceTokenForAuth();
1747
+ }
1748
+ } catch (error) {
1749
+ handleCommandError(error);
1750
+ }
1751
+ });
1752
+ }
1753
+ async function refreshWorkspaceTokenForAuth() {
1754
+ const authData = readAuth();
1755
+ if (!authData?.accessToken || !authData.workspaceId || !authData.organizationId)
1756
+ return;
1757
+ const config = readConfig();
1758
+ try {
1759
+ const wsResult = await issueWorkspaceToken(config.workspaceEndpoint, authData.accessToken, authData.workspaceId, authData.organizationId);
1760
+ writeAuth({
1761
+ ...authData,
1762
+ workspaceToken: wsResult.token,
1763
+ workspaceTokenExpiresAt: new Date(wsResult.expiresAt).getTime()
1764
+ });
1765
+ resetClients();
1766
+ printSuccess("Workspace token refreshed");
1767
+ } catch {
1768
+ printWarning("Workspace token refresh failed — workspace operations may require re-login");
1769
+ }
1770
+ }
1771
+ async function loginWithDeviceCode() {
1772
+ const config = readConfig();
1773
+ const { oidcIssuer, clientId } = resolveOIDCConfig();
1774
+ const oidcConfig = { ...config, oidcIssuer, clientId };
1775
+ printInfo("Starting device code authentication...");
1776
+ const dc = await startDeviceCodeFlow(oidcConfig);
1777
+ console.log("");
1778
+ console.log(` Open: ${sanitizeForTerminal(dc.verificationUri)}`);
1779
+ console.log(` Code: ${sanitizeForTerminal(dc.userCode)}`);
1780
+ console.log("");
1781
+ const opened = await openBrowser(dc.verificationUriComplete ?? dc.verificationUri);
1782
+ if (opened)
1783
+ printInfo("Browser opened");
1784
+ else
1785
+ printWarning("Could not open browser — open the URL manually");
1786
+ printInfo("Waiting for authorization...");
1787
+ const result = await pollForToken(dc.deviceCode, dc.interval, oidcConfig);
1788
+ const accessToken = result.accessToken;
1789
+ const expiresAt = Date.now() + result.expiresIn * 1000;
1790
+ printInfo("Fetching workspaces...");
1791
+ try {
1792
+ const workspaces = await fetchWorkspaces(config.globalEndpoint, accessToken);
1793
+ if (workspaces.length === 0) {
1794
+ writeAuth({
1795
+ accessToken,
1796
+ refreshToken: result.refreshToken,
1797
+ expiresAt,
1798
+ authMethod: "device_code"
1799
+ });
1800
+ resetClients();
1801
+ printSuccess("Authentication successful (no workspaces found)");
1802
+ return;
1803
+ }
1804
+ let selected = workspaces[0];
1805
+ if (workspaces.length > 1) {
1806
+ console.log(`
1807
+ Available workspaces:`);
1808
+ workspaces.forEach((ws, i) => console.log(` ${i + 1}. ${sanitizeForTerminal(ws.name)} (${sanitizeForTerminal(ws.slug)})`));
1809
+ const readline = await import("readline");
1810
+ for (let attempt = 0;attempt < 3; attempt++) {
1811
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1812
+ const answer = await new Promise((resolve) => {
1813
+ rl.question(`
1814
+ Select workspace [1-${workspaces.length}]: `, (a) => {
1815
+ rl.close();
1816
+ resolve(a.trim());
1817
+ });
1818
+ });
1819
+ const idx = parseInt(answer, 10) - 1;
1820
+ if (!Number.isNaN(idx) && idx >= 0 && idx < workspaces.length) {
1821
+ selected = workspaces[idx];
1822
+ break;
1823
+ }
1824
+ if (attempt < 2) {
1825
+ printError(`Invalid selection. Enter a number between 1 and ${workspaces.length}.`);
1826
+ } else {
1827
+ printError("Too many invalid attempts — aborting login.");
1828
+ process.exit(1);
1829
+ }
1830
+ }
1831
+ }
1832
+ printInfo(`Using workspace: ${selected.name} (${selected.id})`);
1833
+ const wsResult = await issueWorkspaceToken(config.workspaceEndpoint, accessToken, selected.id, selected.organizationId);
1834
+ writeAuth({
1835
+ accessToken,
1836
+ refreshToken: result.refreshToken,
1837
+ expiresAt,
1838
+ workspaceToken: wsResult.token,
1839
+ workspaceTokenExpiresAt: new Date(wsResult.expiresAt).getTime(),
1840
+ workspaceId: selected.id,
1841
+ organizationId: selected.organizationId,
1842
+ authMethod: "device_code"
1843
+ });
1844
+ resetClients();
1845
+ printSuccess("Authentication successful");
1846
+ } catch (wsError) {
1847
+ writeAuth({
1848
+ accessToken,
1849
+ refreshToken: result.refreshToken,
1850
+ expiresAt,
1851
+ authMethod: "device_code"
1852
+ });
1853
+ resetClients();
1854
+ printSuccess("Authentication successful");
1855
+ printWarning(`Workspace token not obtained: ${toSafeErrorMessage(wsError)}`);
1856
+ printInfo("Run `bigconsole auth login` again once a workspace is available.");
1857
+ }
1858
+ }
1859
+ async function loginWithClientCredentials(workspaceId) {
1860
+ const config = readConfig();
1861
+ const readline = await import("readline");
1862
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1863
+ const clientId = await new Promise((resolve) => {
1864
+ rl.question("Client ID: ", (a) => resolve(a.trim()));
1865
+ });
1866
+ const out = rl;
1867
+ out._writeToOutput = function maskedWrite(stringToWrite) {
1868
+ if (stringToWrite === `\r
1869
+ ` || stringToWrite === `
1870
+ `) {
1871
+ out.output.write(stringToWrite);
1872
+ return;
1873
+ }
1874
+ out.output.write("*");
1875
+ };
1876
+ const clientSecret = await new Promise((resolve) => {
1877
+ rl.question("Client Secret: ", (a) => {
1878
+ rl.close();
1879
+ resolve(a.trim());
1880
+ });
1881
+ });
1882
+ process.stdout.write(`
1883
+ `);
1884
+ if (!clientId || !clientSecret) {
1885
+ printError("Client ID and secret cannot be empty");
1886
+ process.exit(1);
1887
+ }
1888
+ printInfo("Authenticating app...");
1889
+ const result = await clientCredentialsFlow(config.globalEndpoint, clientId, clientSecret);
1890
+ const accessToken = result.accessToken;
1891
+ const expiresAt = Date.now() + result.expiresIn * 1000;
1892
+ let wsToken;
1893
+ let wsTokenExpiresAt;
1894
+ const wsId = workspaceId;
1895
+ let orgId;
1896
+ if (wsId) {
1897
+ try {
1898
+ const workspaces = await fetchWorkspaces(config.globalEndpoint, accessToken);
1899
+ const ws = workspaces.find((w) => w.id === wsId);
1900
+ if (!ws) {
1901
+ printError(`Workspace ${wsId} not found or not accessible to this app.`);
1902
+ process.exit(1);
1903
+ }
1904
+ orgId = ws.organizationId;
1905
+ const wsResult = await issueWorkspaceToken(config.workspaceEndpoint, accessToken, wsId, orgId);
1906
+ wsToken = wsResult.token;
1907
+ wsTokenExpiresAt = new Date(wsResult.expiresAt).getTime();
1908
+ printInfo(`Workspace token issued for: ${ws.name}`);
1909
+ } catch (err) {
1910
+ printError(`Workspace token issuance failed: ${toSafeErrorMessage(err)}`);
1911
+ process.exit(1);
1912
+ }
1913
+ }
1914
+ writeAuth({
1915
+ accessToken,
1916
+ refreshToken: "",
1917
+ expiresAt,
1918
+ workspaceToken: wsToken,
1919
+ workspaceTokenExpiresAt: wsTokenExpiresAt,
1920
+ workspaceId: wsId,
1921
+ organizationId: orgId,
1922
+ authMethod: "client_credentials",
1923
+ clientId,
1924
+ clientSecret
1925
+ });
1926
+ resetClients();
1927
+ printSuccess("App authentication successful");
1928
+ }
1929
+ // src/commands/cli-config.ts
1930
+ function registerCLIConfigCommands(program) {
1931
+ const cli = program.command("cli-config").description("CLI configuration (endpoints, client ID)");
1932
+ cli.command("show").description("Show current CLI configuration").option("-f, --format <format>", "Output format (json, table, yaml)").action((options) => {
1933
+ const config = readConfig();
1934
+ formatAndOutput(options, config, {
1935
+ headers: ["Key", "Value"],
1936
+ rows: Object.entries(config).map(([k, v]) => [k, v == null ? "-" : String(v)])
1937
+ });
1938
+ });
1939
+ cli.command("path").description("Show CLI config file path").action(() => {
1940
+ console.log(getConfigPath());
1941
+ });
1942
+ cli.command("get <key>").description("Get a single config value").action((key) => {
1943
+ try {
1944
+ if (!KNOWN_CONFIG_KEYS.includes(key)) {
1945
+ printError(`Unknown config key: ${key}. Known keys: ${KNOWN_CONFIG_KEYS.join(", ")}`);
1946
+ process.exit(1);
1947
+ }
1948
+ const config = readConfig();
1949
+ const value = config[key];
1950
+ if (value === undefined || value === null) {
1951
+ console.log("-");
1952
+ } else {
1953
+ console.log(String(value));
1954
+ }
1955
+ } catch (error) {
1956
+ handleCommandError(error);
1957
+ }
1958
+ });
1959
+ cli.command("set <key> <value>").description("Set a config value").action((key, value) => {
1960
+ try {
1961
+ if (!KNOWN_CONFIG_KEYS.includes(key)) {
1962
+ printError(`Unknown config key: ${key}. Known keys: ${KNOWN_CONFIG_KEYS.join(", ")}`);
1963
+ process.exit(1);
1964
+ }
1965
+ const config = readConfig();
1966
+ config[key] = value;
1967
+ writeConfig(config);
1968
+ printSuccess(`Set ${key} = ${value} (last updated ${formatDate(new Date().toISOString())})`);
1969
+ } catch (error) {
1970
+ handleCommandError(error);
1971
+ }
1972
+ });
1973
+ cli.command("reset").description("Reset to default CLI configuration").action(() => {
1974
+ try {
1975
+ writeConfig({ ...DEFAULT_CONFIG });
1976
+ printSuccess("CLI configuration reset to defaults");
1977
+ } catch (error) {
1978
+ handleCommandError(error);
1979
+ }
1980
+ });
1981
+ }
1982
+ // src/commands/workspace.ts
1983
+ function registerWorkspaceCommands(program) {
1984
+ const ws = program.command("workspace").description("Workspace selection and info");
1985
+ ws.command("list").description("List workspaces the current user can access").option("-f, --format <format>", "Output format (json, table, yaml)").action(async (o) => {
1986
+ try {
1987
+ requireAuth();
1988
+ await ensureFreshTokens();
1989
+ const auth = readAuth();
1990
+ const config = readConfig();
1991
+ const workspaces = await fetchWorkspaces(config.globalEndpoint, auth.accessToken);
1992
+ formatAndOutput(o, workspaces, {
1993
+ headers: ["ID", "Name", "Slug", "Org ID", "Tenant ID", "Current"],
1994
+ rows: workspaces.map((w) => [
1995
+ truncate(w.id, 14),
1996
+ w.name,
1997
+ w.slug,
1998
+ truncate(w.organizationId, 14),
1999
+ truncate(w.tenantId, 14),
2000
+ auth.workspaceId === w.id ? "*" : ""
2001
+ ])
2002
+ });
2003
+ } catch (e) {
2004
+ handleCommandError(e);
2005
+ }
2006
+ });
2007
+ ws.command("current").description("Show the currently-selected workspace").option("-f, --format <format>", "Output format").action((o) => {
2008
+ const auth = readAuth();
2009
+ if (!auth?.workspaceId) {
2010
+ printError("No workspace selected. Run `bigconsole workspace set <id>` or re-login.");
2011
+ process.exit(1);
2012
+ }
2013
+ formatAndOutput(o, {
2014
+ workspaceId: auth.workspaceId,
2015
+ organizationId: auth.organizationId ?? "-",
2016
+ workspaceTokenExpiresAt: auth.workspaceTokenExpiresAt ? new Date(auth.workspaceTokenExpiresAt).toISOString() : "-"
2017
+ });
2018
+ });
2019
+ ws.command("set").description("Select a workspace and issue a workspace token").argument("<id>", "Workspace ID").action(async (id) => {
2020
+ try {
2021
+ requireAuth();
2022
+ await ensureFreshTokens();
2023
+ const auth = readAuth();
2024
+ const config = readConfig();
2025
+ const workspaces = await fetchWorkspaces(config.globalEndpoint, auth.accessToken);
2026
+ const ws = workspaces.find((w) => w.id === id);
2027
+ if (!ws) {
2028
+ printError(`Workspace ${id} not found or not accessible. Run \`bigconsole workspace list\`.`);
2029
+ process.exit(1);
2030
+ }
2031
+ const result = await issueWorkspaceToken(config.workspaceEndpoint, auth.accessToken, ws.id, ws.organizationId);
2032
+ writeAuth({
2033
+ ...auth,
2034
+ workspaceId: ws.id,
2035
+ organizationId: ws.organizationId,
2036
+ workspaceToken: result.token,
2037
+ workspaceTokenExpiresAt: new Date(result.expiresAt).getTime()
2038
+ });
2039
+ resetClients();
2040
+ printSuccess(`Switched to workspace: ${ws.name} (${ws.id})`);
2041
+ } catch (e) {
2042
+ handleCommandError(e);
2043
+ }
2044
+ });
2045
+ }
2046
+ // src/commands/dashboard.ts
2047
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
2048
+ function registerDashboardCommands(program) {
2049
+ const dash = program.command("dashboard").description("Dashboard operations");
2050
+ dash.command("list").description("List dashboards in the current workspace").option("--search <term>", "Search by name").option("--category <c>", "Filter by category").option("--visibility <v>", "PRIVATE | WORKSPACE | PUBLIC").option("--include-archived", "Include archived dashboards").option("--first <n>", "Page size", (v) => parseInt(v, 10), 25).option("--after <cursor>", "Cursor for next page").option("-f, --format <format>", "Output format").action(async (o) => {
2051
+ try {
2052
+ const d = await execWorkspace(LIST_DASHBOARDS, {
2053
+ first: o.first ?? 25,
2054
+ after: o.after,
2055
+ search: o.search,
2056
+ category: o.category ? o.category.toUpperCase() : undefined,
2057
+ visibility: o.visibility ? o.visibility.toUpperCase() : undefined,
2058
+ includeArchived: o.includeArchived ?? false
2059
+ });
2060
+ const items = d.listDashboards.edges.map((e) => e.node);
2061
+ formatAndOutput(o, items, {
2062
+ headers: ["ID", "Name", "Status", "Visibility", "Category", "Updated"],
2063
+ rows: items.map((x) => [
2064
+ truncate(x.id, 14),
2065
+ truncate(x.name, 30),
2066
+ formatStatus(x.status),
2067
+ x.visibility,
2068
+ x.category,
2069
+ formatDate(x.updatedAt)
2070
+ ])
2071
+ });
2072
+ } catch (e) {
2073
+ handleCommandError(e);
2074
+ }
2075
+ });
2076
+ dash.command("get").description("Get a dashboard by ID").argument("<id>").option("-f, --format <format>", "Output format").action(async (id, o) => {
2077
+ try {
2078
+ const d = await execWorkspace(GET_DASHBOARD, {
2079
+ id
2080
+ });
2081
+ if (!d.getDashboard) {
2082
+ printError("Dashboard not found");
2083
+ process.exit(1);
2084
+ }
2085
+ formatAndOutput(o, d.getDashboard);
2086
+ } catch (e) {
2087
+ handleCommandError(e);
2088
+ }
2089
+ });
2090
+ dash.command("archive").description("Archive a dashboard (soft-delete, can be restored)").argument("<id>").action(async (id) => {
2091
+ try {
2092
+ const d = await execWorkspace(ARCHIVE_DASHBOARD, {
2093
+ id
2094
+ });
2095
+ printSuccess(`Archived: ${d.archiveDashboard.name} (${d.archiveDashboard.id})`);
2096
+ } catch (e) {
2097
+ handleCommandError(e);
2098
+ }
2099
+ });
2100
+ dash.command("delete").description("Permanently delete a dashboard").argument("<id>").action(async (id) => {
2101
+ try {
2102
+ await execWorkspace(DELETE_DASHBOARD, { id });
2103
+ printSuccess(`Deleted dashboard ${id}`);
2104
+ } catch (e) {
2105
+ handleCommandError(e);
2106
+ }
2107
+ });
2108
+ dash.command("create").description("Create a dashboard").requiredOption("--name <name>", "Dashboard name").requiredOption("--category <cat>", "Category (CUSTOM | EXECUTIVE | FINANCIAL | HR | MARKETING | OPERATIONS | SALES)").option("--description <d>", "Description").option("--visibility <v>", "PRIVATE | WORKSPACE | PUBLIC", "PRIVATE").option("--layout <l>", "GRID | FLEX | TABS", "GRID").option("--no-default-page", "Skip creating the default page").option("--metadata-file <path>", "JSON file with custom metadata").option("--metadata-json <json>", "Inline JSON for custom metadata").option("-f, --format <format>", "Output format").action(async (o) => {
2109
+ try {
2110
+ const metadata = loadJsonInput(o.metadataFile, o.metadataJson);
2111
+ const input = {
2112
+ name: o.name,
2113
+ category: o.category.toUpperCase(),
2114
+ description: o.description,
2115
+ visibility: (o.visibility ?? "PRIVATE").toUpperCase(),
2116
+ layout: (o.layout ?? "GRID").toUpperCase(),
2117
+ createDefaultPage: o.defaultPage !== false,
2118
+ metadata
2119
+ };
2120
+ const d = await execWorkspace(CREATE_DASHBOARD, { input });
2121
+ printSuccess(`Created dashboard: ${d.createBigConsoleDashboard.name} (${d.createBigConsoleDashboard.id})`);
2122
+ formatAndOutput(o, d.createBigConsoleDashboard);
2123
+ } catch (e) {
2124
+ handleCommandError(e);
2125
+ }
2126
+ });
2127
+ dash.command("update").description("Update a dashboard").argument("<id>").option("--name <name>").option("--description <d>").option("--category <cat>").option("--visibility <v>", "PRIVATE | WORKSPACE | PUBLIC").option("--layout <l>", "GRID | FLEX | TABS").option("-f, --format <format>", "Output format").action(async (id, o) => {
2128
+ try {
2129
+ const input = { id };
2130
+ if (o.name)
2131
+ input.name = o.name;
2132
+ if (o.description !== undefined)
2133
+ input.description = o.description;
2134
+ if (o.category)
2135
+ input.category = o.category.toUpperCase();
2136
+ if (o.visibility)
2137
+ input.visibility = o.visibility.toUpperCase();
2138
+ if (o.layout)
2139
+ input.layout = o.layout.toUpperCase();
2140
+ const d = await execWorkspace(UPDATE_DASHBOARD, { input });
2141
+ printSuccess(`Updated dashboard ${id}`);
2142
+ formatAndOutput(o, d.updateBigConsoleDashboard);
2143
+ } catch (e) {
2144
+ handleCommandError(e);
2145
+ }
2146
+ });
2147
+ dash.command("import").description("Import a dashboard from a JSON file (dashboard definition)").requiredOption("--file <path>", "Dashboard JSON file produced by `dashboard export`").option("--name <name>", "Override dashboard name").option("--visibility <v>", "Override visibility: PRIVATE | WORKSPACE | PUBLIC").option("--conflict <strategy>", "Conflict resolution: RENAME | REPLACE | FAIL", "RENAME").action(async (o) => {
2148
+ try {
2149
+ const json = readFileSync4(o.file, "utf8");
2150
+ const input = {
2151
+ json,
2152
+ conflictResolution: (o.conflict ?? "RENAME").toUpperCase()
2153
+ };
2154
+ if (o.name)
2155
+ input.name = o.name;
2156
+ if (o.visibility)
2157
+ input.visibility = o.visibility.toUpperCase();
2158
+ const d = await execWorkspace(IMPORT_DASHBOARD, {
2159
+ input
2160
+ });
2161
+ printSuccess(`Imported dashboard: ${d.importDashboard.name} (${d.importDashboard.id})`);
2162
+ } catch (e) {
2163
+ handleCommandError(e);
2164
+ }
2165
+ });
2166
+ dash.command("export").description("Export a dashboard definition as JSON (stdout or --out file)").argument("<id>").option("--out <path>", "Write JSON to file instead of stdout").option("--include-data-refs", "Include data source references", true).option("--include-webhook-mappings", "Include webhook mappings").option("--pretty", "Pretty-print JSON", true).action(async (id, o) => {
2167
+ try {
2168
+ const options = {
2169
+ includeDataSourceRefs: o.includeDataRefs !== false,
2170
+ includeWebhookMappings: o.includeWebhookMappings ?? false,
2171
+ prettyPrint: o.pretty !== false
2172
+ };
2173
+ const d = await execWorkspace(EXPORT_DASHBOARD, {
2174
+ id,
2175
+ options
2176
+ });
2177
+ if (o.out) {
2178
+ writeFileSync3(o.out, d.exportDashboard, "utf8");
2179
+ printSuccess(`Exported to ${o.out}`);
2180
+ } else {
2181
+ const body = process.stdout.isTTY ? sanitizeForTerminal(d.exportDashboard, { preserveNewlines: true }) : d.exportDashboard;
2182
+ process.stdout.write(body);
2183
+ if (!body.endsWith(`
2184
+ `))
2185
+ process.stdout.write(`
2186
+ `);
2187
+ }
2188
+ } catch (e) {
2189
+ handleCommandError(e);
2190
+ }
2191
+ });
2192
+ }
2193
+ // src/commands/widget.ts
2194
+ function registerWidgetCommands(program) {
2195
+ const w = program.command("widget").description("Widget operations");
2196
+ w.command("list").description("List widgets on a dashboard page").requiredOption("--page <pageId>", "Dashboard page ID").option("--type <t>", "Filter by widget type (METRIC_CARD, CHART, TABLE, ...)").option("--first <n>", "Page size", (v) => parseInt(v, 10), 50).option("--after <cursor>", "Cursor for next page").option("-f, --format <format>", "Output format").action(async (o) => {
2197
+ try {
2198
+ const d = await execWorkspace(LIST_WIDGETS, {
2199
+ pageId: o.page,
2200
+ first: o.first ?? 50,
2201
+ after: o.after,
2202
+ type: o.type ? o.type.toUpperCase() : undefined
2203
+ });
2204
+ const items = d.listWidgets.edges.map((e) => e.node);
2205
+ formatAndOutput(o, items, {
2206
+ headers: ["ID", "Title", "Type", "DataSink", "Last Refreshed", "Updated"],
2207
+ rows: items.map((x) => [
2208
+ truncate(x.id, 14),
2209
+ truncate(x.title ?? "-", 24),
2210
+ x.type,
2211
+ x.dataSinkId ? truncate(x.dataSinkId, 14) : "-",
2212
+ formatDate(x.lastRefreshedAt),
2213
+ formatDate(x.updatedAt)
2214
+ ])
2215
+ });
2216
+ } catch (e) {
2217
+ handleCommandError(e);
2218
+ }
2219
+ });
2220
+ w.command("get").description("Get a widget by ID (includes full config JSON)").argument("<id>").option("-f, --format <format>", "Output format").action(async (id, o) => {
2221
+ try {
2222
+ const d = await execWorkspace(GET_WIDGET, { id });
2223
+ if (!d.getWidget) {
2224
+ printError("Widget not found");
2225
+ process.exit(1);
2226
+ }
2227
+ formatAndOutput(o, d.getWidget);
2228
+ } catch (e) {
2229
+ handleCommandError(e);
2230
+ }
2231
+ });
2232
+ w.command("delete").description("Delete a widget").argument("<id>").action(async (id) => {
2233
+ try {
2234
+ await execWorkspace(DELETE_WIDGET, { id });
2235
+ printSuccess(`Deleted widget ${id}`);
2236
+ } catch (e) {
2237
+ handleCommandError(e);
2238
+ }
2239
+ });
2240
+ w.command("create").description("Create a widget (config is JSON — use --config-file for complex layouts)").requiredOption("--page <id>", "Parent page ID").requiredOption("--type <t>", "Widget type (METRIC_CARD, CHART, TABLE, ...)").option("--title <t>", "Widget title").option("--config-file <path>", "JSON file with widget config").option("--config-json <json>", "Inline JSON config").option("--position-file <path>", "JSON file with position {x,y,width,height}").option("--position-json <json>", "Inline JSON position").option("--datasink <id>", "Bound DataSink ID").option("--parser <id>", "Bound Parser ID").option("--dataset <id>", "Dataset ID within DataSink").option("--cache-ttl <seconds>", "Cache TTL override", (v) => parseInt(v, 10)).option("-f, --format <format>", "Output format").action(async (o) => {
2241
+ try {
2242
+ const config = loadJsonInput(o.configFile, o.configJson);
2243
+ const position = loadJsonInput(o.positionFile, o.positionJson);
2244
+ if (!config) {
2245
+ printError("Widget config is required — provide --config-file or --config-json");
2246
+ process.exit(1);
2247
+ }
2248
+ if (!position) {
2249
+ printError("Position is required — provide --position-file or --position-json");
2250
+ process.exit(1);
2251
+ }
2252
+ const input = {
2253
+ pageId: o.page,
2254
+ type: o.type.toUpperCase(),
2255
+ title: o.title ?? "",
2256
+ config,
2257
+ position
2258
+ };
2259
+ if (o.datasink)
2260
+ input.dataSinkId = o.datasink;
2261
+ if (o.parser)
2262
+ input.parserId = o.parser;
2263
+ if (o.dataset)
2264
+ input.datasetId = o.dataset;
2265
+ if (o.cacheTtl !== undefined)
2266
+ input.cacheTTL = o.cacheTtl;
2267
+ const d = await execWorkspace(CREATE_WIDGET, {
2268
+ input
2269
+ });
2270
+ printSuccess(`Created widget ${d.createBigConsoleWidget.id}`);
2271
+ formatAndOutput(o, d.createBigConsoleWidget);
2272
+ } catch (e) {
2273
+ handleCommandError(e);
2274
+ }
2275
+ });
2276
+ w.command("update").description("Update a widget").argument("<id>").option("--config-file <path>", "Replace widget config from JSON file").option("--config-json <json>", "Replace widget config inline").option("--position-file <path>").option("--position-json <json>").option("--datasink <id>").option("--parser <id>").option("--dataset <id>").option("--cache-ttl <seconds>", "", (v) => parseInt(v, 10)).option("-f, --format <format>", "Output format").action(async (id, o) => {
2277
+ try {
2278
+ const config = loadJsonInput(o.configFile, o.configJson);
2279
+ const position = loadJsonInput(o.positionFile, o.positionJson);
2280
+ const input = { id };
2281
+ if (config !== undefined)
2282
+ input.config = config;
2283
+ if (position !== undefined)
2284
+ input.position = position;
2285
+ if (o.datasink)
2286
+ input.dataSinkId = o.datasink;
2287
+ if (o.parser)
2288
+ input.parserId = o.parser;
2289
+ if (o.dataset)
2290
+ input.datasetId = o.dataset;
2291
+ if (o.cacheTtl !== undefined)
2292
+ input.cacheTTL = o.cacheTtl;
2293
+ const d = await execWorkspace(UPDATE_WIDGET, {
2294
+ input
2295
+ });
2296
+ printSuccess(`Updated widget ${id}`);
2297
+ formatAndOutput(o, d.updateBigConsoleWidget);
2298
+ } catch (e) {
2299
+ handleCommandError(e);
2300
+ }
2301
+ });
2302
+ }
2303
+ // src/utils/parse.ts
2304
+ import { InvalidArgumentError } from "commander";
2305
+ function parseVersion(raw) {
2306
+ if (!/^\d+$/.test(raw)) {
2307
+ throw new InvalidArgumentError("must be a whole number, for example 3");
2308
+ }
2309
+ return Number(raw);
2310
+ }
2311
+
2312
+ // src/commands/datasink.ts
2313
+ function registerDataSinkCommands(program) {
2314
+ const ds = program.command("datasink").description("DataSink operations");
2315
+ ds.command("list").description("List DataSinks in the current workspace").option("--search <term>", "Search by name").option("--status <s>", "Filter by status").option("--setup-state <s>", "Filter by setup state").option("--active <bool>", "Filter by isActive (true/false)").option("--first <n>", "Page size", (v) => parseInt(v, 10), 25).option("--after <cursor>", "Cursor for next page").option("-f, --format <format>", "Output format").action(async (o) => {
2316
+ try {
2317
+ const d = await execWorkspace(LIST_DATASINKS, {
2318
+ first: o.first ?? 25,
2319
+ after: o.after,
2320
+ search: o.search,
2321
+ status: o.status ? o.status.toUpperCase() : undefined,
2322
+ setupState: o.setupState ? o.setupState.toUpperCase() : undefined,
2323
+ isActive: o.active === undefined ? undefined : o.active.toLowerCase() === "true" || o.active === "1"
2324
+ });
2325
+ const items = d.listDataSinks.edges.map((e) => e.node);
2326
+ formatAndOutput(o, items, {
2327
+ headers: ["ID", "Name", "Key", "Status", "Setup", "Active", "Updated"],
2328
+ rows: items.map((x) => [
2329
+ truncate(x.id, 14),
2330
+ truncate(x.name ?? "-", 24),
2331
+ truncate(x.key, 20),
2332
+ formatStatus(x.status),
2333
+ x.setupState,
2334
+ x.isActive ? "yes" : "no",
2335
+ formatDate(x.updatedAt)
2336
+ ])
2337
+ });
2338
+ } catch (e) {
2339
+ handleCommandError(e);
2340
+ }
2341
+ });
2342
+ ds.command("get").description("Get a DataSink by ID or key").option("--id <id>", "DataSink ID").option("--key <key>", "DataSink key").option("-f, --format <format>", "Output format").action(async (o) => {
2343
+ try {
2344
+ if (!o.id && !o.key) {
2345
+ printError("Provide --id or --key");
2346
+ process.exit(1);
2347
+ }
2348
+ const d = await execWorkspace(GET_DATASINK, {
2349
+ id: o.id,
2350
+ key: o.key
2351
+ });
2352
+ if (!d.getDataSink) {
2353
+ printError("DataSink not found");
2354
+ process.exit(1);
2355
+ }
2356
+ formatAndOutput(o, d.getDataSink);
2357
+ } catch (e) {
2358
+ handleCommandError(e);
2359
+ }
2360
+ });
2361
+ ds.command("data").description("Fetch the current data payload of a DataSink").option("--id <id>", "DataSink ID").option("--key <key>", "DataSink key").option("--path <p>", "JSONPath-like filter (e.g. items[0].value)").option("--force-refresh", "Bypass cache").option("-f, --format <format>", "Output format (json recommended)").action(async (o) => {
2362
+ try {
2363
+ if (!o.id && !o.key) {
2364
+ printError("Provide --id or --key");
2365
+ process.exit(1);
2366
+ }
2367
+ const d = await execWorkspace(GET_DATASINK_DATA, {
2368
+ id: o.id,
2369
+ key: o.key,
2370
+ dataPath: o.path,
2371
+ forceRefresh: o.forceRefresh ?? false
2372
+ });
2373
+ formatAndOutput({ format: o.format ?? "json" }, d.getDataSinkData);
2374
+ } catch (e) {
2375
+ handleCommandError(e);
2376
+ }
2377
+ });
2378
+ ds.command("delete").description("Delete a DataSink").argument("<id>").action(async (id) => {
2379
+ try {
2380
+ await execWorkspace(DELETE_DATASINK, { id });
2381
+ printSuccess(`Deleted DataSink ${id}`);
2382
+ } catch (e) {
2383
+ handleCommandError(e);
2384
+ }
2385
+ });
2386
+ ds.command("create").description("Create a DataSink").requiredOption("--key <key>", "Unique key within workspace (e.g. sales_metrics_q4)").option("--name <name>", "Display name").option("--description <d>", "Description").option("--data-file <path>", "JSON file with initial payload").option("--data-json <json>", "Inline JSON for initial payload").option("--config-file <path>", "JSON file with DataSink config").option("--config-json <json>", "Inline JSON for DataSink config").option("-f, --format <format>", "Output format").action(async (o) => {
2387
+ try {
2388
+ const data = loadJsonInput(o.dataFile, o.dataJson);
2389
+ const config = loadJsonInput(o.configFile, o.configJson);
2390
+ const input = { key: o.key };
2391
+ if (o.name)
2392
+ input.name = o.name;
2393
+ if (o.description)
2394
+ input.description = o.description;
2395
+ if (data !== undefined)
2396
+ input.data = data;
2397
+ if (config !== undefined)
2398
+ input.config = config;
2399
+ const d = await execWorkspace(CREATE_DATASINK, {
2400
+ input
2401
+ });
2402
+ printSuccess(`Created DataSink: ${d.createDataSink.key} (${d.createDataSink.id})`);
2403
+ formatAndOutput(o, d.createDataSink);
2404
+ } catch (e) {
2405
+ handleCommandError(e);
2406
+ }
2407
+ });
2408
+ ds.command("update").description("Update a DataSink").argument("<id>").option("--name <name>").option("--description <d>").option("--active <bool>", "true / false").option("--version <n>", "Current DataSink version (optimistic lock); fetched automatically when omitted", parseVersion).option("--data-file <path>", "JSON file to replace data payload").option("--data-json <json>", "Inline JSON to replace data payload").option("-f, --format <format>", "Output format").action(async (id, o) => {
2409
+ try {
2410
+ const data = loadJsonInput(o.dataFile, o.dataJson);
2411
+ let version = o.version;
2412
+ if (version === undefined) {
2413
+ const current = await execWorkspace(GET_DATASINK, { id });
2414
+ if (!current.getDataSink || typeof current.getDataSink.version !== "number") {
2415
+ printError(`DataSink not found or has no version: ${id}`);
2416
+ process.exit(1);
2417
+ }
2418
+ version = current.getDataSink.version;
2419
+ }
2420
+ const input = { id, version };
2421
+ if (o.name)
2422
+ input.name = o.name;
2423
+ if (o.description !== undefined)
2424
+ input.description = o.description;
2425
+ if (o.active !== undefined) {
2426
+ input.isActive = o.active.toLowerCase() === "true" || o.active === "1";
2427
+ }
2428
+ if (data !== undefined)
2429
+ input.data = data;
2430
+ const d = await execWorkspace(UPDATE_DATASINK, {
2431
+ input
2432
+ });
2433
+ printSuccess(`Updated DataSink ${id}`);
2434
+ formatAndOutput(o, d.updateDataSink);
2435
+ } catch (e) {
2436
+ handleCommandError(e);
2437
+ }
2438
+ });
2439
+ }
2440
+ // src/commands/parser.ts
2441
+ function registerParserCommands(program) {
2442
+ const p = program.command("parser").description("Parser operations");
2443
+ p.command("list").description("List parsers in the current workspace (scoped to a console)").requiredOption("--console <id>", "Console (dashboard) ID parsers belong to").option("--status <s>", "Filter by status (active, inactive)").option("--type <t>", "Filter by parser type (jq, jsonata, spark)").option("--input <sinkKey>", "Filter by input sink key").option("--first <n>", "Page size", (v) => parseInt(v, 10), 25).option("--after <cursor>", "Cursor for next page").option("-f, --format <format>", "Output format").action(async (o) => {
2444
+ try {
2445
+ const VALID_STATUSES = new Set(["active", "inactive"]);
2446
+ const VALID_TYPES = new Set(["jq", "jsonata", "spark"]);
2447
+ const filter = { consoleId: o.console };
2448
+ if (o.status) {
2449
+ const s = o.status.toLowerCase();
2450
+ if (!VALID_STATUSES.has(s)) {
2451
+ printError(`Invalid --status "${o.status}". Valid: ${[...VALID_STATUSES].join(", ")}`);
2452
+ process.exit(1);
2453
+ }
2454
+ filter.status = s;
2455
+ }
2456
+ if (o.type) {
2457
+ const t = o.type.toLowerCase();
2458
+ if (!VALID_TYPES.has(t)) {
2459
+ printError(`Invalid --type "${o.type}". Valid: ${[...VALID_TYPES].join(", ")}`);
2460
+ process.exit(1);
2461
+ }
2462
+ filter.type = t;
2463
+ }
2464
+ if (o.input)
2465
+ filter.inputSinkKey = o.input;
2466
+ const d = await execWorkspace(LIST_PARSERS, { first: o.first ?? 25, after: o.after, filter });
2467
+ const items = d.listParsers.edges.map((e) => e.node);
2468
+ formatAndOutput(o, items, {
2469
+ headers: ["ID", "Name", "Type", "Status", "Input", "Output", "Updated"],
2470
+ rows: items.map((x) => [
2471
+ truncate(x.id, 14),
2472
+ truncate(x.name, 24),
2473
+ x.type,
2474
+ formatStatus(x.status),
2475
+ truncate(x.inputSinkKey, 18),
2476
+ truncate(x.outputKey, 18),
2477
+ formatDate(x.updatedAt)
2478
+ ])
2479
+ });
2480
+ } catch (e) {
2481
+ handleCommandError(e);
2482
+ }
2483
+ });
2484
+ p.command("get").description("Get a parser by ID (includes the expression body)").argument("<id>").option("-f, --format <format>", "Output format").action(async (id, o) => {
2485
+ try {
2486
+ const d = await execWorkspace(GET_PARSER, { id });
2487
+ if (!d.parser) {
2488
+ printError("Parser not found");
2489
+ process.exit(1);
2490
+ }
2491
+ formatAndOutput(o, d.parser);
2492
+ } catch (e) {
2493
+ handleCommandError(e);
2494
+ }
2495
+ });
2496
+ p.command("delete").description("Delete a parser").argument("<id>").action(async (id) => {
2497
+ try {
2498
+ await execWorkspace(DELETE_PARSER, { id });
2499
+ printSuccess(`Deleted parser ${id}`);
2500
+ } catch (e) {
2501
+ handleCommandError(e);
2502
+ }
2503
+ });
2504
+ p.command("create").description("Create a parser").requiredOption("--name <name>").requiredOption("--console <id>", "Parent dashboard/console ID").requiredOption("--input-key <key>", "DataSink key this parser reads from").requiredOption("--output-key <key>", "Key for parser output (used by widgets)").requiredOption("--type <t>", "Parser type (jq | jsonata | spark)").option("--expression <expr>", "Parser expression (inline)").option("--expression-file <path>", "Parser expression from file").option("--description <d>").option("--output-widget <id>", "Target widget ID (v2.1)").option("--ttl <seconds>", "Cache TTL", (v) => parseInt(v, 10)).option("-f, --format <format>", "Output format").action(async (o) => {
2505
+ try {
2506
+ const expression = loadTextInput(o.expressionFile, o.expression);
2507
+ if (!expression) {
2508
+ printError("Provide --expression or --expression-file");
2509
+ process.exit(1);
2510
+ }
2511
+ const input = {
2512
+ name: o.name,
2513
+ consoleId: o.console,
2514
+ inputSinkKey: o.inputKey,
2515
+ outputKey: o.outputKey,
2516
+ type: o.type.toLowerCase(),
2517
+ expression
2518
+ };
2519
+ if (o.description)
2520
+ input.description = o.description;
2521
+ if (o.outputWidget)
2522
+ input.outputWidgetId = o.outputWidget;
2523
+ if (o.ttl !== undefined)
2524
+ input.ttl = o.ttl;
2525
+ const d = await execWorkspace(CREATE_PARSER, {
2526
+ input
2527
+ });
2528
+ printSuccess(`Created parser: ${d.createParser.name} (${d.createParser.id})`);
2529
+ formatAndOutput(o, d.createParser);
2530
+ } catch (e) {
2531
+ handleCommandError(e);
2532
+ }
2533
+ });
2534
+ p.command("update").description("Update a parser (requires current version for optimistic concurrency)").argument("<id>").requiredOption("--version <n>", "Current parser version", parseVersion).option("--name <n>").option("--description <d>").option("--input-key <k>").option("--output-key <k>").option("--output-widget <id>").option("--status <s>", "Parser status (active | inactive)").option("--expression <expr>").option("--expression-file <path>").option("--ttl <seconds>", "", (v) => parseInt(v, 10)).option("-f, --format <format>", "Output format").action(async (id, o) => {
2535
+ try {
2536
+ const expression = loadTextInput(o.expressionFile, o.expression);
2537
+ const input = {};
2538
+ if (o.name)
2539
+ input.name = o.name;
2540
+ if (o.description !== undefined)
2541
+ input.description = o.description;
2542
+ if (o.inputKey)
2543
+ input.inputSinkKey = o.inputKey;
2544
+ if (o.outputKey)
2545
+ input.outputKey = o.outputKey;
2546
+ if (o.outputWidget)
2547
+ input.outputWidgetId = o.outputWidget;
2548
+ if (o.status)
2549
+ input.status = o.status.toLowerCase();
2550
+ if (expression)
2551
+ input.expression = expression;
2552
+ if (o.ttl !== undefined)
2553
+ input.ttl = o.ttl;
2554
+ const d = await execWorkspace(UPDATE_PARSER, {
2555
+ id,
2556
+ version: o.version,
2557
+ input
2558
+ });
2559
+ printSuccess(`Updated parser ${id} (now v${d.updateParser.version})`);
2560
+ formatAndOutput(o, d.updateParser);
2561
+ } catch (e) {
2562
+ handleCommandError(e);
2563
+ }
2564
+ });
2565
+ }
2566
+ // src/commands/pipeline.ts
2567
+ function registerPipelineCommands(program) {
2568
+ const p = program.command("pipeline").description("Pipeline operations");
2569
+ p.command("list").description("List pipelines in the current workspace (scoped to a console)").requiredOption("--console <id>", "Console (dashboard) ID pipelines belong to").option("--status <s>", "Filter by status (active, draft, error, inactive)").option("--first <n>", "Page size", (v) => parseInt(v, 10), 25).option("--after <cursor>", "Cursor for next page").option("-f, --format <format>", "Output format").action(async (o) => {
2570
+ try {
2571
+ const VALID_STATUSES = new Set(["active", "draft", "error", "inactive"]);
2572
+ const filter = { consoleId: o.console };
2573
+ if (o.status) {
2574
+ const s = o.status.toLowerCase();
2575
+ if (!VALID_STATUSES.has(s)) {
2576
+ printError(`Invalid --status "${o.status}". Valid: ${[...VALID_STATUSES].join(", ")}`);
2577
+ process.exit(1);
2578
+ }
2579
+ filter.status = s;
2580
+ }
2581
+ const d = await execWorkspace(LIST_PIPELINES, { first: o.first ?? 25, after: o.after, filter });
2582
+ const items = d.listPipelines.edges.map((e) => e.node);
2583
+ formatAndOutput(o, items, {
2584
+ headers: ["ID", "Name", "Status", "Version", "Last Executed", "Updated"],
2585
+ rows: items.map((x) => [
2586
+ truncate(x.id, 14),
2587
+ truncate(x.name, 28),
2588
+ formatStatus(x.status),
2589
+ String(x.version),
2590
+ formatDate(x.lastExecutedAt),
2591
+ formatDate(x.updatedAt)
2592
+ ])
2593
+ });
2594
+ } catch (e) {
2595
+ handleCommandError(e);
2596
+ }
2597
+ });
2598
+ p.command("get").description("Get a pipeline by ID (includes stages)").argument("<id>").option("-f, --format <format>", "Output format").action(async (id, o) => {
2599
+ try {
2600
+ const d = await execWorkspace(GET_PIPELINE, { id });
2601
+ if (!d.pipeline) {
2602
+ printError("Pipeline not found");
2603
+ process.exit(1);
2604
+ }
2605
+ formatAndOutput(o, d.pipeline);
2606
+ } catch (e) {
2607
+ handleCommandError(e);
2608
+ }
2609
+ });
2610
+ p.command("delete").description("Delete a pipeline").argument("<id>").action(async (id) => {
2611
+ try {
2612
+ await execWorkspace(DELETE_PIPELINE, { id });
2613
+ printSuccess(`Deleted pipeline ${id}`);
2614
+ } catch (e) {
2615
+ handleCommandError(e);
2616
+ }
2617
+ });
2618
+ p.command("create").description("Create a pipeline").requiredOption("--name <name>").requiredOption("--console <id>", "Parent dashboard/console ID").option("--description <d>").option("--status <s>", "Initial status (draft | active | inactive | error)").option("--stages-file <path>", "JSON file with array of CreatePipelineStageInput").option("--stages-json <json>", "Inline JSON array for stages").option("-f, --format <format>", "Output format").action(async (o) => {
2619
+ try {
2620
+ const stages = loadJsonInput(o.stagesFile, o.stagesJson);
2621
+ const input = {
2622
+ name: o.name,
2623
+ consoleId: o.console
2624
+ };
2625
+ if (o.description)
2626
+ input.description = o.description;
2627
+ if (o.status)
2628
+ input.status = o.status.toLowerCase();
2629
+ if (stages)
2630
+ input.stages = stages;
2631
+ const d = await execWorkspace(CREATE_PIPELINE, {
2632
+ input
2633
+ });
2634
+ printSuccess(`Created pipeline: ${d.createPipeline.name} (${d.createPipeline.id})`);
2635
+ formatAndOutput(o, d.createPipeline);
2636
+ } catch (e) {
2637
+ handleCommandError(e);
2638
+ }
2639
+ });
2640
+ p.command("update").description("Update a pipeline (requires current version)").argument("<id>").requiredOption("--version <n>", "Current pipeline version", parseVersion).option("--name <n>").option("--description <d>").option("--status <s>", "Pipeline status (draft | active | inactive | error)").option("--stages-file <path>", "JSON file with stages array to replace existing stages").option("--stages-json <json>", "Inline JSON stages array").option("-f, --format <format>", "Output format").action(async (id, o) => {
2641
+ try {
2642
+ const stages = loadJsonInput(o.stagesFile, o.stagesJson);
2643
+ const input = {};
2644
+ if (o.name)
2645
+ input.name = o.name;
2646
+ if (o.description !== undefined)
2647
+ input.description = o.description;
2648
+ if (o.status)
2649
+ input.status = o.status.toLowerCase();
2650
+ if (stages)
2651
+ input.stages = stages;
2652
+ const d = await execWorkspace(UPDATE_PIPELINE, {
2653
+ id,
2654
+ version: o.version,
2655
+ input
2656
+ });
2657
+ printSuccess(`Updated pipeline ${id} (now v${d.updatePipeline.version})`);
2658
+ formatAndOutput(o, d.updatePipeline);
2659
+ } catch (e) {
2660
+ handleCommandError(e);
2661
+ }
2662
+ });
2663
+ }
2664
+ // src/commands/workflow.ts
2665
+ function registerWorkflowCommands(program) {
2666
+ const wf = program.command("workflow").description("Workflow operations");
2667
+ wf.command("list").description("List workflows linked to BigConsole").option("-f, --format <format>", "Output format").action(async (o) => {
2668
+ try {
2669
+ const d = await execWorkspace(LIST_WORKFLOWS, {});
2670
+ const items = d.workflows;
2671
+ formatAndOutput(o, items, {
2672
+ headers: ["ID", "Name", "Key", "Status", "Execs", "Sinks", "Last Run"],
2673
+ rows: items.map((x) => [
2674
+ truncate(x.id, 14),
2675
+ truncate(x.name, 24),
2676
+ truncate(x.key, 18),
2677
+ formatStatus(x.status),
2678
+ String(x.executionCount),
2679
+ String(x.linkedDataSinkCount),
2680
+ formatDate(x.lastExecutedAt)
2681
+ ])
2682
+ });
2683
+ } catch (e) {
2684
+ handleCommandError(e);
2685
+ }
2686
+ });
2687
+ wf.command("get").description("Get a workflow by ID").argument("<id>").option("-f, --format <format>", "Output format").action(async (id, o) => {
2688
+ try {
2689
+ const d = await execWorkspace(GET_WORKFLOW, { id });
2690
+ if (!d.workflow) {
2691
+ printError("Workflow not found");
2692
+ process.exit(1);
2693
+ }
2694
+ formatAndOutput(o, d.workflow);
2695
+ } catch (e) {
2696
+ handleCommandError(e);
2697
+ }
2698
+ });
2699
+ }
2700
+ // src/commands/drilldown.ts
2701
+ function registerDrilldownCommands(program) {
2702
+ const dd = program.command("drilldown").description("Widget drilldown actions");
2703
+ dd.command("list").description("List widget actions (includes drilldowns)").requiredOption("--widget <id>", "Widget ID").option("-f, --format <format>", "Output format").action(async (o) => {
2704
+ try {
2705
+ const d = await execWorkspace(GET_WIDGET_ACTIONS, { widgetId: o.widget });
2706
+ formatAndOutput(o, d.getWidgetActions, {
2707
+ headers: ["ID", "Widget", "Type", "Label", "Created"],
2708
+ rows: d.getWidgetActions.map((a) => [
2709
+ truncate(a.id, 14),
2710
+ truncate(a.widgetId, 14),
2711
+ a.type,
2712
+ truncate(a.label ?? "-", 24),
2713
+ formatDate(a.createdAt)
2714
+ ])
2715
+ });
2716
+ } catch (e) {
2717
+ handleCommandError(e);
2718
+ }
2719
+ });
2720
+ dd.command("delete").description("Delete the drilldown configuration from a widget").requiredOption("--widget <id>", "Widget ID").action(async (o) => {
2721
+ try {
2722
+ const lookup = await execWorkspace(GET_WIDGET_DRILLDOWN, {
2723
+ id: o.widget
2724
+ });
2725
+ const drilldownId = lookup.getWidget?.drilldown?.id;
2726
+ if (!drilldownId) {
2727
+ throw new Error(`Widget ${o.widget} has no drilldown configuration`);
2728
+ }
2729
+ await execWorkspace(DELETE_WIDGET_DRILLDOWN, { id: drilldownId });
2730
+ printSuccess(`Removed drilldown from widget ${o.widget}`);
2731
+ } catch (e) {
2732
+ handleCommandError(e);
2733
+ }
2734
+ });
2735
+ }
2736
+ // src/commands/share.ts
2737
+ function registerShareCommands(program) {
2738
+ const sh = program.command("share").description("Dashboard share management");
2739
+ sh.command("list").description("List shares for a dashboard").requiredOption("--dashboard <id>", "Dashboard ID").option("--active <bool>", "Filter by active status").option("--permission <p>", "Filter by permission (VIEW, EDIT, ...)").option("--recipient-type <t>", "USER | GROUP").option("--first <n>", "Page size", (v) => parseInt(v, 10), 25).option("--after <cursor>").option("-f, --format <format>", "Output format").action(async (o) => {
2740
+ try {
2741
+ const d = await execWorkspace(LIST_DASHBOARD_SHARES, {
2742
+ dashboardId: o.dashboard,
2743
+ first: o.first ?? 25,
2744
+ after: o.after,
2745
+ isActive: o.active === undefined ? undefined : o.active.toLowerCase() === "true" || o.active === "1",
2746
+ permission: o.permission ? o.permission.toUpperCase() : undefined,
2747
+ recipientType: o.recipientType ? o.recipientType.toUpperCase() : undefined
2748
+ });
2749
+ const items = d.listDashboardShares.edges.map((e) => e.node);
2750
+ formatAndOutput(o, items, {
2751
+ headers: ["ID", "Dashboard", "Permission", "Active", "Expires", "Created"],
2752
+ rows: items.map((x) => [
2753
+ truncate(x.id, 14),
2754
+ truncate(x.dashboardId, 14),
2755
+ x.permission,
2756
+ x.isActive ? "yes" : "no",
2757
+ formatDate(x.expiresAt),
2758
+ formatDate(x.createdAt)
2759
+ ])
2760
+ });
2761
+ } catch (e) {
2762
+ handleCommandError(e);
2763
+ }
2764
+ });
2765
+ }
2766
+ // src/commands/template.ts
2767
+ function registerTemplateCommands(program) {
2768
+ const t = program.command("template").description("Dashboard templates");
2769
+ t.command("list").description("List dashboard templates").option("--search <term>").option("--category <c>").option("--featured", "Only featured templates").option("--first <n>", "Page size", (v) => parseInt(v, 10), 25).option("--after <cursor>").option("-f, --format <format>", "Output format").action(async (o) => {
2770
+ try {
2771
+ const d = await execWorkspace(LIST_TEMPLATES, {
2772
+ first: o.first ?? 25,
2773
+ after: o.after,
2774
+ category: o.category ? o.category.toUpperCase() : undefined,
2775
+ search: o.search,
2776
+ isFeatured: o.featured ?? undefined
2777
+ });
2778
+ const items = d.listTemplates.edges.map((e) => e.node);
2779
+ formatAndOutput(o, items, {
2780
+ headers: ["ID", "Name", "Category", "Featured", "Status", "Created"],
2781
+ rows: items.map((x) => [
2782
+ truncate(x.id, 14),
2783
+ truncate(x.name, 30),
2784
+ x.category,
2785
+ x.isFeatured ? "*" : "",
2786
+ x.status,
2787
+ formatDate(x.createdAt)
2788
+ ])
2789
+ });
2790
+ } catch (e) {
2791
+ handleCommandError(e);
2792
+ }
2793
+ });
2794
+ t.command("get").description("Get a template by ID").argument("<id>").option("-f, --format <format>", "Output format").action(async (id, o) => {
2795
+ try {
2796
+ const d = await execWorkspace(GET_TEMPLATE, {
2797
+ id
2798
+ });
2799
+ if (!d.getTemplate) {
2800
+ printError("Template not found");
2801
+ process.exit(1);
2802
+ }
2803
+ formatAndOutput(o, d.getTemplate);
2804
+ } catch (e) {
2805
+ handleCommandError(e);
2806
+ }
2807
+ });
2808
+ }
2809
+ // src/commands/prompt.ts
2810
+ function registerPromptCommands(program) {
2811
+ const p = program.command("prompt").description("AI-driven dashboard generation");
2812
+ p.command("generate").description("Start a new dashboard generation from a natural-language prompt").requiredOption("--prompt <text>", "The natural-language prompt").option("--config-file <path>", "Optional JSON with generation config").option("--config-json <json>", "Optional inline JSON with generation config").option("-f, --format <format>", "Output format").action(async (o) => {
2813
+ try {
2814
+ const extra = loadJsonInput(o.configFile, o.configJson) ?? {};
2815
+ const input = { prompt: o.prompt, ...extra };
2816
+ const d = await execWorkspace(GENERATE_DASHBOARD_FROM_PROMPT, { input });
2817
+ printSuccess(`Generation queued: ${d.generateDashboardFromPrompt.id}`);
2818
+ formatAndOutput(o, d.generateDashboardFromPrompt);
2819
+ } catch (e) {
2820
+ handleCommandError(e);
2821
+ }
2822
+ });
2823
+ p.command("list").description("List prompt generations").option("--status <s>", "Filter by status").option("--first <n>", "Page size", (v) => parseInt(v, 10), 25).option("--after <cursor>").option("-f, --format <format>", "Output format").action(async (o) => {
2824
+ try {
2825
+ const d = await execWorkspace(LIST_PROMPT_GENERATIONS, {
2826
+ first: o.first ?? 25,
2827
+ after: o.after,
2828
+ status: o.status ? o.status.toUpperCase() : undefined
2829
+ });
2830
+ const items = d.listPromptGenerations.edges.map((e) => e.node);
2831
+ formatAndOutput(o, items, {
2832
+ headers: ["ID", "Status", "Prompt", "Created"],
2833
+ rows: items.map((x) => [
2834
+ truncate(x.id, 14),
2835
+ formatStatus(x.status),
2836
+ truncate(x.prompt, 50),
2837
+ formatDate(x.createdAt)
2838
+ ])
2839
+ });
2840
+ } catch (e) {
2841
+ handleCommandError(e);
2842
+ }
2843
+ });
2844
+ p.command("get").description("Get a prompt generation by ID").argument("<id>").option("-f, --format <format>", "Output format").action(async (id, o) => {
2845
+ try {
2846
+ const d = await execWorkspace(GET_PROMPT_GENERATION, { id });
2847
+ if (!d.getPromptGeneration) {
2848
+ printError("Prompt generation not found");
2849
+ process.exit(1);
2850
+ }
2851
+ formatAndOutput(o, d.getPromptGeneration);
2852
+ } catch (e) {
2853
+ handleCommandError(e);
2854
+ }
2855
+ });
2856
+ p.command("cancel").description("Cancel an in-flight prompt generation").argument("<id>").action(async (id) => {
2857
+ try {
2858
+ const d = await execWorkspace(CANCEL_PROMPT_GENERATION, { generationId: id });
2859
+ printSuccess(`Cancelled generation ${id} (status: ${d.cancelPromptGeneration.status})`);
2860
+ } catch (e) {
2861
+ handleCommandError(e);
2862
+ }
2863
+ });
2864
+ }
2865
+ export {
2866
+ BigConsoleRequestError,
2867
+ DEFAULT_CLIENT_ID,
2868
+ DEFAULT_CONFIG,
2869
+ KNOWN_CONFIG_KEYS,
2870
+ PROD_GLOBAL_ENDPOINT,
2871
+ PROD_WORKSPACE_ENDPOINT,
2872
+ getConfigDir,
2873
+ getConfigPath,
2874
+ getConfigValue,
2875
+ globalRequest,
2876
+ readConfig,
2877
+ registerAuthCommands,
2878
+ registerCLIConfigCommands,
2879
+ registerDashboardCommands,
2880
+ registerDataSinkCommands,
2881
+ registerDrilldownCommands,
2882
+ registerParserCommands,
2883
+ registerPipelineCommands,
2884
+ registerPromptCommands,
2885
+ registerShareCommands,
2886
+ registerTemplateCommands,
2887
+ registerWidgetCommands,
2888
+ registerWorkflowCommands,
2889
+ registerWorkspaceCommands,
2890
+ resetClients,
2891
+ resolveOIDCConfig,
2892
+ setConfigValue,
2893
+ workspaceRequest,
2894
+ writeConfig
2895
+ };