@burdenoff/bigconsole-cli 2026.802.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.
Files changed (53) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +235 -0
  3. package/dist/auth/device-code.d.ts +66 -0
  4. package/dist/auth/device-code.js +239 -0
  5. package/dist/auth/store.d.ts +46 -0
  6. package/dist/auth/store.js +90 -0
  7. package/dist/cli.d.ts +5 -0
  8. package/dist/cli.js +126 -0
  9. package/dist/commands/auth.d.ts +5 -0
  10. package/dist/commands/auth.js +352 -0
  11. package/dist/commands/base.d.ts +47 -0
  12. package/dist/commands/base.js +100 -0
  13. package/dist/commands/cli-config.d.ts +6 -0
  14. package/dist/commands/cli-config.js +79 -0
  15. package/dist/commands/dashboard.d.ts +5 -0
  16. package/dist/commands/dashboard.js +220 -0
  17. package/dist/commands/datasink.d.ts +5 -0
  18. package/dist/commands/datasink.js +175 -0
  19. package/dist/commands/drilldown.d.ts +9 -0
  20. package/dist/commands/drilldown.js +47 -0
  21. package/dist/commands/index.d.ts +13 -0
  22. package/dist/commands/index.js +13 -0
  23. package/dist/commands/parser.d.ts +5 -0
  24. package/dist/commands/parser.js +181 -0
  25. package/dist/commands/pipeline.d.ts +5 -0
  26. package/dist/commands/pipeline.js +142 -0
  27. package/dist/commands/prompt.d.ts +5 -0
  28. package/dist/commands/prompt.js +84 -0
  29. package/dist/commands/share.d.ts +8 -0
  30. package/dist/commands/share.js +50 -0
  31. package/dist/commands/template.d.ts +5 -0
  32. package/dist/commands/template.js +62 -0
  33. package/dist/commands/widget.d.ts +5 -0
  34. package/dist/commands/widget.js +164 -0
  35. package/dist/commands/workflow.d.ts +9 -0
  36. package/dist/commands/workflow.js +54 -0
  37. package/dist/commands/workspace.d.ts +9 -0
  38. package/dist/commands/workspace.js +95 -0
  39. package/dist/config/index.d.ts +72 -0
  40. package/dist/config/index.js +151 -0
  41. package/dist/graphql/client.d.ts +26 -0
  42. package/dist/graphql/client.js +274 -0
  43. package/dist/graphql/operations.d.ts +48 -0
  44. package/dist/graphql/operations.js +654 -0
  45. package/dist/index.d.ts +6 -0
  46. package/dist/index.js +6 -0
  47. package/dist/types/index.d.ts +30 -0
  48. package/dist/types/index.js +7 -0
  49. package/dist/utils/index.d.ts +1 -0
  50. package/dist/utils/index.js +1 -0
  51. package/dist/utils/output.d.ts +62 -0
  52. package/dist/utils/output.js +186 -0
  53. package/package.json +116 -0
