@agentuity/cli 0.0.51 → 0.0.53
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/dist/api.js +68 -0
- package/dist/api.js.map +1 -0
- package/dist/auth.js +225 -0
- package/dist/auth.js.map +1 -0
- package/dist/banner.js +35 -0
- package/dist/banner.js.map +1 -0
- package/dist/cli-logger.js +72 -0
- package/dist/cli-logger.js.map +1 -0
- package/dist/cli.js +822 -0
- package/dist/cli.js.map +1 -0
- package/dist/cmd/ai/capabilities/index.js +10 -0
- package/dist/cmd/ai/capabilities/index.js.map +1 -0
- package/dist/cmd/ai/capabilities/show.js +221 -0
- package/dist/cmd/ai/capabilities/show.js.map +1 -0
- package/dist/cmd/ai/index.js +11 -0
- package/dist/cmd/ai/index.js.map +1 -0
- package/dist/cmd/ai/prompt/index.js +10 -0
- package/dist/cmd/ai/prompt/index.js.map +1 -0
- package/dist/cmd/ai/prompt/llm.js +365 -0
- package/dist/cmd/ai/prompt/llm.js.map +1 -0
- package/dist/cmd/ai/schema/index.js +10 -0
- package/dist/cmd/ai/schema/index.js.map +1 -0
- package/dist/cmd/ai/schema/show.js +23 -0
- package/dist/cmd/ai/schema/show.js.map +1 -0
- package/dist/cmd/auth/api.js +85 -0
- package/dist/cmd/auth/api.js.map +1 -0
- package/dist/cmd/auth/index.js +13 -0
- package/dist/cmd/auth/index.js.map +1 -0
- package/dist/cmd/auth/login.js +84 -0
- package/dist/cmd/auth/login.js.map +1 -0
- package/dist/cmd/auth/logout.js +17 -0
- package/dist/cmd/auth/logout.js.map +1 -0
- package/dist/cmd/auth/signup.js +55 -0
- package/dist/cmd/auth/signup.js.map +1 -0
- package/dist/cmd/auth/ssh/add.js +239 -0
- package/dist/cmd/auth/ssh/add.js.map +1 -0
- package/dist/cmd/auth/ssh/api.js +53 -0
- package/dist/cmd/auth/ssh/api.js.map +1 -0
- package/dist/cmd/auth/ssh/delete.js +126 -0
- package/dist/cmd/auth/ssh/delete.js.map +1 -0
- package/dist/cmd/auth/ssh/index.js +11 -0
- package/dist/cmd/auth/ssh/index.js.map +1 -0
- package/dist/cmd/auth/ssh/list.js +70 -0
- package/dist/cmd/auth/ssh/list.js.map +1 -0
- package/dist/cmd/auth/whoami.js +68 -0
- package/dist/cmd/auth/whoami.js.map +1 -0
- package/dist/cmd/build/ast.js +608 -0
- package/dist/cmd/build/ast.js.map +1 -0
- package/dist/cmd/build/ast.test.js +389 -0
- package/dist/cmd/build/ast.test.js.map +1 -0
- package/dist/cmd/build/bundler.js +304 -0
- package/dist/cmd/build/bundler.js.map +1 -0
- package/dist/cmd/build/file.js +10 -0
- package/dist/cmd/build/file.js.map +1 -0
- package/dist/cmd/build/fix-duplicate-exports.js +167 -0
- package/dist/cmd/build/fix-duplicate-exports.js.map +1 -0
- package/dist/cmd/build/fix-duplicate-exports.test.js +300 -0
- package/dist/cmd/build/fix-duplicate-exports.test.js.map +1 -0
- package/dist/cmd/build/index.d.ts.map +1 -1
- package/dist/cmd/build/index.js +81 -0
- package/dist/cmd/build/index.js.map +1 -0
- package/dist/cmd/build/patch/_util.js +42 -0
- package/dist/cmd/build/patch/_util.js.map +1 -0
- package/dist/cmd/build/patch/aisdk.js +65 -0
- package/dist/cmd/build/patch/aisdk.js.map +1 -0
- package/dist/cmd/build/patch/index.js +97 -0
- package/dist/cmd/build/patch/index.js.map +1 -0
- package/dist/cmd/build/patch/llm.js +18 -0
- package/dist/cmd/build/patch/llm.js.map +1 -0
- package/dist/cmd/build/plugin.d.ts.map +1 -1
- package/dist/cmd/build/plugin.js +581 -0
- package/dist/cmd/build/plugin.js.map +1 -0
- package/dist/cmd/cloud/agents/index.js +133 -0
- package/dist/cmd/cloud/agents/index.js.map +1 -0
- package/dist/cmd/cloud/deploy.js +341 -0
- package/dist/cmd/cloud/deploy.js.map +1 -0
- package/dist/cmd/cloud/deployment/index.js +20 -0
- package/dist/cmd/cloud/deployment/index.js.map +1 -0
- package/dist/cmd/cloud/deployment/list.js +89 -0
- package/dist/cmd/cloud/deployment/list.js.map +1 -0
- package/dist/cmd/cloud/deployment/remove.js +60 -0
- package/dist/cmd/cloud/deployment/remove.js.map +1 -0
- package/dist/cmd/cloud/deployment/rollback.js +80 -0
- package/dist/cmd/cloud/deployment/rollback.js.map +1 -0
- package/dist/cmd/cloud/deployment/show.js +106 -0
- package/dist/cmd/cloud/deployment/show.js.map +1 -0
- package/dist/cmd/cloud/deployment/undeploy.js +45 -0
- package/dist/cmd/cloud/deployment/undeploy.js.map +1 -0
- package/dist/cmd/cloud/deployment/utils.js +10 -0
- package/dist/cmd/cloud/deployment/utils.js.map +1 -0
- package/dist/cmd/cloud/domain.js +77 -0
- package/dist/cmd/cloud/domain.js.map +1 -0
- package/dist/cmd/cloud/env/delete.js +50 -0
- package/dist/cmd/cloud/env/delete.js.map +1 -0
- package/dist/cmd/cloud/env/get.js +65 -0
- package/dist/cmd/cloud/env/get.js.map +1 -0
- package/dist/cmd/cloud/env/import.js +113 -0
- package/dist/cmd/cloud/env/import.js.map +1 -0
- package/dist/cmd/cloud/env/index.js +24 -0
- package/dist/cmd/cloud/env/index.js.map +1 -0
- package/dist/cmd/cloud/env/list.js +58 -0
- package/dist/cmd/cloud/env/list.js.map +1 -0
- package/dist/cmd/cloud/env/pull.js +81 -0
- package/dist/cmd/cloud/env/pull.js.map +1 -0
- package/dist/cmd/cloud/env/push.js +61 -0
- package/dist/cmd/cloud/env/push.js.map +1 -0
- package/dist/cmd/cloud/env/set.js +73 -0
- package/dist/cmd/cloud/env/set.js.map +1 -0
- package/dist/cmd/cloud/index.js +31 -0
- package/dist/cmd/cloud/index.js.map +1 -0
- package/dist/cmd/cloud/keyvalue/create-namespace.js +41 -0
- package/dist/cmd/cloud/keyvalue/create-namespace.js.map +1 -0
- package/dist/cmd/cloud/keyvalue/delete-namespace.js +64 -0
- package/dist/cmd/cloud/keyvalue/delete-namespace.js.map +1 -0
- package/dist/cmd/cloud/keyvalue/delete.js +47 -0
- package/dist/cmd/cloud/keyvalue/delete.js.map +1 -0
- package/dist/cmd/cloud/keyvalue/get.js +65 -0
- package/dist/cmd/cloud/keyvalue/get.js.map +1 -0
- package/dist/cmd/cloud/keyvalue/index.js +32 -0
- package/dist/cmd/cloud/keyvalue/index.js.map +1 -0
- package/dist/cmd/cloud/keyvalue/keys.js +50 -0
- package/dist/cmd/cloud/keyvalue/keys.js.map +1 -0
- package/dist/cmd/cloud/keyvalue/list-namespaces.js +37 -0
- package/dist/cmd/cloud/keyvalue/list-namespaces.js.map +1 -0
- package/dist/cmd/cloud/keyvalue/repl.js +277 -0
- package/dist/cmd/cloud/keyvalue/repl.js.map +1 -0
- package/dist/cmd/cloud/keyvalue/search.js +72 -0
- package/dist/cmd/cloud/keyvalue/search.js.map +1 -0
- package/dist/cmd/cloud/keyvalue/set.js +59 -0
- package/dist/cmd/cloud/keyvalue/set.js.map +1 -0
- package/dist/cmd/cloud/keyvalue/stats.js +82 -0
- package/dist/cmd/cloud/keyvalue/stats.js.map +1 -0
- package/dist/cmd/cloud/keyvalue/util.js +19 -0
- package/dist/cmd/cloud/keyvalue/util.js.map +1 -0
- package/dist/cmd/cloud/objectstore/delete-bucket.js +66 -0
- package/dist/cmd/cloud/objectstore/delete-bucket.js.map +1 -0
- package/dist/cmd/cloud/objectstore/delete.js +56 -0
- package/dist/cmd/cloud/objectstore/delete.js.map +1 -0
- package/dist/cmd/cloud/objectstore/get.js +64 -0
- package/dist/cmd/cloud/objectstore/get.js.map +1 -0
- package/dist/cmd/cloud/objectstore/index.js +28 -0
- package/dist/cmd/cloud/objectstore/index.js.map +1 -0
- package/dist/cmd/cloud/objectstore/list-buckets.js +37 -0
- package/dist/cmd/cloud/objectstore/list-buckets.js.map +1 -0
- package/dist/cmd/cloud/objectstore/list-keys.js +52 -0
- package/dist/cmd/cloud/objectstore/list-keys.js.map +1 -0
- package/dist/cmd/cloud/objectstore/put.js +57 -0
- package/dist/cmd/cloud/objectstore/put.js.map +1 -0
- package/dist/cmd/cloud/objectstore/repl.js +219 -0
- package/dist/cmd/cloud/objectstore/repl.js.map +1 -0
- package/dist/cmd/cloud/objectstore/url.js +55 -0
- package/dist/cmd/cloud/objectstore/url.js.map +1 -0
- package/dist/cmd/cloud/objectstore/util.js +18 -0
- package/dist/cmd/cloud/objectstore/util.js.map +1 -0
- package/dist/cmd/cloud/resource/add.js +70 -0
- package/dist/cmd/cloud/resource/add.js.map +1 -0
- package/dist/cmd/cloud/resource/delete.js +126 -0
- package/dist/cmd/cloud/resource/delete.js.map +1 -0
- package/dist/cmd/cloud/resource/index.js +12 -0
- package/dist/cmd/cloud/resource/index.js.map +1 -0
- package/dist/cmd/cloud/resource/list.js +89 -0
- package/dist/cmd/cloud/resource/list.js.map +1 -0
- package/dist/cmd/cloud/scp/download.js +72 -0
- package/dist/cmd/cloud/scp/download.js.map +1 -0
- package/dist/cmd/cloud/scp/index.js +10 -0
- package/dist/cmd/cloud/scp/index.js.map +1 -0
- package/dist/cmd/cloud/scp/upload.js +75 -0
- package/dist/cmd/cloud/scp/upload.js.map +1 -0
- package/dist/cmd/cloud/secret/delete.js +50 -0
- package/dist/cmd/cloud/secret/delete.js.map +1 -0
- package/dist/cmd/cloud/secret/get.js +69 -0
- package/dist/cmd/cloud/secret/get.js.map +1 -0
- package/dist/cmd/cloud/secret/import.js +88 -0
- package/dist/cmd/cloud/secret/import.js.map +1 -0
- package/dist/cmd/cloud/secret/index.js +24 -0
- package/dist/cmd/cloud/secret/index.js.map +1 -0
- package/dist/cmd/cloud/secret/list.js +58 -0
- package/dist/cmd/cloud/secret/list.js.map +1 -0
- package/dist/cmd/cloud/secret/pull.js +81 -0
- package/dist/cmd/cloud/secret/pull.js.map +1 -0
- package/dist/cmd/cloud/secret/push.js +61 -0
- package/dist/cmd/cloud/secret/push.js.map +1 -0
- package/dist/cmd/cloud/secret/set.js +57 -0
- package/dist/cmd/cloud/secret/set.js.map +1 -0
- package/dist/cmd/cloud/session/get.d.ts.map +1 -1
- package/dist/cmd/cloud/session/get.js +155 -0
- package/dist/cmd/cloud/session/get.js.map +1 -0
- package/dist/cmd/cloud/session/index.js +11 -0
- package/dist/cmd/cloud/session/index.js.map +1 -0
- package/dist/cmd/cloud/session/list.js +132 -0
- package/dist/cmd/cloud/session/list.js.map +1 -0
- package/dist/cmd/cloud/session/logs.js +56 -0
- package/dist/cmd/cloud/session/logs.js.map +1 -0
- package/dist/cmd/cloud/ssh.js +67 -0
- package/dist/cmd/cloud/ssh.js.map +1 -0
- package/dist/cmd/dev/agents.js +103 -0
- package/dist/cmd/dev/agents.js.map +1 -0
- package/dist/cmd/dev/api.js +26 -0
- package/dist/cmd/dev/api.js.map +1 -0
- package/dist/cmd/dev/download.js +77 -0
- package/dist/cmd/dev/download.js.map +1 -0
- package/dist/cmd/dev/index.js +745 -0
- package/dist/cmd/dev/index.js.map +1 -0
- package/dist/cmd/dev/sync.js +229 -0
- package/dist/cmd/dev/sync.js.map +1 -0
- package/dist/cmd/dev/templates.js +75 -0
- package/dist/cmd/dev/templates.js.map +1 -0
- package/dist/cmd/index.js +49 -0
- package/dist/cmd/index.js.map +1 -0
- package/dist/cmd/profile/create.js +89 -0
- package/dist/cmd/profile/create.js.map +1 -0
- package/dist/cmd/profile/delete.js +63 -0
- package/dist/cmd/profile/delete.js.map +1 -0
- package/dist/cmd/profile/index.js +14 -0
- package/dist/cmd/profile/index.js.map +1 -0
- package/dist/cmd/profile/list.js +28 -0
- package/dist/cmd/profile/list.js.map +1 -0
- package/dist/cmd/profile/show.js +68 -0
- package/dist/cmd/profile/show.js.map +1 -0
- package/dist/cmd/profile/use.js +37 -0
- package/dist/cmd/profile/use.js.map +1 -0
- package/dist/cmd/project/create.js +92 -0
- package/dist/cmd/project/create.js.map +1 -0
- package/dist/cmd/project/delete.js +117 -0
- package/dist/cmd/project/delete.js.map +1 -0
- package/dist/cmd/project/download.js +217 -0
- package/dist/cmd/project/download.js.map +1 -0
- package/dist/cmd/project/index.js +12 -0
- package/dist/cmd/project/index.js.map +1 -0
- package/dist/cmd/project/list.js +51 -0
- package/dist/cmd/project/list.js.map +1 -0
- package/dist/cmd/project/show.js +54 -0
- package/dist/cmd/project/show.js.map +1 -0
- package/dist/cmd/project/template-flow.js +315 -0
- package/dist/cmd/project/template-flow.js.map +1 -0
- package/dist/cmd/project/templates.js +31 -0
- package/dist/cmd/project/templates.js.map +1 -0
- package/dist/cmd/repl/index.js +444 -0
- package/dist/cmd/repl/index.js.map +1 -0
- package/dist/cmd/version/index.js +29 -0
- package/dist/cmd/version/index.js.map +1 -0
- package/dist/command-prefix.js +37 -0
- package/dist/command-prefix.js.map +1 -0
- package/dist/config.js +536 -0
- package/dist/config.js.map +1 -0
- package/dist/crypto/box.js +382 -0
- package/dist/crypto/box.js.map +1 -0
- package/dist/crypto/box.test.js +317 -0
- package/dist/crypto/box.test.js.map +1 -0
- package/dist/download.js +64 -0
- package/dist/download.js.map +1 -0
- package/dist/env-util.js +219 -0
- package/dist/env-util.js.map +1 -0
- package/dist/env-util.test.js +146 -0
- package/dist/env-util.test.js.map +1 -0
- package/dist/errors.js +177 -0
- package/dist/errors.js.map +1 -0
- package/dist/explain.js +90 -0
- package/dist/explain.js.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/json.js +29 -0
- package/dist/json.js.map +1 -0
- package/dist/legacy-check.js +104 -0
- package/dist/legacy-check.js.map +1 -0
- package/dist/output.js +207 -0
- package/dist/output.js.map +1 -0
- package/dist/repl.js +1176 -0
- package/dist/repl.js.map +1 -0
- package/dist/runtime.js +19 -0
- package/dist/runtime.js.map +1 -0
- package/dist/schema-generator.js +289 -0
- package/dist/schema-generator.js.map +1 -0
- package/dist/schema-parser.js +145 -0
- package/dist/schema-parser.js.map +1 -0
- package/dist/sound.js +44 -0
- package/dist/sound.js.map +1 -0
- package/dist/steps.js +293 -0
- package/dist/steps.js.map +1 -0
- package/dist/terminal.js +130 -0
- package/dist/terminal.js.map +1 -0
- package/dist/tui.js +1124 -0
- package/dist/tui.js.map +1 -0
- package/dist/types.js +163 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/detectSubagent.js +25 -0
- package/dist/utils/detectSubagent.js.map +1 -0
- package/dist/utils/format.js +21 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/zip.js +33 -0
- package/dist/utils/zip.js.map +1 -0
- package/dist/version.js +24 -0
- package/dist/version.js.map +1 -0
- package/package.json +6 -6
- package/src/banner.ts +1 -1
- package/src/cmd/build/index.ts +18 -22
- package/src/cmd/build/plugin.ts +95 -64
- package/src/cmd/cloud/session/get.ts +20 -14
- package/src/cmd/cloud/session/list.ts +1 -1
package/dist/tui.js
ADDED
|
@@ -0,0 +1,1124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal UI utilities for formatted, colorized output
|
|
3
|
+
*
|
|
4
|
+
* Provides semantic helpers for console output with automatic icons and colors.
|
|
5
|
+
* Uses Bun's built-in color support and ANSI escape codes.
|
|
6
|
+
*/
|
|
7
|
+
import { stringWidth } from 'bun';
|
|
8
|
+
import { colorize } from 'json-colorizer';
|
|
9
|
+
import enquirer from 'enquirer';
|
|
10
|
+
import { projectList } from '@agentuity/server';
|
|
11
|
+
import * as readline from 'readline';
|
|
12
|
+
import { getExitCode } from './errors';
|
|
13
|
+
// Icons
|
|
14
|
+
const ICONS = {
|
|
15
|
+
success: '✓',
|
|
16
|
+
error: '✗',
|
|
17
|
+
warning: '⚠',
|
|
18
|
+
info: 'ℹ',
|
|
19
|
+
arrow: '→',
|
|
20
|
+
bullet: '•',
|
|
21
|
+
};
|
|
22
|
+
export function shouldUseColors() {
|
|
23
|
+
return (!process.env.NO_COLOR &&
|
|
24
|
+
!process.env.CI &&
|
|
25
|
+
process.env.TERM !== 'dumb' &&
|
|
26
|
+
!!process.stdout.isTTY);
|
|
27
|
+
}
|
|
28
|
+
// Color definitions (light/dark adaptive) using Bun.color
|
|
29
|
+
function getColors() {
|
|
30
|
+
const USE_COLORS = shouldUseColors();
|
|
31
|
+
if (!USE_COLORS) {
|
|
32
|
+
return {
|
|
33
|
+
success: { light: '', dark: '' },
|
|
34
|
+
error: { light: '', dark: '' },
|
|
35
|
+
warning: { light: '', dark: '' },
|
|
36
|
+
info: { light: '', dark: '' },
|
|
37
|
+
muted: { light: '', dark: '' },
|
|
38
|
+
bold: { light: '', dark: '' },
|
|
39
|
+
link: { light: '', dark: '' },
|
|
40
|
+
reset: '',
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
success: {
|
|
45
|
+
light: Bun.color('#008000', 'ansi') || '\x1b[32m', // green
|
|
46
|
+
dark: Bun.color('#00FF00', 'ansi') || '\x1b[92m', // bright green
|
|
47
|
+
},
|
|
48
|
+
error: {
|
|
49
|
+
light: Bun.color('#CC0000', 'ansi') || '\x1b[31m', // red
|
|
50
|
+
dark: Bun.color('#FF5555', 'ansi') || '\x1b[91m', // bright red
|
|
51
|
+
},
|
|
52
|
+
warning: {
|
|
53
|
+
light: Bun.color('#B58900', 'ansi') || '\x1b[33m', // yellow
|
|
54
|
+
dark: Bun.color('#FFFF55', 'ansi') || '\x1b[93m', // bright yellow
|
|
55
|
+
},
|
|
56
|
+
info: {
|
|
57
|
+
light: Bun.color('#008B8B', 'ansi') || '\x1b[36m', // dark cyan
|
|
58
|
+
dark: Bun.color('#55FFFF', 'ansi') || '\x1b[96m', // bright cyan
|
|
59
|
+
},
|
|
60
|
+
muted: {
|
|
61
|
+
light: Bun.color('#808080', 'ansi') || '\x1b[90m', // gray
|
|
62
|
+
dark: Bun.color('#888888', 'ansi') || '\x1b[90m', // darker gray
|
|
63
|
+
},
|
|
64
|
+
bold: {
|
|
65
|
+
light: '\x1b[1m',
|
|
66
|
+
dark: '\x1b[1m',
|
|
67
|
+
},
|
|
68
|
+
link: {
|
|
69
|
+
light: '\x1b[34;4m', // blue underline (need ANSI for underline)
|
|
70
|
+
dark: '\x1b[94;4m', // bright blue underline
|
|
71
|
+
},
|
|
72
|
+
reset: '\x1b[0m',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
let currentColorScheme = process.env.CI ? 'light' : 'dark';
|
|
76
|
+
export function setColorScheme(scheme) {
|
|
77
|
+
currentColorScheme = scheme;
|
|
78
|
+
process.env.COLOR_SCHEME = scheme;
|
|
79
|
+
}
|
|
80
|
+
export function isDarkMode() {
|
|
81
|
+
return currentColorScheme === 'dark';
|
|
82
|
+
}
|
|
83
|
+
function getColor(colorKey) {
|
|
84
|
+
const COLORS = getColors();
|
|
85
|
+
const color = COLORS[colorKey];
|
|
86
|
+
if (typeof color === 'string') {
|
|
87
|
+
return color;
|
|
88
|
+
}
|
|
89
|
+
return color[currentColorScheme];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Color helpers that return colored strings (for inline use, no icons)
|
|
93
|
+
*/
|
|
94
|
+
export function colorSuccess(text) {
|
|
95
|
+
const color = getColor('success');
|
|
96
|
+
const reset = getColor('reset');
|
|
97
|
+
return `${color}${text}${reset}`;
|
|
98
|
+
}
|
|
99
|
+
export function colorError(text) {
|
|
100
|
+
const color = getColor('error');
|
|
101
|
+
const reset = getColor('reset');
|
|
102
|
+
return `${color}${text}${reset}`;
|
|
103
|
+
}
|
|
104
|
+
export function colorWarning(text) {
|
|
105
|
+
const color = getColor('warning');
|
|
106
|
+
const reset = getColor('reset');
|
|
107
|
+
return `${color}${text}${reset}`;
|
|
108
|
+
}
|
|
109
|
+
export function colorInfo(text) {
|
|
110
|
+
const color = getColor('info');
|
|
111
|
+
const reset = getColor('reset');
|
|
112
|
+
return `${color}${text}${reset}`;
|
|
113
|
+
}
|
|
114
|
+
export function colorMuted(text) {
|
|
115
|
+
const color = getColor('muted');
|
|
116
|
+
const reset = getColor('reset');
|
|
117
|
+
return `${color}${text}${reset}`;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Print a success message with a green checkmark
|
|
121
|
+
*/
|
|
122
|
+
export function success(message) {
|
|
123
|
+
const color = getColor('success');
|
|
124
|
+
const reset = getColor('reset');
|
|
125
|
+
process.stderr.write(`${color}${ICONS.success} ${message}${reset}\n`);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Print an error message with a red X
|
|
129
|
+
*/
|
|
130
|
+
export function error(message) {
|
|
131
|
+
const color = getColor('error');
|
|
132
|
+
const reset = getColor('reset');
|
|
133
|
+
process.stderr.write(`${color}${ICONS.error} ${message}${reset}\n`);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Print an error message with a red X and then exit
|
|
137
|
+
*/
|
|
138
|
+
export function fatal(message, errorCode) {
|
|
139
|
+
const color = getColor('error');
|
|
140
|
+
const reset = getColor('reset');
|
|
141
|
+
process.stderr.write(`${color}${ICONS.error} ${message}${reset}\n`);
|
|
142
|
+
if (errorCode) {
|
|
143
|
+
const exitCode = getExitCode(errorCode);
|
|
144
|
+
process.exit(exitCode);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Print a warning message with a yellow warning icon
|
|
152
|
+
*/
|
|
153
|
+
export function warning(message, asError = false) {
|
|
154
|
+
const color = asError ? getColor('error') : getColor('warning');
|
|
155
|
+
const reset = getColor('reset');
|
|
156
|
+
process.stderr.write(`${color}${ICONS.warning} ${message}${reset}\n`);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Print an info message with a cyan info icon
|
|
160
|
+
*/
|
|
161
|
+
export function info(message) {
|
|
162
|
+
const color = getColor('info');
|
|
163
|
+
const reset = getColor('reset');
|
|
164
|
+
process.stderr.write(`${color}${ICONS.info} ${message}${reset}\n`);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Format text in muted/gray color
|
|
168
|
+
*/
|
|
169
|
+
export function muted(text) {
|
|
170
|
+
const color = getColor('muted');
|
|
171
|
+
const reset = getColor('reset');
|
|
172
|
+
return `${color}${text}${reset}`;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Format text in warn color
|
|
176
|
+
*/
|
|
177
|
+
export function warn(text) {
|
|
178
|
+
const color = getColor('warning');
|
|
179
|
+
const reset = getColor('reset');
|
|
180
|
+
return `${color}${text}${reset}`;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Format text in bold
|
|
184
|
+
*/
|
|
185
|
+
export function bold(text) {
|
|
186
|
+
const color = getColor('bold');
|
|
187
|
+
const reset = getColor('reset');
|
|
188
|
+
return `${color}${text}${reset}`;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Format text as a link (blue and underlined)
|
|
192
|
+
*/
|
|
193
|
+
export function link(url, title) {
|
|
194
|
+
const color = getColor('link');
|
|
195
|
+
const reset = getColor('reset');
|
|
196
|
+
// Check if terminal supports hyperlinks (OSC 8) and colors are enabled
|
|
197
|
+
if (shouldUseColors() && supportsHyperlinks()) {
|
|
198
|
+
return `\x1b]8;;${url}\x07${color}${title ?? url}${reset}\x1b]8;;\x07`;
|
|
199
|
+
}
|
|
200
|
+
return `${color}${url}${reset}`;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Check if terminal supports OSC 8 hyperlinks
|
|
204
|
+
*/
|
|
205
|
+
function supportsHyperlinks() {
|
|
206
|
+
const term = process.env.TERM || '';
|
|
207
|
+
const termProgram = process.env.TERM_PROGRAM || '';
|
|
208
|
+
const wtSession = process.env.WT_SESSION || '';
|
|
209
|
+
// Known terminal programs that support OSC 8
|
|
210
|
+
return (termProgram.includes('iTerm.app') ||
|
|
211
|
+
termProgram.includes('WezTerm') ||
|
|
212
|
+
termProgram.includes('ghostty') ||
|
|
213
|
+
termProgram.includes('Apple_Terminal') ||
|
|
214
|
+
termProgram.includes('Hyper') ||
|
|
215
|
+
term.includes('xterm-kitty') ||
|
|
216
|
+
term.includes('xterm-256color') ||
|
|
217
|
+
wtSession !== '' // Windows Terminal
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Print a bulleted list item
|
|
222
|
+
*/
|
|
223
|
+
export function bullet(message) {
|
|
224
|
+
process.stderr.write(`${ICONS.bullet} ${message}\n`);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Print an arrow item (for showing next steps)
|
|
228
|
+
*/
|
|
229
|
+
export function arrow(message) {
|
|
230
|
+
process.stderr.write(`${ICONS.arrow} ${message}\n`);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Print a blank line
|
|
234
|
+
*/
|
|
235
|
+
export function newline() {
|
|
236
|
+
process.stderr.write('\n');
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Get the display width of a string, handling ANSI codes and OSC 8 hyperlinks
|
|
240
|
+
*
|
|
241
|
+
* Note: Bun.stringWidth() counts OSC 8 hyperlink escape sequences in the width,
|
|
242
|
+
* which causes incorrect alignment. We strip OSC 8 codes first, then use Bun.stringWidth()
|
|
243
|
+
* to handle regular ANSI codes and unicode characters correctly.
|
|
244
|
+
*/
|
|
245
|
+
function getDisplayWidth(str) {
|
|
246
|
+
// Remove OSC-8 hyperlink sequences using Unicode escapes (\u001b = ESC, \u0007 = BEL) to satisfy linter
|
|
247
|
+
// eslint-disable-next-line no-control-regex
|
|
248
|
+
const withoutOSC8 = str.replace(/\u001b\]8;;[^\u0007]*\u0007/g, '');
|
|
249
|
+
return Bun.stringWidth(withoutOSC8);
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Pad a string to a specific length on the right
|
|
253
|
+
*/
|
|
254
|
+
export function padRight(str, length, pad = ' ') {
|
|
255
|
+
const displayWidth = getDisplayWidth(str);
|
|
256
|
+
if (displayWidth >= length) {
|
|
257
|
+
return str;
|
|
258
|
+
}
|
|
259
|
+
return str + pad.repeat(length - displayWidth);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Pad a string to a specific length on the left
|
|
263
|
+
*/
|
|
264
|
+
export function padLeft(str, length, pad = ' ') {
|
|
265
|
+
const displayWidth = getDisplayWidth(str);
|
|
266
|
+
if (displayWidth >= length) {
|
|
267
|
+
return str;
|
|
268
|
+
}
|
|
269
|
+
return pad.repeat(length - displayWidth) + str;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Display a formatted banner with title and body content
|
|
273
|
+
* Creates a bordered box around the content
|
|
274
|
+
*
|
|
275
|
+
* Uses Bun.stringWidth() for accurate width calculation with ANSI codes and unicode
|
|
276
|
+
* Responsive to terminal width - adapts to narrow terminals
|
|
277
|
+
*/
|
|
278
|
+
export function banner(title, body, options) {
|
|
279
|
+
// Get terminal width, default to 120 if not available
|
|
280
|
+
const termWidth = process.stdout.columns || 120;
|
|
281
|
+
const border = {
|
|
282
|
+
topLeft: '╭',
|
|
283
|
+
topRight: '╮',
|
|
284
|
+
bottomLeft: '╰',
|
|
285
|
+
bottomRight: '╯',
|
|
286
|
+
horizontal: '─',
|
|
287
|
+
vertical: '│',
|
|
288
|
+
};
|
|
289
|
+
// Calculate content width first (before wrapping)
|
|
290
|
+
const titleWidth = getDisplayWidth(title);
|
|
291
|
+
const bodyLines = body.split('\n');
|
|
292
|
+
const maxBodyWidth = Math.max(0, ...bodyLines.map((line) => getDisplayWidth(line)));
|
|
293
|
+
const requiredContentWidth = Math.max(titleWidth, maxBodyWidth);
|
|
294
|
+
// Box width = content + borders (2) + side spaces (2)
|
|
295
|
+
const boxWidth = Math.min(requiredContentWidth + 4, termWidth);
|
|
296
|
+
// If required content width exceeds terminal width, skip box and print plain text
|
|
297
|
+
if (requiredContentWidth + 4 > termWidth) {
|
|
298
|
+
console.log('\n' + bold(title));
|
|
299
|
+
console.log(body + '\n');
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
// Inner width is box width minus borders (2) and side spaces (2)
|
|
303
|
+
const innerWidth = boxWidth - 4;
|
|
304
|
+
// Wrap text to fit box width
|
|
305
|
+
const wrappedBodyLines = wrapText(body, innerWidth);
|
|
306
|
+
// Colors
|
|
307
|
+
const borderColor = getColor('muted');
|
|
308
|
+
const titleColor = getColor('info');
|
|
309
|
+
const reset = getColor('reset');
|
|
310
|
+
// Build banner
|
|
311
|
+
const lines = [];
|
|
312
|
+
// Top border
|
|
313
|
+
lines.push(`${borderColor}${border.topLeft}${border.horizontal.repeat(boxWidth - 2)}${border.topRight}${reset}`);
|
|
314
|
+
if (options?.topSpacer === true || options?.topSpacer === undefined) {
|
|
315
|
+
// Empty line
|
|
316
|
+
lines.push(`${borderColor}${border.vertical}${' '.repeat(boxWidth - 2)}${border.vertical}${reset}`);
|
|
317
|
+
}
|
|
318
|
+
// Title (centered and bold)
|
|
319
|
+
const titleDisplayWidth = getDisplayWidth(title);
|
|
320
|
+
if (options?.centerTitle === true || options?.centerTitle === undefined) {
|
|
321
|
+
const titlePadding = Math.max(0, Math.floor((innerWidth - titleDisplayWidth) / 2));
|
|
322
|
+
const titleRightPadding = Math.max(0, innerWidth - titlePadding - titleDisplayWidth);
|
|
323
|
+
const titleLine = ' '.repeat(titlePadding) +
|
|
324
|
+
`${titleColor}${bold(title)}${reset}` +
|
|
325
|
+
' '.repeat(titleRightPadding);
|
|
326
|
+
lines.push(`${borderColor}${border.vertical} ${reset}${titleLine}${borderColor} ${border.vertical}${reset}`);
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
const titleRightPadding = Math.max(0, innerWidth - titleDisplayWidth);
|
|
330
|
+
const titleLine = `${titleColor}${bold(title)}${reset}` + ' '.repeat(titleRightPadding);
|
|
331
|
+
lines.push(`${borderColor}${border.vertical} ${reset}${titleLine}${borderColor} ${border.vertical}${reset}`);
|
|
332
|
+
}
|
|
333
|
+
if (options?.middleSpacer === true || options?.middleSpacer === undefined) {
|
|
334
|
+
// Empty line
|
|
335
|
+
lines.push(`${borderColor}${border.vertical}${' '.repeat(boxWidth - 2)}${border.vertical}${reset}`);
|
|
336
|
+
}
|
|
337
|
+
// Body lines
|
|
338
|
+
for (const line of wrappedBodyLines) {
|
|
339
|
+
const lineWidth = getDisplayWidth(line);
|
|
340
|
+
const linePadding = Math.max(0, innerWidth - lineWidth);
|
|
341
|
+
lines.push(`${borderColor}${border.vertical} ${reset}${line}${' '.repeat(linePadding)}${borderColor} ${border.vertical}${reset}`);
|
|
342
|
+
}
|
|
343
|
+
if (options?.bottomSpacer === true || options?.bottomSpacer === undefined) {
|
|
344
|
+
// Empty line
|
|
345
|
+
lines.push(`${borderColor}${border.vertical}${' '.repeat(boxWidth - 2)}${border.vertical}${reset}`);
|
|
346
|
+
}
|
|
347
|
+
// Bottom border
|
|
348
|
+
lines.push(`${borderColor}${border.bottomLeft}${border.horizontal.repeat(boxWidth - 2)}${border.bottomRight}${reset}`);
|
|
349
|
+
// Print the banner
|
|
350
|
+
console.log('\n' + lines.join('\n') + '\n');
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Wait for any key press before continuing
|
|
354
|
+
* Displays a prompt message and waits for user input
|
|
355
|
+
* Exits with code 1 if CTRL+C is pressed
|
|
356
|
+
*/
|
|
357
|
+
export async function waitForAnyKey(message = 'Press Enter to continue...') {
|
|
358
|
+
process.stdout.write(muted(message));
|
|
359
|
+
// Check if we're in a TTY environment
|
|
360
|
+
if (!process.stdin.isTTY) {
|
|
361
|
+
// Not a TTY (CI/piped), just write newline and exit
|
|
362
|
+
console.log('');
|
|
363
|
+
return Promise.resolve();
|
|
364
|
+
}
|
|
365
|
+
// Set stdin to raw mode to read a single keypress
|
|
366
|
+
process.stdin.setRawMode(true);
|
|
367
|
+
process.stdin.resume();
|
|
368
|
+
let rawModeSet = true;
|
|
369
|
+
return new Promise((resolve) => {
|
|
370
|
+
process.stdin.once('data', (data) => {
|
|
371
|
+
if (rawModeSet && process.stdin.isTTY) {
|
|
372
|
+
process.stdin.setRawMode(false);
|
|
373
|
+
rawModeSet = false;
|
|
374
|
+
}
|
|
375
|
+
process.stdin.pause();
|
|
376
|
+
// Check for CTRL+C (character code 3)
|
|
377
|
+
if (data.length === 1 && data[0] === 3) {
|
|
378
|
+
console.log('\n');
|
|
379
|
+
process.exit(1);
|
|
380
|
+
}
|
|
381
|
+
console.log('');
|
|
382
|
+
resolve();
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Prompts user with a yes/no question
|
|
388
|
+
* Returns true for yes, false for no
|
|
389
|
+
* Exits with code 1 if CTRL+C is pressed
|
|
390
|
+
*/
|
|
391
|
+
export async function confirm(message, defaultValue = true) {
|
|
392
|
+
const suffix = defaultValue ? '[Y/n]' : '[y/N]';
|
|
393
|
+
process.stdout.write(`${message} ${muted(suffix)} `);
|
|
394
|
+
// Check if we're in a TTY environment
|
|
395
|
+
if (!process.stdin.isTTY) {
|
|
396
|
+
console.log('');
|
|
397
|
+
return defaultValue;
|
|
398
|
+
}
|
|
399
|
+
// Set stdin to raw mode to read a single keypress
|
|
400
|
+
process.stdin.setRawMode(true);
|
|
401
|
+
process.stdin.resume();
|
|
402
|
+
let rawModeSet = true;
|
|
403
|
+
return new Promise((resolve) => {
|
|
404
|
+
process.stdin.once('data', (data) => {
|
|
405
|
+
if (rawModeSet && process.stdin.isTTY) {
|
|
406
|
+
process.stdin.setRawMode(false);
|
|
407
|
+
rawModeSet = false;
|
|
408
|
+
}
|
|
409
|
+
process.stdin.pause();
|
|
410
|
+
// Check for CTRL+C (character code 3)
|
|
411
|
+
if (data.length === 1 && data[0] === 3) {
|
|
412
|
+
console.log('\n');
|
|
413
|
+
process.exit(1);
|
|
414
|
+
}
|
|
415
|
+
const input = data.toString().trim().toLowerCase();
|
|
416
|
+
console.log('');
|
|
417
|
+
// Enter key (just newline) uses default
|
|
418
|
+
if (input === '') {
|
|
419
|
+
resolve(defaultValue);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
// Check first character for y/n
|
|
423
|
+
const char = input.charAt(0);
|
|
424
|
+
if (char === 'y') {
|
|
425
|
+
resolve(true);
|
|
426
|
+
}
|
|
427
|
+
else if (char === 'n') {
|
|
428
|
+
resolve(false);
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
// Invalid input, use default
|
|
432
|
+
resolve(defaultValue);
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Display a signup benefits box with cyan border
|
|
439
|
+
* Shows the value proposition for creating an Agentuity account
|
|
440
|
+
*/
|
|
441
|
+
export function showSignupBenefits() {
|
|
442
|
+
const CYAN = Bun.color('cyan', 'ansi-16m');
|
|
443
|
+
const TEXT = currentColorScheme === 'dark' ? Bun.color('white', 'ansi') : Bun.color('black', 'ansi');
|
|
444
|
+
const RESET = '\x1b[0m';
|
|
445
|
+
const lines = [
|
|
446
|
+
'╔════════════════════════════════════════════╗',
|
|
447
|
+
`║ ⨺ Signup for Agentuity ${muted('free')}${CYAN} ║`,
|
|
448
|
+
'║ ║',
|
|
449
|
+
`║ ✓ ${TEXT}Cloud deployment, previews and CI/CD${CYAN} ║`,
|
|
450
|
+
`║ ✓ ${TEXT}AI Gateway, KV, Vector and more${CYAN} ║`,
|
|
451
|
+
`║ ✓ ${TEXT}Observability, Tracing and Logging${CYAN} ║`,
|
|
452
|
+
`║ ✓ ${TEXT}Organization and Team support${CYAN} ║`,
|
|
453
|
+
`║ ✓ ${TEXT}And much more!${CYAN} ║`,
|
|
454
|
+
'╚════════════════════════════════════════════╝',
|
|
455
|
+
];
|
|
456
|
+
console.log('');
|
|
457
|
+
lines.map((line) => console.log(CYAN + line + RESET));
|
|
458
|
+
console.log('');
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Display a message when unauthenticated to let the user know certain capabilities are disabled
|
|
462
|
+
*/
|
|
463
|
+
export function showLoggedOutMessage() {
|
|
464
|
+
const YELLOW = Bun.color('yellow', 'ansi-16m');
|
|
465
|
+
const TEXT = currentColorScheme === 'dark' ? Bun.color('white', 'ansi') : Bun.color('black', 'ansi');
|
|
466
|
+
const RESET = '\x1b[0m';
|
|
467
|
+
const signupTitle = 'Sign up / Login';
|
|
468
|
+
const showInline = supportsHyperlinks();
|
|
469
|
+
const signupURL = 'https://app.agentuity.com/sign-up';
|
|
470
|
+
const signupLink = showInline
|
|
471
|
+
? link(signupURL, signupTitle)
|
|
472
|
+
: ' '.repeat(stringWidth(signupTitle));
|
|
473
|
+
const showNewLine = showInline ? '' : `║ ${RESET}${link(signupURL)}${YELLOW} ║`;
|
|
474
|
+
const lines = [
|
|
475
|
+
'╔══════════════════════════════════════════════╗',
|
|
476
|
+
`║ ⨺ Unauthenticated (local mode) ║`,
|
|
477
|
+
'║ ║',
|
|
478
|
+
`║ ${TEXT}Certain capabilities such as the AI services${YELLOW} ║`,
|
|
479
|
+
`║ ${TEXT}and devmode remote are unavailable when${YELLOW} ║`,
|
|
480
|
+
`║ ${TEXT}unauthenticated.${YELLOW} ${signupLink}${YELLOW} ║`,
|
|
481
|
+
showNewLine,
|
|
482
|
+
'╚══════════════════════════════════════════════╝',
|
|
483
|
+
];
|
|
484
|
+
console.log('');
|
|
485
|
+
lines.filter(Boolean).map((line) => console.log(YELLOW + line + RESET));
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Copy text to clipboard
|
|
489
|
+
* Returns true if successful, false otherwise
|
|
490
|
+
*/
|
|
491
|
+
export async function copyToClipboard(text) {
|
|
492
|
+
try {
|
|
493
|
+
const platform = process.platform;
|
|
494
|
+
if (platform === 'darwin') {
|
|
495
|
+
// macOS - use pbcopy
|
|
496
|
+
const proc = Bun.spawn(['pbcopy'], {
|
|
497
|
+
stdin: 'pipe',
|
|
498
|
+
});
|
|
499
|
+
proc.stdin.write(text);
|
|
500
|
+
proc.stdin.end();
|
|
501
|
+
await proc.exited;
|
|
502
|
+
return proc.exitCode === 0;
|
|
503
|
+
}
|
|
504
|
+
else if (platform === 'win32') {
|
|
505
|
+
// Windows - use clip
|
|
506
|
+
const proc = Bun.spawn(['clip'], {
|
|
507
|
+
stdin: 'pipe',
|
|
508
|
+
});
|
|
509
|
+
proc.stdin.write(text);
|
|
510
|
+
proc.stdin.end();
|
|
511
|
+
await proc.exited;
|
|
512
|
+
return proc.exitCode === 0;
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
// Linux - try xclip first, then xsel
|
|
516
|
+
try {
|
|
517
|
+
const proc = Bun.spawn(['xclip', '-selection', 'clipboard'], {
|
|
518
|
+
stdin: 'pipe',
|
|
519
|
+
});
|
|
520
|
+
proc.stdin.write(text);
|
|
521
|
+
proc.stdin.end();
|
|
522
|
+
await proc.exited;
|
|
523
|
+
return proc.exitCode === 0;
|
|
524
|
+
}
|
|
525
|
+
catch {
|
|
526
|
+
// Try xsel as fallback
|
|
527
|
+
const proc = Bun.spawn(['xsel', '--clipboard', '--input'], {
|
|
528
|
+
stdin: 'pipe',
|
|
529
|
+
});
|
|
530
|
+
proc.stdin.write(text);
|
|
531
|
+
proc.stdin.end();
|
|
532
|
+
await proc.exited;
|
|
533
|
+
return proc.exitCode === 0;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
catch {
|
|
538
|
+
return false;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Extract ANSI codes from the beginning of a string
|
|
543
|
+
*/
|
|
544
|
+
function extractLeadingAnsiCodes(str) {
|
|
545
|
+
// Match ANSI escape sequences at the start of the string
|
|
546
|
+
// eslint-disable-next-line no-control-regex
|
|
547
|
+
const match = str.match(/^(\x1b\[[0-9;]*m)+/);
|
|
548
|
+
return match ? match[0] : '';
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Strip ANSI codes from a string
|
|
552
|
+
*/
|
|
553
|
+
function stripAnsiCodes(str) {
|
|
554
|
+
// Remove all ANSI escape sequences
|
|
555
|
+
// eslint-disable-next-line no-control-regex
|
|
556
|
+
return str.replace(/\x1b\[[0-9;]*m/g, '');
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Check if a string ends with ANSI reset code
|
|
560
|
+
*/
|
|
561
|
+
function endsWithReset(str) {
|
|
562
|
+
return str.endsWith('\x1b[0m') || str.endsWith(getColor('reset'));
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Wrap text to a maximum width
|
|
566
|
+
* Handles explicit newlines and word wrapping
|
|
567
|
+
* Preserves ANSI color codes across wrapped lines
|
|
568
|
+
*/
|
|
569
|
+
function wrapText(text, maxWidth) {
|
|
570
|
+
const allLines = [];
|
|
571
|
+
// First split by explicit newlines
|
|
572
|
+
const paragraphs = text.split('\n');
|
|
573
|
+
for (const paragraph of paragraphs) {
|
|
574
|
+
// Skip empty paragraphs (they become blank lines)
|
|
575
|
+
if (paragraph.trim() === '') {
|
|
576
|
+
allLines.push('');
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
// Record starting index for this paragraph's lines
|
|
580
|
+
const paragraphStart = allLines.length;
|
|
581
|
+
// Extract any leading ANSI codes from the paragraph
|
|
582
|
+
const leadingCodes = extractLeadingAnsiCodes(paragraph);
|
|
583
|
+
const hasReset = endsWithReset(paragraph);
|
|
584
|
+
// Wrap each paragraph
|
|
585
|
+
const words = paragraph.split(' ');
|
|
586
|
+
let currentLine = '';
|
|
587
|
+
for (const word of words) {
|
|
588
|
+
const testLine = currentLine ? `${currentLine} ${word}` : word;
|
|
589
|
+
const testLineWidth = getDisplayWidth(testLine);
|
|
590
|
+
if (testLineWidth <= maxWidth) {
|
|
591
|
+
currentLine = testLine;
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
// If current line has content, save it
|
|
595
|
+
if (currentLine) {
|
|
596
|
+
allLines.push(currentLine);
|
|
597
|
+
}
|
|
598
|
+
// If the word itself is longer than maxWidth, just use it as is
|
|
599
|
+
// (better to have a long line than break in the middle)
|
|
600
|
+
// But if we have leading codes and this isn't the first line, apply them
|
|
601
|
+
if (leadingCodes && currentLine) {
|
|
602
|
+
// Strip any existing codes from the word to avoid duplication
|
|
603
|
+
const strippedWord = stripAnsiCodes(word);
|
|
604
|
+
currentLine = leadingCodes + strippedWord;
|
|
605
|
+
}
|
|
606
|
+
else {
|
|
607
|
+
currentLine = word;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
if (currentLine) {
|
|
612
|
+
allLines.push(currentLine);
|
|
613
|
+
}
|
|
614
|
+
// If the original paragraph had ANSI codes and ended with reset,
|
|
615
|
+
// ensure each wrapped line ends with reset (only for this paragraph's lines)
|
|
616
|
+
if (leadingCodes && hasReset) {
|
|
617
|
+
for (let i = paragraphStart; i < allLines.length; i++) {
|
|
618
|
+
if (!endsWithReset(allLines[i])) {
|
|
619
|
+
allLines[i] += getColor('reset');
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
return allLines.length > 0 ? allLines : [''];
|
|
625
|
+
}
|
|
626
|
+
export async function spinner(messageOrOptions, callback) {
|
|
627
|
+
// Normalize to options format
|
|
628
|
+
let options;
|
|
629
|
+
if (typeof messageOrOptions === 'string') {
|
|
630
|
+
if (callback === undefined) {
|
|
631
|
+
throw new Error('callback is required when first argument is a string');
|
|
632
|
+
}
|
|
633
|
+
options = { type: 'simple', message: messageOrOptions, callback };
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
options = messageOrOptions;
|
|
637
|
+
}
|
|
638
|
+
const message = options.message;
|
|
639
|
+
const reset = getColor('reset');
|
|
640
|
+
// Check if progress should be disabled (from global options)
|
|
641
|
+
const { getOutputOptions, shouldDisableProgress } = await import('./output');
|
|
642
|
+
const outputOptions = getOutputOptions();
|
|
643
|
+
const noProgress = outputOptions ? shouldDisableProgress(outputOptions) : false;
|
|
644
|
+
// If no TTY or progress disabled, just execute the callback without animation
|
|
645
|
+
if (!process.stderr.isTTY || noProgress) {
|
|
646
|
+
try {
|
|
647
|
+
const result = options.type === 'progress'
|
|
648
|
+
? await options.callback(() => { })
|
|
649
|
+
: typeof options.callback === 'function'
|
|
650
|
+
? await options.callback()
|
|
651
|
+
: await options.callback;
|
|
652
|
+
// If clearOnSuccess is true, don't show success message
|
|
653
|
+
if (!options.clearOnSuccess) {
|
|
654
|
+
const successColor = getColor('success');
|
|
655
|
+
console.error(`${successColor}${ICONS.success} ${message}${reset}`);
|
|
656
|
+
}
|
|
657
|
+
return result;
|
|
658
|
+
}
|
|
659
|
+
catch (err) {
|
|
660
|
+
const errorColor = getColor('error');
|
|
661
|
+
console.error(`${errorColor}${ICONS.error} ${message}${reset}`);
|
|
662
|
+
throw err;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
const frames = ['◐', '◓', '◑', '◒'];
|
|
666
|
+
const spinnerColors = [
|
|
667
|
+
{ light: '\x1b[36m', dark: '\x1b[96m' }, // cyan
|
|
668
|
+
{ light: '\x1b[34m', dark: '\x1b[94m' }, // blue
|
|
669
|
+
{ light: '\x1b[35m', dark: '\x1b[95m' }, // magenta
|
|
670
|
+
{ light: '\x1b[36m', dark: '\x1b[96m' }, // cyan
|
|
671
|
+
];
|
|
672
|
+
const bold = '\x1b[1m';
|
|
673
|
+
const cyanColor = { light: '\x1b[36m', dark: '\x1b[96m' }[currentColorScheme];
|
|
674
|
+
let frameIndex = 0;
|
|
675
|
+
let currentProgress;
|
|
676
|
+
// Hide cursor
|
|
677
|
+
process.stderr.write('\x1B[?25l');
|
|
678
|
+
// Start animation
|
|
679
|
+
const interval = setInterval(() => {
|
|
680
|
+
const colorDef = spinnerColors[frameIndex % spinnerColors.length];
|
|
681
|
+
const color = colorDef[currentColorScheme];
|
|
682
|
+
const frame = `${color}${bold}${frames[frameIndex % frames.length]}${reset}`;
|
|
683
|
+
// Add progress indicator if available
|
|
684
|
+
const progressIndicator = currentProgress !== undefined
|
|
685
|
+
? ` ${cyanColor}${Math.floor(currentProgress)}%${reset}`
|
|
686
|
+
: '';
|
|
687
|
+
// Clear line and render
|
|
688
|
+
process.stderr.write('\r\x1B[K' + `${frame} ${message}${progressIndicator}`);
|
|
689
|
+
frameIndex++;
|
|
690
|
+
}, 120);
|
|
691
|
+
// Progress callback
|
|
692
|
+
const progressCallback = (progress) => {
|
|
693
|
+
currentProgress = Math.min(100, Math.max(0, progress));
|
|
694
|
+
};
|
|
695
|
+
try {
|
|
696
|
+
// Execute callback
|
|
697
|
+
const result = options.type === 'progress'
|
|
698
|
+
? await options.callback(progressCallback)
|
|
699
|
+
: typeof options.callback === 'function'
|
|
700
|
+
? await options.callback()
|
|
701
|
+
: await options.callback;
|
|
702
|
+
// Clear interval and line
|
|
703
|
+
clearInterval(interval);
|
|
704
|
+
process.stderr.write('\r\x1B[K');
|
|
705
|
+
// If clearOnSuccess is false, show success message
|
|
706
|
+
if (!options.clearOnSuccess) {
|
|
707
|
+
// Show success
|
|
708
|
+
const successColor = getColor('success');
|
|
709
|
+
console.error(`${successColor}${ICONS.success} ${message}${reset}`);
|
|
710
|
+
}
|
|
711
|
+
// Show cursor
|
|
712
|
+
process.stderr.write('\x1B[?25h');
|
|
713
|
+
return result;
|
|
714
|
+
}
|
|
715
|
+
catch (err) {
|
|
716
|
+
// Clear interval and line
|
|
717
|
+
clearInterval(interval);
|
|
718
|
+
process.stderr.write('\r\x1B[K');
|
|
719
|
+
// Show error
|
|
720
|
+
const errorColor = getColor('error');
|
|
721
|
+
console.error(`${errorColor}${ICONS.error} ${message}${reset}`);
|
|
722
|
+
// Show cursor
|
|
723
|
+
process.stderr.write('\x1B[?25h');
|
|
724
|
+
throw err;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
728
|
+
* Run an external command and stream its output with a live UI
|
|
729
|
+
*
|
|
730
|
+
* Displays the command with a colored $ prompt:
|
|
731
|
+
* - Blue while running
|
|
732
|
+
* - Green on successful exit (code 0)
|
|
733
|
+
* - Red on failed exit (code != 0)
|
|
734
|
+
*
|
|
735
|
+
* Shows the last 3 lines of output as it streams.
|
|
736
|
+
*/
|
|
737
|
+
export async function runCommand(options) {
|
|
738
|
+
const { command, cmd, cwd, env, clearOnSuccess = false, truncate = true, maxLinesOutput = 3, maxLinesOnFailure = 10, } = options;
|
|
739
|
+
const isTTY = process.stdout.isTTY;
|
|
740
|
+
// If not a TTY, just run the command normally and log output
|
|
741
|
+
if (!isTTY) {
|
|
742
|
+
const proc = Bun.spawn(cmd, {
|
|
743
|
+
cwd,
|
|
744
|
+
env: { ...process.env, ...env },
|
|
745
|
+
stdout: 'inherit',
|
|
746
|
+
stderr: 'inherit',
|
|
747
|
+
});
|
|
748
|
+
return await proc.exited;
|
|
749
|
+
}
|
|
750
|
+
// Colors using Bun.color
|
|
751
|
+
const blue = currentColorScheme === 'light'
|
|
752
|
+
? Bun.color('#0000FF', 'ansi') || '\x1b[34m'
|
|
753
|
+
: Bun.color('#5C9CFF', 'ansi') || '\x1b[94m';
|
|
754
|
+
const green = getColor('success');
|
|
755
|
+
const red = getColor('error');
|
|
756
|
+
const cmdColor = currentColorScheme === 'light'
|
|
757
|
+
? '\x1b[1m' + (Bun.color('#00008B', 'ansi') || '\x1b[34m')
|
|
758
|
+
: Bun.color('#FFFFFF', 'ansi') || '\x1b[97m'; // bold dark blue / white
|
|
759
|
+
const mutedColor = Bun.color('#808080', 'ansi') || '\x1b[90m';
|
|
760
|
+
const reset = getColor('reset');
|
|
761
|
+
// Get terminal width
|
|
762
|
+
const termWidth = process.stdout.columns || 80;
|
|
763
|
+
const maxCmdWidth = Math.min(40, termWidth);
|
|
764
|
+
const maxLineWidth = Math.min(80, termWidth);
|
|
765
|
+
// Truncate command if needed
|
|
766
|
+
let displayCmd = command;
|
|
767
|
+
if (getDisplayWidth(displayCmd) > maxCmdWidth) {
|
|
768
|
+
// Simple truncation for now - could be smarter about this
|
|
769
|
+
displayCmd = displayCmd.slice(0, maxCmdWidth - 3) + '...';
|
|
770
|
+
}
|
|
771
|
+
// Store all output lines, display subset based on context
|
|
772
|
+
const allOutputLines = [];
|
|
773
|
+
let linesRendered = 0;
|
|
774
|
+
// Hide cursor
|
|
775
|
+
process.stdout.write('\x1B[?25l');
|
|
776
|
+
// Render the command and output lines in place
|
|
777
|
+
const renderOutput = (linesToShow) => {
|
|
778
|
+
// Move cursor up to start of our output area
|
|
779
|
+
if (linesRendered > 0) {
|
|
780
|
+
process.stdout.write(`\x1b[${linesRendered}A`);
|
|
781
|
+
}
|
|
782
|
+
// Render command line
|
|
783
|
+
process.stdout.write(`\r\x1b[K${blue}$${reset} ${cmdColor}${displayCmd}${reset}\n`);
|
|
784
|
+
// Get last N lines to display
|
|
785
|
+
const displayLines = allOutputLines.slice(-linesToShow);
|
|
786
|
+
// Render output lines
|
|
787
|
+
for (const line of displayLines) {
|
|
788
|
+
// Truncate line if needed
|
|
789
|
+
let displayLine = line;
|
|
790
|
+
if (getDisplayWidth(displayLine) > maxLineWidth) {
|
|
791
|
+
displayLine = displayLine.slice(0, maxLineWidth - 3) + '...';
|
|
792
|
+
}
|
|
793
|
+
process.stdout.write(`\r\x1b[K${mutedColor}${displayLine}${reset}\n`);
|
|
794
|
+
}
|
|
795
|
+
// Update count of lines we've rendered (command + output lines)
|
|
796
|
+
linesRendered = 1 + displayLines.length;
|
|
797
|
+
};
|
|
798
|
+
// Initial display
|
|
799
|
+
renderOutput(maxLinesOutput);
|
|
800
|
+
try {
|
|
801
|
+
// Spawn the command
|
|
802
|
+
const proc = Bun.spawn(cmd, {
|
|
803
|
+
cwd,
|
|
804
|
+
env: { ...process.env, ...env },
|
|
805
|
+
stdout: 'pipe',
|
|
806
|
+
stderr: 'pipe',
|
|
807
|
+
});
|
|
808
|
+
// Process output streams
|
|
809
|
+
const processStream = async (stream) => {
|
|
810
|
+
const reader = stream.getReader();
|
|
811
|
+
const decoder = new TextDecoder();
|
|
812
|
+
let buffer = '';
|
|
813
|
+
try {
|
|
814
|
+
while (true) {
|
|
815
|
+
const { done, value } = await reader.read();
|
|
816
|
+
if (done)
|
|
817
|
+
break;
|
|
818
|
+
buffer += decoder.decode(value, { stream: true });
|
|
819
|
+
const lines = buffer.split('\n');
|
|
820
|
+
buffer = lines.pop() || ''; // Keep incomplete line in buffer
|
|
821
|
+
for (const line of lines) {
|
|
822
|
+
if (line.trim()) {
|
|
823
|
+
allOutputLines.push(line);
|
|
824
|
+
renderOutput(maxLinesOutput); // Show last N lines while streaming
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
finally {
|
|
830
|
+
reader.releaseLock();
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
// Process both stdout and stderr
|
|
834
|
+
await Promise.all([processStream(proc.stdout), processStream(proc.stderr)]);
|
|
835
|
+
// Wait for process to exit
|
|
836
|
+
const exitCode = await proc.exited;
|
|
837
|
+
// If clearOnSuccess is true and command succeeded, clear everything
|
|
838
|
+
if (clearOnSuccess && exitCode === 0) {
|
|
839
|
+
if (linesRendered > 0) {
|
|
840
|
+
// Move up to the command line
|
|
841
|
+
process.stdout.write(`\x1b[${linesRendered}A`);
|
|
842
|
+
// Clear each line (entire line) and move cursor back up
|
|
843
|
+
for (let i = 0; i < linesRendered; i++) {
|
|
844
|
+
process.stdout.write('\x1b[2K'); // Clear entire line
|
|
845
|
+
if (i < linesRendered - 1) {
|
|
846
|
+
process.stdout.write('\x1b[B'); // Move down one line
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
// Move cursor back up to original position
|
|
850
|
+
process.stdout.write(`\x1b[${linesRendered}A\r`);
|
|
851
|
+
}
|
|
852
|
+
return exitCode;
|
|
853
|
+
}
|
|
854
|
+
// Clear all rendered lines completely
|
|
855
|
+
if (linesRendered > 0) {
|
|
856
|
+
// Move up to the command line (first line of our output)
|
|
857
|
+
process.stdout.write(`\x1b[${linesRendered}A`);
|
|
858
|
+
// Move to beginning of line and clear from cursor to end of screen
|
|
859
|
+
process.stdout.write('\r\x1b[J');
|
|
860
|
+
}
|
|
861
|
+
// Determine icon based on exit code
|
|
862
|
+
const icon = exitCode === 0 ? ICONS.success : ICONS.error;
|
|
863
|
+
const statusColor = exitCode === 0 ? green : red;
|
|
864
|
+
// Show final status: icon + command
|
|
865
|
+
process.stdout.write(`\r\x1b[K${statusColor}${icon}${reset} ${cmdColor}${displayCmd}${reset}\n`);
|
|
866
|
+
// Determine how many lines to show in final output
|
|
867
|
+
const finalLinesToShow = exitCode === 0 ? maxLinesOutput : maxLinesOnFailure;
|
|
868
|
+
// Show final output lines
|
|
869
|
+
const finalOutputLines = allOutputLines.slice(-finalLinesToShow);
|
|
870
|
+
for (const line of finalOutputLines) {
|
|
871
|
+
let displayLine = line;
|
|
872
|
+
if (truncate && getDisplayWidth(displayLine) > maxLineWidth) {
|
|
873
|
+
displayLine = displayLine.slice(0, maxLineWidth - 3) + '...';
|
|
874
|
+
}
|
|
875
|
+
process.stdout.write(`\r\x1b[K${mutedColor}${displayLine}${reset}\n`);
|
|
876
|
+
}
|
|
877
|
+
return exitCode;
|
|
878
|
+
}
|
|
879
|
+
catch (err) {
|
|
880
|
+
// Move cursor up to clear our UI
|
|
881
|
+
if (linesRendered > 0) {
|
|
882
|
+
process.stdout.write(`\x1b[${linesRendered}A`);
|
|
883
|
+
// Clear all our lines
|
|
884
|
+
for (let i = 0; i < linesRendered; i++) {
|
|
885
|
+
process.stdout.write('\r\x1b[K\n');
|
|
886
|
+
}
|
|
887
|
+
process.stdout.write(`\x1b[${linesRendered}A`);
|
|
888
|
+
}
|
|
889
|
+
// Show error status
|
|
890
|
+
process.stdout.write(`\r\x1b[K${red}$${reset} ${cmdColor}${displayCmd}${reset}\n`);
|
|
891
|
+
// Log the error
|
|
892
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
893
|
+
console.error(`${red}${ICONS.error} Failed to spawn command: ${errorMsg}${reset}`);
|
|
894
|
+
if (cwd) {
|
|
895
|
+
console.error(`${mutedColor} cwd: ${cwd}${reset}`);
|
|
896
|
+
}
|
|
897
|
+
console.error(`${mutedColor} cmd: ${cmd.join(' ')}${reset}`);
|
|
898
|
+
return 1; // Return non-zero exit code
|
|
899
|
+
}
|
|
900
|
+
finally {
|
|
901
|
+
// Always restore cursor visibility
|
|
902
|
+
process.stdout.write('\x1B[?25h');
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Prompt user for text input
|
|
907
|
+
* Returns the input string
|
|
908
|
+
*/
|
|
909
|
+
export async function prompt(message) {
|
|
910
|
+
process.stdout.write(message);
|
|
911
|
+
// Check if we're in a TTY environment
|
|
912
|
+
if (!process.stdin.isTTY) {
|
|
913
|
+
console.log('');
|
|
914
|
+
return '';
|
|
915
|
+
}
|
|
916
|
+
// Use readline for full line input
|
|
917
|
+
const rl = readline.createInterface({
|
|
918
|
+
input: process.stdin,
|
|
919
|
+
output: process.stdout,
|
|
920
|
+
});
|
|
921
|
+
return new Promise((resolve) => {
|
|
922
|
+
rl.question('', (answer) => {
|
|
923
|
+
rl.close();
|
|
924
|
+
resolve(answer);
|
|
925
|
+
});
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
export async function selectOrganization(orgs, initial) {
|
|
929
|
+
if (orgs.length === 0) {
|
|
930
|
+
fatal('You do not belong to any organizations.\n' +
|
|
931
|
+
'Please contact support or create an organization at https://agentuity.com');
|
|
932
|
+
}
|
|
933
|
+
if (process.env.AGENTUITY_CLOUD_ORG_ID) {
|
|
934
|
+
const org = orgs.find((o) => o.id === process.env.AGENTUITY_CLOUD_ORG_ID);
|
|
935
|
+
if (org) {
|
|
936
|
+
return org.id;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
if (!process.stdin.isTTY) {
|
|
940
|
+
if (orgs.length === 1) {
|
|
941
|
+
return orgs[0].id;
|
|
942
|
+
}
|
|
943
|
+
if (initial) {
|
|
944
|
+
return initial;
|
|
945
|
+
}
|
|
946
|
+
fatal('Organization selection required but cannot prompt in non-interactive environment. Set AGENTUITY_CLOUD_ORG_ID or provide a default organization using --org-id');
|
|
947
|
+
}
|
|
948
|
+
const response = await enquirer.prompt({
|
|
949
|
+
type: 'select',
|
|
950
|
+
name: 'action',
|
|
951
|
+
message: 'Select an organization',
|
|
952
|
+
initial: initial || (orgs.length === 1 ? orgs[0].id : undefined),
|
|
953
|
+
choices: orgs.map((o) => ({ message: o.name, name: o.id })),
|
|
954
|
+
});
|
|
955
|
+
return response.action;
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* show a project list picker
|
|
959
|
+
*
|
|
960
|
+
* @param apiClient
|
|
961
|
+
* @param showDeployment
|
|
962
|
+
* @returns
|
|
963
|
+
*/
|
|
964
|
+
export async function showProjectList(apiClient, showDeploymentId = false) {
|
|
965
|
+
const projects = await spinner({
|
|
966
|
+
message: 'Fetching projects',
|
|
967
|
+
clearOnSuccess: true,
|
|
968
|
+
callback: () => {
|
|
969
|
+
return projectList(apiClient, showDeploymentId);
|
|
970
|
+
},
|
|
971
|
+
});
|
|
972
|
+
if (projects.length === 0) {
|
|
973
|
+
return '';
|
|
974
|
+
}
|
|
975
|
+
// TODO: might want to sort by the last org_id we used
|
|
976
|
+
if (projects) {
|
|
977
|
+
projects.sort((a, b) => {
|
|
978
|
+
return a.name.localeCompare(b.name);
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
const response = await enquirer.prompt({
|
|
982
|
+
type: 'select',
|
|
983
|
+
name: 'id',
|
|
984
|
+
message: 'Select a project:',
|
|
985
|
+
choices: projects.map((p) => ({
|
|
986
|
+
name: p.id,
|
|
987
|
+
message: `${p.name.padEnd(25, ' ')} ${muted(p.id)} ${showDeploymentId ? muted(p.latestDeploymentId ?? 'no deployment') : ''}`,
|
|
988
|
+
})),
|
|
989
|
+
});
|
|
990
|
+
return response.id;
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Show a profile list picker
|
|
994
|
+
*
|
|
995
|
+
* @param profiles List of profiles to choose from
|
|
996
|
+
* @param message Prompt message
|
|
997
|
+
* @returns The name of the selected profile
|
|
998
|
+
*/
|
|
999
|
+
export async function showProfileList(profiles, message = 'Select a profile:') {
|
|
1000
|
+
if (profiles.length === 0) {
|
|
1001
|
+
warning('No profiles found');
|
|
1002
|
+
process.exit(0);
|
|
1003
|
+
}
|
|
1004
|
+
// If only one profile, just return it? No, let them confirm/see it if they asked to pick?
|
|
1005
|
+
// But for "use" it implies switching. If only one, you are already on it or it's the only choice.
|
|
1006
|
+
// But for delete, you might want to delete the only one.
|
|
1007
|
+
// So always show list.
|
|
1008
|
+
// Find currently selected profile for initial selection
|
|
1009
|
+
const selectedProfile = profiles.find((p) => p.selected);
|
|
1010
|
+
const initial = selectedProfile ? selectedProfile.name : undefined;
|
|
1011
|
+
// If non-interactive, return initial or first
|
|
1012
|
+
if (!process.stdin.isTTY) {
|
|
1013
|
+
if (initial)
|
|
1014
|
+
return initial;
|
|
1015
|
+
if (profiles.length === 1) {
|
|
1016
|
+
return profiles[0].name;
|
|
1017
|
+
}
|
|
1018
|
+
fatal('Profile selection required but cannot prompt in non-interactive environment. ' +
|
|
1019
|
+
'Pass a profile name explicitly when running non-interactively.');
|
|
1020
|
+
}
|
|
1021
|
+
const response = await enquirer.prompt({
|
|
1022
|
+
type: 'select',
|
|
1023
|
+
name: 'name',
|
|
1024
|
+
message: message,
|
|
1025
|
+
initial: initial,
|
|
1026
|
+
choices: profiles.map((p) => ({
|
|
1027
|
+
name: p.name,
|
|
1028
|
+
message: p.selected ? `${p.name.padEnd(15, ' ')} ${muted('(current)')}` : p.name,
|
|
1029
|
+
})),
|
|
1030
|
+
});
|
|
1031
|
+
return response.name;
|
|
1032
|
+
}
|
|
1033
|
+
export function json(value) {
|
|
1034
|
+
const stringValue = typeof value === 'string' ? value : JSON.stringify(value, null, 2);
|
|
1035
|
+
if (shouldUseColors() && process.stdout.isTTY) {
|
|
1036
|
+
try {
|
|
1037
|
+
console.log(colorize(stringValue));
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
catch {
|
|
1041
|
+
/* */
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
console.log(stringValue);
|
|
1045
|
+
}
|
|
1046
|
+
export function plural(count, singular, plural) {
|
|
1047
|
+
switch (count) {
|
|
1048
|
+
case 0:
|
|
1049
|
+
return plural;
|
|
1050
|
+
case 1:
|
|
1051
|
+
return singular;
|
|
1052
|
+
default:
|
|
1053
|
+
return plural;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Display data in a formatted table using console-table-printer
|
|
1058
|
+
*
|
|
1059
|
+
* Supports two modes:
|
|
1060
|
+
* 1. Simple mode: Pass data array and optional column names
|
|
1061
|
+
* 2. Advanced mode: Pass column configurations with custom names and alignment
|
|
1062
|
+
*
|
|
1063
|
+
* @param data - Array of data objects to display
|
|
1064
|
+
* @param columns - Column names or column configurations
|
|
1065
|
+
* @param options - Additional options
|
|
1066
|
+
* @returns If render=true, returns the table as a string, otherwise prints to stdout
|
|
1067
|
+
*/
|
|
1068
|
+
export function table(data, columns, options) {
|
|
1069
|
+
// Dynamic import to avoid type errors (console-table-printer has poor typings)
|
|
1070
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
1071
|
+
const { Table } = require('console-table-printer');
|
|
1072
|
+
if (!data || data.length === 0) {
|
|
1073
|
+
return options?.render ? '' : undefined;
|
|
1074
|
+
}
|
|
1075
|
+
// Determine if we're using advanced column config or simple column names
|
|
1076
|
+
const isAdvancedMode = columns && columns.length > 0 && typeof columns[0] === 'object';
|
|
1077
|
+
let tableConfig;
|
|
1078
|
+
if (isAdvancedMode) {
|
|
1079
|
+
// Advanced mode: use provided column configurations
|
|
1080
|
+
tableConfig = {
|
|
1081
|
+
columns: columns.map((col) => ({
|
|
1082
|
+
name: col.name,
|
|
1083
|
+
alignment: col.alignment || 'left',
|
|
1084
|
+
})),
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
else {
|
|
1088
|
+
// Simple mode: determine column names from data or columns parameter
|
|
1089
|
+
const columnNames = columns
|
|
1090
|
+
? columns.map((c) => String(c))
|
|
1091
|
+
: data.length > 0
|
|
1092
|
+
? Object.keys(data[0])
|
|
1093
|
+
: [];
|
|
1094
|
+
tableConfig = {
|
|
1095
|
+
columns: columnNames.map((name) => ({
|
|
1096
|
+
name,
|
|
1097
|
+
alignment: 'left',
|
|
1098
|
+
})),
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
const t = new Table(tableConfig);
|
|
1102
|
+
// Add rows to table
|
|
1103
|
+
for (const row of data) {
|
|
1104
|
+
if (columns && !isAdvancedMode) {
|
|
1105
|
+
// Simple mode with column filtering
|
|
1106
|
+
const filtered = {};
|
|
1107
|
+
for (const col of columns) {
|
|
1108
|
+
filtered[String(col)] = row[col];
|
|
1109
|
+
}
|
|
1110
|
+
t.addRow(filtered);
|
|
1111
|
+
}
|
|
1112
|
+
else {
|
|
1113
|
+
// Advanced mode or no column filtering
|
|
1114
|
+
t.addRow(row);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
if (options?.render) {
|
|
1118
|
+
return t.render();
|
|
1119
|
+
}
|
|
1120
|
+
else {
|
|
1121
|
+
t.printTable();
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
//# sourceMappingURL=tui.js.map
|