@agentuity/cli 0.0.42 → 0.0.44
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/AGENTS.md +1 -1
- package/README.md +1 -1
- package/bin/cli.ts +7 -5
- package/dist/api.d.ts +3 -3
- package/dist/api.d.ts.map +1 -1
- package/dist/auth.d.ts +10 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/banner.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cmd/auth/api.d.ts +4 -4
- package/dist/cmd/auth/api.d.ts.map +1 -1
- package/dist/cmd/auth/index.d.ts.map +1 -1
- package/dist/cmd/auth/login.d.ts.map +1 -1
- package/dist/cmd/auth/signup.d.ts.map +1 -1
- package/dist/cmd/auth/ssh/add.d.ts +2 -0
- package/dist/cmd/auth/ssh/add.d.ts.map +1 -0
- package/dist/cmd/auth/ssh/api.d.ts +16 -0
- package/dist/cmd/auth/ssh/api.d.ts.map +1 -0
- package/dist/cmd/auth/ssh/delete.d.ts +2 -0
- package/dist/cmd/auth/ssh/delete.d.ts.map +1 -0
- package/dist/cmd/auth/ssh/index.d.ts +3 -0
- package/dist/cmd/auth/ssh/index.d.ts.map +1 -0
- package/dist/cmd/auth/ssh/list.d.ts +2 -0
- package/dist/cmd/auth/ssh/list.d.ts.map +1 -0
- package/dist/cmd/auth/whoami.d.ts +2 -0
- package/dist/cmd/auth/whoami.d.ts.map +1 -0
- package/dist/cmd/bundle/ast.d.ts +14 -3
- package/dist/cmd/bundle/ast.d.ts.map +1 -1
- package/dist/cmd/bundle/ast.test.d.ts +2 -0
- package/dist/cmd/bundle/ast.test.d.ts.map +1 -0
- package/dist/cmd/bundle/bundler.d.ts +6 -1
- package/dist/cmd/bundle/bundler.d.ts.map +1 -1
- package/dist/cmd/bundle/file.d.ts.map +1 -1
- package/dist/cmd/bundle/fix-duplicate-exports.d.ts +2 -0
- package/dist/cmd/bundle/fix-duplicate-exports.d.ts.map +1 -0
- package/dist/cmd/bundle/fix-duplicate-exports.test.d.ts +2 -0
- package/dist/cmd/bundle/fix-duplicate-exports.test.d.ts.map +1 -0
- package/dist/cmd/bundle/index.d.ts +1 -1
- package/dist/cmd/bundle/index.d.ts.map +1 -1
- package/dist/cmd/bundle/plugin.d.ts +2 -0
- package/dist/cmd/bundle/plugin.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -0
- package/dist/cmd/cloud/domain.d.ts +17 -0
- package/dist/cmd/cloud/domain.d.ts.map +1 -0
- package/dist/cmd/cloud/index.d.ts.map +1 -0
- package/dist/cmd/cloud/resource/add.d.ts +2 -0
- package/dist/cmd/cloud/resource/add.d.ts.map +1 -0
- package/dist/cmd/cloud/resource/delete.d.ts +2 -0
- package/dist/cmd/cloud/resource/delete.d.ts.map +1 -0
- package/dist/cmd/cloud/resource/index.d.ts +3 -0
- package/dist/cmd/cloud/resource/index.d.ts.map +1 -0
- package/dist/cmd/cloud/resource/list.d.ts +2 -0
- package/dist/cmd/cloud/resource/list.d.ts.map +1 -0
- package/dist/cmd/cloud/scp/download.d.ts +2 -0
- package/dist/cmd/cloud/scp/download.d.ts.map +1 -0
- package/dist/cmd/cloud/scp/index.d.ts +3 -0
- package/dist/cmd/cloud/scp/index.d.ts.map +1 -0
- package/dist/cmd/cloud/scp/upload.d.ts +2 -0
- package/dist/cmd/cloud/scp/upload.d.ts.map +1 -0
- package/dist/cmd/cloud/ssh.d.ts +2 -0
- package/dist/cmd/cloud/ssh.d.ts.map +1 -0
- package/dist/cmd/dev/api.d.ts +18 -0
- package/dist/cmd/dev/api.d.ts.map +1 -0
- package/dist/cmd/dev/download.d.ts +11 -0
- package/dist/cmd/dev/download.d.ts.map +1 -0
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/templates.d.ts +3 -0
- package/dist/cmd/dev/templates.d.ts.map +1 -0
- package/dist/cmd/env/delete.d.ts +2 -0
- package/dist/cmd/env/delete.d.ts.map +1 -0
- package/dist/cmd/env/get.d.ts +2 -0
- package/dist/cmd/env/get.d.ts.map +1 -0
- package/dist/cmd/env/import.d.ts +2 -0
- package/dist/cmd/env/import.d.ts.map +1 -0
- package/dist/cmd/env/index.d.ts +2 -0
- package/dist/cmd/env/index.d.ts.map +1 -0
- package/dist/cmd/env/list.d.ts.map +1 -0
- package/dist/cmd/env/pull.d.ts +2 -0
- package/dist/cmd/env/pull.d.ts.map +1 -0
- package/dist/cmd/env/push.d.ts +2 -0
- package/dist/cmd/env/push.d.ts.map +1 -0
- package/dist/cmd/env/set.d.ts +2 -0
- package/dist/cmd/env/set.d.ts.map +1 -0
- package/dist/cmd/profile/show.d.ts.map +1 -1
- package/dist/cmd/project/create.d.ts.map +1 -1
- package/dist/cmd/project/delete.d.ts.map +1 -1
- package/dist/cmd/project/download.d.ts +1 -1
- package/dist/cmd/project/download.d.ts.map +1 -1
- package/dist/cmd/project/list.d.ts.map +1 -1
- package/dist/cmd/project/show.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.d.ts +5 -1
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/cmd/secret/delete.d.ts +2 -0
- package/dist/cmd/secret/delete.d.ts.map +1 -0
- package/dist/cmd/secret/get.d.ts +2 -0
- package/dist/cmd/secret/get.d.ts.map +1 -0
- package/dist/cmd/secret/import.d.ts +2 -0
- package/dist/cmd/secret/import.d.ts.map +1 -0
- package/dist/cmd/secret/index.d.ts +2 -0
- package/dist/cmd/secret/index.d.ts.map +1 -0
- package/dist/cmd/secret/list.d.ts +2 -0
- package/dist/cmd/secret/list.d.ts.map +1 -0
- package/dist/cmd/secret/pull.d.ts +2 -0
- package/dist/cmd/secret/pull.d.ts.map +1 -0
- package/dist/cmd/secret/push.d.ts +2 -0
- package/dist/cmd/secret/push.d.ts.map +1 -0
- package/dist/cmd/secret/set.d.ts +2 -0
- package/dist/cmd/secret/set.d.ts.map +1 -0
- package/dist/cmd/version/index.d.ts.map +1 -1
- package/dist/config.d.ts +11 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/crypto/box.d.ts +65 -0
- package/dist/crypto/box.d.ts.map +1 -0
- package/dist/crypto/box.test.d.ts +2 -0
- package/dist/crypto/box.test.d.ts.map +1 -0
- package/dist/download.d.ts.map +1 -1
- package/dist/env-util.d.ts +67 -0
- package/dist/env-util.d.ts.map +1 -0
- package/dist/env-util.test.d.ts +2 -0
- package/dist/env-util.test.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/schema-parser.d.ts.map +1 -1
- package/dist/steps.d.ts +4 -1
- package/dist/steps.d.ts.map +1 -1
- package/dist/terminal.d.ts.map +1 -1
- package/dist/tui.d.ts +32 -2
- package/dist/tui.d.ts.map +1 -1
- package/dist/types.d.ts +250 -127
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/detectSubagent.d.ts +15 -0
- package/dist/utils/detectSubagent.d.ts.map +1 -0
- package/dist/utils/zip.d.ts +7 -0
- package/dist/utils/zip.d.ts.map +1 -0
- package/package.json +11 -3
- package/src/api-errors.md +2 -2
- package/src/api.ts +12 -7
- package/src/auth.ts +116 -7
- package/src/banner.ts +13 -6
- package/src/cli.ts +709 -36
- package/src/cmd/auth/api.ts +10 -16
- package/src/cmd/auth/index.ts +3 -1
- package/src/cmd/auth/login.ts +24 -8
- package/src/cmd/auth/signup.ts +15 -11
- package/src/cmd/auth/ssh/add.ts +263 -0
- package/src/cmd/auth/ssh/api.ts +94 -0
- package/src/cmd/auth/ssh/delete.ts +102 -0
- package/src/cmd/auth/ssh/index.ts +10 -0
- package/src/cmd/auth/ssh/list.ts +74 -0
- package/src/cmd/auth/whoami.ts +69 -0
- package/src/cmd/bundle/ast.test.ts +565 -0
- package/src/cmd/bundle/ast.ts +457 -44
- package/src/cmd/bundle/bundler.ts +255 -57
- package/src/cmd/bundle/file.ts +6 -12
- package/src/cmd/bundle/fix-duplicate-exports.test.ts +387 -0
- package/src/cmd/bundle/fix-duplicate-exports.ts +204 -0
- package/src/cmd/bundle/index.ts +11 -11
- package/src/cmd/bundle/patch/aisdk.ts +1 -1
- package/src/cmd/bundle/plugin.ts +373 -53
- package/src/cmd/cloud/deploy.ts +336 -0
- package/src/cmd/cloud/domain.ts +92 -0
- package/src/cmd/cloud/index.ts +11 -0
- package/src/cmd/cloud/resource/add.ts +56 -0
- package/src/cmd/cloud/resource/delete.ts +120 -0
- package/src/cmd/cloud/resource/index.ts +11 -0
- package/src/cmd/cloud/resource/list.ts +69 -0
- package/src/cmd/cloud/scp/download.ts +59 -0
- package/src/cmd/cloud/scp/index.ts +9 -0
- package/src/cmd/cloud/scp/upload.ts +62 -0
- package/src/cmd/cloud/ssh.ts +68 -0
- package/src/cmd/dev/api.ts +46 -0
- package/src/cmd/dev/download.ts +111 -0
- package/src/cmd/dev/index.ts +362 -34
- package/src/cmd/dev/templates.ts +84 -0
- package/src/cmd/env/delete.ts +47 -0
- package/src/cmd/env/get.ts +53 -0
- package/src/cmd/env/import.ts +102 -0
- package/src/cmd/env/index.ts +22 -0
- package/src/cmd/env/list.ts +56 -0
- package/src/cmd/env/pull.ts +80 -0
- package/src/cmd/env/push.ts +37 -0
- package/src/cmd/env/set.ts +71 -0
- package/src/cmd/index.ts +2 -2
- package/src/cmd/profile/show.ts +15 -6
- package/src/cmd/project/create.ts +7 -2
- package/src/cmd/project/delete.ts +75 -18
- package/src/cmd/project/download.ts +3 -3
- package/src/cmd/project/list.ts +8 -8
- package/src/cmd/project/show.ts +3 -7
- package/src/cmd/project/template-flow.ts +186 -48
- package/src/cmd/secret/delete.ts +40 -0
- package/src/cmd/secret/get.ts +54 -0
- package/src/cmd/secret/import.ts +64 -0
- package/src/cmd/secret/index.ts +22 -0
- package/src/cmd/secret/list.ts +56 -0
- package/src/cmd/secret/pull.ts +78 -0
- package/src/cmd/secret/push.ts +37 -0
- package/src/cmd/secret/set.ts +45 -0
- package/src/cmd/version/index.ts +2 -1
- package/src/config.ts +257 -27
- package/src/crypto/box.test.ts +431 -0
- package/src/crypto/box.ts +477 -0
- package/src/download.ts +1 -0
- package/src/env-util.test.ts +194 -0
- package/src/env-util.ts +290 -0
- package/src/index.ts +5 -1
- package/src/schema-parser.ts +2 -3
- package/src/steps.ts +144 -10
- package/src/terminal.ts +24 -23
- package/src/tui.ts +208 -68
- package/src/types.ts +292 -202
- package/src/utils/detectSubagent.ts +31 -0
- package/src/utils/zip.ts +38 -0
- package/dist/cmd/example/create-user.d.ts +0 -2
- package/dist/cmd/example/create-user.d.ts.map +0 -1
- package/dist/cmd/example/create.d.ts +0 -2
- package/dist/cmd/example/create.d.ts.map +0 -1
- package/dist/cmd/example/deploy.d.ts.map +0 -1
- package/dist/cmd/example/index.d.ts.map +0 -1
- package/dist/cmd/example/list.d.ts.map +0 -1
- package/dist/cmd/example/optional-auth.d.ts +0 -3
- package/dist/cmd/example/optional-auth.d.ts.map +0 -1
- package/dist/cmd/example/run-command.d.ts +0 -2
- package/dist/cmd/example/run-command.d.ts.map +0 -1
- package/dist/cmd/example/sound.d.ts +0 -3
- package/dist/cmd/example/sound.d.ts.map +0 -1
- package/dist/cmd/example/spinner.d.ts +0 -2
- package/dist/cmd/example/spinner.d.ts.map +0 -1
- package/dist/cmd/example/steps.d.ts +0 -2
- package/dist/cmd/example/steps.d.ts.map +0 -1
- package/dist/cmd/example/version.d.ts +0 -2
- package/dist/cmd/example/version.d.ts.map +0 -1
- package/dist/logger.d.ts +0 -24
- package/dist/logger.d.ts.map +0 -1
- package/src/cmd/example/create-user.ts +0 -38
- package/src/cmd/example/create.ts +0 -31
- package/src/cmd/example/deploy.ts +0 -36
- package/src/cmd/example/index.ts +0 -29
- package/src/cmd/example/list.ts +0 -32
- package/src/cmd/example/optional-auth.ts +0 -38
- package/src/cmd/example/run-command.ts +0 -45
- package/src/cmd/example/sound.ts +0 -14
- package/src/cmd/example/spinner.ts +0 -44
- package/src/cmd/example/steps.ts +0 -66
- package/src/cmd/example/version.ts +0 -13
- package/src/logger.ts +0 -235
- /package/dist/cmd/{example → cloud}/deploy.d.ts +0 -0
- /package/dist/cmd/{example → cloud}/index.d.ts +0 -0
- /package/dist/cmd/{example → env}/list.d.ts +0 -0
package/src/env-util.ts
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for handling .env files
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
|
|
7
|
+
export interface EnvVars {
|
|
8
|
+
[key: string]: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Find the appropriate .env file to use for user environment variables.
|
|
13
|
+
* Always returns .env.production path (will be created if needed).
|
|
14
|
+
* .env should only contain AGENTUITY_SDK_KEY.
|
|
15
|
+
*/
|
|
16
|
+
export async function findEnvFile(dir: string): Promise<string> {
|
|
17
|
+
return join(dir, '.env.production');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Find an existing env file for reading.
|
|
22
|
+
* Preference: .env.production > .env
|
|
23
|
+
*/
|
|
24
|
+
export async function findExistingEnvFile(dir: string): Promise<string> {
|
|
25
|
+
const productionEnv = join(dir, '.env.production');
|
|
26
|
+
const defaultEnv = join(dir, '.env');
|
|
27
|
+
|
|
28
|
+
if (await Bun.file(productionEnv).exists()) {
|
|
29
|
+
return productionEnv;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return defaultEnv;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Parse a single line from an .env file
|
|
37
|
+
* Handles comments, empty lines, and quoted values
|
|
38
|
+
*/
|
|
39
|
+
export function parseEnvLine(line: string): { key: string; value: string } | null {
|
|
40
|
+
const trimmed = line.trim();
|
|
41
|
+
|
|
42
|
+
// Skip empty lines and comments
|
|
43
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const equalIndex = trimmed.indexOf('=');
|
|
48
|
+
if (equalIndex === -1) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const key = trimmed.slice(0, equalIndex).trim();
|
|
53
|
+
let value = trimmed.slice(equalIndex + 1).trim();
|
|
54
|
+
|
|
55
|
+
// Remove surrounding quotes if present
|
|
56
|
+
if (
|
|
57
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
58
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
59
|
+
) {
|
|
60
|
+
value = value.slice(1, -1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return { key, value };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Read and parse an .env file
|
|
68
|
+
*/
|
|
69
|
+
export async function readEnvFile(path: string): Promise<EnvVars> {
|
|
70
|
+
const file = Bun.file(path);
|
|
71
|
+
|
|
72
|
+
if (!(await file.exists())) {
|
|
73
|
+
return {};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const content = await file.text();
|
|
77
|
+
const lines = content.split('\n');
|
|
78
|
+
const env: EnvVars = {};
|
|
79
|
+
|
|
80
|
+
for (const line of lines) {
|
|
81
|
+
const parsed = parseEnvLine(line);
|
|
82
|
+
if (parsed) {
|
|
83
|
+
env[parsed.key] = parsed.value;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return env;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Write environment variables to an .env file
|
|
92
|
+
* Optionally skip certain keys (like AGENTUITY_SDK_KEY)
|
|
93
|
+
*/
|
|
94
|
+
export async function writeEnvFile(
|
|
95
|
+
path: string,
|
|
96
|
+
vars: EnvVars,
|
|
97
|
+
options?: {
|
|
98
|
+
skipKeys?: string[];
|
|
99
|
+
addComment?: (key: string) => string | null;
|
|
100
|
+
}
|
|
101
|
+
): Promise<void> {
|
|
102
|
+
const skipKeys = options?.skipKeys || [];
|
|
103
|
+
const lines: string[] = [];
|
|
104
|
+
|
|
105
|
+
// Sort keys for consistent output
|
|
106
|
+
const sortedKeys = Object.keys(vars).sort();
|
|
107
|
+
|
|
108
|
+
for (const key of sortedKeys) {
|
|
109
|
+
if (skipKeys.includes(key)) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const value = vars[key];
|
|
114
|
+
|
|
115
|
+
// Add comment if provided
|
|
116
|
+
if (options?.addComment) {
|
|
117
|
+
const comment = options.addComment(key);
|
|
118
|
+
if (comment) {
|
|
119
|
+
lines.push(`# ${comment}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Write key=value
|
|
124
|
+
lines.push(`${key}=${value}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const content = lines.join('\n') + '\n';
|
|
128
|
+
await Bun.write(path, content);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Merge environment variables with special handling
|
|
133
|
+
* - Later values override earlier values
|
|
134
|
+
* - Can filter out keys (like AGENTUITY_* keys)
|
|
135
|
+
*/
|
|
136
|
+
export function mergeEnvVars(
|
|
137
|
+
base: EnvVars,
|
|
138
|
+
updates: EnvVars,
|
|
139
|
+
options?: {
|
|
140
|
+
filterPrefix?: string;
|
|
141
|
+
}
|
|
142
|
+
): EnvVars {
|
|
143
|
+
const merged = { ...base };
|
|
144
|
+
const filterPrefix = options?.filterPrefix;
|
|
145
|
+
|
|
146
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
147
|
+
// Skip keys with filter prefix if specified
|
|
148
|
+
if (filterPrefix && key.startsWith(filterPrefix)) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
merged[key] = value;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return merged;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Filter out AGENTUITY_ prefixed keys from env vars
|
|
160
|
+
* This is used when pushing to the cloud to avoid sending SDK keys
|
|
161
|
+
*/
|
|
162
|
+
export function filterAgentuitySdkKeys(vars: EnvVars): EnvVars {
|
|
163
|
+
const filtered: EnvVars = {};
|
|
164
|
+
|
|
165
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
166
|
+
if (!key.startsWith('AGENTUITY_')) {
|
|
167
|
+
filtered[key] = value;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return filtered;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Split env vars into env and secrets based on key names
|
|
176
|
+
* Convention: Keys ending with _SECRET, _KEY, _TOKEN, _PASSWORD are secrets
|
|
177
|
+
*/
|
|
178
|
+
export function splitEnvAndSecrets(vars: EnvVars): {
|
|
179
|
+
env: EnvVars;
|
|
180
|
+
secrets: EnvVars;
|
|
181
|
+
} {
|
|
182
|
+
const env: EnvVars = {};
|
|
183
|
+
const secrets: EnvVars = {};
|
|
184
|
+
|
|
185
|
+
const secretSuffixes = ['_SECRET', '_KEY', '_TOKEN', '_PASSWORD', '_PRIVATE'];
|
|
186
|
+
|
|
187
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
188
|
+
// Skip AGENTUITY_ prefixed keys
|
|
189
|
+
if (key.startsWith('AGENTUITY_')) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const isSecret = secretSuffixes.some((suffix) => key.endsWith(suffix));
|
|
194
|
+
|
|
195
|
+
if (isSecret) {
|
|
196
|
+
secrets[key] = value;
|
|
197
|
+
} else {
|
|
198
|
+
env[key] = value;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return { env, secrets };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Mask a secret value for display
|
|
207
|
+
*/
|
|
208
|
+
export function maskSecret(value: string): string {
|
|
209
|
+
if (!value) {
|
|
210
|
+
return '';
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (value.length <= 8) {
|
|
214
|
+
return '***';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Show first 4 and last 4 characters
|
|
218
|
+
return `${value.slice(0, 4)}...${value.slice(-4)}`;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Detect if a key or value looks like it should be a secret
|
|
223
|
+
*/
|
|
224
|
+
export function looksLikeSecret(key: string, value: string): boolean {
|
|
225
|
+
// Check key name for secret-like patterns
|
|
226
|
+
const secretKeyPatterns = [
|
|
227
|
+
/_SECRET$/i,
|
|
228
|
+
/_KEY$/i,
|
|
229
|
+
/_TOKEN$/i,
|
|
230
|
+
/_PASSWORD$/i,
|
|
231
|
+
/_PRIVATE$/i,
|
|
232
|
+
/_CERT$/i,
|
|
233
|
+
/_CERTIFICATE$/i,
|
|
234
|
+
/^SECRET_/i,
|
|
235
|
+
/^API_?KEY/i,
|
|
236
|
+
/^JWT/i,
|
|
237
|
+
/PASSWORD/i,
|
|
238
|
+
/CREDENTIAL/i,
|
|
239
|
+
/AUTH.*KEY/i,
|
|
240
|
+
];
|
|
241
|
+
|
|
242
|
+
const keyLooksSecret = secretKeyPatterns.some((pattern) => pattern.test(key));
|
|
243
|
+
if (keyLooksSecret) {
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Check value for secret-like patterns
|
|
248
|
+
if (!value || value.length < 8) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// JWT pattern (header.payload.signature)
|
|
253
|
+
if (/^eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(value)) {
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Bearer token pattern
|
|
258
|
+
if (/^Bearer\s+[A-Za-z0-9_-]{20,}$/i.test(value)) {
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// AWS/Cloud provider key patterns
|
|
263
|
+
if (/^(AKIA|ASIA)[A-Z0-9]{16}$/.test(value)) {
|
|
264
|
+
// AWS access key
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// GitHub token patterns
|
|
269
|
+
if (/^gh[ps]_[A-Za-z0-9_]{36,}$/.test(value)) {
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Generic long alphanumeric strings (likely API keys)
|
|
274
|
+
// Exclude UUIDs (8-4-4-4-12 format) and simple alphanumeric IDs
|
|
275
|
+
const isUUID = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(value);
|
|
276
|
+
if (!isUUID && /^[A-Za-z0-9_-]{32,}$/.test(value) && !/^[0-9]+$/.test(value)) {
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// PEM-encoded certificates or private keys
|
|
281
|
+
if (
|
|
282
|
+
value.includes('BEGIN CERTIFICATE') ||
|
|
283
|
+
value.includes('BEGIN PRIVATE KEY') ||
|
|
284
|
+
value.includes('BEGIN RSA PRIVATE KEY')
|
|
285
|
+
) {
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return false;
|
|
290
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -17,7 +17,11 @@ export {
|
|
|
17
17
|
getAuth,
|
|
18
18
|
} from './config';
|
|
19
19
|
export { APIClient, getAPIBaseURL, getAppBaseURL } from './api';
|
|
20
|
-
export {
|
|
20
|
+
export {
|
|
21
|
+
ConsoleLogger,
|
|
22
|
+
createLogger,
|
|
23
|
+
type ColorScheme as LoggerColorScheme,
|
|
24
|
+
} from '@agentuity/server';
|
|
21
25
|
export { showBanner } from './banner';
|
|
22
26
|
export { discoverCommands } from './cmd';
|
|
23
27
|
export { detectColorScheme } from './terminal';
|
package/src/schema-parser.ts
CHANGED
|
@@ -203,9 +203,8 @@ export function buildValidationInput(
|
|
|
203
203
|
if (schemas.options) {
|
|
204
204
|
const parsed = parseOptionsSchema(schemas.options);
|
|
205
205
|
for (const opt of parsed) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
206
|
+
// Always include the option value (even if undefined) so zod can apply defaults
|
|
207
|
+
result.options[opt.name] = rawOptions[opt.name];
|
|
209
208
|
}
|
|
210
209
|
}
|
|
211
210
|
|
package/src/steps.ts
CHANGED
|
@@ -6,6 +6,59 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { ColorScheme } from './terminal';
|
|
9
|
+
import type { LogLevel } from './types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get the appropriate exit function (Bun.exit or process.exit)
|
|
13
|
+
*/
|
|
14
|
+
function getExitFn(): (code: number) => never {
|
|
15
|
+
const bunExit = (globalThis as { Bun?: { exit?: (code: number) => never } }).Bun?.exit;
|
|
16
|
+
return typeof bunExit === 'function' ? bunExit : process.exit.bind(process);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Install interrupt handlers (SIGINT/SIGTERM + TTY raw mode for Ctrl+C)
|
|
21
|
+
*/
|
|
22
|
+
function installInterruptHandlers(onInterrupt: () => void): () => void {
|
|
23
|
+
const cleanupFns: Array<() => void> = [];
|
|
24
|
+
|
|
25
|
+
const sigHandler = () => onInterrupt();
|
|
26
|
+
process.on('SIGINT', sigHandler);
|
|
27
|
+
process.on('SIGTERM', sigHandler);
|
|
28
|
+
cleanupFns.push(() => {
|
|
29
|
+
process.off('SIGINT', sigHandler);
|
|
30
|
+
process.off('SIGTERM', sigHandler);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// TTY raw mode fallback for Bun/Windows/inconsistent SIGINT delivery
|
|
34
|
+
const stdin = process.stdin as unknown as NodeJS.ReadStream;
|
|
35
|
+
if (stdin && stdin.isTTY) {
|
|
36
|
+
const onData = (buf: Buffer) => {
|
|
37
|
+
// Ctrl+C is ASCII ETX (0x03)
|
|
38
|
+
if (buf.length === 1 && buf[0] === 0x03) onInterrupt();
|
|
39
|
+
};
|
|
40
|
+
try {
|
|
41
|
+
stdin.setRawMode?.(true);
|
|
42
|
+
} catch {
|
|
43
|
+
// ignore if not supported
|
|
44
|
+
}
|
|
45
|
+
stdin.resume?.();
|
|
46
|
+
stdin.on('data', onData);
|
|
47
|
+
cleanupFns.push(() => {
|
|
48
|
+
stdin.off?.('data', onData);
|
|
49
|
+
stdin.pause?.();
|
|
50
|
+
try {
|
|
51
|
+
stdin.setRawMode?.(false);
|
|
52
|
+
} catch {
|
|
53
|
+
// ignore if setRawMode fails
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return () => {
|
|
59
|
+
for (const fn of cleanupFns.splice(0)) fn();
|
|
60
|
+
};
|
|
61
|
+
}
|
|
9
62
|
|
|
10
63
|
// Spinner frames
|
|
11
64
|
const FRAMES = ['◐', '◓', '◑', '◒'];
|
|
@@ -35,7 +88,7 @@ const COLORS = {
|
|
|
35
88
|
// Spinner color sequence
|
|
36
89
|
const SPINNER_COLORS = ['cyan', 'blue', 'magenta', 'cyan'] as const;
|
|
37
90
|
|
|
38
|
-
let currentColorScheme: ColorScheme = 'dark';
|
|
91
|
+
let currentColorScheme: ColorScheme = process.env.CI ? 'light' : 'dark';
|
|
39
92
|
|
|
40
93
|
export function setStepsColorScheme(scheme: ColorScheme): void {
|
|
41
94
|
currentColorScheme = scheme;
|
|
@@ -117,8 +170,10 @@ type StepState =
|
|
|
117
170
|
* Each step runs its callback while showing a spinner animation.
|
|
118
171
|
* Steps can complete with success, skipped, or error status.
|
|
119
172
|
* Exits with code 1 if any step errors.
|
|
173
|
+
*
|
|
174
|
+
* When there's no TTY or log level is debug/trace, uses plain output instead of TUI.
|
|
120
175
|
*/
|
|
121
|
-
export async function runSteps(steps: Step[]): Promise<void> {
|
|
176
|
+
export async function runSteps(steps: Step[], logLevel?: LogLevel): Promise<void> {
|
|
122
177
|
const state: StepState[] = steps.map((s) => {
|
|
123
178
|
const stepType = s.type === 'progress' ? 'progress' : 'simple';
|
|
124
179
|
return stepType === 'progress'
|
|
@@ -130,26 +185,59 @@ export async function runSteps(steps: Step[]): Promise<void> {
|
|
|
130
185
|
: { type: 'simple' as const, label: s.label, run: s.run as () => Promise<StepOutcome> };
|
|
131
186
|
});
|
|
132
187
|
|
|
188
|
+
// Detect if we should use TUI (animated) or plain mode
|
|
189
|
+
const useTUI =
|
|
190
|
+
process.stdout.isTTY && (!logLevel || ['info', 'warn', 'error'].includes(logLevel));
|
|
191
|
+
|
|
192
|
+
if (useTUI) {
|
|
193
|
+
await runStepsTUI(state);
|
|
194
|
+
} else {
|
|
195
|
+
await runStepsPlain(state);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Run steps with animated TUI (original behavior)
|
|
201
|
+
*/
|
|
202
|
+
async function runStepsTUI(state: StepState[]): Promise<void> {
|
|
133
203
|
// Hide cursor
|
|
134
204
|
process.stdout.write('\x1B[?25l');
|
|
135
205
|
|
|
206
|
+
// Track active interval and interrupted state
|
|
207
|
+
let activeInterval: ReturnType<typeof setInterval> | null = null;
|
|
208
|
+
let interrupted = false;
|
|
209
|
+
|
|
210
|
+
// Set up Ctrl+C handler for graceful exit
|
|
211
|
+
const exit = getExitFn();
|
|
212
|
+
const onInterrupt = () => {
|
|
213
|
+
if (interrupted) return;
|
|
214
|
+
interrupted = true;
|
|
215
|
+
if (activeInterval) clearInterval(activeInterval);
|
|
216
|
+
process.stdout.write('\x1B[?25h\n'); // Show cursor
|
|
217
|
+
exit(130);
|
|
218
|
+
};
|
|
219
|
+
const restoreInterrupts = installInterruptHandlers(onInterrupt);
|
|
220
|
+
|
|
136
221
|
try {
|
|
137
222
|
// Initial render
|
|
138
223
|
process.stdout.write(renderSteps(state, -1) + '\n');
|
|
139
224
|
|
|
140
225
|
for (let stepIndex = 0; stepIndex < state.length; stepIndex++) {
|
|
226
|
+
if (interrupted) break;
|
|
227
|
+
|
|
141
228
|
const step = state[stepIndex];
|
|
142
229
|
let frameIndex = 0;
|
|
230
|
+
let currentFrame = '';
|
|
143
231
|
|
|
144
232
|
// Start spinner animation
|
|
145
|
-
|
|
233
|
+
activeInterval = setInterval(() => {
|
|
146
234
|
const colorKey = SPINNER_COLORS[frameIndex % SPINNER_COLORS.length];
|
|
147
235
|
const color = getColor(colorKey);
|
|
148
|
-
|
|
236
|
+
currentFrame = `${color}${COLORS.bold}${FRAMES[frameIndex % FRAMES.length]}${COLORS.reset}`;
|
|
149
237
|
|
|
150
238
|
// Move cursor up to the top of checklist
|
|
151
239
|
process.stdout.write(`\x1B[${state.length}A`);
|
|
152
|
-
process.stdout.write(renderSteps(state, stepIndex,
|
|
240
|
+
process.stdout.write(renderSteps(state, stepIndex, currentFrame) + '\n');
|
|
153
241
|
|
|
154
242
|
frameIndex++;
|
|
155
243
|
}, 120);
|
|
@@ -158,9 +246,9 @@ export async function runSteps(steps: Step[]): Promise<void> {
|
|
|
158
246
|
const progressCallback: ProgressCallback = (progress: number) => {
|
|
159
247
|
step.progress = Math.min(100, Math.max(0, progress));
|
|
160
248
|
|
|
161
|
-
// Move cursor up
|
|
249
|
+
// Move cursor up and render with current spinner frame
|
|
162
250
|
process.stdout.write(`\x1B[${state.length}A`);
|
|
163
|
-
process.stdout.write(renderSteps(state, stepIndex) + '\n');
|
|
251
|
+
process.stdout.write(renderSteps(state, stepIndex, currentFrame) + '\n');
|
|
164
252
|
};
|
|
165
253
|
|
|
166
254
|
try {
|
|
@@ -175,12 +263,15 @@ export async function runSteps(steps: Step[]): Promise<void> {
|
|
|
175
263
|
};
|
|
176
264
|
}
|
|
177
265
|
|
|
178
|
-
|
|
266
|
+
if (activeInterval) {
|
|
267
|
+
clearInterval(activeInterval);
|
|
268
|
+
activeInterval = null;
|
|
269
|
+
}
|
|
179
270
|
|
|
180
271
|
// Clear progress and final render with outcome
|
|
181
272
|
step.progress = undefined;
|
|
182
273
|
process.stdout.write(`\x1B[${state.length}A`);
|
|
183
|
-
process.stdout.write(renderSteps(state,
|
|
274
|
+
process.stdout.write(renderSteps(state, -1) + '\n');
|
|
184
275
|
|
|
185
276
|
// If error, show error message and exit
|
|
186
277
|
if (step.outcome?.status === 'error') {
|
|
@@ -192,11 +283,52 @@ export async function runSteps(steps: Step[]): Promise<void> {
|
|
|
192
283
|
}
|
|
193
284
|
|
|
194
285
|
// Show cursor again
|
|
195
|
-
process.stdout.write('\x1B[?25h
|
|
286
|
+
process.stdout.write('\x1B[?25h');
|
|
196
287
|
} catch (err) {
|
|
197
288
|
// Ensure cursor is shown even if something goes wrong
|
|
198
289
|
process.stdout.write('\x1B[?25h');
|
|
199
290
|
throw err;
|
|
291
|
+
} finally {
|
|
292
|
+
// Remove signal/TTY handlers
|
|
293
|
+
restoreInterrupts();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Run steps in plain mode (no TUI animations)
|
|
299
|
+
*/
|
|
300
|
+
async function runStepsPlain(state: StepState[]): Promise<void> {
|
|
301
|
+
const grayColor = getColor('gray');
|
|
302
|
+
const greenColor = getColor('green');
|
|
303
|
+
const yellowColor = getColor('yellow');
|
|
304
|
+
const redColor = getColor('red');
|
|
305
|
+
|
|
306
|
+
for (const step of state) {
|
|
307
|
+
// Run the step (no progress callback for plain mode)
|
|
308
|
+
try {
|
|
309
|
+
const outcome = step.type === 'progress' ? await step.run(() => {}) : await step.run();
|
|
310
|
+
step.outcome = outcome;
|
|
311
|
+
} catch (err) {
|
|
312
|
+
step.outcome = {
|
|
313
|
+
status: 'error',
|
|
314
|
+
message: err instanceof Error ? err.message : String(err),
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Print final state only
|
|
319
|
+
if (step.outcome?.status === 'success') {
|
|
320
|
+
console.log(`${greenColor}${ICONS.success}${COLORS.reset} ${step.label}`);
|
|
321
|
+
} else if (step.outcome?.status === 'skipped') {
|
|
322
|
+
const reason = step.outcome.reason
|
|
323
|
+
? ` ${grayColor}(${step.outcome.reason})${COLORS.reset}`
|
|
324
|
+
: '';
|
|
325
|
+
console.log(`${yellowColor}${ICONS.skipped}${COLORS.reset} ${step.label}${reason}`);
|
|
326
|
+
} else if (step.outcome?.status === 'error') {
|
|
327
|
+
console.log(`${redColor}${ICONS.error}${COLORS.reset} ${step.label}`);
|
|
328
|
+
const errorColor = getColor('red');
|
|
329
|
+
console.error(`\n${errorColor}Error: ${step.outcome.message}${COLORS.reset}\n`);
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
200
332
|
}
|
|
201
333
|
}
|
|
202
334
|
|
|
@@ -222,6 +354,7 @@ function renderSteps(steps: StepState[], activeIndex: number, spinner?: string):
|
|
|
222
354
|
const lines: string[] = [];
|
|
223
355
|
|
|
224
356
|
steps.forEach((s, i) => {
|
|
357
|
+
// Don't show progress indicator for steps with outcomes (success/skipped/error)
|
|
225
358
|
if (s.outcome?.status === 'success') {
|
|
226
359
|
lines.push(
|
|
227
360
|
`${greenColor}${ICONS.success}${COLORS.reset} ${grayColor}${COLORS.strikethrough}${s.label}${COLORS.reset}`
|
|
@@ -234,6 +367,7 @@ function renderSteps(steps: StepState[], activeIndex: number, spinner?: string):
|
|
|
234
367
|
} else if (s.outcome?.status === 'error') {
|
|
235
368
|
lines.push(`${redColor}${ICONS.error}${COLORS.reset} ${s.label}`);
|
|
236
369
|
} else if (i === activeIndex && spinner) {
|
|
370
|
+
// Only show progress for active step with spinner
|
|
237
371
|
const progressIndicator = s.progress !== undefined ? renderProgress(s.progress) : '';
|
|
238
372
|
lines.push(`${spinner} ${s.label}${progressIndicator}`);
|
|
239
373
|
} else {
|
package/src/terminal.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export type ColorScheme = 'light' | 'dark';
|
|
2
2
|
|
|
3
|
+
const defaultMode = process.env.CI ? 'light' : 'dark';
|
|
4
|
+
|
|
3
5
|
export async function detectColorScheme(): Promise<ColorScheme> {
|
|
4
6
|
const debug = process.env.DEBUG_COLORS === 'true';
|
|
5
7
|
|
|
@@ -14,28 +16,27 @@ export async function detectColorScheme(): Promise<ColorScheme> {
|
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
// Check if we have stdout TTY at minimum
|
|
17
|
-
if (
|
|
18
|
-
if (debug) console.log('[DEBUG] stdout
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
19
|
+
if (process.stdout.isTTY) {
|
|
20
|
+
if (debug) console.log('[DEBUG] stdout is a TTY, defaulting to dark');
|
|
21
|
+
|
|
22
|
+
// Try to query terminal background color using OSC 11 (most reliable)
|
|
23
|
+
if (debug) console.log('[DEBUG] Querying terminal background with OSC 11...');
|
|
24
|
+
try {
|
|
25
|
+
const bgColor = await queryTerminalBackground();
|
|
26
|
+
if (bgColor) {
|
|
27
|
+
const luminance = calculateLuminance(bgColor);
|
|
28
|
+
const scheme = luminance > 0.5 ? 'light' : 'dark';
|
|
29
|
+
if (debug)
|
|
30
|
+
console.log(
|
|
31
|
+
`[DEBUG] OSC 11 response: rgb(${bgColor.r},${bgColor.g},${bgColor.b}), luminance: ${luminance.toFixed(2)}, scheme: ${scheme}`
|
|
32
|
+
);
|
|
33
|
+
return scheme;
|
|
34
|
+
} else {
|
|
35
|
+
if (debug) console.log('[DEBUG] OSC 11 query timed out or no response');
|
|
36
|
+
}
|
|
37
|
+
} catch (error) {
|
|
38
|
+
if (debug) console.log('[DEBUG] OSC 11 query failed:', error);
|
|
36
39
|
}
|
|
37
|
-
} catch (error) {
|
|
38
|
-
if (debug) console.log('[DEBUG] OSC 11 query failed:', error);
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
// Fall back to COLORFGBG environment variable (less reliable)
|
|
@@ -56,8 +57,8 @@ export async function detectColorScheme(): Promise<ColorScheme> {
|
|
|
56
57
|
return scheme;
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
if (debug) console.log('[DEBUG] Defaulting to
|
|
60
|
-
return
|
|
60
|
+
if (debug) console.log('[DEBUG] Defaulting to %s', defaultMode);
|
|
61
|
+
return defaultMode;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
interface RGBColor {
|