@bussolabs/closeyourit-cli 0.0.21 → 0.0.25

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 (38) hide show
  1. package/dist/base.d.ts +4 -0
  2. package/dist/base.js +5 -1
  3. package/dist/commands/agents/list.js +1 -1
  4. package/dist/commands/agents/runs/list.js +1 -1
  5. package/dist/commands/cron-monitors/list.js +1 -1
  6. package/dist/commands/errors/list.js +1 -1
  7. package/dist/commands/ideas/list.js +1 -1
  8. package/dist/commands/ideas/show.js +3 -3
  9. package/dist/commands/kb/ask.js +1 -1
  10. package/dist/commands/monitors/list.js +1 -1
  11. package/dist/commands/run.d.ts +12 -0
  12. package/dist/commands/run.js +33 -0
  13. package/dist/commands/secrets/delete.d.ts +13 -0
  14. package/dist/commands/secrets/delete.js +26 -0
  15. package/dist/commands/secrets/download.d.ts +12 -0
  16. package/dist/commands/secrets/download.js +41 -0
  17. package/dist/commands/secrets/get.d.ts +13 -0
  18. package/dist/commands/secrets/get.js +30 -0
  19. package/dist/commands/secrets/import.d.ts +22 -0
  20. package/dist/commands/secrets/import.js +71 -0
  21. package/dist/commands/secrets/list.d.ts +10 -0
  22. package/dist/commands/secrets/list.js +32 -0
  23. package/dist/commands/secrets/set.d.ts +15 -0
  24. package/dist/commands/secrets/set.js +36 -0
  25. package/dist/commands/secrets/sync.d.ts +9 -0
  26. package/dist/commands/secrets/sync.js +20 -0
  27. package/dist/commands/servers/list.js +1 -1
  28. package/dist/commands/tickets/list.js +1 -1
  29. package/dist/commands/tickets/show.js +8 -8
  30. package/dist/commands/tokens/list.js +1 -1
  31. package/dist/lib/output.d.ts +17 -1
  32. package/dist/lib/output.js +140 -5
  33. package/dist/lib/stdin.d.ts +3 -0
  34. package/dist/lib/stdin.js +11 -0
  35. package/dist/lib/subprocess.d.ts +6 -0
  36. package/dist/lib/subprocess.js +16 -0
  37. package/oclif.manifest.json +2315 -1857
  38. package/package.json +4 -1
@@ -11,7 +11,23 @@ export declare function sanitize(value: unknown): string;
11
11
  * Every other C0/C1 control char is still defused per line.
12
12
  */
13
13
  export declare function sanitizeMultiline(value: unknown): string;
14
+ /** Terminal display width of a string (wide code points count as 2, zero-width joiners/VS as 0). */
15
+ export declare function displayWidth(value: string): number;
14
16
  /** Minimal dependency-free table renderer for human output. */
15
17
  export declare function renderTable(headers: string[], rows: string[][]): string;
16
- /** Pretty-print the scalar fields of a record as `key: value` lines. */
18
+ /**
19
+ * Status dots make problems scannable at a glance. Four fixed-width circles (no variation
20
+ * selectors, so table columns stay aligned): down=🔴, warn=🟡, ok=🟢, neutral=⚪.
21
+ */
22
+ export type StatusTone = 'down' | 'warn' | 'ok' | 'neutral';
23
+ /**
24
+ * Status dot for a value. With `color` (a Tailwind family name) it uses the colour map — the stable
25
+ * token for org-renamed status/priority; otherwise the monitoring string-enum map. Unknown → neutral.
26
+ */
27
+ export declare function statusDot(value: unknown, color?: string): string;
28
+ /** A table cell with its status dot prefixed (the emoji survives `sanitize`). */
29
+ export declare function statusCell(value: unknown, color?: string): string;
30
+ /** A section heading: optional emoji + title, underlined with dashes sized to the title. */
31
+ export declare function section(title: string, icon?: string): string;
32
+ /** Pretty-print the scalar fields of a record as `key: value` lines (status keys get a dot). */
17
33
  export declare function renderRecord(obj: Record<string, unknown>): string;
@@ -2,7 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.sanitize = sanitize;
4
4
  exports.sanitizeMultiline = sanitizeMultiline;
5
+ exports.displayWidth = displayWidth;
5
6
  exports.renderTable = renderTable;
7
+ exports.statusDot = statusDot;
8
+ exports.statusCell = statusCell;
9
+ exports.section = section;
6
10
  exports.renderRecord = renderRecord;
