@agentuity/cli 0.1.13 → 0.1.15
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/auth.d.ts +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +12 -7
- package/dist/auth.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +94 -97
- package/dist/cli.js.map +1 -1
- package/dist/cmd/ai/index.d.ts.map +1 -1
- package/dist/cmd/ai/index.js +6 -1
- package/dist/cmd/ai/index.js.map +1 -1
- package/dist/cmd/ai/opencode/index.d.ts +3 -0
- package/dist/cmd/ai/opencode/index.d.ts.map +1 -0
- package/dist/cmd/ai/opencode/index.js +27 -0
- package/dist/cmd/ai/opencode/index.js.map +1 -0
- package/dist/cmd/ai/opencode/install.d.ts +3 -0
- package/dist/cmd/ai/opencode/install.d.ts.map +1 -0
- package/dist/cmd/ai/opencode/install.js +102 -0
- package/dist/cmd/ai/opencode/install.js.map +1 -0
- package/dist/cmd/ai/opencode/run.d.ts +3 -0
- package/dist/cmd/ai/opencode/run.d.ts.map +1 -0
- package/dist/cmd/ai/opencode/run.js +88 -0
- package/dist/cmd/ai/opencode/run.js.map +1 -0
- package/dist/cmd/ai/opencode/uninstall.d.ts +3 -0
- package/dist/cmd/ai/opencode/uninstall.d.ts.map +1 -0
- package/dist/cmd/ai/opencode/uninstall.js +82 -0
- package/dist/cmd/ai/opencode/uninstall.js.map +1 -0
- package/dist/cmd/auth/index.d.ts.map +1 -1
- package/dist/cmd/auth/index.js +3 -0
- package/dist/cmd/auth/index.js.map +1 -1
- package/dist/cmd/auth/org/index.d.ts +2 -0
- package/dist/cmd/auth/org/index.d.ts.map +1 -0
- package/dist/cmd/auth/org/index.js +121 -0
- package/dist/cmd/auth/org/index.js.map +1 -0
- package/dist/cmd/build/vite/beacon-plugin.d.ts +19 -0
- package/dist/cmd/build/vite/beacon-plugin.d.ts.map +1 -0
- package/dist/cmd/build/vite/beacon-plugin.js +137 -0
- package/dist/cmd/build/vite/beacon-plugin.js.map +1 -0
- package/dist/cmd/build/vite/vite-builder.d.ts +2 -0
- package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-builder.js +12 -2
- package/dist/cmd/build/vite/vite-builder.js.map +1 -1
- package/dist/cmd/build/webanalytics-generator.js +25 -9
- package/dist/cmd/build/webanalytics-generator.js.map +1 -1
- package/dist/cmd/cloud/db/get.d.ts.map +1 -1
- package/dist/cmd/cloud/db/get.js +7 -0
- package/dist/cmd/cloud/db/get.js.map +1 -1
- package/dist/cmd/cloud/db/list.d.ts.map +1 -1
- package/dist/cmd/cloud/db/list.js +19 -6
- package/dist/cmd/cloud/db/list.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +24 -1
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
- package/dist/cmd/cloud/deployment/show.js +5 -0
- package/dist/cmd/cloud/deployment/show.js.map +1 -1
- package/dist/cmd/cloud/index.d.ts.map +1 -1
- package/dist/cmd/cloud/index.js +3 -0
- package/dist/cmd/cloud/index.js.map +1 -1
- package/dist/cmd/cloud/region/index.d.ts +2 -0
- package/dist/cmd/cloud/region/index.d.ts.map +1 -0
- package/dist/cmd/cloud/region/index.js +136 -0
- package/dist/cmd/cloud/region/index.js.map +1 -0
- package/dist/cmd/cloud/sandbox/snapshot/build.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/build.js +35 -5
- package/dist/cmd/cloud/sandbox/snapshot/build.js.map +1 -1
- package/dist/cmd/cloud/scp/download.d.ts.map +1 -1
- package/dist/cmd/cloud/scp/download.js +4 -2
- package/dist/cmd/cloud/scp/download.js.map +1 -1
- package/dist/cmd/cloud/scp/upload.d.ts.map +1 -1
- package/dist/cmd/cloud/scp/upload.js +4 -2
- package/dist/cmd/cloud/scp/upload.js.map +1 -1
- package/dist/cmd/cloud/ssh.d.ts.map +1 -1
- package/dist/cmd/cloud/ssh.js +3 -1
- package/dist/cmd/cloud/ssh.js.map +1 -1
- package/dist/cmd/cloud/storage/get.d.ts.map +1 -1
- package/dist/cmd/cloud/storage/get.js +12 -5
- package/dist/cmd/cloud/storage/get.js.map +1 -1
- package/dist/cmd/cloud/storage/list.d.ts.map +1 -1
- package/dist/cmd/cloud/storage/list.js +10 -0
- package/dist/cmd/cloud/storage/list.js.map +1 -1
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +62 -5
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/help/index.d.ts.map +1 -1
- package/dist/cmd/help/index.js +8 -18
- package/dist/cmd/help/index.js.map +1 -1
- package/dist/cmd/project/create.d.ts.map +1 -1
- package/dist/cmd/project/create.js +18 -9
- package/dist/cmd/project/create.js.map +1 -1
- package/dist/cmd/project/download.d.ts +3 -1
- package/dist/cmd/project/download.d.ts.map +1 -1
- package/dist/cmd/project/download.js +5 -0
- package/dist/cmd/project/download.js.map +1 -1
- package/dist/cmd/project/import.d.ts +2 -0
- package/dist/cmd/project/import.d.ts.map +1 -0
- package/dist/cmd/project/import.js +88 -0
- package/dist/cmd/project/import.js.map +1 -0
- package/dist/cmd/project/index.d.ts.map +1 -1
- package/dist/cmd/project/index.js +3 -0
- package/dist/cmd/project/index.js.map +1 -1
- package/dist/cmd/project/reconcile.d.ts +67 -0
- package/dist/cmd/project/reconcile.d.ts.map +1 -0
- package/dist/cmd/project/reconcile.js +458 -0
- package/dist/cmd/project/reconcile.js.map +1 -0
- package/dist/cmd/project/template-flow.d.ts +13 -1
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.js +56 -18
- package/dist/cmd/project/template-flow.js.map +1 -1
- package/dist/config.d.ts +8 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +50 -21
- package/dist/config.js.map +1 -1
- package/dist/legacy-check.d.ts.map +1 -1
- package/dist/legacy-check.js +8 -0
- package/dist/legacy-check.js.map +1 -1
- package/dist/program-ref.d.ts +4 -0
- package/dist/program-ref.d.ts.map +1 -0
- package/dist/program-ref.js +8 -0
- package/dist/program-ref.js.map +1 -0
- package/dist/regions.d.ts +8 -0
- package/dist/regions.d.ts.map +1 -0
- package/dist/regions.js +77 -0
- package/dist/regions.js.map +1 -0
- package/dist/tui/prompt.d.ts.map +1 -1
- package/dist/tui/prompt.js +7 -1
- package/dist/tui/prompt.js.map +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +79 -25
- package/dist/tui.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +7 -7
- package/src/auth.ts +14 -13
- package/src/cli.ts +118 -114
- package/src/cmd/ai/index.ts +6 -1
- package/src/cmd/ai/opencode/index.ts +28 -0
- package/src/cmd/ai/opencode/install.ts +120 -0
- package/src/cmd/ai/opencode/run.ts +103 -0
- package/src/cmd/ai/opencode/uninstall.ts +90 -0
- package/src/cmd/auth/index.ts +3 -0
- package/src/cmd/auth/org/index.ts +142 -0
- package/src/cmd/build/vite/beacon-plugin.ts +164 -0
- package/src/cmd/build/vite/vite-builder.ts +19 -2
- package/src/cmd/build/webanalytics-generator.ts +25 -9
- package/src/cmd/cloud/db/get.ts +7 -0
- package/src/cmd/cloud/db/list.ts +20 -6
- package/src/cmd/cloud/deploy.ts +32 -1
- package/src/cmd/cloud/deployment/show.ts +5 -0
- package/src/cmd/cloud/index.ts +3 -0
- package/src/cmd/cloud/region/index.ts +157 -0
- package/src/cmd/cloud/sandbox/snapshot/build.ts +42 -5
- package/src/cmd/cloud/scp/download.ts +6 -2
- package/src/cmd/cloud/scp/upload.ts +6 -2
- package/src/cmd/cloud/ssh.ts +5 -1
- package/src/cmd/cloud/storage/get.ts +12 -5
- package/src/cmd/cloud/storage/list.ts +11 -0
- package/src/cmd/dev/index.ts +62 -5
- package/src/cmd/help/index.ts +8 -22
- package/src/cmd/project/create.ts +19 -9
- package/src/cmd/project/download.ts +7 -1
- package/src/cmd/project/import.ts +98 -0
- package/src/cmd/project/index.ts +3 -0
- package/src/cmd/project/reconcile.ts +606 -0
- package/src/cmd/project/template-flow.ts +69 -18
- package/src/config.ts +58 -22
- package/src/legacy-check.ts +10 -0
- package/src/program-ref.ts +11 -0
- package/src/regions.ts +95 -0
- package/src/tui/prompt.ts +10 -3
- package/src/tui.ts +82 -26
- package/src/types.ts +2 -0
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
getServiceUrls,
|
|
11
11
|
APIClient as ServerAPIClient,
|
|
12
12
|
createResources,
|
|
13
|
+
validateDatabaseName,
|
|
13
14
|
} from '@agentuity/server';
|
|
14
15
|
import type { Logger } from '@agentuity/core';
|
|
15
16
|
import * as tui from '../../tui';
|
|
@@ -56,7 +57,20 @@ interface CreateFlowOptions {
|
|
|
56
57
|
apiClient?: APIClient;
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
export
|
|
60
|
+
export interface CreateFlowResult {
|
|
61
|
+
projectId?: string;
|
|
62
|
+
orgId?: string;
|
|
63
|
+
name: string;
|
|
64
|
+
path: string;
|
|
65
|
+
template: string;
|
|
66
|
+
installed: boolean;
|
|
67
|
+
built: boolean;
|
|
68
|
+
domains?: string[];
|
|
69
|
+
success: boolean;
|
|
70
|
+
error?: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function runCreateFlow(options: CreateFlowOptions): Promise<CreateFlowResult> {
|
|
60
74
|
const {
|
|
61
75
|
projectName: initialProjectName,
|
|
62
76
|
dir: targetDir,
|
|
@@ -168,7 +182,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
|
|
|
168
182
|
const home = homedir();
|
|
169
183
|
if (dest === '/' || dest === home) {
|
|
170
184
|
logger.fatal(`Refusing to delete protected path: ${dest}`, ErrorCode.VALIDATION_FAILED);
|
|
171
|
-
return;
|
|
185
|
+
return undefined as never;
|
|
172
186
|
}
|
|
173
187
|
rmSync(dest, { recursive: true, force: true });
|
|
174
188
|
tui.success(`Deleted ${dest}`);
|
|
@@ -193,7 +207,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
|
|
|
193
207
|
`Template "${initialTemplate}" not found\n\nAvailable templates:\n${availableTemplates}`,
|
|
194
208
|
ErrorCode.RESOURCE_NOT_FOUND
|
|
195
209
|
);
|
|
196
|
-
return;
|
|
210
|
+
return undefined as never;
|
|
197
211
|
}
|
|
198
212
|
selectedTemplate = found;
|
|
199
213
|
} else if (skipPrompts || templates.length === 1) {
|
|
@@ -222,7 +236,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
|
|
|
222
236
|
const found = templates.find((t) => t.id === templateId);
|
|
223
237
|
if (!found) {
|
|
224
238
|
logger.fatal('Template selection failed', ErrorCode.USER_CANCELLED);
|
|
225
|
-
return;
|
|
239
|
+
return undefined as never;
|
|
226
240
|
}
|
|
227
241
|
selectedTemplate = found;
|
|
228
242
|
}
|
|
@@ -237,7 +251,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
|
|
|
237
251
|
});
|
|
238
252
|
|
|
239
253
|
// Setup project (replace placeholders, install deps, build)
|
|
240
|
-
await setupProject({
|
|
254
|
+
const setupResult = await setupProject({
|
|
241
255
|
dest,
|
|
242
256
|
projectName: projectName === '.' ? basename(dest) : projectName,
|
|
243
257
|
dirName: dirName === '.' ? basename(dest) : dirName,
|
|
@@ -246,15 +260,24 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
|
|
|
246
260
|
logger,
|
|
247
261
|
});
|
|
248
262
|
|
|
249
|
-
//
|
|
250
|
-
if (!
|
|
263
|
+
// If setup failed, skip resource prompts and registration - just show error and return
|
|
264
|
+
if (!setupResult.success) {
|
|
265
|
+
tui.warning('Project setup failed. Skipping resource configuration.');
|
|
266
|
+
return {
|
|
267
|
+
name: projectName,
|
|
268
|
+
path: dest,
|
|
269
|
+
template: selectedTemplate.id,
|
|
270
|
+
installed: !options.noInstall,
|
|
271
|
+
built: false,
|
|
272
|
+
success: false,
|
|
273
|
+
error: 'Project setup completed with errors',
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Add separator bar if we're going to show resource prompts
|
|
278
|
+
if (!skipPrompts && auth && apiClient && catalystClient && orgId && region) {
|
|
251
279
|
const { symbols, tuiColors } = tui;
|
|
252
|
-
console.log(
|
|
253
|
-
console.log(`${tuiColors.secondary(symbols.bar)} ${tuiColors.muted(selectedTemplate.name)}`);
|
|
254
|
-
// Only add bar if we're going to show resource prompts
|
|
255
|
-
if (auth && apiClient && catalystClient && orgId && region) {
|
|
256
|
-
console.log(tuiColors.secondary(symbols.bar));
|
|
257
|
-
}
|
|
280
|
+
console.log(tuiColors.secondary(symbols.bar));
|
|
258
281
|
}
|
|
259
282
|
|
|
260
283
|
let _domains = domains;
|
|
@@ -342,10 +365,17 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
|
|
|
342
365
|
}
|
|
343
366
|
switch (choices.db_action) {
|
|
344
367
|
case 'Create New': {
|
|
345
|
-
const
|
|
368
|
+
const dbNameInput = await prompt.text({
|
|
346
369
|
message: 'Database name',
|
|
347
|
-
hint: 'Optional -
|
|
370
|
+
hint: 'Optional - lowercase letters, digits, underscores only',
|
|
371
|
+
validate: (value: string) => {
|
|
372
|
+
const trimmed = value.trim();
|
|
373
|
+
if (trimmed === '') return true;
|
|
374
|
+
const result = validateDatabaseName(trimmed);
|
|
375
|
+
return result.valid ? true : result.error!;
|
|
376
|
+
},
|
|
348
377
|
});
|
|
378
|
+
const dbName = dbNameInput.trim() || undefined;
|
|
349
379
|
const dbDescription = await prompt.text({
|
|
350
380
|
message: 'Database description',
|
|
351
381
|
hint: 'Optional - press Enter to skip',
|
|
@@ -357,7 +387,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
|
|
|
357
387
|
return createResources(catalystClient, orgId, region!, [
|
|
358
388
|
{
|
|
359
389
|
type: 'db',
|
|
360
|
-
name: dbName
|
|
390
|
+
name: dbName,
|
|
361
391
|
description: dbDescription || undefined,
|
|
362
392
|
},
|
|
363
393
|
]);
|
|
@@ -578,7 +608,11 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
|
|
|
578
608
|
|
|
579
609
|
// Show completion message
|
|
580
610
|
if (!skipPrompts) {
|
|
581
|
-
|
|
611
|
+
if (setupResult.success) {
|
|
612
|
+
tui.success('✨ Project created successfully!\n');
|
|
613
|
+
} else {
|
|
614
|
+
tui.warning('Project created with errors (see above)\n');
|
|
615
|
+
}
|
|
582
616
|
|
|
583
617
|
// Show next steps in a box with primary color for commands
|
|
584
618
|
if (dirName !== '.') {
|
|
@@ -598,7 +632,11 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
|
|
|
598
632
|
`${tui.tuiColors.muted('⭐️ Follow us:')} ${tui.link('https://github.com/agentuity/sdk')}`
|
|
599
633
|
);
|
|
600
634
|
} else {
|
|
601
|
-
|
|
635
|
+
if (setupResult.success) {
|
|
636
|
+
tui.success('✨ Project created successfully!');
|
|
637
|
+
} else {
|
|
638
|
+
tui.warning('Project created with errors');
|
|
639
|
+
}
|
|
602
640
|
}
|
|
603
641
|
|
|
604
642
|
playSound();
|
|
@@ -616,6 +654,19 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
|
|
|
616
654
|
if (authEnabled && !templateHasAuth) {
|
|
617
655
|
printIntegrationExamples();
|
|
618
656
|
}
|
|
657
|
+
|
|
658
|
+
return {
|
|
659
|
+
projectId,
|
|
660
|
+
orgId,
|
|
661
|
+
name: projectName,
|
|
662
|
+
path: dest,
|
|
663
|
+
template: selectedTemplate.id,
|
|
664
|
+
installed: !options.noInstall,
|
|
665
|
+
built: !options.noBuild && setupResult.success,
|
|
666
|
+
domains: _domains,
|
|
667
|
+
success: setupResult.success,
|
|
668
|
+
error: setupResult.success ? undefined : 'Project setup completed with errors',
|
|
669
|
+
};
|
|
619
670
|
}
|
|
620
671
|
|
|
621
672
|
/**
|
package/src/config.ts
CHANGED
|
@@ -359,6 +359,29 @@ export async function saveOrgId(orgId: string): Promise<void> {
|
|
|
359
359
|
await saveConfig(config);
|
|
360
360
|
}
|
|
361
361
|
|
|
362
|
+
export async function clearOrgId(): Promise<void> {
|
|
363
|
+
const config = await getOrInitConfig();
|
|
364
|
+
if (config.preferences) {
|
|
365
|
+
delete (config.preferences as Record<string, unknown>).orgId;
|
|
366
|
+
await saveConfig(config);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export async function saveRegion(region: string): Promise<void> {
|
|
371
|
+
const config = await getOrInitConfig();
|
|
372
|
+
config.preferences = config.preferences || {};
|
|
373
|
+
(config.preferences as Record<string, unknown>).region = region;
|
|
374
|
+
await saveConfig(config);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export async function clearRegion(): Promise<void> {
|
|
378
|
+
const config = await getOrInitConfig();
|
|
379
|
+
if (config.preferences) {
|
|
380
|
+
delete (config.preferences as Record<string, unknown>).region;
|
|
381
|
+
await saveConfig(config);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
362
385
|
export async function getAuth(): Promise<AuthData | null> {
|
|
363
386
|
const config = await loadConfig();
|
|
364
387
|
const profileName = config?.name || defaultProfileName;
|
|
@@ -590,26 +613,29 @@ export async function createProjectConfig(dir: string, config: InitialProjectCon
|
|
|
590
613
|
await Bun.write(envPath, content);
|
|
591
614
|
await chmod(envPath, 0o600);
|
|
592
615
|
|
|
593
|
-
// generate the vscode settings
|
|
616
|
+
// generate the vscode settings (only if they don't already exist)
|
|
594
617
|
const vscodeDir = join(dir, '.vscode');
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
'
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
{
|
|
606
|
-
fileMatch: ['agentuity.json'],
|
|
607
|
-
url: 'https://agentuity.dev/schema/cli/v1/agentuity.json',
|
|
618
|
+
const settingsPath = join(vscodeDir, 'settings.json');
|
|
619
|
+
if (!(await Bun.file(settingsPath).exists())) {
|
|
620
|
+
mkdirSync(vscodeDir, { recursive: true });
|
|
621
|
+
|
|
622
|
+
const settings = {
|
|
623
|
+
'search.exclude': {
|
|
624
|
+
'**/.git/**': true,
|
|
625
|
+
'**/node_modules/**': true,
|
|
626
|
+
'**/bun.lock': true,
|
|
627
|
+
'**/.agentuity/**': true,
|
|
608
628
|
},
|
|
609
|
-
|
|
610
|
-
|
|
629
|
+
'json.schemas': [
|
|
630
|
+
{
|
|
631
|
+
fileMatch: ['agentuity.json'],
|
|
632
|
+
url: 'https://agentuity.dev/schema/cli/v1/agentuity.json',
|
|
633
|
+
},
|
|
634
|
+
],
|
|
635
|
+
};
|
|
611
636
|
|
|
612
|
-
|
|
637
|
+
await Bun.write(settingsPath, JSON.stringify(settings, null, 2));
|
|
638
|
+
}
|
|
613
639
|
}
|
|
614
640
|
|
|
615
641
|
export async function updateProjectConfig(
|
|
@@ -721,13 +747,18 @@ interface RegionsCacheData {
|
|
|
721
747
|
/**
|
|
722
748
|
* Get the default region using priority ordering:
|
|
723
749
|
* 1. AGENTUITY_REGION environment variable
|
|
724
|
-
* 2.
|
|
725
|
-
* 3.
|
|
750
|
+
* 2. 'local' for local profile
|
|
751
|
+
* 3. Saved region preference (config.preferences.region)
|
|
752
|
+
* 4. First entry in region-{profile}.json (nearest region, sorted by distance)
|
|
753
|
+
* 5. 'usc' fallback
|
|
726
754
|
*
|
|
727
755
|
* Used for API calls that can hit any Catalyst instance (global database operations).
|
|
728
756
|
* Note: This is NOT called when --region flag is provided (handled at command level).
|
|
729
757
|
*/
|
|
730
|
-
export async function getDefaultRegion(
|
|
758
|
+
export async function getDefaultRegion(
|
|
759
|
+
profileName = 'production',
|
|
760
|
+
config?: Config | null
|
|
761
|
+
): Promise<string> {
|
|
731
762
|
// 1. Check environment variable first
|
|
732
763
|
if (process.env.AGENTUITY_REGION) {
|
|
733
764
|
return process.env.AGENTUITY_REGION;
|
|
@@ -738,7 +769,12 @@ export async function getDefaultRegion(profileName = 'production'): Promise<stri
|
|
|
738
769
|
return 'local';
|
|
739
770
|
}
|
|
740
771
|
|
|
741
|
-
// 3. Check
|
|
772
|
+
// 3. Check saved region preference
|
|
773
|
+
if (config?.preferences?.region) {
|
|
774
|
+
return config.preferences.region;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// 4. Check cached regions file (sorted by distance)
|
|
742
778
|
try {
|
|
743
779
|
const cachePath = join(getDefaultConfigDir(), `regions-${profileName}.json`);
|
|
744
780
|
const file = Bun.file(cachePath);
|
|
@@ -752,7 +788,7 @@ export async function getDefaultRegion(profileName = 'production'): Promise<stri
|
|
|
752
788
|
// Fall through to default
|
|
753
789
|
}
|
|
754
790
|
|
|
755
|
-
//
|
|
791
|
+
// 5. Final fallback
|
|
756
792
|
return 'usc';
|
|
757
793
|
}
|
|
758
794
|
|
package/src/legacy-check.ts
CHANGED
|
@@ -23,6 +23,11 @@ export async function checkLegacyCLI(): Promise<void> {
|
|
|
23
23
|
join(homeDir, '.local/bin/agentuity'), // XDG user bin
|
|
24
24
|
];
|
|
25
25
|
|
|
26
|
+
// Exclude the currently running executable from the legacy check
|
|
27
|
+
// This prevents the new CLI from detecting itself as legacy when installed
|
|
28
|
+
// in standard system locations like /usr/local/bin
|
|
29
|
+
const currentExecutable = process.execPath;
|
|
30
|
+
|
|
26
31
|
const foundInstalls: LegacyInstall[] = [];
|
|
27
32
|
|
|
28
33
|
// Check if Homebrew manages the agentuity package
|
|
@@ -40,6 +45,11 @@ export async function checkLegacyCLI(): Promise<void> {
|
|
|
40
45
|
|
|
41
46
|
// Check file system locations
|
|
42
47
|
for (const location of legacyLocations) {
|
|
48
|
+
// Skip if this is the currently running executable
|
|
49
|
+
if (location === currentExecutable) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
43
53
|
const file = Bun.file(location);
|
|
44
54
|
if (await file.exists()) {
|
|
45
55
|
try {
|
package/src/regions.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Region caching utilities for avoiding repeated API calls
|
|
3
|
+
*/
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { mkdir, unlink } from 'node:fs/promises';
|
|
6
|
+
import type { Logger } from '@agentuity/core';
|
|
7
|
+
import { listRegions, type RegionList } from '@agentuity/server';
|
|
8
|
+
import { getDefaultConfigDir } from './config';
|
|
9
|
+
import type { APIClient } from './api';
|
|
10
|
+
|
|
11
|
+
const REGIONS_CACHE_MAX_AGE_MS = 5 * 24 * 60 * 60 * 1000; // 5 days
|
|
12
|
+
const LEGACY_REGIONS_CACHE_FILE = 'regions.json';
|
|
13
|
+
|
|
14
|
+
function getRegionsCacheFile(profileName: string): string {
|
|
15
|
+
return `regions-${profileName}.json`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function removeLegacyRegionsCache(logger: Logger): Promise<void> {
|
|
19
|
+
try {
|
|
20
|
+
const legacyPath = join(getDefaultConfigDir(), LEGACY_REGIONS_CACHE_FILE);
|
|
21
|
+
const file = Bun.file(legacyPath);
|
|
22
|
+
if (await file.exists()) {
|
|
23
|
+
await unlink(legacyPath);
|
|
24
|
+
logger.trace('removed legacy regions cache file');
|
|
25
|
+
}
|
|
26
|
+
} catch {
|
|
27
|
+
// Ignore errors when removing legacy file
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface RegionsCacheData {
|
|
32
|
+
timestamp: number;
|
|
33
|
+
regions: RegionList;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function getCachedRegions(profileName: string, logger: Logger): Promise<RegionList | null> {
|
|
37
|
+
try {
|
|
38
|
+
// Clean up legacy single-file cache from older versions
|
|
39
|
+
await removeLegacyRegionsCache(logger);
|
|
40
|
+
|
|
41
|
+
const cachePath = join(getDefaultConfigDir(), getRegionsCacheFile(profileName));
|
|
42
|
+
const file = Bun.file(cachePath);
|
|
43
|
+
if (!(await file.exists())) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const data: RegionsCacheData = await file.json();
|
|
47
|
+
const age = Date.now() - data.timestamp;
|
|
48
|
+
if (age > REGIONS_CACHE_MAX_AGE_MS) {
|
|
49
|
+
logger.trace('regions cache expired for profile %s (age: %dms)', profileName, age);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
logger.trace('using cached regions for profile %s (age: %dms)', profileName, age);
|
|
53
|
+
return data.regions;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
logger.trace('failed to read regions cache for profile %s: %s', profileName, error);
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function saveRegionsCache(
|
|
61
|
+
profileName: string,
|
|
62
|
+
regions: RegionList,
|
|
63
|
+
logger: Logger
|
|
64
|
+
): Promise<void> {
|
|
65
|
+
try {
|
|
66
|
+
const cacheDir = getDefaultConfigDir();
|
|
67
|
+
await mkdir(cacheDir, { recursive: true });
|
|
68
|
+
const cachePath = join(cacheDir, getRegionsCacheFile(profileName));
|
|
69
|
+
const data: RegionsCacheData = {
|
|
70
|
+
timestamp: Date.now(),
|
|
71
|
+
regions,
|
|
72
|
+
};
|
|
73
|
+
await Bun.write(cachePath, JSON.stringify(data));
|
|
74
|
+
logger.trace('saved regions cache for profile %s', profileName);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
logger.trace('failed to save regions cache for profile %s: %s', profileName, error);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Fetch regions from API with caching
|
|
82
|
+
*/
|
|
83
|
+
export async function fetchRegionsWithCache(
|
|
84
|
+
profileName: string,
|
|
85
|
+
apiClient: APIClient,
|
|
86
|
+
logger: Logger
|
|
87
|
+
): Promise<RegionList> {
|
|
88
|
+
const cached = await getCachedRegions(profileName, logger);
|
|
89
|
+
if (cached) {
|
|
90
|
+
return cached;
|
|
91
|
+
}
|
|
92
|
+
const regions = await listRegions(apiClient);
|
|
93
|
+
await saveRegionsCache(profileName, regions, logger);
|
|
94
|
+
return regions;
|
|
95
|
+
}
|
package/src/tui/prompt.ts
CHANGED
|
@@ -166,9 +166,16 @@ export class PromptFlow {
|
|
|
166
166
|
readline.moveCursor(process.stdout, 0, -linesToClear);
|
|
167
167
|
readline.clearScreenDown(process.stdout);
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
// If value is empty, only show message and separator (no value line)
|
|
170
|
+
if (value === '') {
|
|
171
|
+
process.stdout.write(
|
|
172
|
+
`${colors.completed(symbols.completed)} ${message}\n${colors.secondary(symbols.bar)}\n`
|
|
173
|
+
);
|
|
174
|
+
} else {
|
|
175
|
+
process.stdout.write(
|
|
176
|
+
`${colors.completed(symbols.completed)} ${message}\n${colors.secondary(symbols.bar)} ${colors.muted(value)}\n${colors.secondary(symbols.bar)}\n`
|
|
177
|
+
);
|
|
178
|
+
}
|
|
172
179
|
|
|
173
180
|
this.states.push({
|
|
174
181
|
type: 'completed',
|
package/src/tui.ts
CHANGED
|
@@ -247,7 +247,8 @@ export function getSeverityColor(severity: string): (text: string) => string {
|
|
|
247
247
|
export function success(message: string): void {
|
|
248
248
|
const color = getColor('success');
|
|
249
249
|
const reset = getColor('reset');
|
|
250
|
-
|
|
250
|
+
// Clear line first to ensure no leftover content from previous output
|
|
251
|
+
process.stderr.write(`\r\x1b[2K${color}${ICONS.success} ${message}${reset}\n`);
|
|
251
252
|
}
|
|
252
253
|
|
|
253
254
|
/**
|
|
@@ -348,6 +349,11 @@ export function link(url: string, title?: string, color = getColor('link')): str
|
|
|
348
349
|
* Check if terminal supports OSC 8 hyperlinks
|
|
349
350
|
*/
|
|
350
351
|
export function supportsHyperlinks(): boolean {
|
|
352
|
+
// No hyperlink support without a TTY
|
|
353
|
+
if (!process.stdout.isTTY) {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
|
|
351
357
|
const term = process.env.TERM || '';
|
|
352
358
|
const termProgram = process.env.TERM_PROGRAM || '';
|
|
353
359
|
const wtSession = process.env.WT_SESSION || '';
|
|
@@ -360,7 +366,6 @@ export function supportsHyperlinks(): boolean {
|
|
|
360
366
|
termProgram.includes('Apple_Terminal') ||
|
|
361
367
|
termProgram.includes('Hyper') ||
|
|
362
368
|
term.includes('xterm-kitty') ||
|
|
363
|
-
term.includes('xterm-256color') ||
|
|
364
369
|
wtSession !== '' // Windows Terminal
|
|
365
370
|
);
|
|
366
371
|
}
|
|
@@ -816,9 +821,7 @@ export function showLoggedOutMessage(appBaseUrl: string, hasProfile = false): vo
|
|
|
816
821
|
const RESET = '\x1b[0m';
|
|
817
822
|
|
|
818
823
|
const signupTitle = hasProfile ? 'Login' : 'Sign up / Login';
|
|
819
|
-
const signupURL = hasProfile
|
|
820
|
-
? `${appBaseUrl}/sign-in`
|
|
821
|
-
: `${appBaseUrl}/sign-up`;
|
|
824
|
+
const signupURL = hasProfile ? `${appBaseUrl}/sign-in` : `${appBaseUrl}/sign-up`;
|
|
822
825
|
const showInline = supportsHyperlinks();
|
|
823
826
|
const signupLink = showInline
|
|
824
827
|
? link(signupURL, signupTitle)
|
|
@@ -827,7 +830,11 @@ export function showLoggedOutMessage(appBaseUrl: string, hasProfile = false): vo
|
|
|
827
830
|
// Padding needed: 46 - 17 - signupTitle.length - 1 (space before link) = 28 - signupTitle.length
|
|
828
831
|
const paddingLength = 28 - signupTitle.length;
|
|
829
832
|
const padding = ' '.repeat(paddingLength);
|
|
830
|
-
|
|
833
|
+
// When not showing inline hyperlink, show URL on separate line with proper padding
|
|
834
|
+
// Box format: "║ " + content + "║" = 48 chars total
|
|
835
|
+
// Content area = 46 chars, with leading space = 45 chars for URL + padding
|
|
836
|
+
const urlPadding = Math.max(0, 45 - signupURL.length);
|
|
837
|
+
const showNewLine = showInline ? '' : `║ ${RESET}${link(signupURL)}${YELLOW}${' '.repeat(urlPadding)}║`;
|
|
831
838
|
|
|
832
839
|
const lines = [
|
|
833
840
|
'╔══════════════════════════════════════════════╗',
|
|
@@ -842,6 +849,8 @@ export function showLoggedOutMessage(appBaseUrl: string, hasProfile = false): vo
|
|
|
842
849
|
|
|
843
850
|
console.log('');
|
|
844
851
|
lines.filter(Boolean).map((line) => console.log(YELLOW + line + RESET));
|
|
852
|
+
console.log('');
|
|
853
|
+
console.log('');
|
|
845
854
|
}
|
|
846
855
|
|
|
847
856
|
/**
|
|
@@ -1301,11 +1310,17 @@ export async function spinner<T>(
|
|
|
1301
1310
|
// Stop animation
|
|
1302
1311
|
clearInterval(interval);
|
|
1303
1312
|
|
|
1304
|
-
// Move cursor to start of output, clear
|
|
1313
|
+
// Move cursor to start of output, clear only our lines (not to end of screen)
|
|
1305
1314
|
if (linesRendered > 0) {
|
|
1306
1315
|
process.stderr.write(`\x1b[${linesRendered}A`);
|
|
1316
|
+
for (let i = 0; i < linesRendered; i++) {
|
|
1317
|
+
process.stderr.write('\x1b[2K'); // Clear entire line
|
|
1318
|
+
if (i < linesRendered - 1) {
|
|
1319
|
+
process.stderr.write('\x1b[B'); // Move down one line
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
process.stderr.write(`\x1b[${linesRendered}A\r`); // Move back up
|
|
1307
1323
|
}
|
|
1308
|
-
process.stderr.write('\x1b[J'); // Clear from cursor to end of screen
|
|
1309
1324
|
process.stderr.write('\x1B[?25h'); // Show cursor
|
|
1310
1325
|
|
|
1311
1326
|
process.exit(130); // Standard exit code for SIGINT
|
|
@@ -1361,11 +1376,22 @@ export async function spinner<T>(
|
|
|
1361
1376
|
// Stop animation first
|
|
1362
1377
|
clearInterval(interval);
|
|
1363
1378
|
|
|
1364
|
-
// Move cursor to start of output, clear
|
|
1379
|
+
// Move cursor to start of output, clear only our lines (not to end of screen)
|
|
1365
1380
|
if (linesRendered > 0) {
|
|
1366
1381
|
process.stderr.write(`\x1b[${linesRendered}A`);
|
|
1382
|
+
for (let i = 0; i < linesRendered; i++) {
|
|
1383
|
+
process.stderr.write('\r\x1b[2K'); // Clear entire line
|
|
1384
|
+
if (i < linesRendered - 1) {
|
|
1385
|
+
process.stderr.write('\x1b[B'); // Move down one line
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
// After loop, cursor is at last cleared line (linesRendered - 1 from start)
|
|
1389
|
+
// Move up (linesRendered - 1) to get back to start position
|
|
1390
|
+
if (linesRendered > 1) {
|
|
1391
|
+
process.stderr.write(`\x1b[${linesRendered - 1}A`);
|
|
1392
|
+
}
|
|
1393
|
+
process.stderr.write('\r');
|
|
1367
1394
|
}
|
|
1368
|
-
process.stderr.write('\x1b[J'); // Clear from cursor to end of screen
|
|
1369
1395
|
process.stderr.write('\x1B[?25h'); // Show cursor
|
|
1370
1396
|
|
|
1371
1397
|
// If clearOnSuccess is false, show success message
|
|
@@ -1382,11 +1408,22 @@ export async function spinner<T>(
|
|
|
1382
1408
|
// Stop animation first
|
|
1383
1409
|
clearInterval(interval);
|
|
1384
1410
|
|
|
1385
|
-
// Move cursor to start of output, clear
|
|
1411
|
+
// Move cursor to start of output, clear only our lines (not to end of screen)
|
|
1386
1412
|
if (linesRendered > 0) {
|
|
1387
1413
|
process.stderr.write(`\x1b[${linesRendered}A`);
|
|
1414
|
+
for (let i = 0; i < linesRendered; i++) {
|
|
1415
|
+
process.stderr.write('\r\x1b[2K'); // Clear entire line
|
|
1416
|
+
if (i < linesRendered - 1) {
|
|
1417
|
+
process.stderr.write('\x1b[B'); // Move down one line
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
// After loop, cursor is at last cleared line (linesRendered - 1 from start)
|
|
1421
|
+
// Move up (linesRendered - 1) to get back to start position
|
|
1422
|
+
if (linesRendered > 1) {
|
|
1423
|
+
process.stderr.write(`\x1b[${linesRendered - 1}A`);
|
|
1424
|
+
}
|
|
1425
|
+
process.stderr.write('\r');
|
|
1388
1426
|
}
|
|
1389
|
-
process.stderr.write('\x1b[J'); // Clear from cursor to end of screen
|
|
1390
1427
|
process.stderr.write('\x1B[?25h'); // Show cursor
|
|
1391
1428
|
|
|
1392
1429
|
// Show error
|
|
@@ -1559,7 +1596,8 @@ export async function runCommand(options: CommandRunnerOptions): Promise<number>
|
|
|
1559
1596
|
|
|
1560
1597
|
for (const line of lines) {
|
|
1561
1598
|
if (line.trim()) {
|
|
1562
|
-
|
|
1599
|
+
// Strip ANSI codes from command output to prevent cursor/display issues
|
|
1600
|
+
allOutputLines.push(stripAnsi(line));
|
|
1563
1601
|
renderOutput(maxLinesOutput); // Show last N lines while streaming
|
|
1564
1602
|
}
|
|
1565
1603
|
}
|
|
@@ -1580,25 +1618,44 @@ export async function runCommand(options: CommandRunnerOptions): Promise<number>
|
|
|
1580
1618
|
if (linesRendered > 0) {
|
|
1581
1619
|
// Move up to the command line
|
|
1582
1620
|
process.stdout.write(`\x1b[${linesRendered}A`);
|
|
1583
|
-
// Clear each line (entire line)
|
|
1621
|
+
// Clear each line (entire line)
|
|
1584
1622
|
for (let i = 0; i < linesRendered; i++) {
|
|
1585
|
-
process.stdout.write('\x1b[2K'); // Clear entire line
|
|
1623
|
+
process.stdout.write('\r\x1b[2K'); // Clear entire line
|
|
1586
1624
|
if (i < linesRendered - 1) {
|
|
1587
1625
|
process.stdout.write('\x1b[B'); // Move down one line
|
|
1588
1626
|
}
|
|
1589
1627
|
}
|
|
1590
|
-
//
|
|
1591
|
-
|
|
1628
|
+
// After loop, cursor is at last cleared line (linesRendered - 1 from start)
|
|
1629
|
+
// Move up (linesRendered - 1) to get back to start position
|
|
1630
|
+
if (linesRendered > 1) {
|
|
1631
|
+
process.stdout.write(`\x1b[${linesRendered - 1}A`);
|
|
1632
|
+
}
|
|
1633
|
+
process.stdout.write('\r');
|
|
1592
1634
|
}
|
|
1593
1635
|
return exitCode;
|
|
1594
1636
|
}
|
|
1595
1637
|
|
|
1596
|
-
//
|
|
1638
|
+
// Determine how many lines to show in final output
|
|
1639
|
+
const finalLinesToShow = exitCode === 0 ? maxLinesOutput : maxLinesOnFailure;
|
|
1640
|
+
const finalOutputLines = allOutputLines.slice(-finalLinesToShow);
|
|
1641
|
+
|
|
1642
|
+
// Clear all rendered lines completely (only our lines, not previous output)
|
|
1597
1643
|
if (linesRendered > 0) {
|
|
1598
1644
|
// Move up to the command line (first line of our output)
|
|
1599
1645
|
process.stdout.write(`\x1b[${linesRendered}A`);
|
|
1600
|
-
//
|
|
1601
|
-
|
|
1646
|
+
// Clear the lines we rendered during streaming
|
|
1647
|
+
for (let i = 0; i < linesRendered; i++) {
|
|
1648
|
+
process.stdout.write('\r\x1b[2K'); // Clear entire line
|
|
1649
|
+
if (i < linesRendered - 1) {
|
|
1650
|
+
process.stdout.write('\x1b[B'); // Move down one line
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
// After loop, cursor is at last cleared line (linesRendered - 1 from start)
|
|
1654
|
+
// Move up (linesRendered - 1) to get back to start position
|
|
1655
|
+
if (linesRendered > 1) {
|
|
1656
|
+
process.stdout.write(`\x1b[${linesRendered - 1}A`);
|
|
1657
|
+
}
|
|
1658
|
+
process.stdout.write('\r');
|
|
1602
1659
|
}
|
|
1603
1660
|
|
|
1604
1661
|
// Determine icon based on exit code
|
|
@@ -1610,19 +1667,18 @@ export async function runCommand(options: CommandRunnerOptions): Promise<number>
|
|
|
1610
1667
|
`\r\x1b[K${statusColor}${icon}${reset} ${cmdColor}${displayCmd}${reset}\n`
|
|
1611
1668
|
);
|
|
1612
1669
|
|
|
1613
|
-
//
|
|
1614
|
-
const finalLinesToShow = exitCode === 0 ? maxLinesOutput : maxLinesOnFailure;
|
|
1615
|
-
|
|
1616
|
-
// Show final output lines
|
|
1617
|
-
const finalOutputLines = allOutputLines.slice(-finalLinesToShow);
|
|
1670
|
+
// Show final output lines (clearing each line first in case we're using more lines than before)
|
|
1618
1671
|
for (const line of finalOutputLines) {
|
|
1619
1672
|
const displayLine =
|
|
1620
1673
|
truncate && getDisplayWidth(line) > maxLineWidth
|
|
1621
1674
|
? truncateToWidth(line, maxLineWidth)
|
|
1622
1675
|
: line;
|
|
1623
|
-
process.stdout.write(`\
|
|
1676
|
+
process.stdout.write(`\x1b[2K${mutedColor}${displayLine}${reset}\n`);
|
|
1624
1677
|
}
|
|
1625
1678
|
|
|
1679
|
+
// If we're showing more lines than we had before, the extra lines may contain old content
|
|
1680
|
+
// We've already written over them, so they're clean now
|
|
1681
|
+
|
|
1626
1682
|
return exitCode;
|
|
1627
1683
|
} catch (err) {
|
|
1628
1684
|
// Move cursor up to clear our UI
|
package/src/types.ts
CHANGED
|
@@ -50,6 +50,7 @@ export const ConfigSchema = zod.object({
|
|
|
50
50
|
last_legacy_warning: zod.number().optional().describe('Last legacy CLI warning timestamp'),
|
|
51
51
|
signup_banner_shown: zod.boolean().optional().describe('If the signup banner was shown'),
|
|
52
52
|
orgId: zod.string().optional().describe('Default organization ID'),
|
|
53
|
+
region: zod.string().optional().describe('Default cloud region'),
|
|
53
54
|
project_dir: zod.string().optional().describe('Last used project directory'),
|
|
54
55
|
})
|
|
55
56
|
.optional()
|
|
@@ -61,6 +62,7 @@ export const ConfigSchema = zod.object({
|
|
|
61
62
|
})
|
|
62
63
|
.optional()
|
|
63
64
|
.describe('the gravity client information'),
|
|
65
|
+
|
|
64
66
|
});
|
|
65
67
|
|
|
66
68
|
export type Config = zod.infer<typeof ConfigSchema>;
|