@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 +39 -11
- package/dist/auth/device-code.js +20 -15
- package/dist/cli.js +5 -2
- package/dist/commands/auth.js +6 -5
- package/dist/commands/base.d.ts +11 -0
- package/dist/commands/base.js +22 -2
- package/dist/commands/dashboard.js +7 -4
- package/dist/commands/drilldown.js +9 -1
- package/dist/commands/prompt.js +1 -1
- package/dist/graphql/client.d.ts +12 -0
- package/dist/graphql/client.js +15 -35
- package/dist/graphql/operations.d.ts +13 -12
- package/dist/graphql/operations.js +44 -18
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/output.d.ts +29 -7
- package/dist/utils/output.js +113 -62
- package/dist/utils/sanitize.d.ts +79 -0
- package/dist/utils/sanitize.js +347 -0
- package/package.json +39 -32
|
@@ -38,10 +38,10 @@ export const LIST_DASHBOARDS = `
|
|
|
38
38
|
$first: Int
|
|
39
39
|
$after: String
|
|
40
40
|
$search: String
|
|
41
|
-
$visibility:
|
|
42
|
-
$category:
|
|
41
|
+
$visibility: BigConsoleDashboardVisibility
|
|
42
|
+
$category: BigConsoleDashboardCategory
|
|
43
43
|
$includeArchived: Boolean
|
|
44
|
-
$sortBy:
|
|
44
|
+
$sortBy: BigConsoleDashboardSortField
|
|
45
45
|
$sortOrder: SortOrder
|
|
46
46
|
) {
|
|
47
47
|
listDashboards(
|
|
@@ -106,7 +106,7 @@ export const DELETE_DASHBOARD = `
|
|
|
106
106
|
// Widgets (workspace gateway)
|
|
107
107
|
// ---------------------------------------------------------------------------
|
|
108
108
|
export const LIST_WIDGETS = `
|
|
109
|
-
query ListWidgets($pageId: ID!, $first: Int, $after: String, $type:
|
|
109
|
+
query ListWidgets($pageId: ID!, $first: Int, $after: String, $type: BigConsoleWidgetType) {
|
|
110
110
|
listWidgets(pageId: $pageId, first: $first, after: $after, type: $type) {
|
|
111
111
|
edges {
|
|
112
112
|
node {
|
|
@@ -204,12 +204,25 @@ export const GET_DATASINK = `
|
|
|
204
204
|
`;
|
|
205
205
|
export const GET_DATASINK_DATA = `
|
|
206
206
|
query GetDataSinkData($id: ID, $key: String, $dataPath: String, $forceRefresh: Boolean) {
|
|
207
|
-
getDataSinkData(id: $id, key: $key, dataPath: $dataPath, forceRefresh: $forceRefresh)
|
|
207
|
+
getDataSinkData(id: $id, key: $key, dataPath: $dataPath, forceRefresh: $forceRefresh) {
|
|
208
|
+
raw
|
|
209
|
+
extracted
|
|
210
|
+
cached
|
|
211
|
+
fetchedAt
|
|
212
|
+
version
|
|
213
|
+
totalCount
|
|
214
|
+
hasMore
|
|
215
|
+
limit
|
|
216
|
+
offset
|
|
217
|
+
}
|
|
208
218
|
}
|
|
209
219
|
`;
|
|
210
220
|
export const DELETE_DATASINK = `
|
|
211
221
|
mutation DeleteDataSink($id: ID!) {
|
|
212
|
-
deleteDataSink(id: $id)
|
|
222
|
+
deleteDataSink(id: $id) {
|
|
223
|
+
id
|
|
224
|
+
name
|
|
225
|
+
}
|
|
213
226
|
}
|
|
214
227
|
`;
|
|
215
228
|
// ---------------------------------------------------------------------------
|
|
@@ -342,7 +355,7 @@ export const UPDATE_DASHBOARD = `
|
|
|
342
355
|
}
|
|
343
356
|
`;
|
|
344
357
|
export const IMPORT_DASHBOARD = `
|
|
345
|
-
mutation ImportDashboard($input:
|
|
358
|
+
mutation ImportDashboard($input: BigConsoleImportDashboardInput!) {
|
|
346
359
|
importDashboard(input: $input) {
|
|
347
360
|
id
|
|
348
361
|
name
|
|
@@ -354,8 +367,8 @@ export const IMPORT_DASHBOARD = `
|
|
|
354
367
|
}
|
|
355
368
|
`;
|
|
356
369
|
export const EXPORT_DASHBOARD = `
|
|
357
|
-
query ExportDashboard($id: ID!, $
|
|
358
|
-
exportDashboard(id: $id,
|
|
370
|
+
query ExportDashboard($id: ID!, $options: BigConsoleExportDashboardOptions) {
|
|
371
|
+
exportDashboard(id: $id, options: $options)
|
|
359
372
|
}
|
|
360
373
|
`;
|
|
361
374
|
// ---------------------------------------------------------------------------
|
|
@@ -474,15 +487,28 @@ export const GET_WIDGET_ACTIONS = `
|
|
|
474
487
|
id
|
|
475
488
|
widgetId
|
|
476
489
|
type
|
|
490
|
+
name
|
|
477
491
|
label
|
|
478
|
-
|
|
492
|
+
order
|
|
493
|
+
disabled
|
|
494
|
+
hidden
|
|
479
495
|
createdAt
|
|
480
496
|
}
|
|
481
497
|
}
|
|
482
498
|
`;
|
|
483
499
|
export const DELETE_WIDGET_DRILLDOWN = `
|
|
484
|
-
mutation DeleteWidgetDrilldown($
|
|
485
|
-
deleteWidgetDrilldown(
|
|
500
|
+
mutation DeleteWidgetDrilldown($id: ID!) {
|
|
501
|
+
deleteWidgetDrilldown(id: $id)
|
|
502
|
+
}
|
|
503
|
+
`;
|
|
504
|
+
export const GET_WIDGET_DRILLDOWN = `
|
|
505
|
+
query GetWidgetDrilldown($id: ID!) {
|
|
506
|
+
getWidget(id: $id) {
|
|
507
|
+
id
|
|
508
|
+
drilldown {
|
|
509
|
+
id
|
|
510
|
+
}
|
|
511
|
+
}
|
|
486
512
|
}
|
|
487
513
|
`;
|
|
488
514
|
// ---------------------------------------------------------------------------
|
|
@@ -494,8 +520,8 @@ export const LIST_DASHBOARD_SHARES = `
|
|
|
494
520
|
$first: Int
|
|
495
521
|
$after: String
|
|
496
522
|
$isActive: Boolean
|
|
497
|
-
$permission:
|
|
498
|
-
$recipientType:
|
|
523
|
+
$permission: BigConsoleDashboardSharePermission
|
|
524
|
+
$recipientType: BigConsoleDashboardShareRecipientType
|
|
499
525
|
) {
|
|
500
526
|
listDashboardShares(
|
|
501
527
|
dashboardId: $dashboardId
|
|
@@ -527,7 +553,7 @@ export const LIST_TEMPLATES = `
|
|
|
527
553
|
query ListTemplates(
|
|
528
554
|
$first: Int
|
|
529
555
|
$after: String
|
|
530
|
-
$category:
|
|
556
|
+
$category: BigConsoleDashboardCategory
|
|
531
557
|
$search: String
|
|
532
558
|
$isFeatured: Boolean
|
|
533
559
|
) {
|
|
@@ -571,7 +597,7 @@ export const GET_TEMPLATE = `
|
|
|
571
597
|
// Prompt (AI) — generate, list, get, cancel
|
|
572
598
|
// ---------------------------------------------------------------------------
|
|
573
599
|
export const GENERATE_DASHBOARD_FROM_PROMPT = `
|
|
574
|
-
mutation GenerateDashboardFromPrompt($input:
|
|
600
|
+
mutation GenerateDashboardFromPrompt($input: BigConsoleGenerateDashboardFromPromptInput!) {
|
|
575
601
|
generateDashboardFromPrompt(input: $input) {
|
|
576
602
|
id
|
|
577
603
|
status
|
|
@@ -607,8 +633,8 @@ export const GET_PROMPT_GENERATION = `
|
|
|
607
633
|
}
|
|
608
634
|
`;
|
|
609
635
|
export const CANCEL_PROMPT_GENERATION = `
|
|
610
|
-
mutation CancelPromptGeneration($
|
|
611
|
-
cancelPromptGeneration(
|
|
636
|
+
mutation CancelPromptGeneration($generationId: ID!) {
|
|
637
|
+
cancelPromptGeneration(generationId: $generationId) {
|
|
612
638
|
id
|
|
613
639
|
status
|
|
614
640
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* @burdenoff/bigconsole-cli — public API exports.
|
|
3
3
|
*/
|
|
4
4
|
export * from './config/index.js';
|
|
5
|
-
export { workspaceRequest, globalRequest, resetClients } from './graphql/client.js';
|
|
5
|
+
export { workspaceRequest, globalRequest, resetClients, BigConsoleRequestError, } from './graphql/client.js';
|
|
6
6
|
export * from './commands/index.js';
|
package/dist/index.js
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* @burdenoff/bigconsole-cli — public API exports.
|
|
3
3
|
*/
|
|
4
4
|
export * from './config/index.js';
|
|
5
|
-
export { workspaceRequest, globalRequest, resetClients } from './graphql/client.js';
|
|
5
|
+
export { workspaceRequest, globalRequest, resetClients, BigConsoleRequestError, } from './graphql/client.js';
|
|
6
6
|
export * from './commands/index.js';
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
package/dist/utils/output.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Output utilities for CLI display.
|
|
3
3
|
* Provides consistent formatting for both interactive and non-interactive modes.
|
|
4
|
+
*
|
|
5
|
+
* stdout carries only command data (JSON, YAML, tables); status lines
|
|
6
|
+
* (success, info, warning, error) go to stderr so piping stays clean. Remote
|
|
7
|
+
* strings are made inert before they reach the terminal: tables and status
|
|
8
|
+
* lines pass through `sanitizeForTerminal`, while JSON and YAML stay exact
|
|
9
|
+
* data and only use escape forms their own syntax defines.
|
|
4
10
|
*/
|
|
5
11
|
export type OutputFormat = 'json' | 'table' | 'yaml';
|
|
6
12
|
/**
|
|
@@ -13,32 +19,47 @@ export declare function isInteractive(): boolean;
|
|
|
13
19
|
export declare function getFormat(options: {
|
|
14
20
|
format?: string;
|
|
15
21
|
}): OutputFormat;
|
|
22
|
+
/**
|
|
23
|
+
* Serialize data as pretty-printed JSON. Characters in
|
|
24
|
+
* {@link JSON_DISPLAY_ESCAPES} can only occur inside JSON strings, so writing
|
|
25
|
+
* them as `\uXXXX` escapes leaves the parsed value unchanged.
|
|
26
|
+
*/
|
|
27
|
+
export declare function toJson(data: unknown): string;
|
|
16
28
|
/**
|
|
17
29
|
* Print data as formatted JSON.
|
|
18
30
|
*/
|
|
19
31
|
export declare function printJson(data: unknown): void;
|
|
20
32
|
/**
|
|
21
|
-
* Print data as a simple ASCII table.
|
|
33
|
+
* Print data as a simple ASCII table. Headers and cells are sanitized for the
|
|
34
|
+
* terminal (single line, no control sequences) before widths are measured;
|
|
35
|
+
* columns are padded to at most {@link MAX_COLUMN_WIDTH} characters.
|
|
22
36
|
*/
|
|
23
37
|
export declare function printTable(headers: string[], rows: string[][]): void;
|
|
24
38
|
/**
|
|
25
|
-
*
|
|
39
|
+
* Serialize data as YAML with js-yaml, restricted to the JSON schema (no
|
|
40
|
+
* custom tags) and without anchors/aliases. Control characters are escaped by
|
|
41
|
+
* the dumper; bidirectional controls, which YAML treats as printable, are
|
|
42
|
+
* written as `\uXXXX` inside double-quoted scalars.
|
|
43
|
+
*/
|
|
44
|
+
export declare function toYaml(data: unknown): string;
|
|
45
|
+
/**
|
|
46
|
+
* Print data as YAML.
|
|
26
47
|
*/
|
|
27
48
|
export declare function printYaml(data: unknown): void;
|
|
28
49
|
/**
|
|
29
|
-
* Print a success message.
|
|
50
|
+
* Print a success message (stderr).
|
|
30
51
|
*/
|
|
31
52
|
export declare function printSuccess(message: string): void;
|
|
32
53
|
/**
|
|
33
|
-
* Print an error message to stderr.
|
|
54
|
+
* Print an error message to stderr. Credential-shaped values are masked.
|
|
34
55
|
*/
|
|
35
56
|
export declare function printError(message: string): void;
|
|
36
57
|
/**
|
|
37
|
-
* Print a warning message.
|
|
58
|
+
* Print a warning message to stderr. Credential-shaped values are masked.
|
|
38
59
|
*/
|
|
39
60
|
export declare function printWarning(message: string): void;
|
|
40
61
|
/**
|
|
41
|
-
* Print an info message.
|
|
62
|
+
* Print an info message (stderr).
|
|
42
63
|
*/
|
|
43
64
|
export declare function printInfo(message: string): void;
|
|
44
65
|
/**
|
|
@@ -57,6 +78,7 @@ export declare function outputData(format: OutputFormat, data: unknown, tableCon
|
|
|
57
78
|
rows: string[][];
|
|
58
79
|
}): void;
|
|
59
80
|
/**
|
|
60
|
-
*
|
|
81
|
+
* Sanitize a string for the terminal, then truncate it to a maximum length,
|
|
82
|
+
* appending an ellipsis if needed.
|
|
61
83
|
*/
|
|
62
84
|
export declare function truncate(str: string | null | undefined, maxLen: number): string;
|
package/dist/utils/output.js
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Output utilities for CLI display.
|
|
3
3
|
* Provides consistent formatting for both interactive and non-interactive modes.
|
|
4
|
+
*
|
|
5
|
+
* stdout carries only command data (JSON, YAML, tables); status lines
|
|
6
|
+
* (success, info, warning, error) go to stderr so piping stays clean. Remote
|
|
7
|
+
* strings are made inert before they reach the terminal: tables and status
|
|
8
|
+
* lines pass through `sanitizeForTerminal`, while JSON and YAML stay exact
|
|
9
|
+
* data and only use escape forms their own syntax defines.
|
|
4
10
|
*/
|
|
11
|
+
import { DEFAULT_SCALAR_STYLE_RULES, JSON_SCHEMA, SCALAR_STYLE, dump, strTag, } from 'js-yaml';
|
|
12
|
+
import { escapeBidiControls, redactSecrets, sanitizeForTerminal, toUnicodeEscape, } from './sanitize.js';
|
|
13
|
+
/** Widest a table column is padded to; longer cells extend past it. */
|
|
14
|
+
const MAX_COLUMN_WIDTH = 50;
|
|
5
15
|
/**
|
|
6
16
|
* Check if the current terminal session is interactive (TTY).
|
|
7
17
|
*/
|
|
@@ -16,101 +26,140 @@ export function getFormat(options) {
|
|
|
16
26
|
const f = options.format.toLowerCase();
|
|
17
27
|
if (f === 'json' || f === 'table' || f === 'yaml')
|
|
18
28
|
return f;
|
|
29
|
+
if (f === 'yml')
|
|
30
|
+
return 'yaml';
|
|
19
31
|
}
|
|
20
32
|
return isInteractive() ? 'table' : 'json';
|
|
21
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Characters that are valid raw inside JSON strings but act on, or are
|
|
36
|
+
* invisible in, a terminal: DEL, C1 controls, line/paragraph separators and
|
|
37
|
+
* bidirectional formatting controls.
|
|
38
|
+
*/
|
|
39
|
+
const JSON_DISPLAY_ESCAPES = /[\u007f-\u009f\u2028\u2029\u202a-\u202e\u2066-\u2069]/g;
|
|
40
|
+
/**
|
|
41
|
+
* Serialize data as pretty-printed JSON. Characters in
|
|
42
|
+
* {@link JSON_DISPLAY_ESCAPES} can only occur inside JSON strings, so writing
|
|
43
|
+
* them as `\uXXXX` escapes leaves the parsed value unchanged.
|
|
44
|
+
*/
|
|
45
|
+
export function toJson(data) {
|
|
46
|
+
const json = JSON.stringify(data, null, 2) ?? 'null';
|
|
47
|
+
return json.replace(JSON_DISPLAY_ESCAPES, toUnicodeEscape);
|
|
48
|
+
}
|
|
22
49
|
/**
|
|
23
50
|
* Print data as formatted JSON.
|
|
24
51
|
*/
|
|
25
52
|
export function printJson(data) {
|
|
26
|
-
console.log(
|
|
53
|
+
console.log(toJson(data));
|
|
27
54
|
}
|
|
28
55
|
/**
|
|
29
|
-
* Print data as a simple ASCII table.
|
|
56
|
+
* Print data as a simple ASCII table. Headers and cells are sanitized for the
|
|
57
|
+
* terminal (single line, no control sequences) before widths are measured;
|
|
58
|
+
* columns are padded to at most {@link MAX_COLUMN_WIDTH} characters.
|
|
30
59
|
*/
|
|
31
60
|
export function printTable(headers, rows) {
|
|
32
61
|
if (headers.length === 0)
|
|
33
62
|
return;
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const cell = row[i] ?? '';
|
|
38
|
-
if (cell.length > max)
|
|
39
|
-
max = cell.length;
|
|
40
|
-
}
|
|
41
|
-
return Math.min(max, 50);
|
|
42
|
-
});
|
|
63
|
+
const header = headers.map((h) => sanitizeForTerminal(h));
|
|
64
|
+
const body = rows.map((row) => headers.map((_, i) => sanitizeForTerminal(row[i])));
|
|
65
|
+
const colWidths = header.map((h, i) => Math.min(body.reduce((max, row) => Math.max(max, (row[i] ?? '').length), h.length), MAX_COLUMN_WIDTH));
|
|
43
66
|
const separator = colWidths.map((w) => '-'.repeat(w + 2)).join('+');
|
|
44
|
-
const formatRow = (cells) => cells.map((cell, i) => ` ${
|
|
45
|
-
console.log(formatRow(
|
|
67
|
+
const formatRow = (cells) => cells.map((cell, i) => ` ${cell.padEnd(colWidths[i] ?? 0)} `).join('|');
|
|
68
|
+
console.log(formatRow(header));
|
|
46
69
|
console.log(separator);
|
|
47
|
-
for (const row of
|
|
70
|
+
for (const row of body) {
|
|
48
71
|
console.log(formatRow(row));
|
|
49
72
|
}
|
|
50
73
|
}
|
|
74
|
+
function allowsStyle(mask, style) {
|
|
75
|
+
return (mask & (1 << style)) !== 0;
|
|
76
|
+
}
|
|
77
|
+
/** Bidirectional formatting controls (see `escapeBidiControls`). */
|
|
78
|
+
const BIDI_CONTROL = /[\u202a-\u202e\u2066-\u2069]/;
|
|
51
79
|
/**
|
|
52
|
-
*
|
|
80
|
+
* Strings that read back as the same string under the YAML 1.2 JSON and core
|
|
81
|
+
* schemas and under YAML 1.1: a letter first, then only letters, digits,
|
|
82
|
+
* spaces and `_ . / @ + -`, and not one of the words those schemas map to
|
|
83
|
+
* booleans or null.
|
|
53
84
|
*/
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
85
|
+
const UNAMBIGUOUS_PLAIN = /^\p{L}[\p{L}\p{N} _./@+-]*$/u;
|
|
86
|
+
const NON_STRING_WORDS = /^(?:y|n|yes|no|on|off|true|false|null)$/i;
|
|
87
|
+
/**
|
|
88
|
+
* Double-quote any scalar containing a bidirectional formatting control.
|
|
89
|
+
* Double-quoted YAML supports `\uXXXX` escapes, so `toYaml` can then write
|
|
90
|
+
* those characters visibly without changing the parsed value.
|
|
91
|
+
*/
|
|
92
|
+
const doubleQuoteBidiControls = (layout) => {
|
|
93
|
+
if (BIDI_CONTROL.test(layout.node.value))
|
|
94
|
+
layout.style = SCALAR_STYLE.DOUBLE_QUOTED;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Quote string scalars (keys and values) that another YAML parser could read
|
|
98
|
+
* as a number, boolean, null, date or merge key. The JSON schema alone leaves
|
|
99
|
+
* values such as `yes`, `~`, `0x1F`, `2026-01-01` or the empty string plain.
|
|
100
|
+
* Runs after the default rules, so strings with invisible characters are
|
|
101
|
+
* already double-quoted and multi-line strings already use block scalars.
|
|
102
|
+
*/
|
|
103
|
+
const quoteAmbiguousStrings = (layout) => {
|
|
104
|
+
if (layout.style !== SCALAR_STYLE.PLAIN || layout.node.tag !== strTag.tagName)
|
|
57
105
|
return;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
106
|
+
const value = layout.node.value;
|
|
107
|
+
if (UNAMBIGUOUS_PLAIN.test(value) && !NON_STRING_WORDS.test(value))
|
|
108
|
+
return;
|
|
109
|
+
layout.style = allowsStyle(layout.allowedStylesMask, SCALAR_STYLE.SINGLE_QUOTED)
|
|
110
|
+
? SCALAR_STYLE.SINGLE_QUOTED
|
|
111
|
+
: SCALAR_STYLE.DOUBLE_QUOTED;
|
|
112
|
+
};
|
|
113
|
+
const YAML_STYLE_RULES = [
|
|
114
|
+
doubleQuoteBidiControls,
|
|
115
|
+
...Object.values(DEFAULT_SCALAR_STYLE_RULES),
|
|
116
|
+
quoteAmbiguousStrings,
|
|
117
|
+
];
|
|
118
|
+
/**
|
|
119
|
+
* Serialize data as YAML with js-yaml, restricted to the JSON schema (no
|
|
120
|
+
* custom tags) and without anchors/aliases. Control characters are escaped by
|
|
121
|
+
* the dumper; bidirectional controls, which YAML treats as printable, are
|
|
122
|
+
* written as `\uXXXX` inside double-quoted scalars.
|
|
123
|
+
*/
|
|
124
|
+
export function toYaml(data) {
|
|
125
|
+
const yaml = dump(data ?? null, {
|
|
126
|
+
schema: JSON_SCHEMA,
|
|
127
|
+
noRefs: true,
|
|
128
|
+
skipInvalid: true,
|
|
129
|
+
lineWidth: -1,
|
|
130
|
+
scalarStyleRules: YAML_STYLE_RULES,
|
|
131
|
+
});
|
|
132
|
+
return escapeBidiControls(yaml).trimEnd();
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Print data as YAML.
|
|
136
|
+
*/
|
|
137
|
+
export function printYaml(data) {
|
|
138
|
+
console.log(toYaml(data));
|
|
90
139
|
}
|
|
91
140
|
/**
|
|
92
|
-
* Print a success message.
|
|
141
|
+
* Print a success message (stderr).
|
|
93
142
|
*/
|
|
94
143
|
export function printSuccess(message) {
|
|
95
|
-
console.
|
|
144
|
+
console.error(`\u2713 ${sanitizeForTerminal(message)}`);
|
|
96
145
|
}
|
|
97
146
|
/**
|
|
98
|
-
* Print an error message to stderr.
|
|
147
|
+
* Print an error message to stderr. Credential-shaped values are masked.
|
|
99
148
|
*/
|
|
100
149
|
export function printError(message) {
|
|
101
|
-
console.error(`\u2717 ${message}`);
|
|
150
|
+
console.error(`\u2717 ${redactSecrets(sanitizeForTerminal(message))}`);
|
|
102
151
|
}
|
|
103
152
|
/**
|
|
104
|
-
* Print a warning message.
|
|
153
|
+
* Print a warning message to stderr. Credential-shaped values are masked.
|
|
105
154
|
*/
|
|
106
155
|
export function printWarning(message) {
|
|
107
|
-
console.warn(`! ${message}`);
|
|
156
|
+
console.warn(`! ${redactSecrets(sanitizeForTerminal(message))}`);
|
|
108
157
|
}
|
|
109
158
|
/**
|
|
110
|
-
* Print an info message.
|
|
159
|
+
* Print an info message (stderr).
|
|
111
160
|
*/
|
|
112
161
|
export function printInfo(message) {
|
|
113
|
-
console.
|
|
162
|
+
console.error(`\u2139 ${sanitizeForTerminal(message)}`);
|
|
114
163
|
}
|
|
115
164
|
/**
|
|
116
165
|
* Format an ISO date string for display.
|
|
@@ -175,12 +224,14 @@ export function outputData(format, data, tableConfig) {
|
|
|
175
224
|
}
|
|
176
225
|
}
|
|
177
226
|
/**
|
|
178
|
-
*
|
|
227
|
+
* Sanitize a string for the terminal, then truncate it to a maximum length,
|
|
228
|
+
* appending an ellipsis if needed.
|
|
179
229
|
*/
|
|
180
230
|
export function truncate(str, maxLen) {
|
|
181
231
|
if (!str)
|
|
182
232
|
return '-';
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
233
|
+
const clean = sanitizeForTerminal(str);
|
|
234
|
+
if (clean.length <= maxLen)
|
|
235
|
+
return clean;
|
|
236
|
+
return clean.substring(0, maxLen - 3) + '...';
|
|
186
237
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text hygiene for everything the CLI prints.
|
|
3
|
+
*
|
|
4
|
+
* Remote data (resource names, server error messages, HTTP response bodies)
|
|
5
|
+
* ends up in terminals, CI logs and support captures. The helpers here:
|
|
6
|
+
* - keep terminal output inert: escape sequences and control characters are
|
|
7
|
+
* removed, and bidirectional formatting characters are made visible;
|
|
8
|
+
* - mask credential-shaped values (bearer tokens, JWTs, URL credentials and
|
|
9
|
+
* values stored under secret-like keys);
|
|
10
|
+
* - turn any thrown value into a short, single-line message that is built
|
|
11
|
+
* from an allowlist of fields and never includes request variables,
|
|
12
|
+
* request headers or raw response bodies.
|
|
13
|
+
*
|
|
14
|
+
* The module is side-effect free and imports nothing from the rest of the CLI,
|
|
15
|
+
* so every layer can use it (and unit-test module mocks never replace it).
|
|
16
|
+
*/
|
|
17
|
+
/** Longest error message shown to the user. */
|
|
18
|
+
export declare const MAX_ERROR_MESSAGE_LENGTH = 500;
|
|
19
|
+
/** Longest excerpt of an HTTP response body or server message kept in an error. */
|
|
20
|
+
export declare const MAX_HTTP_BODY_EXCERPT_LENGTH = 200;
|
|
21
|
+
export interface TerminalSanitizeOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Keep line structure: CRLF and CR become LF, LF and TAB are kept, and
|
|
24
|
+
* NEL/LS/PS are shown as `\uXXXX`. By default (single-line mode) every line
|
|
25
|
+
* break and TAB becomes a single space.
|
|
26
|
+
*/
|
|
27
|
+
preserveNewlines?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/** `\uXXXX` spelling of a single UTF-16 code unit (valid in JSON and YAML). */
|
|
30
|
+
export declare function toUnicodeEscape(char: string): string;
|
|
31
|
+
/** Make bidirectional formatting controls visible as `\uXXXX` text. */
|
|
32
|
+
export declare function escapeBidiControls(text: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* Make a value safe to print on a terminal.
|
|
35
|
+
*
|
|
36
|
+
* - ANSI/VT escape sequences (CSI, OSC, DCS, SOS, PM, APC and two-character
|
|
37
|
+
* ESC sequences, in 7-bit and 8-bit form) are removed entirely.
|
|
38
|
+
* - Other C0 controls, DEL and C1 controls are removed.
|
|
39
|
+
* - Line breaks and TAB become a space (see {@link TerminalSanitizeOptions}).
|
|
40
|
+
* - Bidirectional formatting controls are shown as `\uXXXX` text so the
|
|
41
|
+
* printed text reads in its stored order.
|
|
42
|
+
*
|
|
43
|
+
* `null`/`undefined` become an empty string. The function is idempotent.
|
|
44
|
+
*/
|
|
45
|
+
export declare function sanitizeForTerminal(value: unknown, options?: TerminalSanitizeOptions): string;
|
|
46
|
+
/**
|
|
47
|
+
* Mask credential-shaped values in free text: values under secret-like keys
|
|
48
|
+
* (`password`, `token`, `refreshToken`, `clientSecret`, `Authorization`, ...),
|
|
49
|
+
* `Bearer` credentials, JWT-shaped strings and credentials embedded in URLs.
|
|
50
|
+
*/
|
|
51
|
+
export declare function redactSecrets(text: string): string;
|
|
52
|
+
/**
|
|
53
|
+
* Reduce untrusted text to one printable, credential-masked line of at most
|
|
54
|
+
* `maxLength` characters. Returns an empty string when nothing is left.
|
|
55
|
+
*/
|
|
56
|
+
export declare function toDisplayLine(value: unknown, maxLength?: number): string;
|
|
57
|
+
/** Short, printable excerpt of an HTTP response body for an error message. */
|
|
58
|
+
export declare function summarizeHttpBody(body: unknown): string;
|
|
59
|
+
/** Safe, user-facing description of a failed request. */
|
|
60
|
+
export interface SafeErrorDescription {
|
|
61
|
+
message: string;
|
|
62
|
+
/** HTTP status of the response, when there was one. */
|
|
63
|
+
status?: number;
|
|
64
|
+
/** Stable GraphQL error code (`extensions.code`), when the server sent one. */
|
|
65
|
+
code?: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Describe a failure of a GraphQL request for display. Network-level
|
|
69
|
+
* `TypeError`s (fetch failures) are reported as network errors; aborted
|
|
70
|
+
* requests as timeouts.
|
|
71
|
+
*/
|
|
72
|
+
export declare function describeRequestError(error: unknown): SafeErrorDescription;
|
|
73
|
+
/**
|
|
74
|
+
* Convert any thrown value into a message that is safe to print: a single
|
|
75
|
+
* line, at most {@link MAX_ERROR_MESSAGE_LENGTH} characters, free of terminal
|
|
76
|
+
* control sequences and with credential-shaped values masked. Response-bearing
|
|
77
|
+
* errors are described from an allowlist of fields only.
|
|
78
|
+
*/
|
|
79
|
+
export declare function toSafeErrorMessage(error: unknown): string;
|