7
11
  /**
8
12
  * Neutralize terminal control sequences (CWE-150) in server-supplied strings.
@@ -29,6 +33,32 @@ function sanitizeMultiline(value) {
29
33
  .map((line) => sanitize(line))
30
34
  .join('\n');
31
35
  }
36
+ // Code-point ranges that render as 2 terminal columns (CJK + emoji + symbols). Emoji like 🔴 have a
37
+ // JS `.length` of 2 while ⚪ (U+26AA) has `.length` 1 — yet both render 2-wide, so padding a column by
38
+ // `.length` misaligns rows that mix them. Measuring by code point keeps the status column square.
39
+ const WIDE_RANGES = [
40
+ [0x1100, 0x115f], // Hangul Jamo
41
+ [0x2600, 0x27bf], // Misc symbols + Dingbats (⚪ ⚫ ✅ …)
42
+ [0x2b00, 0x2bff], // Misc symbols and arrows
43
+ [0x2e80, 0xa4cf], // CJK & friends
44
+ [0xac00, 0xd7a3], // Hangul syllables
45
+ [0xf900, 0xfaff], // CJK compatibility ideographs
46
+ [0xfe30, 0xfe4f], // CJK compatibility forms
47
+ [0xff00, 0xff60], // Fullwidth forms
48
+ [0xffe0, 0xffe6], // Fullwidth signs
49
+ [0x1f000, 0x1faff], // emoji & pictographs (🔴 🟡 🟢 …)
50
+ ];
51
+ /** Terminal display width of a string (wide code points count as 2, zero-width joiners/VS as 0). */
52
+ function displayWidth(value) {
53
+ let width = 0;
54
+ for (const ch of value) {
55
+ const cp = ch.codePointAt(0) ?? 0;
56
+ if (cp === 0x200d || (cp >= 0xfe00 && cp <= 0xfe0f) || (cp >= 0x0300 && cp <= 0x036f))
57
+ continue;
58
+ width += WIDE_RANGES.some(([lo, hi]) => cp >= lo && cp <= hi) ? 2 : 1;
59
+ }
60
+ return width;
61
+ }
32
62
  /** Minimal dependency-free table renderer for human output. */
