agentuity-vscode 0.1.10 → 0.1.12
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/.vscodeignore +4 -0
- package/dist/extension.js +14361 -0
- package/package.json +1 -1
- package/resources/icon.png +0 -0
- package/src/core/cliClient.ts +128 -147
- package/src/core/sandboxManager.ts +46 -5
- package/src/features/chat/agentTools.ts +71 -3
- package/src/features/sandboxExplorer/index.ts +160 -36
- package/src/features/sandboxExplorer/sandboxTreeData.ts +77 -22
- package/src/features/sandboxExplorer/statusBar.ts +15 -2
|
@@ -507,7 +507,11 @@ export class ListSandboxesTool implements vscode.LanguageModelTool<ListSandboxes
|
|
|
507
507
|
|
|
508
508
|
const sandboxes = result.data.map((s) => ({
|
|
509
509
|
id: s.sandboxId,
|
|
510
|
+
name: s.name,
|
|
511
|
+
description: s.description,
|
|
510
512
|
status: s.status,
|
|
513
|
+
runtime: s.runtimeName ?? s.runtimeId,
|
|
514
|
+
runtimeId: s.runtimeId,
|
|
511
515
|
region: s.region,
|
|
512
516
|
createdAt: s.createdAt,
|
|
513
517
|
resources: s.resources,
|
|
@@ -529,12 +533,57 @@ export class ListSandboxesTool implements vscode.LanguageModelTool<ListSandboxes
|
|
|
529
533
|
}
|
|
530
534
|
}
|
|
531
535
|
|
|
536
|
+
export interface ListSandboxRuntimesInput {
|
|
537
|
+
limit?: number;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
export class ListSandboxRuntimesTool implements vscode.LanguageModelTool<ListSandboxRuntimesInput> {
|
|
541
|
+
async invoke(
|
|
542
|
+
options: vscode.LanguageModelToolInvocationOptions<ListSandboxRuntimesInput>,
|
|
543
|
+
_token: vscode.CancellationToken
|
|
544
|
+
): Promise<vscode.LanguageModelToolResult> {
|
|
545
|
+
const cli = getCliClient();
|
|
546
|
+
const limit = options.input.limit ?? 50;
|
|
547
|
+
const result = await cli.sandboxRuntimeList({ limit });
|
|
548
|
+
|
|
549
|
+
if (!result.success || !result.data) {
|
|
550
|
+
throw new Error(`Failed to list runtimes: ${result.error || 'Unknown error'}`);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
const output = result.data.runtimes.map((rt) => ({
|
|
554
|
+
id: rt.id,
|
|
555
|
+
name: rt.name,
|
|
556
|
+
description: rt.description,
|
|
557
|
+
tags: rt.tags,
|
|
558
|
+
url: rt.url,
|
|
559
|
+
}));
|
|
560
|
+
|
|
561
|
+
return new vscode.LanguageModelToolResult([
|
|
562
|
+
new vscode.LanguageModelTextPart(JSON.stringify(output, null, 2)),
|
|
563
|
+
]);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
async prepareInvocation(
|
|
567
|
+
_options: vscode.LanguageModelToolInvocationPrepareOptions<ListSandboxRuntimesInput>,
|
|
568
|
+
_token: vscode.CancellationToken
|
|
569
|
+
): Promise<vscode.PreparedToolInvocation> {
|
|
570
|
+
return {
|
|
571
|
+
invocationMessage: 'Listing sandbox runtimes...',
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
532
576
|
export interface CreateSandboxInput {
|
|
533
577
|
memory?: string;
|
|
534
578
|
cpu?: string;
|
|
535
579
|
network?: boolean;
|
|
536
580
|
snapshot?: string;
|
|
537
581
|
dependencies?: string[];
|
|
582
|
+
// New fields
|
|
583
|
+
runtime?: string;
|
|
584
|
+
runtimeId?: string;
|
|
585
|
+
name?: string;
|
|
586
|
+
description?: string;
|
|
538
587
|
}
|
|
539
588
|
|
|
540
589
|
export class CreateSandboxTool implements vscode.LanguageModelTool<CreateSandboxInput> {
|
|
@@ -549,6 +598,10 @@ export class CreateSandboxTool implements vscode.LanguageModelTool<CreateSandbox
|
|
|
549
598
|
network: options.input.network,
|
|
550
599
|
snapshot: options.input.snapshot,
|
|
551
600
|
dependencies: options.input.dependencies,
|
|
601
|
+
runtime: options.input.runtime,
|
|
602
|
+
runtimeId: options.input.runtimeId,
|
|
603
|
+
name: options.input.name,
|
|
604
|
+
description: options.input.description,
|
|
552
605
|
};
|
|
553
606
|
|
|
554
607
|
const result = await cli.sandboxCreate(createOptions);
|
|
@@ -557,10 +610,20 @@ export class CreateSandboxTool implements vscode.LanguageModelTool<CreateSandbox
|
|
|
557
610
|
throw new Error(`Failed to create sandbox: ${result.error || 'Unknown error'}`);
|
|
558
611
|
}
|
|
559
612
|
|
|
613
|
+
const info = result.data;
|
|
614
|
+
const lines = [
|
|
615
|
+
'Sandbox created successfully!',
|
|
616
|
+
'',
|
|
617
|
+
`ID: ${info.sandboxId}`,
|
|
618
|
+
info.name ? `Name: ${info.name}` : '',
|
|
619
|
+
info.description ? `Description: ${info.description}` : '',
|
|
620
|
+
`Runtime: ${info.runtimeName ?? info.runtimeId ?? 'bun:1'}`,
|
|
621
|
+
`Status: ${info.status}`,
|
|
622
|
+
`Region: ${info.region}`,
|
|
623
|
+
].filter(Boolean);
|
|
624
|
+
|
|
560
625
|
return new vscode.LanguageModelToolResult([
|
|
561
|
-
new vscode.LanguageModelTextPart(
|
|
562
|
-
`Sandbox created successfully!\n\nID: ${result.data.sandboxId}\nStatus: ${result.data.status}\nRegion: ${result.data.region}`
|
|
563
|
-
),
|
|
626
|
+
new vscode.LanguageModelTextPart(lines.join('\n')),
|
|
564
627
|
]);
|
|
565
628
|
}
|
|
566
629
|
|
|
@@ -680,6 +743,7 @@ export class ExecuteInSandboxTool implements vscode.LanguageModelTool<ExecuteInS
|
|
|
680
743
|
export interface CreateSnapshotInput {
|
|
681
744
|
sandboxId: string;
|
|
682
745
|
tag?: string;
|
|
746
|
+
region?: string;
|
|
683
747
|
}
|
|
684
748
|
|
|
685
749
|
export class CreateSnapshotTool implements vscode.LanguageModelTool<CreateSnapshotInput> {
|
|
@@ -759,6 +823,10 @@ export function registerAgentTools(context: vscode.ExtensionContext): void {
|
|
|
759
823
|
vscode.lm.registerTool('agentuity_list_sandboxes', new ListSandboxesTool())
|
|
760
824
|
);
|
|
761
825
|
|
|
826
|
+
context.subscriptions.push(
|
|
827
|
+
vscode.lm.registerTool('agentuity_list_sandbox_runtimes', new ListSandboxRuntimesTool())
|
|
828
|
+
);
|
|
829
|
+
|
|
762
830
|
context.subscriptions.push(
|
|
763
831
|
vscode.lm.registerTool('agentuity_create_sandbox', new CreateSandboxTool())
|
|
764
832
|
);
|
|
@@ -455,6 +455,59 @@ function registerCommands(
|
|
|
455
455
|
|
|
456
456
|
// ==================== Command Implementations ====================
|
|
457
457
|
|
|
458
|
+
interface RuntimePickItem extends vscode.QuickPickItem {
|
|
459
|
+
runtime?: string;
|
|
460
|
+
runtimeId?: string;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Show a quick pick to select a sandbox runtime.
|
|
465
|
+
* Returns undefined if cancelled, or the selected runtime info.
|
|
466
|
+
*/
|
|
467
|
+
async function pickSandboxRuntime(): Promise<{ runtime?: string; runtimeId?: string } | undefined> {
|
|
468
|
+
const cli = getCliClient();
|
|
469
|
+
|
|
470
|
+
// Show progress while loading runtimes
|
|
471
|
+
const result = await vscode.window.withProgress(
|
|
472
|
+
{
|
|
473
|
+
location: vscode.ProgressLocation.Notification,
|
|
474
|
+
title: 'Loading runtimes...',
|
|
475
|
+
cancellable: false,
|
|
476
|
+
},
|
|
477
|
+
async () => cli.sandboxRuntimeList({ limit: 50 })
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
if (!result.success || !result.data || !result.data.runtimes.length) {
|
|
481
|
+
// Fall back to default runtime if listing fails
|
|
482
|
+
const choice = await vscode.window.showQuickPick<RuntimePickItem>(
|
|
483
|
+
[
|
|
484
|
+
{ label: 'Use default runtime (base:latest)', runtime: undefined },
|
|
485
|
+
{ label: 'Cancel', runtime: '__cancel__' },
|
|
486
|
+
],
|
|
487
|
+
{ placeHolder: 'Runtime selection (runtime list unavailable)' }
|
|
488
|
+
);
|
|
489
|
+
if (!choice || choice.runtime === '__cancel__') {
|
|
490
|
+
return undefined;
|
|
491
|
+
}
|
|
492
|
+
return {}; // no runtime specified => CLI default
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const items: RuntimePickItem[] = result.data.runtimes.map((rt) => ({
|
|
496
|
+
label: rt.name,
|
|
497
|
+
description: rt.description,
|
|
498
|
+
detail: rt.tags?.length ? rt.tags.join(', ') : undefined,
|
|
499
|
+
runtime: rt.name,
|
|
500
|
+
runtimeId: rt.id,
|
|
501
|
+
}));
|
|
502
|
+
|
|
503
|
+
const picked = await vscode.window.showQuickPick(items, {
|
|
504
|
+
placeHolder: 'Select a sandbox runtime',
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
if (!picked) return undefined;
|
|
508
|
+
return { runtime: picked.runtime, runtimeId: picked.runtimeId };
|
|
509
|
+
}
|
|
510
|
+
|
|
458
511
|
async function createSandbox(provider: SandboxTreeDataProvider): Promise<void> {
|
|
459
512
|
const config = vscode.workspace.getConfiguration('agentuity');
|
|
460
513
|
const defaultMemory = config.get<string>('sandbox.defaultMemory', '512Mi');
|
|
@@ -464,8 +517,8 @@ async function createSandbox(provider: SandboxTreeDataProvider): Promise<void> {
|
|
|
464
517
|
// Quick pick for basic vs advanced
|
|
465
518
|
const mode = await vscode.window.showQuickPick(
|
|
466
519
|
[
|
|
467
|
-
{ label: 'Quick Create', description: '
|
|
468
|
-
{ label: 'Custom', description: 'Configure resources and options' },
|
|
520
|
+
{ label: 'Quick Create', description: 'bun:1 runtime with default settings' },
|
|
521
|
+
{ label: 'Custom', description: 'Configure runtime, resources and options' },
|
|
469
522
|
],
|
|
470
523
|
{ placeHolder: 'How do you want to create the sandbox?' }
|
|
471
524
|
);
|
|
@@ -475,6 +528,28 @@ async function createSandbox(provider: SandboxTreeDataProvider): Promise<void> {
|
|
|
475
528
|
let options: SandboxCreateOptions = {};
|
|
476
529
|
|
|
477
530
|
if (mode.label === 'Custom') {
|
|
531
|
+
// Name (optional)
|
|
532
|
+
const name = await vscode.window.showInputBox({
|
|
533
|
+
prompt: 'Sandbox name (optional)',
|
|
534
|
+
placeHolder: 'e.g., my-feature-env',
|
|
535
|
+
});
|
|
536
|
+
if (name === undefined) return;
|
|
537
|
+
options.name = name || undefined;
|
|
538
|
+
|
|
539
|
+
// Description (optional)
|
|
540
|
+
const description = await vscode.window.showInputBox({
|
|
541
|
+
prompt: 'Sandbox description (optional)',
|
|
542
|
+
placeHolder: 'e.g., Sandbox for feature-xyz integration tests',
|
|
543
|
+
});
|
|
544
|
+
if (description === undefined) return;
|
|
545
|
+
options.description = description || undefined;
|
|
546
|
+
|
|
547
|
+
// Runtime selection
|
|
548
|
+
const runtimeSelection = await pickSandboxRuntime();
|
|
549
|
+
if (runtimeSelection === undefined) return;
|
|
550
|
+
options.runtime = runtimeSelection.runtime;
|
|
551
|
+
options.runtimeId = runtimeSelection.runtimeId;
|
|
552
|
+
|
|
478
553
|
// Memory
|
|
479
554
|
const memory = await vscode.window.showInputBox({
|
|
480
555
|
prompt: 'Memory limit',
|
|
@@ -513,7 +588,9 @@ async function createSandbox(provider: SandboxTreeDataProvider): Promise<void> {
|
|
|
513
588
|
options.dependencies = deps.split(/\s+/).filter(Boolean);
|
|
514
589
|
}
|
|
515
590
|
} else {
|
|
591
|
+
// Quick create uses bun:1 runtime
|
|
516
592
|
options = {
|
|
593
|
+
runtime: 'bun:1',
|
|
517
594
|
memory: defaultMemory,
|
|
518
595
|
cpu: defaultCpu,
|
|
519
596
|
network: defaultNetwork,
|
|
@@ -531,7 +608,12 @@ async function createSandbox(provider: SandboxTreeDataProvider): Promise<void> {
|
|
|
531
608
|
const result = await cli.sandboxCreate(options);
|
|
532
609
|
|
|
533
610
|
if (result.success && result.data) {
|
|
534
|
-
|
|
611
|
+
const info = result.data;
|
|
612
|
+
const displayName = info.name || info.sandboxId.slice(0, 12);
|
|
613
|
+
const runtimeDisplay = info.runtimeName ?? info.runtimeId ?? 'bun:1';
|
|
614
|
+
vscode.window.showInformationMessage(
|
|
615
|
+
`Sandbox "${displayName}" created with runtime ${runtimeDisplay}`
|
|
616
|
+
);
|
|
535
617
|
await provider.forceRefresh();
|
|
536
618
|
} else {
|
|
537
619
|
vscode.window.showErrorMessage(`Failed to create sandbox: ${result.error}`);
|
|
@@ -544,6 +626,23 @@ async function createSandboxFromSnapshot(
|
|
|
544
626
|
snapshotId: string,
|
|
545
627
|
provider: SandboxTreeDataProvider
|
|
546
628
|
): Promise<void> {
|
|
629
|
+
// Optional: prompt for name/description when creating from snapshot
|
|
630
|
+
const name = await vscode.window.showInputBox({
|
|
631
|
+
prompt: 'Sandbox name (optional)',
|
|
632
|
+
placeHolder: 'e.g., my-feature-env',
|
|
633
|
+
});
|
|
634
|
+
if (name === undefined) return;
|
|
635
|
+
|
|
636
|
+
const description = await vscode.window.showInputBox({
|
|
637
|
+
prompt: 'Sandbox description (optional)',
|
|
638
|
+
placeHolder: 'e.g., Restored from snapshot for testing',
|
|
639
|
+
});
|
|
640
|
+
if (description === undefined) return;
|
|
641
|
+
|
|
642
|
+
// Runtime selection
|
|
643
|
+
const runtimeSelection = await pickSandboxRuntime();
|
|
644
|
+
if (runtimeSelection === undefined) return;
|
|
645
|
+
|
|
547
646
|
await vscode.window.withProgress(
|
|
548
647
|
{
|
|
549
648
|
location: vscode.ProgressLocation.Notification,
|
|
@@ -552,10 +651,21 @@ async function createSandboxFromSnapshot(
|
|
|
552
651
|
},
|
|
553
652
|
async () => {
|
|
554
653
|
const cli = getCliClient();
|
|
555
|
-
const result = await cli.sandboxCreate({
|
|
654
|
+
const result = await cli.sandboxCreate({
|
|
655
|
+
snapshot: snapshotId,
|
|
656
|
+
name: name || undefined,
|
|
657
|
+
description: description || undefined,
|
|
658
|
+
runtime: runtimeSelection.runtime,
|
|
659
|
+
runtimeId: runtimeSelection.runtimeId,
|
|
660
|
+
});
|
|
556
661
|
|
|
557
662
|
if (result.success && result.data) {
|
|
558
|
-
|
|
663
|
+
const info = result.data;
|
|
664
|
+
const displayName = info.name || info.sandboxId.slice(0, 12);
|
|
665
|
+
const runtimeDisplay = info.runtimeName ?? info.runtimeId ?? 'bun:1';
|
|
666
|
+
vscode.window.showInformationMessage(
|
|
667
|
+
`Sandbox "${displayName}" created from snapshot with runtime ${runtimeDisplay}`
|
|
668
|
+
);
|
|
559
669
|
await provider.forceRefresh();
|
|
560
670
|
} else {
|
|
561
671
|
vscode.window.showErrorMessage(`Failed to create sandbox: ${result.error}`);
|
|
@@ -702,9 +812,7 @@ function executeInTerminal(sandboxId: string, command: string): void {
|
|
|
702
812
|
}
|
|
703
813
|
|
|
704
814
|
terminal.show();
|
|
705
|
-
terminal.sendText(
|
|
706
|
-
`${cliPath} cloud sandbox exec ${sandboxId} --region ${cli.getSandboxRegion()} -- ${command}`
|
|
707
|
-
);
|
|
815
|
+
terminal.sendText(`${cliPath} cloud sandbox exec ${sandboxId} -- ${command}`);
|
|
708
816
|
}
|
|
709
817
|
|
|
710
818
|
function disposeTerminals(): void {
|
|
@@ -723,22 +831,19 @@ async function viewSandboxFile(sandboxId: string, filePath: string): Promise<voi
|
|
|
723
831
|
},
|
|
724
832
|
async () => {
|
|
725
833
|
const cli = getCliClient();
|
|
726
|
-
// Use a stable temp directory for sandbox files
|
|
727
834
|
const sandboxTmpDir = path.join(os.tmpdir(), 'agentuity-sandbox', sandboxId.slice(0, 12));
|
|
728
835
|
fs.mkdirSync(sandboxTmpDir, { recursive: true });
|
|
729
836
|
|
|
730
837
|
const fileName = path.basename(filePath);
|
|
731
838
|
const localPath = path.join(sandboxTmpDir, fileName);
|
|
732
839
|
|
|
733
|
-
// Build full remote path under sandbox home
|
|
734
840
|
const fullRemotePath = filePath.startsWith('/')
|
|
735
841
|
? filePath
|
|
736
842
|
: `${CliClient.SANDBOX_HOME}/${filePath}`;
|
|
737
843
|
|
|
738
|
-
const result = await cli.sandboxCpFromSandbox(sandboxId, fullRemotePath, localPath);
|
|
844
|
+
const result = await cli.sandboxCpFromSandbox(sandboxId, fullRemotePath, localPath, false);
|
|
739
845
|
|
|
740
846
|
if (result.success) {
|
|
741
|
-
// Track this file for save-back
|
|
742
847
|
sandboxFileMap.set(localPath, {
|
|
743
848
|
sandboxId,
|
|
744
849
|
remotePath: fullRemotePath,
|
|
@@ -760,7 +865,7 @@ async function uploadSavedFile(
|
|
|
760
865
|
provider: SandboxTreeDataProvider
|
|
761
866
|
): Promise<void> {
|
|
762
867
|
const cli = getCliClient();
|
|
763
|
-
const result = await cli.sandboxCpToSandbox(sandboxId, localPath, remotePath);
|
|
868
|
+
const result = await cli.sandboxCpToSandbox(sandboxId, localPath, remotePath, false);
|
|
764
869
|
|
|
765
870
|
if (result.success) {
|
|
766
871
|
vscode.window.showInformationMessage(`Saved to sandbox: ${path.basename(remotePath)}`);
|
|
@@ -1057,8 +1162,7 @@ async function viewEnv(sandboxId: string): Promise<void> {
|
|
|
1057
1162
|
},
|
|
1058
1163
|
async () => {
|
|
1059
1164
|
const cli = getCliClient();
|
|
1060
|
-
|
|
1061
|
-
const result = await cli.sandboxExec(sandboxId, ['env']);
|
|
1165
|
+
const result = await cli.sandboxExec(sandboxId, ['env'], {});
|
|
1062
1166
|
|
|
1063
1167
|
if (result.success && result.data) {
|
|
1064
1168
|
const content = result.data.output || '(no environment variables)';
|
|
@@ -1332,32 +1436,42 @@ async function downloadFile(url: string, destPath: string): Promise<void> {
|
|
|
1332
1436
|
}
|
|
1333
1437
|
|
|
1334
1438
|
async function uploadToSandbox(uri: vscode.Uri): Promise<void> {
|
|
1335
|
-
const
|
|
1439
|
+
const cli = getCliClient();
|
|
1336
1440
|
|
|
1337
|
-
|
|
1338
|
-
|
|
1441
|
+
// Fetch all sandboxes
|
|
1442
|
+
const listResult = await vscode.window.withProgress(
|
|
1443
|
+
{
|
|
1444
|
+
location: vscode.ProgressLocation.Notification,
|
|
1445
|
+
title: 'Loading sandboxes...',
|
|
1446
|
+
cancellable: false,
|
|
1447
|
+
},
|
|
1448
|
+
async () => cli.sandboxList()
|
|
1449
|
+
);
|
|
1450
|
+
|
|
1451
|
+
if (!listResult.success || !listResult.data || listResult.data.length === 0) {
|
|
1452
|
+
vscode.window.showWarningMessage('No sandboxes available. Create a sandbox first.');
|
|
1339
1453
|
return;
|
|
1340
1454
|
}
|
|
1341
1455
|
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1456
|
+
const sandboxes = listResult.data;
|
|
1457
|
+
|
|
1458
|
+
// Pick a sandbox
|
|
1459
|
+
const picked = await vscode.window.showQuickPick(
|
|
1460
|
+
sandboxes.map((s) => ({
|
|
1461
|
+
label: s.name || s.sandboxId.slice(0, 12),
|
|
1462
|
+
description: `${s.status} · ${s.runtimeName ?? s.runtimeId ?? 'base'}`,
|
|
1463
|
+
detail: s.sandboxId,
|
|
1464
|
+
sandbox: s,
|
|
1465
|
+
})),
|
|
1466
|
+
{ placeHolder: 'Select sandbox to upload to' }
|
|
1467
|
+
);
|
|
1468
|
+
|
|
1469
|
+
if (!picked) return;
|
|
1470
|
+
const selectedSandbox = picked.sandbox;
|
|
1357
1471
|
|
|
1358
1472
|
const remotePath = await vscode.window.showInputBox({
|
|
1359
1473
|
prompt: 'Remote path',
|
|
1360
|
-
value:
|
|
1474
|
+
value: DEFAULT_SANDBOX_PATH,
|
|
1361
1475
|
});
|
|
1362
1476
|
|
|
1363
1477
|
if (!remotePath) return;
|
|
@@ -1369,11 +1483,21 @@ async function uploadToSandbox(uri: vscode.Uri): Promise<void> {
|
|
|
1369
1483
|
cancellable: false,
|
|
1370
1484
|
},
|
|
1371
1485
|
async () => {
|
|
1372
|
-
const cli = getCliClient();
|
|
1373
1486
|
const stats = await vscode.workspace.fs.stat(uri);
|
|
1374
1487
|
const isDir = stats.type === vscode.FileType.Directory;
|
|
1375
1488
|
|
|
1376
|
-
|
|
1489
|
+
// Build full remote path including filename
|
|
1490
|
+
const fileName = path.basename(uri.fsPath);
|
|
1491
|
+
const fullRemotePath = remotePath.endsWith('/')
|
|
1492
|
+
? `${remotePath}${fileName}`
|
|
1493
|
+
: `${remotePath}/${fileName}`;
|
|
1494
|
+
|
|
1495
|
+
const result = await cli.sandboxCpToSandbox(
|
|
1496
|
+
selectedSandbox.sandboxId,
|
|
1497
|
+
uri.fsPath,
|
|
1498
|
+
fullRemotePath,
|
|
1499
|
+
isDir
|
|
1500
|
+
);
|
|
1377
1501
|
|
|
1378
1502
|
if (result.success) {
|
|
1379
1503
|
vscode.window.showInformationMessage(`Uploaded to ${remotePath}`);
|
|
@@ -39,7 +39,8 @@ export class SandboxTreeItem extends vscode.TreeItem {
|
|
|
39
39
|
public readonly parentSandboxId?: string,
|
|
40
40
|
public readonly categoryType?: 'files' | 'snapshots' | 'executions',
|
|
41
41
|
public readonly linkedData?: LinkedSandbox,
|
|
42
|
-
public readonly filePath?: string
|
|
42
|
+
public readonly filePath?: string,
|
|
43
|
+
public readonly region?: string
|
|
43
44
|
) {
|
|
44
45
|
super(label, collapsibleState);
|
|
45
46
|
this.setupItem();
|
|
@@ -92,9 +93,17 @@ export class SandboxTreeItem extends vscode.TreeItem {
|
|
|
92
93
|
}
|
|
93
94
|
this.contextValue = contextValue;
|
|
94
95
|
|
|
95
|
-
// Set description
|
|
96
|
+
// Set description with status and runtime
|
|
96
97
|
const statusLabel = status.charAt(0).toUpperCase() + status.slice(1);
|
|
97
|
-
|
|
98
|
+
const runtimeLabel = this.sandboxData.runtimeName ?? this.sandboxData.runtimeId ?? 'bun:1';
|
|
99
|
+
let desc = `${statusLabel} · ${runtimeLabel}`;
|
|
100
|
+
if (this.sandboxData.url) {
|
|
101
|
+
desc += ' 🌐';
|
|
102
|
+
}
|
|
103
|
+
if (isLinked) {
|
|
104
|
+
desc += ' [linked]';
|
|
105
|
+
}
|
|
106
|
+
this.description = desc;
|
|
98
107
|
|
|
99
108
|
// Set tooltip
|
|
100
109
|
this.tooltip = this.formatSandboxTooltip();
|
|
@@ -135,12 +144,35 @@ export class SandboxTreeItem extends vscode.TreeItem {
|
|
|
135
144
|
private formatSandboxTooltip(): string {
|
|
136
145
|
if (!this.sandboxData) return '';
|
|
137
146
|
|
|
138
|
-
const lines = [
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
`
|
|
143
|
-
|
|
147
|
+
const lines: string[] = [];
|
|
148
|
+
|
|
149
|
+
// Name and description first if present
|
|
150
|
+
if (this.sandboxData.name) {
|
|
151
|
+
lines.push(`Name: ${this.sandboxData.name}`);
|
|
152
|
+
}
|
|
153
|
+
if (this.sandboxData.description) {
|
|
154
|
+
lines.push(`Description: ${this.sandboxData.description}`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
lines.push(`ID: ${this.sandboxData.sandboxId}`);
|
|
158
|
+
lines.push(`Status: ${this.sandboxData.status}`);
|
|
159
|
+
|
|
160
|
+
// Runtime info
|
|
161
|
+
const runtimeDisplay = this.sandboxData.runtimeName ?? this.sandboxData.runtimeId ?? 'bun:1';
|
|
162
|
+
lines.push(`Runtime: ${runtimeDisplay}`);
|
|
163
|
+
|
|
164
|
+
if (this.sandboxData.region) {
|
|
165
|
+
lines.push(`Region: ${this.sandboxData.region}`);
|
|
166
|
+
}
|
|
167
|
+
lines.push(`Created: ${new Date(this.sandboxData.createdAt).toLocaleString()}`);
|
|
168
|
+
|
|
169
|
+
// Network/URL info
|
|
170
|
+
if (this.sandboxData.url) {
|
|
171
|
+
lines.push(`URL: ${this.sandboxData.url}`);
|
|
172
|
+
}
|
|
173
|
+
if (this.sandboxData.networkPort) {
|
|
174
|
+
lines.push(`Port: ${this.sandboxData.networkPort}`);
|
|
175
|
+
}
|
|
144
176
|
|
|
145
177
|
if (this.sandboxData.resources) {
|
|
146
178
|
const r = this.sandboxData.resources;
|
|
@@ -382,20 +414,21 @@ export class SandboxTreeDataProvider implements vscode.TreeDataProvider<SandboxT
|
|
|
382
414
|
|
|
383
415
|
// Category children
|
|
384
416
|
if (element.itemType === 'category' && element.parentSandboxId) {
|
|
417
|
+
const region = element.sandboxData?.region;
|
|
385
418
|
switch (element.categoryType) {
|
|
386
419
|
case 'files':
|
|
387
420
|
// Pass undefined for root listing (CLI defaults to sandbox home)
|
|
388
|
-
return this.getFilesChildren(element.parentSandboxId, undefined);
|
|
421
|
+
return this.getFilesChildren(element.parentSandboxId, undefined, region);
|
|
389
422
|
case 'snapshots':
|
|
390
|
-
return this.getSnapshotsChildren(element.parentSandboxId);
|
|
423
|
+
return this.getSnapshotsChildren(element.parentSandboxId, region);
|
|
391
424
|
case 'executions':
|
|
392
|
-
return this.getExecutionsChildren(element.parentSandboxId);
|
|
425
|
+
return this.getExecutionsChildren(element.parentSandboxId, region);
|
|
393
426
|
}
|
|
394
427
|
}
|
|
395
428
|
|
|
396
429
|
// Directory children
|
|
397
430
|
if (element.itemType === 'directory' && element.parentSandboxId && element.filePath) {
|
|
398
|
-
return this.getFilesChildren(element.parentSandboxId, element.filePath);
|
|
431
|
+
return this.getFilesChildren(element.parentSandboxId, element.filePath, element.region);
|
|
399
432
|
}
|
|
400
433
|
|
|
401
434
|
// Snapshot children (files from snapshot get)
|
|
@@ -469,7 +502,11 @@ export class SandboxTreeDataProvider implements vscode.TreeDataProvider<SandboxT
|
|
|
469
502
|
// Add sandbox items
|
|
470
503
|
for (const sandbox of this.sandboxes) {
|
|
471
504
|
const linked = linkedSandboxes.find((l) => l.sandboxId === sandbox.sandboxId);
|
|
472
|
-
|
|
505
|
+
// Prefer server-side name, then linked alias, then short ID
|
|
506
|
+
const displayName =
|
|
507
|
+
sandbox.name && sandbox.name.trim().length > 0
|
|
508
|
+
? sandbox.name
|
|
509
|
+
: linked?.name || sandbox.sandboxId.slice(0, 12);
|
|
473
510
|
|
|
474
511
|
items.push(
|
|
475
512
|
new SandboxTreeItem(
|
|
@@ -552,15 +589,18 @@ export class SandboxTreeDataProvider implements vscode.TreeDataProvider<SandboxT
|
|
|
552
589
|
];
|
|
553
590
|
}
|
|
554
591
|
|
|
555
|
-
private async getFilesChildren(
|
|
592
|
+
private async getFilesChildren(
|
|
593
|
+
sandboxId: string,
|
|
594
|
+
dirPath?: string,
|
|
595
|
+
region?: string
|
|
596
|
+
): Promise<SandboxTreeItem[]> {
|
|
556
597
|
// Always fetch from root to get full file list, then filter
|
|
557
598
|
const cacheKey = `${sandboxId}:root`;
|
|
558
599
|
|
|
559
600
|
// Check cache first - always cache from root
|
|
560
601
|
if (!this.filesCache.has(cacheKey)) {
|
|
561
602
|
const cli = getCliClient();
|
|
562
|
-
|
|
563
|
-
const result = await cli.sandboxLs(sandboxId);
|
|
603
|
+
const result = await cli.sandboxLs(sandboxId, undefined);
|
|
564
604
|
|
|
565
605
|
if (result.success && result.data) {
|
|
566
606
|
this.filesCache.set(cacheKey, result.data);
|
|
@@ -621,12 +661,16 @@ export class SandboxTreeDataProvider implements vscode.TreeDataProvider<SandboxT
|
|
|
621
661
|
sandboxId,
|
|
622
662
|
undefined,
|
|
623
663
|
undefined,
|
|
624
|
-
file.path // Use the full path from CLI
|
|
664
|
+
file.path, // Use the full path from CLI
|
|
665
|
+
region // Pass region to child items
|
|
625
666
|
);
|
|
626
667
|
});
|
|
627
668
|
}
|
|
628
669
|
|
|
629
|
-
private async getSnapshotsChildren(
|
|
670
|
+
private async getSnapshotsChildren(
|
|
671
|
+
sandboxId: string,
|
|
672
|
+
region?: string
|
|
673
|
+
): Promise<SandboxTreeItem[]> {
|
|
630
674
|
if (!this.snapshotsCache.has(sandboxId)) {
|
|
631
675
|
const cli = getCliClient();
|
|
632
676
|
const result = await cli.snapshotList(sandboxId);
|
|
@@ -663,7 +707,11 @@ export class SandboxTreeDataProvider implements vscode.TreeDataProvider<SandboxT
|
|
|
663
707
|
undefined,
|
|
664
708
|
snap,
|
|
665
709
|
undefined,
|
|
666
|
-
sandboxId
|
|
710
|
+
sandboxId,
|
|
711
|
+
undefined,
|
|
712
|
+
undefined,
|
|
713
|
+
undefined,
|
|
714
|
+
region
|
|
667
715
|
)
|
|
668
716
|
);
|
|
669
717
|
}
|
|
@@ -720,7 +768,10 @@ export class SandboxTreeDataProvider implements vscode.TreeDataProvider<SandboxT
|
|
|
720
768
|
});
|
|
721
769
|
}
|
|
722
770
|
|
|
723
|
-
private async getExecutionsChildren(
|
|
771
|
+
private async getExecutionsChildren(
|
|
772
|
+
sandboxId: string,
|
|
773
|
+
region?: string
|
|
774
|
+
): Promise<SandboxTreeItem[]> {
|
|
724
775
|
if (!this.executionsCache.has(sandboxId)) {
|
|
725
776
|
const cli = getCliClient();
|
|
726
777
|
const result = await cli.executionList(sandboxId);
|
|
@@ -756,7 +807,11 @@ export class SandboxTreeDataProvider implements vscode.TreeDataProvider<SandboxT
|
|
|
756
807
|
undefined,
|
|
757
808
|
undefined,
|
|
758
809
|
exec,
|
|
759
|
-
sandboxId
|
|
810
|
+
sandboxId,
|
|
811
|
+
undefined,
|
|
812
|
+
undefined,
|
|
813
|
+
undefined,
|
|
814
|
+
region
|
|
760
815
|
)
|
|
761
816
|
);
|
|
762
817
|
}
|
|
@@ -40,8 +40,20 @@ export function updateStatusBar(): void {
|
|
|
40
40
|
} else if (linked.length === 1) {
|
|
41
41
|
const sandbox = linked[0];
|
|
42
42
|
const name = sandbox.name || sandbox.sandboxId.slice(0, 8);
|
|
43
|
+
const runtime = sandbox.runtimeName ?? sandbox.runtimeId ?? 'bun:1';
|
|
43
44
|
statusBarItem.text = `$(vm) ${name}`;
|
|
44
|
-
|
|
45
|
+
|
|
46
|
+
const tooltipLines = [
|
|
47
|
+
`Sandbox: ${sandbox.sandboxId}`,
|
|
48
|
+
`Runtime: ${runtime}`,
|
|
49
|
+
`Linked: ${new Date(sandbox.linkedAt).toLocaleDateString()}`,
|
|
50
|
+
`Last sync: ${sandbox.lastSyncedAt ? new Date(sandbox.lastSyncedAt).toLocaleString() : 'Never'}`,
|
|
51
|
+
];
|
|
52
|
+
if (sandbox.description) {
|
|
53
|
+
tooltipLines.unshift(`Description: ${sandbox.description}`);
|
|
54
|
+
}
|
|
55
|
+
tooltipLines.push('', 'Click for sandbox options');
|
|
56
|
+
statusBarItem.tooltip = tooltipLines.join('\n');
|
|
45
57
|
statusBarItem.backgroundColor = undefined;
|
|
46
58
|
} else {
|
|
47
59
|
statusBarItem.text = `$(vm) ${linked.length} Sandboxes`;
|
|
@@ -125,9 +137,10 @@ async function showSandboxQuickPick(): Promise<void> {
|
|
|
125
137
|
|
|
126
138
|
for (const sandbox of linked) {
|
|
127
139
|
const name = sandbox.name || sandbox.sandboxId.slice(0, 8);
|
|
140
|
+
const runtime = sandbox.runtimeName ?? sandbox.runtimeId ?? 'bun:1';
|
|
128
141
|
items.push({
|
|
129
142
|
label: `$(vm) ${name}`,
|
|
130
|
-
description: sandbox.sandboxId,
|
|
143
|
+
description: `${runtime} · ${sandbox.sandboxId.slice(0, 8)}`,
|
|
131
144
|
detail: sandbox.lastSyncedAt
|
|
132
145
|
? `Last synced: ${new Date(sandbox.lastSyncedAt).toLocaleString()}`
|
|
133
146
|
: 'Never synced',
|