@burdenoff/bigconsole-cli 2026.802.1 → 2026.1029.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/README.md CHANGED
@@ -19,12 +19,6 @@ A powerful command-line interface for managing bigconsole operations with GraphQ
19
19
  npm install -g @burdenoff/bigconsole-cli
20
20
  ```
21
21
 
22
- ### Global Installation (Alpha/Pre-release)
23
-
24
- ```bash
25
- npm install -g @burdenoff/alpha-bigconsole-cli
26
- ```
27
-
28
22
  ### Local Installation
29
23
 
30
24
  ```bash
@@ -138,9 +132,25 @@ All commands support different output formats:
138
132
  ```bash
139
133
  bigconsole users list --format json
140
134
  bigconsole users list --format table
141
- bigconsole users list --format yaml
135
+ bigconsole users list --format yaml # `yml` is accepted as an alias
142
136
  ```
143
137
 
138
+ - **stdout carries only command data.** Status lines (`✓`, `ℹ`, `!`, `✗`) are
139
+ written to stderr, so `--format json|yaml` output can be piped straight into
140
+ `jq` / `yq`.
141
+ - **JSON** is standard `JSON.stringify` output; C1 control characters, line and
142
+ paragraph separators and bidirectional formatting characters are written as
143
+ `\uXXXX` escapes (the parsed data is unchanged).
144
+ - **YAML** is produced by js-yaml with the YAML 1.2 JSON schema. Strings that a
145
+ YAML 1.1 or 1.2 parser could read as another type (`yes`, `~`, `0x1F`,
146
+ `2026-01-01`, ...) are quoted, so the output parses back to the same data.
147
+ - **Tables** are for people: control sequences are removed, line breaks and tabs
148
+ are shown as spaces, and bidirectional formatting characters are shown as
149
+ `\uXXXX`.
150
+ - **Errors** are a single bounded line. Failed requests report the HTTP status,
151
+ the server's first GraphQL error and its code; request variables, headers and
152
+ response bodies are never printed, and credential-shaped values are masked.
153
+
144
154
  ## Global Options
145
155
 
146
156
  - `--endpoint <endpoint>`: Override the API endpoint for a single command
@@ -199,12 +209,30 @@ The CLI stores configuration in `~/.config/bigconsole-cli/config.json`. This inc
199
209
  - `accessToken`: Authentication token
200
210
  - `refreshToken`: Refresh token for authentication
201
211
 
202
- ## CI/CD
212
+ ## Releasing
213
+
214
+ `main` is the only branch. The **Publish Production** workflow
215
+ (`.github/workflows/publish-production.yml`) publishes it to npmjs:
216
+
217
+ 1. Bump `version` in `package.json` (CalVer `YYYY.MDD.PATCH`, e.g. `2026.1029.1`)
218
+ and add a `CHANGELOG.md` entry.
219
+ 2. Push to `main`. The `build` job lints, type-checks, tests, builds and packs
220
+ the CLI; the `publish` job (environment `Production`, org secret `NPM_TOKEN`)
221
+ publishes the tarball with dist-tags `latest` and `dev`.
222
+ 3. A version that is already on the registry is skipped, so a push without a
223
+ version bump publishes nothing. The workflow can be re-run from `main`
224
+ (workflow_dispatch).
225
+
226
+ The former `@burdenoff/alpha-bigconsole-cli` channel and its `alpha` branch are
227
+ retired.
203
228
 
204
- The project includes automated CI/CD pipelines:
229
+ ### Schema contract
205
230
 
206
- - **Alpha builds** - Published to `@burdenoff/alpha-bigconsole-cli` from `alpha` branch
207
- - **Production builds** - Published to `@burdenoff/bigconsole-cli` from `main` branch
231
+ `tests/unit/graphql/schema-contract.test.ts` validates every workspace operation
232
+ in `src/graphql/operations.ts` against `graphql/bigconsole-subgraph.graphql`,
233
+ the SDL that wspace-bigconsole-svc publishes to Hive. When the service schema
234
+ changes, run `bun run schema:pull` (needs `gh` with read access to the service
235
+ repo) and fix any operation the test rejects.
208
236
 
209
237
  ## Testing
210
238
 
@@ -3,6 +3,16 @@
3
3
  * Used for CLI authentication (similar to `gh auth login`).
4
4
  */
5
5
  import fetch from 'cross-fetch';
6
+ import { MAX_HTTP_BODY_EXCERPT_LENGTH, summarizeHttpBody, toDisplayLine, } from '../utils/sanitize.js';
7
+ /** Bounded, printable form of a server-supplied error message. */
8
+ function serverMessage(message) {
9
+ return toDisplayLine(message, MAX_HTTP_BODY_EXCERPT_LENGTH) || 'Unknown error';
10
+ }
11
+ /** Error for a non-2xx response; only a short, sanitized body excerpt is kept. */
12
+ async function httpFailure(prefix, response) {
13
+ const body = await response.text().catch(() => '');
14
+ return new Error(`${prefix} (${response.status}): ${summarizeHttpBody(body)}`);
15
+ }
6
16
  /**
7
17
  * Start the device code flow by requesting a device code from the OIDC issuer.
8
18
  */
@@ -18,8 +28,7 @@ export async function startDeviceCodeFlow(config) {
18
28
  body: body.toString(),
19
29
  });
20
30
  if (!response.ok) {
21
- const text = await response.text().catch(() => 'Unknown error');
22
- throw new Error(`Device code request failed (${response.status}): ${text}`);
31
+ throw await httpFailure('Device code request failed', response);
23
32
  }