33
63
  function renderTable(headers, rows) {
34
64
  const safeHeaders = headers.map(sanitize);
@@ -36,15 +66,120 @@ function renderTable(headers, rows) {
36
66
  if (safeRows.length === 0) {
37
67
  return `${safeHeaders.join(' ')}\n(no results)`;
38
68
  }
39
- const widths = safeHeaders.map((header, col) => Math.max(header.length, ...safeRows.map((row) => (row[col] ?? '').length)));
40
- const line = (cols) => cols.map((cell, col) => (cell ?? '').padEnd(widths[col])).join(' ').trimEnd();
69
+ const widths = safeHeaders.map((header, col) => Math.max(displayWidth(header), ...safeRows.map((row) => displayWidth(row[col] ?? ''))));
70
+ const pad = (cell, col) => cell + ' '.repeat(Math.max(0, widths[col] - displayWidth(cell)));
71
+ const line = (cols) => cols.map((cell, col) => pad(cell ?? '', col)).join(' ').trimEnd();
41
72
  const separator = widths.map((width) => '-'.repeat(width));
42
73
  return [line(safeHeaders), line(separator), ...safeRows.map(line)].join('\n');
43
74
  }
44
- /** Pretty-print the scalar fields of a record as `key: value` lines. */
75
+ const TONE_DOT = {
76
+ down: '🔴',
77
+ neutral: '⚪',
78
+ ok: '🟢',
79
+ warn: '🟡',
80
+ };
81
+ /**
82
+ * Stable enum strings of the monitoring domains → tone (keys are lowercase). These come straight
83
+ * from the model enums and never get org-renamed, so the CLI maps them without any backend help.
84
+ * Caveats baked in: agent-run failure is `fail` (not `failed`); a paused monitor/host is neutral.
85
+ */
86
+ const STATUS_TONE = {
87
+ // ideas
88
+ archived: 'neutral',
89
+ // errors — status
90
+ ignored: 'neutral',
91
+ resolved: 'ok',
92
+ unresolved: 'down',
93
+ // errors — level
94
+ debug: 'neutral',
95
+ error: 'down',
96
+ fatal: 'down',
97
+ info: 'ok',
98
+ warning: 'warn',
99
+ // uptime monitor + server host
100
+ down: 'down',
101
+ paused: 'neutral',
102
+ pending: 'warn',
103
+ unknown: 'neutral',
104
+ up: 'ok',
105
+ // cron monitor
106
+ late: 'warn',
107
+ missed: 'down',
108
+ ok: 'ok',
109
+ // ideas
110
+ converted: 'ok',
111
+ open: 'warn',
112
+ // incident phase
113
+ detected: 'down',
114
+ fixing: 'warn',
115
+ investigating: 'warn',
116
+ monitoring: 'warn',
117
+ // agent run
118
+ fail: 'down',
119
+ running: 'warn',
120
+ success: 'ok',
121
+ // token
122
+ active: 'ok',
123
+ expired: 'neutral',
124
+ revoked: 'neutral',
125
+ };
126
+ /**
127
+ * Badge colour (Tailwind family name) → tone. Ticket status/priority labels are org-customisable,
128
+ * so the serializer ships the badge colour as a stable token and the CLI translates it here —
129
+ * identical to what the web badge shows. Unknown colour → falls back to the value string map.
130
+ */
131
+ const COLOR_TONE = {
132
+ amber: 'warn',
133
+ emerald: 'ok',
134
+ gray: 'neutral',
135
+ green: 'ok',
136
+ indigo: 'warn',
137
+ orange: 'down',
138
+ red: 'down',
139
+ sky: 'ok',
140
+ teal: 'ok',
141
+ violet: 'warn',
142
+ };
143
+ /**
144
+ * Status dot for a value. With `color` (a Tailwind family name) it uses the colour map — the stable
145
+ * token for org-renamed status/priority; otherwise the monitoring string-enum map. Unknown → neutral.
146
+ */
147
+ function statusDot(value, color) {
148
+ const tone = (color ? COLOR_TONE[color.trim().toLowerCase()] : undefined) ??
149
+ STATUS_TONE[String(value ?? '').trim().toLowerCase()] ??
150
+ 'neutral';
151
+ return TONE_DOT[tone];
152
+ }
153
+ /** A table cell with its status dot prefixed (the emoji survives `sanitize`). */
154
+ function statusCell(value, color) {
155
+ return `${statusDot(value, color)} ${value ?? ''}`;
156
+ }
157
+ /** A section heading: optional emoji + title, underlined with dashes sized to the title. */
158
+ function section(title, icon) {
159
+ return `\n${icon ? `${icon} ` : ''}${title}\n${'-'.repeat(title.length)}`;
160
+ }
161
+ // Record keys whose value is a status/level/phase → get a dot. Keys with a `<key>_color` sibling
162
+ // (ticket status/priority) dot by colour; the enum-string keys below dot by the string map.
163
+ const ENUM_STATUS_KEYS = new Set(['status', 'current_status', 'last_run_status', 'level', 'phase']);
164
+ // Metadata keys used only to pick a dot colour — never printed as their own line.
165
+ const HIDDEN_KEYS = new Set(['status_color', 'priority_color']);
166
+ /** The dot prefix (with trailing space) for a record line, or '' when the key isn't status-like. */
167
+ function recordDot(key, value, obj) {
168
+ const color = obj[`${key}_color`];
169
+ if (typeof color === 'string' && color)
170
+ return `${statusDot(value, color)} `;
171
+ if (ENUM_STATUS_KEYS.has(key))
172
+ return `${statusDot(value)} `;
173
+ return '';
174
+ }
175
+ /** Pretty-print the scalar fields of a record as `key: value` lines (status keys get a dot). */
45
176
  function renderRecord(obj) {
46
177
  const lines = Object.entries(obj)
47
- .filter(([, value]) => value === null || ['string', 'number', 'boolean'].includes(typeof value))
48
- .map(([key, value]) => `${sanitize(key)}: ${value === null ? '-' : sanitize(value)}`);
178
+ .filter(([key, value]) => !HIDDEN_KEYS.has(key) && (value === null || ['string', 'number', 'boolean'].includes(typeof value)))
179
+ .map(([key, value]) => {
180
+ if (value === null)
181
+ return `${sanitize(key)}: -`;
182
+ return `${sanitize(key)}: ${recordDot(key, value, obj)}${sanitize(value)}`;
183
+ });
49
184
  return lines.length > 0 ? lines.join('\n') : JSON.stringify(obj, null, 2);
50
185
  }
@@ -0,0 +1,3 @@
1
+ /** Read a whole stream (default: process.stdin) into a UTF-8 string. Used by `secrets import` to accept
2
+ * piped input, e.g. `doppler secrets download --format json | cyi secrets import --format json`. */
3
+ export declare function readStream(stream?: AsyncIterable<Buffer | string>): Promise<string>;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readStream = readStream;
4
+ /** Read a whole stream (default: process.stdin) into a UTF-8 string. Used by `secrets import` to accept
5
+ * piped input, e.g. `doppler secrets download --format json | cyi secrets import --format json`. */
6
+ async function readStream(stream = process.stdin) {
7
+ const chunks = [];
8
+ for await (const chunk of stream)
9
+ chunks.push(Buffer.from(chunk));
10
+ return Buffer.concat(chunks).toString('utf8');
11
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Run a child process inheriting stdio, resolving with its exit code. Unlike `openUrl` (browser.ts)
3
+ * this is NOT detached/unref'd: `cyi run` must stay attached and propagate the child's exit code
4
+ * (like `doppler run`). A terminating signal maps to a non-zero code.
5
+ */
6
+ export declare function runCommand(command: string, args: string[], env: NodeJS.ProcessEnv): Promise<number>;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runCommand = runCommand;
4
+ const node_child_process_1 = require("node:child_process");
5
+ /**
6
+ * Run a child process inheriting stdio, resolving with its exit code. Unlike `openUrl` (browser.ts)
7
+ * this is NOT detached/unref'd: `cyi run` must stay attached and propagate the child's exit code
8
+ * (like `doppler run`). A terminating signal maps to a non-zero code.
9
+ */
10
+ function runCommand(command, args, env) {
11
+ return new Promise((resolve, reject) => {
12
+ const child = (0, node_child_process_1.spawn)(command, args, { stdio: 'inherit', env });
13
+ child.on('error', reject);
14
+ child.on('exit', (code, signal) => resolve(code ?? (signal ? 1 : 0)));
15
+ });
16
+ }