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.
@@ -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: 'Use default settings' },
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
- vscode.window.showInformationMessage(`Sandbox created: ${result.data.sandboxId}`);
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({ snapshot: snapshotId });
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
- vscode.window.showInformationMessage(`Sandbox created: ${result.data.sandboxId}`);
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
- // Use exec to run 'env' command to get actual runtime environment
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 linked = getSandboxManager().getLinkedSandboxes();
1439
+ const cli = getCliClient();
1336
1440
 
1337
- if (linked.length === 0) {
1338
- vscode.window.showWarningMessage('No sandbox linked. Link a sandbox first.');
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
- let sandboxId: string;
1343
- if (linked.length === 1) {
1344
- sandboxId = linked[0].sandboxId;
1345
- } else {
1346
- const picked = await vscode.window.showQuickPick(
1347
- linked.map((l) => ({
1348
- label: l.name || l.sandboxId,
1349
- description: l.sandboxId,
1350
- sandboxId: l.sandboxId,
1351
- })),
1352
- { placeHolder: 'Select sandbox to upload to' }
1353
- );
1354
- if (!picked) return;
1355
- sandboxId = picked.sandboxId;
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: linked.find((l) => l.sandboxId === sandboxId)?.remotePath || DEFAULT_SANDBOX_PATH,
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
- const result = await cli.sandboxCpToSandbox(sandboxId, uri.fsPath, remotePath, isDir);
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
- this.description = isLinked ? `${statusLabel} [linked]` : statusLabel;
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
- `ID: ${this.sandboxData.sandboxId}`,
140
- `Status: ${this.sandboxData.status}`,
141
- `Region: ${this.sandboxData.region}`,
142
- `Created: ${new Date(this.sandboxData.createdAt).toLocaleString()}`,
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
- const displayName = linked?.name || sandbox.sandboxId;
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(sandboxId: string, dirPath?: string): Promise<SandboxTreeItem[]> {
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
- // Always fetch from root (no path) to get complete file list
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(sandboxId: string): Promise<SandboxTreeItem[]> {
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(sandboxId: string): Promise<SandboxTreeItem[]> {
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
- statusBarItem.tooltip = `Sandbox: ${sandbox.sandboxId}\nLinked: ${new Date(sandbox.linkedAt).toLocaleDateString()}\nLast sync: ${sandbox.lastSyncedAt ? new Date(sandbox.lastSyncedAt).toLocaleString() : 'Never'}\n\nClick for sandbox options`;
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',