24
33
  const data = (await response.json());
25
34
  return {
@@ -63,7 +72,7 @@ export async function pollForToken(deviceCode, interval, config) {
63
72
  // Treat it like authorization_pending — keep polling.
64
73
  continue;
65
74
  }
66
- throw new Error(`Token endpoint returned non-JSON response (status ${response.status}): ${raw.slice(0, 200)}`);
75
+ throw new Error(`Token endpoint returned non-JSON response (status ${response.status}): ${summarizeHttpBody(raw)}`);
67
76
  }
68
77
  if (response.ok && data['access_token']) {
69
78
  return {
@@ -84,7 +93,7 @@ export async function pollForToken(deviceCode, interval, config) {
84
93
  case 'access_denied':
85
94
  throw new Error('Authorization was denied by the user.');
86
95
  default:
87
- throw new Error(`Device code polling failed: ${data['error_description'] ?? errorCode ?? 'Unknown error'}`);
96
+ throw new Error(`Device code polling failed: ${serverMessage(data['error_description'] ?? errorCode)}`);
88
97
  }
89
98
  }
90
99
  }
@@ -109,12 +118,11 @@ export async function clientCredentialsFlow(globalEndpoint, clientId, clientSecr
109
118
  }),
110
119
  });
111
120
  if (!response.ok) {
112
- const text = await response.text().catch(() => 'Unknown error');
113
- throw new Error(`App authentication failed (${response.status}): ${text}`);
121
+ throw await httpFailure('App authentication failed', response);
114
122
  }
115
123
  const data = (await response.json());
116
124
  if (data.errors?.length) {
117
- throw new Error(`App authentication failed: ${data.errors[0].message}`);
125
+ throw new Error(`App authentication failed: ${serverMessage(data.errors[0]?.message)}`);
118
126
  }
119
127
  if (!data.data?.authenticateApp?.accessToken) {
120
128
  throw new Error('App authentication failed: no access token returned');
@@ -146,17 +154,15 @@ export async function issueWorkspaceToken(workspaceEndpoint, authToken, workspac
146
154
  }),
147
155
  });
148
156
  if (!response.ok) {
149
- const text = await response.text().catch(() => 'Unknown error');
150
- throw new Error(`Workspace token request failed (${response.status}): ${text}`);
157
+ throw await httpFailure('Workspace token request failed', response);
151
158
  }
152
159
  const data = (await response.json());
153
160
  if (data.errors?.length) {
154
- throw new Error(`Workspace token failed: ${data.errors[0].message}`);
161
+ throw new Error(`Workspace token failed: ${serverMessage(data.errors[0]?.message)}`);
155
162
  }
156
163
  const result = data.data?.issueWorkspaceToken;
157
164
  if (!result?.success || !result.token) {
158
- const errMsg = result?.error?.message ?? 'Unknown error';
159
- throw new Error(`Workspace token failed: ${errMsg}`);
165
+ throw new Error(`Workspace token failed: ${serverMessage(result?.error?.message)}`);
160
166
  }
161
167
  return { token: result.token, expiresAt: result.expiresAt, scopes: result.scopes ?? [] };
162
168
  }
@@ -180,12 +186,11 @@ export async function fetchWorkspaces(globalEndpoint, authToken) {
180
186
  body: JSON.stringify({ query, variables: { limit: 50 } }),
181
187
  });
182
188
  if (!response.ok) {
183
- const text = await response.text().catch(() => 'Unknown error');
184
- throw new Error(`Failed to fetch workspaces (${response.status}): ${text}`);
189
+ throw await httpFailure('Failed to fetch workspaces', response);
185
190
  }
186
191
  const data = (await response.json());
187
192
  if (data.errors?.length) {
188
- throw new Error(`Failed to fetch workspaces: ${data.errors[0].message}`);
193
+ throw new Error(`Failed to fetch workspaces: ${serverMessage(data.errors[0]?.message)}`);
189
194
  }
190
195
  return data.data?.myWorkspaces?.items ?? [];
191
196
  }
package/dist/cli.js CHANGED
@@ -9,7 +9,9 @@ import updateNotifier from 'update-notifier';
9
9
  import { readFileSync } from 'fs';
10
10
  import { join, dirname } from 'path';
11
11
  import { fileURLToPath } from 'url';
12
+ import { sanitizeForTerminal } from './utils/sanitize.js';
12
13
  import { registerAuthCommands, registerCLIConfigCommands, registerWorkspaceCommands, registerDashboardCommands, registerWidgetCommands, registerDataSinkCommands, registerParserCommands, registerPipelineCommands, registerWorkflowCommands, registerDrilldownCommands, registerShareCommands, registerTemplateCommands, registerPromptCommands, } from './commands/index.js';
14
+ import { inheritGlobalFormat } from './commands/base.js';
13
15
  const __filename = fileURLToPath(import.meta.url);
14
16
  const __dirname = dirname(__filename);