@@ -0,0 +1,164 @@
1
+ /**
2
+ * widget commands — list by page, get, delete.
3
+ */
4
+ import * as ops from '../graphql/operations.js';
5
+ import { execWorkspace, formatAndOutput, handleCommandError, loadJsonInput } from './base.js';
6
+ import { printSuccess, printError, formatDate, truncate } from '../utils/output.js';
7
+ export function registerWidgetCommands(program) {
8
+ const w = program.command('widget').description('Widget operations');
9
+ w.command('list')
10
+ .description('List widgets on a dashboard page')
11
+ .requiredOption('--page <pageId>', 'Dashboard page ID')
12
+ .option('--type <t>', 'Filter by widget type (METRIC_CARD, CHART, TABLE, ...)')
13
+ .option('--first <n>', 'Page size', (v) => parseInt(v, 10), 50)
14
+ .option('--after <cursor>', 'Cursor for next page')
15
+ .option('-f, --format <format>', 'Output format')
16
+ .action(async (o) => {
17
+ try {
18
+ const d = await execWorkspace(ops.LIST_WIDGETS, {
19
+ pageId: o.page,
20
+ first: o.first ?? 50,
21
+ after: o.after,
22
+ type: o.type ? o.type.toUpperCase() : undefined,
23
+ });
24
+ const items = d.listWidgets.edges.map((e) => e.node);
25
+ formatAndOutput(o, items, {
26
+ headers: ['ID', 'Title', 'Type', 'DataSink', 'Last Refreshed', 'Updated'],
27
+ rows: items.map((x) => [
28
+ truncate(x.id, 14),
29
+ truncate(x.title ?? '-', 24),
30
+ x.type,
31
+ x.dataSinkId ? truncate(x.dataSinkId, 14) : '-',
32
+ formatDate(x.lastRefreshedAt),
33
+ formatDate(x.updatedAt),
34
+ ]),
35
+ });
36
+ }
37
+ catch (e) {
38
+ handleCommandError(e);
39
+ }
40
+ });
41
+ w.command('get')
42
+ .description('Get a widget by ID (includes full config JSON)')
43
+ .argument('<id>')
44
+ .option('-f, --format <format>', 'Output format')
45
+ .action(async (id, o) => {
46
+ try {
47
+ const d = await execWorkspace(ops.GET_WIDGET, { id });
48
+ if (!d.getWidget) {
49
+ printError('Widget not found');
50
+ process.exit(1);
51
+ }
52
+ formatAndOutput(o, d.getWidget);
53
+ }
54
+ catch (e) {
55
+ handleCommandError(e);
56
+ }
57
+ });
58
+ w.command('delete')
59
+ .description('Delete a widget')
60
+ .argument('<id>')
61
+ .action(async (id) => {
62
+ try {
63
+ await execWorkspace(ops.DELETE_WIDGET, { id });
64
+ printSuccess(`Deleted widget ${id}`);
65
+ }
66
+ catch (e) {
67
+ handleCommandError(e);
68
+ }
69
+ });
70
+ w.command('create')
71
+ .description('Create a widget (config is JSON — use --config-file for complex layouts)')
72
+ .requiredOption('--page <id>', 'Parent page ID')
73
+ .requiredOption('--type <t>', 'Widget type (METRIC_CARD, CHART, TABLE, ...)')
74
+ .option('--title <t>', 'Widget title')
75
+ .option('--config-file <path>', 'JSON file with widget config')
76
+ .option('--config-json <json>', 'Inline JSON config')
77
+ .option('--position-file <path>', 'JSON file with position {x,y,w,h}')
78
+ .option('--position-json <json>', 'Inline JSON position')
79
+ .option('--datasink <id>', 'Bound DataSink ID')
80
+ .option('--parser <id>', 'Bound Parser ID')
81
+ .option('--dataset <id>', 'Dataset ID within DataSink')
82
+ .option('--cache-ttl <seconds>', 'Cache TTL override', (v) => parseInt(v, 10))
83
+ .option('-f, --format <format>', 'Output format')
84
+ .action(async (o) => {
85
+ try {
86
+ const config = loadJsonInput(o.configFile, o.configJson);
87
+ const position = loadJsonInput(o.positionFile, o.positionJson);
88
+ if (!config) {
89
+ printError('Widget config is required — provide --config-file or --config-json');
90
+ process.exit(1);
91
+ }
92
+ if (!position) {
93
+ printError('Position is required — provide --position-file or --position-json');
94
+ process.exit(1);
95
+ }
96
+ // `type` and `title` are top-level required fields on
97
+ // BigConsoleCreateWidgetInput. Previously we stuffed title into
98
+ // config and dropped type entirely — the mutation would have
99
+ // failed with a schema validation error on the first real call.
100
+ const input = {
101
+ pageId: o.page,
102
+ type: o.type.toUpperCase(),
103
+ title: o.title ?? '',
104
+ config,
105
+ position,
106
+ };
107
+ if (o.datasink)
108
+ input.dataSinkId = o.datasink;
109
+ if (o.parser)
110
+ input.parserId = o.parser;
111
+ if (o.dataset)
112
+ input.datasetId = o.dataset;
113
+ if (o.cacheTtl !== undefined)
114
+ input.cacheTTL = o.cacheTtl;
115
+ const d = await execWorkspace(ops.CREATE_WIDGET, {
116
+ input,
117
+ });
118
+ printSuccess(`Created widget ${d.createBigConsoleWidget.id}`);
119
+ formatAndOutput(o, d.createBigConsoleWidget);
120
+ }
121
+ catch (e) {
122
+ handleCommandError(e);
123
+ }
124
+ });
125
+ w.command('update')
126
+ .description('Update a widget')
127
+ .argument('<id>')
128
+ .option('--config-file <path>', 'Replace widget config from JSON file')
129
+ .option('--config-json <json>', 'Replace widget config inline')
130
+ .option('--position-file <path>')
131
+ .option('--position-json <json>')
132
+ .option('--datasink <id>')
133
+ .option('--parser <id>')
134
+ .option('--dataset <id>')
135
+ .option('--cache-ttl <seconds>', '', (v) => parseInt(v, 10))
136
+ .option('-f, --format <format>', 'Output format')
137
+ .action(async (id, o) => {
138
+ try {
139
+ const config = loadJsonInput(o.configFile, o.configJson);
140
+ const position = loadJsonInput(o.positionFile, o.positionJson);
141
+ const input = { id };
142
+ if (config !== undefined)
143
+ input.config = config;
144
+ if (position !== undefined)
145
+ input.position = position;
146
+ if (o.datasink)
147
+ input.dataSinkId = o.datasink;
148
+ if (o.parser)
149
+ input.parserId = o.parser;
150
+ if (o.dataset)
151
+ input.datasetId = o.dataset;
152
+ if (o.cacheTtl !== undefined)
153
+ input.cacheTTL = o.cacheTtl;
154
+ const d = await execWorkspace(ops.UPDATE_WIDGET, {
155
+ input,
156
+ });
157
+ printSuccess(`Updated widget ${id}`);
158
+ formatAndOutput(o, d.updateBigConsoleWidget);
159
+ }
160
+ catch (e) {
161
+ handleCommandError(e);
162
+ }
163
+ });
164
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * workflow commands — list, get.
3
+ *
4
+ * Workflows are FluidGrids-backed; CRUD mutations live on the FluidGrids
5
+ * gateway rather than bigconsole-svc. Create/update/delete are not exposed
6
+ * here in Phase 1B to avoid a misleading surface.
7
+ */
8
+ import { Command } from 'commander';
9
+ export declare function registerWorkflowCommands(program: Command): void;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * workflow commands — list, get.
3
+ *
4
+ * Workflows are FluidGrids-backed; CRUD mutations live on the FluidGrids
5
+ * gateway rather than bigconsole-svc. Create/update/delete are not exposed
6
+ * here in Phase 1B to avoid a misleading surface.
7
+ */
8
+ import * as ops from '../graphql/operations.js';
9
+ import { execWorkspace, formatAndOutput, handleCommandError } from './base.js';
10
+ import { printError, formatDate, formatStatus, truncate } from '../utils/output.js';
11
+ export function registerWorkflowCommands(program) {
12
+ const wf = program.command('workflow').description('Workflow operations');
13
+ wf.command('list')
14
+ .description('List workflows linked to BigConsole')
15
+ .option('-f, --format <format>', 'Output format')
16
+ .action(async (o) => {
17
+ try {
18
+ const d = await execWorkspace(ops.LIST_WORKFLOWS, {});
19
+ const items = d.workflows;
20
+ formatAndOutput(o, items, {
21
+ headers: ['ID', 'Name', 'Key', 'Status', 'Execs', 'Sinks', 'Last Run'],
22
+ rows: items.map((x) => [
23
+ truncate(x.id, 14),
24
+ truncate(x.name, 24),
25
+ truncate(x.key, 18),
26
+ formatStatus(x.status),
27
+ String(x.executionCount),
28
+ String(x.linkedDataSinkCount),
29
+ formatDate(x.lastExecutedAt),
30
+ ]),
31
+ });
32
+ }
33
+ catch (e) {
34
+ handleCommandError(e);
35
+ }
36
+ });
37
+ wf.command('get')
38
+ .description('Get a workflow by ID')
39
+ .argument('<id>')
40
+ .option('-f, --format <format>', 'Output format')
41
+ .action(async (id, o) => {
42
+ try {
43
+ const d = await execWorkspace(ops.GET_WORKFLOW, { id });
44
+ if (!d.workflow) {
45
+ printError('Workflow not found');
46
+ process.exit(1);
47
+ }
48
+ formatAndOutput(o, d.workflow);
49
+ }
50
+ catch (e) {
51
+ handleCommandError(e);
52
+ }
53
+ });
54
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * workspace commands — list workspaces, switch current, show current.
3
+ *
4
+ * Workspace selection in this CLI is done at `auth login` time (we call
5
+ * IssueWorkspaceToken then). These commands let a logged-in user switch to
6
+ * a different workspace without re-running the full device-code flow.
7
+ */
8
+ import { Command } from 'commander';
9
+ export declare function registerWorkspaceCommands(program: Command): void;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * workspace commands — list workspaces, switch current, show current.
3
+ *
4
+ * Workspace selection in this CLI is done at `auth login` time (we call
5
+ * IssueWorkspaceToken then). These commands let a logged-in user switch to
6
+ * a different workspace without re-running the full device-code flow.
7
+ */
8
+ import { readConfig } from '../config/index.js';
9
+ import { readAuth, writeAuth } from '../auth/store.js';
10
+ import { fetchWorkspaces, issueWorkspaceToken } from '../auth/device-code.js';
11
+ import { ensureFreshTokens, resetClients } from '../graphql/client.js';
12
+ import { printSuccess, printError, truncate } from '../utils/output.js';
13
+ import { formatAndOutput, handleCommandError, requireAuth } from './base.js';
14
+ export function registerWorkspaceCommands(program) {
15
+ const ws = program.command('workspace').description('Workspace selection and info');
16
+ ws.command('list')
17
+ .description('List workspaces the current user can access')
18
+ .option('-f, --format <format>', 'Output format (json, table, yaml)')
19
+ .action(async (o) => {
20
+ try {
21
+ requireAuth();
22
+ // Refresh platform token before hitting the global endpoint —
23
+ // `fetchWorkspaces` does a raw fetch() and doesn't go through
24
+ // `workspaceRequest`/`globalRequest`, so it otherwise bypasses
25
+ // the auto-refresh path in the GraphQL client.
26
+ await ensureFreshTokens();
27
+ const auth = readAuth();
28
+ const config = readConfig();
29
+ const workspaces = await fetchWorkspaces(config.globalEndpoint, auth.accessToken);
30
+ formatAndOutput(o, workspaces, {
31
+ headers: ['ID', 'Name', 'Slug', 'Org ID', 'Tenant ID', 'Current'],
32
+ rows: workspaces.map((w) => [
33
+ truncate(w.id, 14),
34
+ w.name,
35
+ w.slug,
36
+ truncate(w.organizationId, 14),
37
+ truncate(w.tenantId, 14),
38
+ auth.workspaceId === w.id ? '*' : '',
39
+ ]),
40
+ });
41
+ }
42
+ catch (e) {
43
+ handleCommandError(e);
44
+ }
45
+ });
46
+ ws.command('current')
47
+ .description('Show the currently-selected workspace')
48
+ .option('-f, --format <format>', 'Output format')
49
+ .action((o) => {
50
+ const auth = readAuth();
51
+ if (!auth?.workspaceId) {
52
+ printError('No workspace selected. Run `bigconsole workspace set <id>` or re-login.');
53
+ process.exit(1);
54
+ }
55
+ formatAndOutput(o, {
56
+ workspaceId: auth.workspaceId,
57
+ organizationId: auth.organizationId ?? '-',
58
+ workspaceTokenExpiresAt: auth.workspaceTokenExpiresAt
59
+ ? new Date(auth.workspaceTokenExpiresAt).toISOString()
60
+ : '-',
61
+ });
62
+ });
63
+ ws.command('set')
64
+ .description('Select a workspace and issue a workspace token')
65
+ .argument('<id>', 'Workspace ID')
66
+ .action(async (id) => {
67
+ try {
68
+ requireAuth();
69
+ // Same reasoning as `workspace list`: refresh before the raw
70
+ // `fetchWorkspaces` call so we don't send a near-expiry token.
71
+ await ensureFreshTokens();
72
+ const auth = readAuth();
73
+ const config = readConfig();
74
+ const workspaces = await fetchWorkspaces(config.globalEndpoint, auth.accessToken);
75
+ const ws = workspaces.find((w) => w.id === id);
76
+ if (!ws) {
77
+ printError(`Workspace ${id} not found or not accessible. Run \`bigconsole workspace list\`.`);
78
+ process.exit(1);
79
+ }
80
+ const result = await issueWorkspaceToken(config.workspaceEndpoint, auth.accessToken, ws.id, ws.organizationId);
81
+ writeAuth({
82
+ ...auth,
83
+ workspaceId: ws.id,
84
+ organizationId: ws.organizationId,
85
+ workspaceToken: result.token,
86
+ workspaceTokenExpiresAt: new Date(result.expiresAt).getTime(),
87
+ });
88
+ resetClients();
89
+ printSuccess(`Switched to workspace: ${ws.name} (${ws.id})`);
90
+ }
91
+ catch (e) {
92
+ handleCommandError(e);
93
+ }
94
+ });
95
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * CLI configuration management.
3
+ * Persists CLI-level settings to ~/.config/bigconsole-cli/config.json
4
+ */
5
+ export interface CLIConfig {
6
+ /** Workspace gateway GraphQL endpoint */
7
+ workspaceEndpoint: string;
8
+ /** Global gateway GraphQL endpoint */
9
+ globalEndpoint: string;
10
+ /** OIDC issuer URL for OAuth2 flows */
11
+ oidcIssuer?: string;
12
+ /** OAuth2 client ID */
13
+ clientId?: string;
14
+ /** Default output format */
15
+ defaultFormat?: string;
16
+ /** Enable verbose logging */
17
+ verbose?: boolean;
18
+ /** Auth service endpoint for workspace token exchange */
19
+ authServiceEndpoint?: string;
20
+ }
21
+ /**
22
+ * Well-known client ID for the BigConsole CLI.
23
+ * This is a system app auto-created by global-auth-svc on startup.
24
+ * Same pattern as GitHub CLI (gh), Claude Code, Stripe CLI.
25
+ */
26
+ export declare const DEFAULT_CLIENT_ID = "burdenoff_cli_bigconsole";
27
+ /** Production workspace gateway endpoint */
28
+ export declare const PROD_WORKSPACE_ENDPOINT = "https://graphqlworkspaces.burdenoff.com/workspaces/graphql";
29
+ /** Production global gateway endpoint */
30
+ export declare const PROD_GLOBAL_ENDPOINT = "https://graphql.burdenoff.com/global/graphql";
31
+ export declare const DEFAULT_CONFIG: CLIConfig;
32
+ /**
33
+ * Read the CLI configuration, merging with defaults.
34
+ *
35
+ * Resolution order (later wins):
36
+ * 1. Built-in production defaults
37
+ * 2. Environment variables BIGCONSOLE_WORKSPACE_ENDPOINT, BIGCONSOLE_GLOBAL_ENDPOINT,
38
+ * BIGCONSOLE_OIDC_ISSUER, BIGCONSOLE_CLIENT_ID
39
+ * 3. Values stored in ~/.config/bigconsole-cli/config.json
40
+ */
41
+ export declare function readConfig(): CLIConfig;
42
+ /**
43
+ * Write the CLI configuration to disk.
44
+ */
45
+ export declare function writeConfig(config: CLIConfig): void;
46
+ /**
47
+ * Get a single config value by key.
48
+ */
49
+ export declare function getConfigValue(key: string): string | undefined;
50
+ /**
51
+ * Set a single config value by key.
52
+ */
53
+ export declare function setConfigValue(key: string, value: string): void;
54
+ /**
55
+ * Get the path to the config file.
56
+ */
57
+ export declare function getConfigPath(): string;
58
+ /**
59
+ * Get the config directory path.
60
+ */
61
+ export declare function getConfigDir(): string;
62
+ /**
63
+ * Resolve OIDC configuration with smart defaults.
64
+ * - clientId defaults to the built-in BigConsole CLI system app
65
+ * - oidcIssuer derives from the configured globalEndpoint
66
+ * Users can override both via cli-config if they have a custom OIDC provider.
67
+ */
68
+ export declare function resolveOIDCConfig(): {
69
+ oidcIssuer: string;
70
+ clientId: string;
71
+ };
72
+ export declare const KNOWN_CONFIG_KEYS: readonly ["workspaceEndpoint", "globalEndpoint", "oidcIssuer", "clientId", "defaultFormat", "verbose", "authServiceEndpoint"];
@@ -0,0 +1,151 @@
1
+ /**
2
+ * CLI configuration management.
3
+ * Persists CLI-level settings to ~/.config/bigconsole-cli/config.json
4
+ */
5
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync } from 'fs';
6
+ import { join } from 'path';
7
+ import { homedir } from 'os';
8
+ const CONFIG_DIR = join(homedir(), '.config', 'bigconsole-cli');
9
+ const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
10
+ /**
11
+ * Well-known client ID for the BigConsole CLI.
12
+ * This is a system app auto-created by global-auth-svc on startup.
13
+ * Same pattern as GitHub CLI (gh), Claude Code, Stripe CLI.
14
+ */
15
+ export const DEFAULT_CLIENT_ID = 'burdenoff_cli_bigconsole';
16
+ /** Production OIDC issuer (global gateway) */
17
+ const PROD_OIDC_ISSUER = 'https://graphql.burdenoff.com/global';
18
+ /** Production workspace gateway endpoint */
19
+ export const PROD_WORKSPACE_ENDPOINT = 'https://graphqlworkspaces.burdenoff.com/workspaces/graphql';
20
+ /** Production global gateway endpoint */
21
+ export const PROD_GLOBAL_ENDPOINT = 'https://graphql.burdenoff.com/global/graphql';
22
+ export const DEFAULT_CONFIG = {
23
+ workspaceEndpoint: PROD_WORKSPACE_ENDPOINT,
24
+ globalEndpoint: PROD_GLOBAL_ENDPOINT,
25
+ };
26
+ function ensureConfigDir() {
27
+ if (!existsSync(CONFIG_DIR)) {
28
+ mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
29
+ }
30
+ }
31
+ function getEnvOverrides() {
32
+ const overrides = {};
33
+ if (process.env.BIGCONSOLE_WORKSPACE_ENDPOINT) {
34
+ overrides.workspaceEndpoint = process.env.BIGCONSOLE_WORKSPACE_ENDPOINT;
35
+ }
36
+ if (process.env.BIGCONSOLE_GLOBAL_ENDPOINT) {
37
+ overrides.globalEndpoint = process.env.BIGCONSOLE_GLOBAL_ENDPOINT;
38
+ }
39
+ if (process.env.BIGCONSOLE_OIDC_ISSUER) {
40
+ overrides.oidcIssuer = process.env.BIGCONSOLE_OIDC_ISSUER;
41
+ }
42
+ if (process.env.BIGCONSOLE_CLIENT_ID) {
43
+ overrides.clientId = process.env.BIGCONSOLE_CLIENT_ID;
44
+ }
45
+ return overrides;
46
+ }
47
+ /**
48
+ * Read the CLI configuration, merging with defaults.
49
+ *
50
+ * Resolution order (later wins):
51
+ * 1. Built-in production defaults
52
+ * 2. Environment variables BIGCONSOLE_WORKSPACE_ENDPOINT, BIGCONSOLE_GLOBAL_ENDPOINT,
53
+ * BIGCONSOLE_OIDC_ISSUER, BIGCONSOLE_CLIENT_ID
54
+ * 3. Values stored in ~/.config/bigconsole-cli/config.json
55
+ */
56
+ export function readConfig() {
57
+ try {
58
+ const envOverrides = getEnvOverrides();
59
+ if (!existsSync(CONFIG_FILE))
60
+ return { ...DEFAULT_CONFIG, ...envOverrides };
61
+ const raw = readFileSync(CONFIG_FILE, 'utf8');
62
+ const stored = JSON.parse(raw);
63
+ return { ...DEFAULT_CONFIG, ...envOverrides, ...stored };
64
+ }
65
+ catch {
66
+ return { ...DEFAULT_CONFIG };
67
+ }
68
+ }
69
+ /**
70
+ * Write the CLI configuration to disk.
71
+ */
72
+ export function writeConfig(config) {
73
+ ensureConfigDir();
74
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { encoding: 'utf8' });
75
+ try {
76
+ chmodSync(CONFIG_FILE, 0o600);
77
+ }
78
+ catch {
79
+ // chmodSync may fail on Windows, non-critical
80
+ }
81
+ }
82
+ /**
83
+ * Get a single config value by key.
84
+ */
85
+ export function getConfigValue(key) {
86
+ const config = readConfig();
87
+ return config[key];
88
+ }
89
+ /**
90
+ * Set a single config value by key.
91
+ */
92
+ export function setConfigValue(key, value) {
93
+ const config = readConfig();
94
+ config[key] = value;
95
+ writeConfig(config);
96
+ }
97
+ /**
98
+ * Get the path to the config file.
99
+ */
100
+ export function getConfigPath() {
101
+ return CONFIG_FILE;
102
+ }
103
+ /**
104
+ * Get the config directory path.
105
+ */
106
+ export function getConfigDir() {
107
+ return CONFIG_DIR;
108
+ }
109
+ /**
110
+ * Resolve OIDC configuration with smart defaults.
111
+ * - clientId defaults to the built-in BigConsole CLI system app
112
+ * - oidcIssuer derives from the configured globalEndpoint
113
+ * Users can override both via cli-config if they have a custom OIDC provider.
114
+ */
115
+ export function resolveOIDCConfig() {
116
+ const config = readConfig();
117
+ // Client ID: user override > built-in default
118
+ const clientId = config.clientId || DEFAULT_CLIENT_ID;
119
+ // OIDC issuer: user override > derive from any non-prod endpoint > prod default.
120
+ //
121
+ // Strip the trailing `/graphql` from the endpoint so OIDC routes (device-code,
122
+ // token, etc.) are mounted at the parent path. The second `.replace()` that
123
+ // used to target `/global/graphql$` was dead code — the first replace always
124
+ // strips the trailing `/graphql` first.
125
+ //
126
+ // Deriving from the endpoint also covers staging / self-hosted global
127
+ // gateways, not just localhost: if the user has explicitly pointed
128
+ // `globalEndpoint` somewhere non-prod the issuer should follow, and only the
129
+ // implicit prod endpoint falls back to `PROD_OIDC_ISSUER`.
130
+ let oidcIssuer = config.oidcIssuer;
131
+ if (!oidcIssuer) {
132
+ const globalEndpoint = config.globalEndpoint;
133
+ const isProdEndpoint = globalEndpoint.includes('graphql.burdenoff.com');
134
+ if (isProdEndpoint) {
135
+ oidcIssuer = PROD_OIDC_ISSUER;
136
+ }
137
+ else {
138
+ oidcIssuer = globalEndpoint.replace(/\/graphql$/, '');
139
+ }
140
+ }
141
+ return { oidcIssuer, clientId };
142
+ }
143
+ export const KNOWN_CONFIG_KEYS = [
144
+ 'workspaceEndpoint',
145
+ 'globalEndpoint',
146
+ 'oidcIssuer',
147
+ 'clientId',
148
+ 'defaultFormat',
149
+ 'verbose',
150
+ 'authServiceEndpoint',
151
+ ];
@@ -0,0 +1,26 @@
1
+ /**
2
+ * GraphQL client for CLI operations.
3
+ * Dual-endpoint: workspace gateway (bigconsole ops) + global gateway (auth ops).
4
+ * Auto-injects auth headers, auto-refreshes both platform and workspace tokens.
5
+ */
6
+ import { type RequestDocument, type Variables } from 'graphql-request';
7
+ export declare function ensureFreshTokens(): Promise<void>;
8
+ /**
9
+ * Execute a GraphQL request against the workspace gateway.
10
+ * Auto-refreshes both platform and workspace tokens when they are about to expire.
11
+ */
12
+ export declare function workspaceRequest<T>(document: RequestDocument, variables?: Variables): Promise<T>;
13
+ /**
14
+ * Execute a GraphQL request against the global gateway (auth operations).
15
+ * Does NOT auto-refresh tokens (to prevent loops during refresh itself).
16
+ */
17
+ export declare function globalRequest<T>(document: RequestDocument, variables?: Variables): Promise<T>;
18
+ /**
19
+ * Execute a request against the workspace gateway with ONLY the platform auth token.
20
+ * Used for IssueWorkspaceToken which does not require an existing workspace token.
21
+ */
22
+ export declare function workspaceAuthOnlyRequest<T>(document: RequestDocument, variables?: Variables): Promise<T>;
23
+ /**
24
+ * Reset cached clients (useful after auth changes).
25
+ */
26
+ export declare function resetClients(): void;