@burdenoff/bigconsole-cli 2026.1029.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.
@@ -1,142 +0,0 @@
1
- /**
2
- * pipeline commands — list, 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, formatStatus, truncate } from '../utils/output.js';
7
- export function registerPipelineCommands(program) {
8
- const p = program.command('pipeline').description('Pipeline operations');
9
- p.command('list')
10
- .description('List pipelines in the current workspace (scoped to a console)')
11
- .requiredOption('--console <id>', 'Console (dashboard) ID pipelines belong to')
12
- .option('--status <s>', 'Filter by status (active, draft, error, inactive)')
13
- .option('--first <n>', 'Page size', (v) => parseInt(v, 10), 25)
14
- .option('--after <cursor>', 'Cursor for next page')
15
- .option('-f, --format <format>', 'Output format')
16
- .action(async (o) => {
17
- try {
18
- // Canonical PipelineStatus enum values are lowercase — match schema exactly.
19
- const VALID_STATUSES = new Set(['active', 'draft', 'error', 'inactive']);
20
- const filter = { consoleId: o.console };
21
- if (o.status) {
22
- const s = o.status.toLowerCase();
23
- if (!VALID_STATUSES.has(s)) {
24
- printError(`Invalid --status "${o.status}". Valid: ${[...VALID_STATUSES].join(', ')}`);
25
- process.exit(1);
26
- }
27
- filter.status = s;
28
- }
29
- const d = await execWorkspace(ops.LIST_PIPELINES, { first: o.first ?? 25, after: o.after, filter });
30
- const items = d.listPipelines.edges.map((e) => e.node);
31
- formatAndOutput(o, items, {
32
- headers: ['ID', 'Name', 'Status', 'Version', 'Last Executed', 'Updated'],
33
- rows: items.map((x) => [
34
- truncate(x.id, 14),
35
- truncate(x.name, 28),
36
- formatStatus(x.status),
37
- String(x.version),
38
- formatDate(x.lastExecutedAt),
39
- formatDate(x.updatedAt),
40
- ]),
41
- });
42
- }
43
- catch (e) {
44
- handleCommandError(e);
45
- }
46
- });
47
- p.command('get')
48
- .description('Get a pipeline by ID (includes stages)')
49
- .argument('<id>')
50
- .option('-f, --format <format>', 'Output format')
51
- .action(async (id, o) => {
52
- try {
53
- const d = await execWorkspace(ops.GET_PIPELINE, { id });
54
- if (!d.pipeline) {
55
- printError('Pipeline not found');
56
- process.exit(1);
57
- }
58
- formatAndOutput(o, d.pipeline);
59
- }
60
- catch (e) {
61
- handleCommandError(e);
62
- }
63
- });
64
- p.command('delete')
65
- .description('Delete a pipeline')
66
- .argument('<id>')
67
- .action(async (id) => {
68
- try {
69
- await execWorkspace(ops.DELETE_PIPELINE, { id });
70
- printSuccess(`Deleted pipeline ${id}`);
71
- }
72
- catch (e) {
73
- handleCommandError(e);
74
- }
75
- });
76
- p.command('create')
77
- .description('Create a pipeline')
78
- .requiredOption('--name <name>')
79
- .requiredOption('--console <id>', 'Parent dashboard/console ID')
80
- .option('--description <d>')
81
- .option('--status <s>', 'Initial status (DRAFT | ACTIVE | ...)')
82
- .option('--stages-file <path>', 'JSON file with array of CreatePipelineStageInput')
83
- .option('--stages-json <json>', 'Inline JSON array for stages')
84
- .option('-f, --format <format>', 'Output format')
85
- .action(async (o) => {
86
- try {
87
- const stages = loadJsonInput(o.stagesFile, o.stagesJson);
88
- const input = {
89
- name: o.name,
90
- consoleId: o.console,
91
- };
92
- if (o.description)
93
- input.description = o.description;
94
- if (o.status)
95
- input.status = o.status.toUpperCase();
96
- if (stages)
97
- input.stages = stages;
98
- const d = await execWorkspace(ops.CREATE_PIPELINE, {
99
- input,
100
- });
101
- printSuccess(`Created pipeline: ${d.createPipeline.name} (${d.createPipeline.id})`);
102
- formatAndOutput(o, d.createPipeline);
103
- }
104
- catch (e) {
105
- handleCommandError(e);
106
- }
107
- });
108
- p.command('update')
109
- .description('Update a pipeline (requires current version)')
110
- .argument('<id>')
111
- .requiredOption('--version <n>', 'Current pipeline version', (v) => parseInt(v, 10))
112
- .option('--name <n>')
113
- .option('--description <d>')
114
- .option('--status <s>')
115
- .option('--stages-file <path>', 'JSON file with stages array to replace existing stages')
116
- .option('--stages-json <json>', 'Inline JSON stages array')
117
- .option('-f, --format <format>', 'Output format')
118
- .action(async (id, o) => {
119
- try {
120
- const stages = loadJsonInput(o.stagesFile, o.stagesJson);
121
- const input = {};
122
- if (o.name)
123
- input.name = o.name;
124
- if (o.description !== undefined)
125
- input.description = o.description;
126
- if (o.status)
127
- input.status = o.status.toUpperCase();
128
- if (stages)
129
- input.stages = stages;
130
- const d = await execWorkspace(ops.UPDATE_PIPELINE, {
131
- id,
132
- version: o.version,
133
- input,
134
- });
135
- printSuccess(`Updated pipeline ${id} (now v${d.updatePipeline.version})`);
136
- formatAndOutput(o, d.updatePipeline);
137
- }
138
- catch (e) {
139
- handleCommandError(e);
140
- }
141
- });
142
- }
@@ -1,84 +0,0 @@
1
- /**
2
- * prompt commands — AI-driven dashboard generation (generate, list, get, cancel).
3
- */
4
- import * as ops from '../graphql/operations.js';
5
- import { execWorkspace, formatAndOutput, handleCommandError, loadJsonInput } from './base.js';
6
- import { printSuccess, printError, formatDate, formatStatus, truncate } from '../utils/output.js';
7
- export function registerPromptCommands(program) {
8
- const p = program.command('prompt').description('AI-driven dashboard generation');
9
- p.command('generate')
10
- .description('Start a new dashboard generation from a natural-language prompt')
11
- .requiredOption('--prompt <text>', 'The natural-language prompt')
12
- .option('--config-file <path>', 'Optional JSON with generation config')
13
- .option('--config-json <json>', 'Optional inline JSON with generation config')
14
- .option('-f, --format <format>', 'Output format')
15
- .action(async (o) => {
16
- try {
17
- const extra = loadJsonInput(o.configFile, o.configJson) ?? {};
18
- const input = { prompt: o.prompt, ...extra };
19
- const d = await execWorkspace(ops.GENERATE_DASHBOARD_FROM_PROMPT, { input });
20
- printSuccess(`Generation queued: ${d.generateDashboardFromPrompt.id}`);
21
- formatAndOutput(o, d.generateDashboardFromPrompt);
22
- }
23
- catch (e) {
24
- handleCommandError(e);
25
- }
26
- });
27
- p.command('list')
28
- .description('List prompt generations')
29
- .option('--status <s>', 'Filter by status')
30
- .option('--first <n>', 'Page size', (v) => parseInt(v, 10), 25)
31
- .option('--after <cursor>')
32
- .option('-f, --format <format>', 'Output format')
33
- .action(async (o) => {
34
- try {
35
- const d = await execWorkspace(ops.LIST_PROMPT_GENERATIONS, {
36
- first: o.first ?? 25,
37
- after: o.after,
38
- status: o.status ? o.status.toUpperCase() : undefined,
39
- });
40
- const items = d.listPromptGenerations.edges.map((e) => e.node);
41
- formatAndOutput(o, items, {
42
- headers: ['ID', 'Status', 'Prompt', 'Created'],
43
- rows: items.map((x) => [
44
- truncate(x.id, 14),
45
- formatStatus(x.status),
46
- truncate(x.prompt, 50),
47
- formatDate(x.createdAt),
48
- ]),
49
- });
50
- }
51
- catch (e) {
52
- handleCommandError(e);
53
- }
54
- });
55
- p.command('get')
56
- .description('Get a prompt generation by ID')
57
- .argument('<id>')
58
- .option('-f, --format <format>', 'Output format')
59
- .action(async (id, o) => {
60
- try {
61
- const d = await execWorkspace(ops.GET_PROMPT_GENERATION, { id });
62
- if (!d.getPromptGeneration) {
63
- printError('Prompt generation not found');
64
- process.exit(1);
65
- }
66
- formatAndOutput(o, d.getPromptGeneration);
67
- }
68
- catch (e) {
69
- handleCommandError(e);
70
- }
71
- });
72
- p.command('cancel')
73
- .description('Cancel an in-flight prompt generation')
74
- .argument('<id>')
75
- .action(async (id) => {
76
- try {
77
- const d = await execWorkspace(ops.CANCEL_PROMPT_GENERATION, { generationId: id });
78
- printSuccess(`Cancelled generation ${id} (status: ${d.cancelPromptGeneration.status})`);
79
- }
80
- catch (e) {
81
- handleCommandError(e);
82
- }
83
- });
84
- }
@@ -1,50 +0,0 @@
1
- /**
2
- * share commands — list dashboard shares.
3
- *
4
- * Create / revoke are deferred until we have a clean UX for recipient
5
- * selection (user/app/link token) + expiry.
6
- */
7
- import * as ops from '../graphql/operations.js';
8
- import { execWorkspace, formatAndOutput, handleCommandError } from './base.js';
9
- import { formatDate, truncate } from '../utils/output.js';
10
- export function registerShareCommands(program) {
11
- const sh = program.command('share').description('Dashboard share management');
12
- sh.command('list')
13
- .description('List shares for a dashboard')
14
- .requiredOption('--dashboard <id>', 'Dashboard ID')
15
- .option('--active <bool>', 'Filter by active status')
16
- .option('--permission <p>', 'Filter by permission (VIEW, EDIT, ...)')
17
- .option('--recipient-type <t>', 'USER | APP | LINK')
18
- .option('--first <n>', 'Page size', (v) => parseInt(v, 10), 25)
19
- .option('--after <cursor>')
20
- .option('-f, --format <format>', 'Output format')
21
- .action(async (o) => {
22
- try {
23
- const d = await execWorkspace(ops.LIST_DASHBOARD_SHARES, {
24
- dashboardId: o.dashboard,
25
- first: o.first ?? 25,
26
- after: o.after,
27
- isActive: o.active === undefined
28
- ? undefined
29
- : o.active.toLowerCase() === 'true' || o.active === '1',
30
- permission: o.permission ? o.permission.toUpperCase() : undefined,
31
- recipientType: o.recipientType ? o.recipientType.toUpperCase() : undefined,
32
- });
33
- const items = d.listDashboardShares.edges.map((e) => e.node);
34
- formatAndOutput(o, items, {
35
- headers: ['ID', 'Dashboard', 'Permission', 'Active', 'Expires', 'Created'],
36
- rows: items.map((x) => [
37
- truncate(x.id, 14),
38
- truncate(x.dashboardId, 14),
39
- x.permission,
40
- x.isActive ? 'yes' : 'no',
41
- formatDate(x.expiresAt),
42
- formatDate(x.createdAt),
43
- ]),
44
- });
45
- }
46
- catch (e) {
47
- handleCommandError(e);
48
- }
49
- });
50
- }
@@ -1,62 +0,0 @@
1
- /**
2
- * template commands — list, get dashboard templates.
3
- */
4
- import * as ops from '../graphql/operations.js';
5
- import { execWorkspace, formatAndOutput, handleCommandError } from './base.js';
6
- import { printError, formatDate, truncate } from '../utils/output.js';
7
- export function registerTemplateCommands(program) {
8
- const t = program.command('template').description('Dashboard templates');
9
- t.command('list')
10
- .description('List dashboard templates')
11
- .option('--search <term>')
12
- .option('--category <c>')
13
- .option('--featured', 'Only featured templates')
14
- .option('--first <n>', 'Page size', (v) => parseInt(v, 10), 25)
15
- .option('--after <cursor>')
16
- .option('-f, --format <format>', 'Output format')
17
- .action(async (o) => {
18
- try {
19
- const d = await execWorkspace(ops.LIST_TEMPLATES, {
20
- first: o.first ?? 25,
21
- after: o.after,
22
- category: o.category ? o.category.toUpperCase() : undefined,
23
- search: o.search,
24
- isFeatured: o.featured ?? undefined,
25
- });
26
- const items = d.listTemplates.edges.map((e) => e.node);
27
- formatAndOutput(o, items, {
28
- headers: ['ID', 'Name', 'Category', 'Featured', 'Status', 'Created'],
29
- rows: items.map((x) => [
30
- truncate(x.id, 14),
31
- truncate(x.name, 30),
32
- x.category,
33
- x.isFeatured ? '*' : '',
34
- x.status,
35
- formatDate(x.createdAt),
36
- ]),
37
- });
38
- }
39
- catch (e) {
40
- handleCommandError(e);
41
- }
42
- });
43
- t.command('get')
44
- .description('Get a template by ID')
45
- .argument('<id>')
46
- .option('-f, --format <format>', 'Output format')
47
- .action(async (id, o) => {
48
- try {
49
- const d = await execWorkspace(ops.GET_TEMPLATE, {
50
- id,
51
- });
52
- if (!d.getTemplate) {
53
- printError('Template not found');
54
- process.exit(1);
55
- }
56
- formatAndOutput(o, d.getTemplate);
57
- }
58
- catch (e) {
59
- handleCommandError(e);
60
- }
61
- });
62
- }
@@ -1,164 +0,0 @@
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
- }
@@ -1,54 +0,0 @@
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
- }
@@ -1,95 +0,0 @@
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
- }