15
17
  let pkg = {
@@ -60,6 +62,7 @@ program
60
62
  .option('-f, --format <format>', 'Output format (json, table, yaml)')
61
63
  .option('--verbose', 'Enable verbose logging')
62
64
  .option('--no-color', 'Disable colored output');
65
+ program.hook('preAction', (_program, actionCommand) => inheritGlobalFormat(actionCommand));
63
66
  registerAuthCommands(program);
64
67
  registerCLIConfigCommands(program);
65
68
  registerWorkspaceCommands(program);
@@ -116,8 +119,8 @@ ${chalk.bold('Configuration:')} ${chalk.dim('~/.config/bigconsole-cli/')}
116
119
  ${chalk.bold('Docs:')} ${chalk.blue('https://docs.bigconsole.com/cli')}
117
120
  `);
118
121
  program.on('command:*', () => {
119
- console.error(chalk.red(`Invalid command: ${program.args.join(' ')}`));
120
- console.log(chalk.dim('See --help for a list of available commands.'));
122
+ console.error(chalk.red(`Invalid command: ${sanitizeForTerminal(program.args.join(' '))}`));
123
+ console.error(chalk.dim('See --help for a list of available commands.'));
121
124
  process.exit(1);
122
125
  });
123
126
  program.parse();
@@ -7,6 +7,7 @@ import { startDeviceCodeFlow, pollForToken, openBrowser, clientCredentialsFlow,
7
7
  import { globalRequest, resetClients } from '../graphql/client.js';
8
8
  import { SIGN_OUT, REFRESH_TOKEN } from '../graphql/operations.js';
9
9
  import { printSuccess, printError, printInfo, printWarning, formatDate } from '../utils/output.js';
10
+ import { sanitizeForTerminal, toSafeErrorMessage } from '../utils/sanitize.js';
10
11
  import { handleCommandError, formatAndOutput } from './base.js';
11
12
  export function registerAuthCommands(program) {
12
13
  const auth = program.command('auth').description('Authentication commands');
@@ -185,8 +186,8 @@ async function loginWithDeviceCode() {
185
186
  printInfo('Starting device code authentication...');
186
187
  const dc = await startDeviceCodeFlow(oidcConfig);
187
188
  console.log('');
188
- console.log(` Open: ${dc.verificationUri}`);
189
- console.log(` Code: ${dc.userCode}`);
189
+ console.log(` Open: ${sanitizeForTerminal(dc.verificationUri)}`);
190
+ console.log(` Code: ${sanitizeForTerminal(dc.userCode)}`);
190
191
  console.log('');
191
192
  const opened = await openBrowser(dc.verificationUriComplete ?? dc.verificationUri);
192
193
  if (opened)
@@ -214,7 +215,7 @@ async function loginWithDeviceCode() {
214
215
  let selected = workspaces[0];
215
216
  if (workspaces.length > 1) {
216
217
  console.log('\nAvailable workspaces:');
217
- workspaces.forEach((ws, i) => console.log(` ${i + 1}. ${ws.name} (${ws.slug})`));
218
+ workspaces.forEach((ws, i) => console.log(` ${i + 1}. ${sanitizeForTerminal(ws.name)} (${sanitizeForTerminal(ws.slug)})`));
218
219
  const readline = await import('readline');
219
220
  // Re-prompt up to 3 times on invalid input rather than silently
220
221
  // defaulting to workspaces[0] — users who mistype expect to see
@@ -265,7 +266,7 @@ async function loginWithDeviceCode() {
265
266
  });
266
267
  resetClients();
267
268
  printSuccess('Authentication successful');
268
- printWarning(`Workspace token not obtained: ${wsError instanceof Error ? wsError.message : String(wsError)}`);
269
+ printWarning(`Workspace token not obtained: ${toSafeErrorMessage(wsError)}`);
269
270
  printInfo('Run `bigconsole auth login` again once a workspace is available.');
270
271
  }
271
272
  }
@@ -331,7 +332,7 @@ async function loginWithClientCredentials(workspaceId) {
331
332
  printInfo(`Workspace token issued for: ${ws.name}`);
332
333
  }
333
334
  catch (err) {
334
- printError(`Workspace token issuance failed: ${err instanceof Error ? err.message : String(err)}`);
335
+ printError(`Workspace token issuance failed: ${toSafeErrorMessage(err)}`);
335
336
  process.exit(1);
336
337
  }
337
338
  }
@@ -2,6 +2,7 @@
2
2
  * Shared command utilities.
3
3
  * Provides auth wrappers, format helpers, and error handling for all commands.
4
4
  */
5
+ import type { Command } from 'commander';
5
6
  import type { RequestDocument, Variables } from 'graphql-request';
6
7
  /**
7
8
  * Load a JSON-valued command input from either --file or --json, mutually
@@ -33,8 +34,18 @@ export declare function execWorkspace<T>(document: RequestDocument, variables?:
33
34
  export declare function execGlobal<T>(document: RequestDocument, variables?: Variables): Promise<T>;
34
35
  /**
35
36
  * Handle command errors consistently, printing user-friendly messages.
37
+ * The message is reduced to a single safe line (see `toSafeErrorMessage`):
38
+ * request variables, headers and raw response bodies are never printed.
36
39
  */
37
40
  export declare function handleCommandError(error: unknown): never;
41
+ /**
42
+ * Commander recognises program-level options anywhere on the command line, so
43
+ * `-f/--format` written after a subcommand is stored on the program rather
44
+ * than on the subcommand. Copy it to the command about to run when that
45
+ * command declares its own format option and did not receive a value.
46
+ * Registered as a `preAction` hook in the CLI entry point.
47
+ */
48
+ export declare function inheritGlobalFormat(actionCommand: Command): void;
38
49
  /**
39
50
  * Get format and output data for a command.
40
51
  * Standard pattern used by all list/get commands.
@@ -6,6 +6,7 @@ import { readFileSync } from 'fs';
6
6
  import { readAuth } from '../auth/store.js';
7
7
  import { workspaceRequest, globalRequest } from '../graphql/client.js';
8
8
  import { printError, getFormat, outputData } from '../utils/output.js';
9
+ import { toSafeErrorMessage } from '../utils/sanitize.js';
9
10
  /**
10
11
  * Load a JSON-valued command input from either --file or --json, mutually
11
12
  * exclusive. Returns undefined if neither is provided. Exits with a clear
@@ -84,12 +85,31 @@ export async function execGlobal(document, variables) {
84
85
  }
85
86
  /**
86
87
  * Handle command errors consistently, printing user-friendly messages.
88
+ * The message is reduced to a single safe line (see `toSafeErrorMessage`):
89
+ * request variables, headers and raw response bodies are never printed.
87
90
  */
88
91
  export function handleCommandError(error) {
89
- const message = error instanceof Error ? error.message : String(error);
90
- printError(message);
92
+ printError(toSafeErrorMessage(error));
91
93
  process.exit(1);
92
94
  }
95
+ /**
96
+ * Commander recognises program-level options anywhere on the command line, so
97
+ * `-f/--format` written after a subcommand is stored on the program rather
98
+ * than on the subcommand. Copy it to the command about to run when that
99
+ * command declares its own format option and did not receive a value.
100
+ * Registered as a `preAction` hook in the CLI entry point.
101
+ */
102
+ export function inheritGlobalFormat(actionCommand) {
103
+ const declaresFormat = actionCommand.options.some((o) => o.attributeName() === 'format');
104
+ if (!declaresFormat)
105
+ return;
106
+ const current = actionCommand.getOptionValue('format');
107
+ if (current !== undefined)
108
+ return;
109
+ const { format } = actionCommand.optsWithGlobals();
110
+ if (format !== undefined)
111
+ actionCommand.setOptionValue('format', format);
112
+ }
93
113
  /**
94
114
  * Get format and output data for a command.
95
115
  * Standard pattern used by all list/get commands.
@@ -5,6 +5,7 @@ import { readFileSync, writeFileSync } from 'fs';
5
5
  import * as ops from '../graphql/operations.js';
6
6
  import { execWorkspace, formatAndOutput, handleCommandError, loadJsonInput } from './base.js';
7
7
  import { printSuccess, printError, formatDate, formatStatus, truncate } from '../utils/output.js';
8
+ import { sanitizeForTerminal } from '../utils/sanitize.js';
8
9
  export function registerDashboardCommands(program) {
9
10
  const dash = program.command('dashboard').description('Dashboard operations');
10
11
  dash
@@ -187,7 +188,6 @@ export function registerDashboardCommands(program) {
187
188
  .description('Export a dashboard definition as JSON (stdout or --out file)')
188
189
  .argument('<id>')
189
190
  .option('--out <path>', 'Write JSON to file instead of stdout')
190
- .option('--include-comments', 'Include dashboard comments')
191
191
  .option('--include-data-refs', 'Include data source references', true)
192
192
  .option('--include-webhook-mappings', 'Include webhook mappings')
193
193
  .option('--pretty', 'Pretty-print JSON', true)
@@ -200,7 +200,6 @@ export function registerDashboardCommands(program) {
200
200
  };
201
201
  const d = await execWorkspace(ops.EXPORT_DASHBOARD, {
202
202
  id,
203
- includeComments: o.includeComments ?? false,
204
203
  options,
205
204
  });
206
205
  if (o.out) {
@@ -208,8 +207,12 @@ export function registerDashboardCommands(program) {
208
207
  printSuccess(`Exported to ${o.out}`);
209
208
  }
210
209
  else {
211
- process.stdout.write(d.exportDashboard);
212
- if (!d.exportDashboard.endsWith('\n'))
210
+ // Byte-exact when piped or redirected; made inert for a terminal.
211
+ const body = process.stdout.isTTY
212
+ ? sanitizeForTerminal(d.exportDashboard, { preserveNewlines: true })
213
+ : d.exportDashboard;
214
+ process.stdout.write(body);
215
+ if (!body.endsWith('\n'))
213
216
  process.stdout.write('\n');
214
217
  }
215
218
  }
@@ -37,7 +37,15 @@ export function registerDrilldownCommands(program) {
37
37
  .requiredOption('--widget <id>', 'Widget ID')
38
38
  .action(async (o) => {
39
39
  try {
40
- await execWorkspace(ops.DELETE_WIDGET_DRILLDOWN, { widgetId: o.widget });
40
+ // deleteWidgetDrilldown takes the drilldown's own id; resolve it from the widget.
41
+ const lookup = await execWorkspace(ops.GET_WIDGET_DRILLDOWN, {
42
+ id: o.widget,
43
+ });
44
+ const drilldownId = lookup.getWidget?.drilldown?.id;
45
+ if (!drilldownId) {
46
+ throw new Error(`Widget ${o.widget} has no drilldown configuration`);
47
+ }
48
+ await execWorkspace(ops.DELETE_WIDGET_DRILLDOWN, { id: drilldownId });
41
49
  printSuccess(`Removed drilldown from widget ${o.widget}`);
42
50
  }
43
51
  catch (e) {
@@ -74,7 +74,7 @@ export function registerPromptCommands(program) {
74
74
  .argument('<id>')
75
75
  .action(async (id) => {
76
76
  try {
77
- const d = await execWorkspace(ops.CANCEL_PROMPT_GENERATION, { id });
77
+ const d = await execWorkspace(ops.CANCEL_PROMPT_GENERATION, { generationId: id });
78
78
  printSuccess(`Cancelled generation ${id} (status: ${d.cancelPromptGeneration.status})`);
79
79
  }
80
80
  catch (e) {
@@ -4,6 +4,18 @@
4
4
  * Auto-injects auth headers, auto-refreshes both platform and workspace tokens.
5
5
  */
6
6
  import { type RequestDocument, type Variables } from 'graphql-request';
7
+ import { type SafeErrorDescription } from '../utils/sanitize.js';
8
+ /**
9
+ * Error thrown for a failed GraphQL request. Its message is built only from
10
+ * the HTTP status, the server's first GraphQL error and its error code, so it
11
+ * is safe to print. The underlying client error is deliberately not attached
12
+ * (no `cause`): it carries the request variables and the raw response body.
13
+ */
14
+ export declare class BigConsoleRequestError extends Error {
15
+ readonly status: number | undefined;
16
+ readonly code: string | undefined;
17
+ constructor(description: SafeErrorDescription);
18
+ }
7
19
  export declare function ensureFreshTokens(): Promise<void>;
8
20
  /**
9
21
  * Execute a GraphQL request against the workspace gateway.
@@ -8,6 +8,7 @@ import { readConfig } from '../config/index.js';
8
8
  import { readAuth, writeAuth, isTokenExpiringSoon, isWorkspaceTokenExpiringSoon, } from '../auth/store.js';
9
9
  import { issueWorkspaceToken, clientCredentialsFlow } from '../auth/device-code.js';
10
10
  import { printError } from '../utils/output.js';
11
+ import { describeRequestError, toSafeErrorMessage, } from '../utils/sanitize.js';
11
12
  const DEFAULT_TIMEOUT = 30000;
12
13
  const MAX_RETRIES = 3;
13
14
  const INITIAL_RETRY_DELAY_MS = 1000;
@@ -25,39 +26,18 @@ function isNetworkOrServerError(error) {
25
26
  return false;
26
27
  }
27
28
  /**
28
- * Trim a backend-provided error message to something safe + readable.
29
- * Subgraph errors can carry stack fragments, multi-line traces, or very
30
- * long strings; for user-facing CLI output we only want the first line
31
- * and a reasonable length cap. Returns undefined if the input isn't a
32
- * usable string so callers can fall through to the next source.
29
+ * Error thrown for a failed GraphQL request. Its message is built only from
30
+ * the HTTP status, the server's first GraphQL error and its error code, so it
31
+ * is safe to print. The underlying client error is deliberately not attached
32
+ * (no `cause`): it carries the request variables and the raw response body.
33
33
  */
34
- function sanitizeBackendMessage(raw) {
35
- if (typeof raw !== 'string' || raw.length === 0)
36
- return undefined;
37
- const firstLine = raw.split(/\r?\n/)[0].trim();
38
- if (!firstLine)
39
- return undefined;
40
- const MAX = 500;
41
- return firstLine.length > MAX ? `${firstLine.slice(0, MAX)}…` : firstLine;
42
- }
43
- function extractErrorMessage(error) {
44
- if (error && typeof error === 'object' && 'response' in error) {
45
- const resp = error;
46
- const firstError = resp.response?.errors?.[0];
47
- // Federated subgraph errors land under extensions.originalError.message;
48
- // the top-level message is usually "Unexpected error." and useless.
49
- const original = sanitizeBackendMessage(firstError?.extensions?.originalError?.message);
50
- if (original)
51
- return original;
52
- const wrapper = sanitizeBackendMessage(firstError?.message);
53
- if (wrapper)
54
- return wrapper;
55
- if (resp.message)
56
- return resp.message;
34
+ export class BigConsoleRequestError extends Error {
35
+ constructor(description) {
36
+ super(description.message);
37
+ this.name = 'BigConsoleRequestError';
38
+ this.status = description.status;
39
+ this.code = description.code;
57
40
  }
58
- if (error instanceof Error)
59
- return error.message;
60
- return String(error);
61
41
  }
62
42
  let workspaceClient = null;
63
43
  let globalClient = null;
@@ -109,7 +89,7 @@ async function refreshPlatformToken() {
109
89
  globalClient = null;
110
90
  }
111
91
  catch (error) {
112
- printError(`App token refresh failed: ${extractErrorMessage(error)}`);
92
+ printError(`App token refresh failed: ${toSafeErrorMessage(error)}`);
113
93
  }
114
94
  return;
115
95
  }
@@ -143,7 +123,7 @@ async function refreshPlatformToken() {
143
123
  globalClient = null;
144
124
  }
145
125
  catch (error) {
146
- printError(`Token refresh failed: ${extractErrorMessage(error)}`);
126
+ printError(`Token refresh failed: ${toSafeErrorMessage(error)}`);
147
127
  }
148
128
  }
149
129
  /**
@@ -164,7 +144,7 @@ async function refreshWorkspaceToken() {
164
144
  workspaceClient = null;
165
145
  }
166
146
  catch (error) {
167
- printError(`Workspace token refresh failed: ${extractErrorMessage(error)}`);
147
+ printError(`Workspace token refresh failed: ${toSafeErrorMessage(error)}`);
168
148
  }
169
149
  }
170
150
  export async function ensureFreshTokens() {
@@ -231,7 +211,7 @@ async function executeWithRetry(client, document, variables) {
231
211
  break;
232
212
  }
233
213
  }
234
- throw new Error(extractErrorMessage(lastError));
214
+ throw new BigConsoleRequestError(describeRequestError(lastError));
235
215
  }
236
216
  /**
237
217
  * Execute a GraphQL request against the workspace gateway.
@@ -6,17 +6,17 @@
6
6
  export declare const SIGN_OUT = "\n mutation SignOut {\n signOut\n }\n";
7
7
  export declare const REFRESH_TOKEN = "\n mutation RefreshToken($token: String!) {\n refreshToken(token: $token) {\n accessToken\n refreshToken\n expiresIn\n }\n }\n";
8
8
  export declare const MY_WORKSPACES = "\n query MyWorkspaces($limit: Int) {\n myWorkspaces(limit: $limit) {\n items { id name slug tenantId organizationId }\n }\n }\n";
9
- export declare const LIST_DASHBOARDS = "\n query ListDashboards(\n $first: Int\n $after: String\n $search: String\n $visibility: DashboardVisibility\n $category: DashboardCategory\n $includeArchived: Boolean\n $sortBy: DashboardSortField\n $sortOrder: SortOrder\n ) {\n listDashboards(\n first: $first\n after: $after\n search: $search\n visibility: $visibility\n category: $category\n includeArchived: $includeArchived\n sortBy: $sortBy\n sortOrder: $sortOrder\n ) {\n edges {\n node {\n id\n name\n description\n status\n visibility\n category\n ownerId\n createdAt\n updatedAt\n }\n }\n pageInfo { hasNextPage endCursor }\n totalCount\n }\n }\n";
9
+ export declare const LIST_DASHBOARDS = "\n query ListDashboards(\n $first: Int\n $after: String\n $search: String\n $visibility: BigConsoleDashboardVisibility\n $category: BigConsoleDashboardCategory\n $includeArchived: Boolean\n $sortBy: BigConsoleDashboardSortField\n $sortOrder: SortOrder\n ) {\n listDashboards(\n first: $first\n after: $after\n search: $search\n visibility: $visibility\n category: $category\n includeArchived: $includeArchived\n sortBy: $sortBy\n sortOrder: $sortOrder\n ) {\n edges {\n node {\n id\n name\n description\n status\n visibility\n category\n ownerId\n createdAt\n updatedAt\n }\n }\n pageInfo { hasNextPage endCursor }\n totalCount\n }\n }\n";
10
10
  export declare const GET_DASHBOARD = "\n query GetDashboard($id: ID!) {\n getDashboard(id: $id) {\n id\n name\n description\n status\n visibility\n category\n ownerId\n definitionVersion\n createdAt\n updatedAt\n }\n }\n";
11
11
  export declare const ARCHIVE_DASHBOARD = "\n mutation ArchiveDashboard($id: ID!) {\n archiveDashboard(id: $id) {\n id\n name\n status\n }\n }\n";
12
12
  export declare const DELETE_DASHBOARD = "\n mutation DeleteDashboard($id: ID!) {\n deleteBigConsoleDashboard(id: $id)\n }\n";
13
- export declare const LIST_WIDGETS = "\n query ListWidgets($pageId: ID!, $first: Int, $after: String, $type: WidgetType) {\n listWidgets(pageId: $pageId, first: $first, after: $after, type: $type) {\n edges {\n node {\n id\n pageId\n dashboardId\n type\n title\n dataSinkId\n lastRefreshedAt\n createdAt\n updatedAt\n }\n }\n pageInfo { hasNextPage endCursor }\n totalCount\n }\n }\n";
13
+ export declare const LIST_WIDGETS = "\n query ListWidgets($pageId: ID!, $first: Int, $after: String, $type: BigConsoleWidgetType) {\n listWidgets(pageId: $pageId, first: $first, after: $after, type: $type) {\n edges {\n node {\n id\n pageId\n dashboardId\n type\n title\n dataSinkId\n lastRefreshedAt\n createdAt\n updatedAt\n }\n }\n pageInfo { hasNextPage endCursor }\n totalCount\n }\n }\n";
14
14
  export declare const GET_WIDGET = "\n query GetWidget($id: ID!) {\n getWidget(id: $id) {\n id\n pageId\n dashboardId\n type\n title\n config\n dataSinkId\n datasetId\n lastRefreshedAt\n cacheTTL\n createdAt\n updatedAt\n }\n }\n";
15
15
  export declare const DELETE_WIDGET = "\n mutation DeleteWidget($id: ID!) {\n deleteBigConsoleWidget(id: $id)\n }\n";
16
16
  export declare const LIST_DATASINKS = "\n query ListDataSinks(\n $first: Int\n $after: String\n $search: String\n $status: DataSinkStatus\n $setupState: DataSinkSetupState\n $isActive: Boolean\n ) {\n listDataSinks(\n first: $first\n after: $after\n search: $search\n status: $status\n setupState: $setupState\n isActive: $isActive\n ) {\n edges {\n node {\n id\n name\n key\n description\n status\n setupState\n isActive\n createdAt\n updatedAt\n }\n }\n pageInfo { hasNextPage endCursor }\n totalCount\n }\n }\n";
17
17
  export declare const GET_DATASINK = "\n query GetDataSink($id: ID, $key: String) {\n getDataSink(id: $id, key: $key) {\n id\n name\n key\n description\n status\n setupState\n isActive\n createdAt\n updatedAt\n }\n }\n";
18
- export declare const GET_DATASINK_DATA = "\n query GetDataSinkData($id: ID, $key: String, $dataPath: String, $forceRefresh: Boolean) {\n getDataSinkData(id: $id, key: $key, dataPath: $dataPath, forceRefresh: $forceRefresh)\n }\n";
19
- export declare const DELETE_DATASINK = "\n mutation DeleteDataSink($id: ID!) {\n deleteDataSink(id: $id)\n }\n";
18
+ export declare const GET_DATASINK_DATA = "\n query GetDataSinkData($id: ID, $key: String, $dataPath: String, $forceRefresh: Boolean) {\n getDataSinkData(id: $id, key: $key, dataPath: $dataPath, forceRefresh: $forceRefresh) {\n raw\n extracted\n cached\n fetchedAt\n version\n totalCount\n hasMore\n limit\n offset\n }\n }\n";
19
+ export declare const DELETE_DATASINK = "\n mutation DeleteDataSink($id: ID!) {\n deleteDataSink(id: $id) {\n id\n name\n }\n }\n";
20
20
  export declare const LIST_PARSERS = "\n query ListParsers($first: Int, $after: String, $filter: ParserFilterInput) {\n listParsers(first: $first, after: $after, filter: $filter) {\n edges {\n node {\n id\n name\n description\n type\n status\n inputSinkKey\n outputKey\n outputWidgetId\n ttl\n version\n createdAt\n updatedAt\n }\n }\n pageInfo { hasNextPage endCursor }\n totalCount\n }\n }\n";
21
21
  export declare const GET_PARSER = "\n query GetParser($id: ID!) {\n parser(id: $id) {\n id\n name\n description\n type\n status\n expression\n inputSinkKey\n outputKey\n outputWidgetId\n ttl\n version\n createdAt\n updatedAt\n }\n }\n";
22
22
  export declare const DELETE_PARSER = "\n mutation DeleteParser($id: ID!) {\n deleteParser(id: $id)\n }\n";
@@ -25,8 +25,8 @@ export declare const GET_PIPELINE = "\n query GetPipeline($id: ID!) {\n pipe
25
25
  export declare const DELETE_PIPELINE = "\n mutation DeletePipeline($id: ID!) {\n deletePipeline(id: $id)\n }\n";
26
26
  export declare const CREATE_DASHBOARD = "\n mutation CreateDashboard($input: BigConsoleCreateDashboardInput!) {\n createBigConsoleDashboard(input: $input) {\n id\n name\n status\n visibility\n category\n createdAt\n }\n }\n";
27
27
  export declare const UPDATE_DASHBOARD = "\n mutation UpdateDashboard($input: BigConsoleUpdateDashboardInput!) {\n updateBigConsoleDashboard(input: $input) {\n id\n name\n status\n visibility\n category\n updatedAt\n }\n }\n";
28
- export declare const IMPORT_DASHBOARD = "\n mutation ImportDashboard($input: ImportDashboardInput!) {\n importDashboard(input: $input) {\n id\n name\n status\n visibility\n category\n createdAt\n }\n }\n";
29
- export declare const EXPORT_DASHBOARD = "\n query ExportDashboard($id: ID!, $includeComments: Boolean, $options: ExportDashboardOptions) {\n exportDashboard(id: $id, includeComments: $includeComments, options: $options)\n }\n";
28
+ export declare const IMPORT_DASHBOARD = "\n mutation ImportDashboard($input: BigConsoleImportDashboardInput!) {\n importDashboard(input: $input) {\n id\n name\n status\n visibility\n category\n createdAt\n }\n }\n";
29
+ export declare const EXPORT_DASHBOARD = "\n query ExportDashboard($id: ID!, $options: BigConsoleExportDashboardOptions) {\n exportDashboard(id: $id, options: $options)\n }\n";
30
30
  export declare const CREATE_WIDGET = "\n mutation CreateWidget($input: BigConsoleCreateWidgetInput!) {\n createBigConsoleWidget(input: $input) {\n id\n pageId\n type\n title\n createdAt\n }\n }\n";
31
31
  export declare const UPDATE_WIDGET = "\n mutation UpdateWidget($input: BigConsoleUpdateWidgetInput!) {\n updateBigConsoleWidget(input: $input) {\n id\n pageId\n type\n title\n updatedAt\n }\n }\n";
32
32
  export declare const CREATE_DATASINK = "\n mutation CreateDataSink($input: CreateDataSinkInput!) {\n createDataSink(input: $input) {\n id\n name\n key\n status\n setupState\n isActive\n createdAt\n }\n }\n";
@@ -35,14 +35,15 @@ export declare const CREATE_PARSER = "\n mutation CreateParser($input: CreatePa
35
35
  export declare const UPDATE_PARSER = "\n mutation UpdateParser($id: ID!, $version: Int!, $input: UpdateParserInput!) {\n updateParser(id: $id, version: $version, input: $input) {\n id\n name\n type\n status\n version\n updatedAt\n }\n }\n";
36
36
  export declare const CREATE_PIPELINE = "\n mutation CreatePipeline($input: CreatePipelineInput!) {\n createPipeline(input: $input) {\n id\n name\n status\n version\n createdAt\n }\n }\n";
37
37
  export declare const UPDATE_PIPELINE = "\n mutation UpdatePipeline($id: ID!, $version: Int!, $input: UpdatePipelineInput!) {\n updatePipeline(id: $id, version: $version, input: $input) {\n id\n name\n status\n version\n updatedAt\n }\n }\n";
38
- export declare const GET_WIDGET_ACTIONS = "\n query GetWidgetActions($widgetId: ID!) {\n getWidgetActions(widgetId: $widgetId) {\n id\n widgetId\n type\n label\n config\n createdAt\n }\n }\n";
39
- export declare const DELETE_WIDGET_DRILLDOWN = "\n mutation DeleteWidgetDrilldown($widgetId: ID!) {\n deleteWidgetDrilldown(widgetId: $widgetId)\n }\n";
40
- export declare const LIST_DASHBOARD_SHARES = "\n query ListDashboardShares(\n $dashboardId: ID!\n $first: Int\n $after: String\n $isActive: Boolean\n $permission: SharePermission\n $recipientType: ShareRecipientType\n ) {\n listDashboardShares(\n dashboardId: $dashboardId\n first: $first\n after: $after\n isActive: $isActive\n permission: $permission\n recipientType: $recipientType\n ) {\n edges {\n node {\n id\n dashboardId\n permission\n isActive\n expiresAt\n createdAt\n }\n }\n pageInfo { hasNextPage endCursor }\n totalCount\n }\n }\n";
41
- export declare const LIST_TEMPLATES = "\n query ListTemplates(\n $first: Int\n $after: String\n $category: DashboardCategory\n $search: String\n $isFeatured: Boolean\n ) {\n listTemplates(\n first: $first\n after: $after\n category: $category\n search: $search\n isFeatured: $isFeatured\n ) {\n edges {\n node {\n id\n name\n description\n category\n isFeatured\n status\n createdAt\n }\n }\n pageInfo { hasNextPage endCursor }\n totalCount\n }\n }\n";
38
+ export declare const GET_WIDGET_ACTIONS = "\n query GetWidgetActions($widgetId: ID!) {\n getWidgetActions(widgetId: $widgetId) {\n id\n widgetId\n type\n name\n label\n order\n disabled\n hidden\n createdAt\n }\n }\n";
39
+ export declare const DELETE_WIDGET_DRILLDOWN = "\n mutation DeleteWidgetDrilldown($id: ID!) {\n deleteWidgetDrilldown(id: $id)\n }\n";
40
+ export declare const GET_WIDGET_DRILLDOWN = "\n query GetWidgetDrilldown($id: ID!) {\n getWidget(id: $id) {\n id\n drilldown {\n id\n }\n }\n }\n";
41
+ export declare const LIST_DASHBOARD_SHARES = "\n query ListDashboardShares(\n $dashboardId: ID!\n $first: Int\n $after: String\n $isActive: Boolean\n $permission: BigConsoleDashboardSharePermission\n $recipientType: BigConsoleDashboardShareRecipientType\n ) {\n listDashboardShares(\n dashboardId: $dashboardId\n first: $first\n after: $after\n isActive: $isActive\n permission: $permission\n recipientType: $recipientType\n ) {\n edges {\n node {\n id\n dashboardId\n permission\n isActive\n expiresAt\n createdAt\n }\n }\n pageInfo { hasNextPage endCursor }\n totalCount\n }\n }\n";
42
+ export declare const LIST_TEMPLATES = "\n query ListTemplates(\n $first: Int\n $after: String\n $category: BigConsoleDashboardCategory\n $search: String\n $isFeatured: Boolean\n ) {\n listTemplates(\n first: $first\n after: $after\n category: $category\n search: $search\n isFeatured: $isFeatured\n ) {\n edges {\n node {\n id\n name\n description\n category\n isFeatured\n status\n createdAt\n }\n }\n pageInfo { hasNextPage endCursor }\n totalCount\n }\n }\n";
42
43
  export declare const GET_TEMPLATE = "\n query GetTemplate($id: ID!) {\n getTemplate(id: $id) {\n id\n name\n description\n category\n isFeatured\n status\n createdAt\n }\n }\n";
43
- export declare const GENERATE_DASHBOARD_FROM_PROMPT = "\n mutation GenerateDashboardFromPrompt($input: GenerateDashboardFromPromptInput!) {\n generateDashboardFromPrompt(input: $input) {\n id\n status\n prompt\n createdAt\n }\n }\n";
44
+ export declare const GENERATE_DASHBOARD_FROM_PROMPT = "\n mutation GenerateDashboardFromPrompt($input: BigConsoleGenerateDashboardFromPromptInput!) {\n generateDashboardFromPrompt(input: $input) {\n id\n status\n prompt\n createdAt\n }\n }\n";
44
45
  export declare const LIST_PROMPT_GENERATIONS = "\n query ListPromptGenerations($first: Int, $after: String, $status: PromptGenerationStatus) {\n listPromptGenerations(first: $first, after: $after, status: $status) {\n edges {\n node {\n id\n status\n prompt\n createdAt\n }\n }\n pageInfo { hasNextPage endCursor }\n totalCount\n }\n }\n";
45
46
  export declare const GET_PROMPT_GENERATION = "\n query GetPromptGeneration($id: ID!) {\n getPromptGeneration(id: $id) {\n id\n status\n prompt\n createdAt\n }\n }\n";
46
- export declare const CANCEL_PROMPT_GENERATION = "\n mutation CancelPromptGeneration($id: ID!) {\n cancelPromptGeneration(id: $id) {\n id\n status\n }\n }\n";
47
+ export declare const CANCEL_PROMPT_GENERATION = "\n mutation CancelPromptGeneration($generationId: ID!) {\n cancelPromptGeneration(generationId: $generationId) {\n id\n status\n }\n }\n";
47
48
  export declare const LIST_WORKFLOWS = "\n query ListWorkflows($filter: FluidGridsWorkflowFilterInput) {\n workflows(filter: $filter) {\n id\n name\n key\n description\n status\n executionCount\n linkedDataSinkCount\n lastExecutedAt\n createdAt\n updatedAt\n }\n }\n";
48
49
  export declare const GET_WORKFLOW = "\n query GetWorkflow($id: ID!) {\n workflow(id: $id) {\n id\n name\n key\n description\n status\n executionCount\n linkedDataSinkCount\n externalWorkflowId\n lastExecutionId\n lastExecutedAt\n webhookUrl\n createdAt\n updatedAt\n }\n }\n";