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/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "agentuity-vscode",
3
3
  "displayName": "Agentuity VSCode Extension",
4
4
  "description": "Build, deploy, and manage AI agents with Agentuity",
5
- "version": "0.1.10",
5
+ "version": "0.1.12",
6
6
  "publisher": "agentuity",
7
7
  "license": "Apache-2.0",
8
8
  "repository": {
Binary file
@@ -103,19 +103,6 @@ export class CliClient {
103
103
  return args;
104
104
  }
105
105
 
106
- /**
107
- * Append --region flag to args using region from agentuity.json.
108
- * Used for commands that require region but don't accept --dir.
109
- * The --region flag is a subcommand option, so it must come after the command.
110
- */
111
- private withRegion(args: string[]): string[] {
112
- const region = this.getProjectRegion();
113
- if (region) {
114
- return [...args, '--region', region];
115
- }
116
- return args;
117
- }
118
-
119
106
  /**
120
107
  * Get the environment variables for CLI execution.
121
108
  * Sets TERM_PROGRAM=vscode to ensure CLI disables interactive mode.
@@ -329,19 +316,26 @@ export class CliClient {
329
316
  return this.exec<string>(['ai', 'prompt', 'llm'], { format: 'text' });
330
317
  }
331
318
 
332
- // Database methods (require region - pass --region from agentuity.json)
333
319
  async listDatabases(): Promise<CliResult<DbListResponse>> {
334
- return this.exec<DbListResponse>(this.withRegion(['cloud', 'db', 'list']), {
320
+ return this.exec<DbListResponse>(['cloud', 'db', 'list'], {
335
321
  format: 'json',
336
322
  });
337
323
  }
338
324
 
339
325
  async getDatabase(name: string): Promise<CliResult<DbInfo>> {
340
- return this.exec<DbInfo>(this.withRegion(['cloud', 'db', 'get', name]), {
326
+ return this.exec<DbInfo>(['cloud', 'db', 'get', name], {
341
327
  format: 'json',
342
328
  });
343
329
  }
344
330
 
331
+ async createDatabase(options: DbCreateOptions): Promise<CliResult<DbInfo>> {
332
+ const args = ['cloud', 'db', 'create', '--name', options.name];
333
+ if (options.description) {
334
+ args.push('--description', options.description);
335
+ }
336
+ return this.exec<DbInfo>(args, { format: 'json', timeout: 60000 });
337
+ }
338
+
345
339
  async getDbLogs(
346
340
  name: string,
347
341
  opts?: { limit?: number; hasError?: boolean; sessionId?: string }
@@ -356,12 +350,11 @@ export class CliClient {
356
350
  if (opts?.sessionId) {
357
351
  args.push('--session-id', opts.sessionId);
358
352
  }
359
- return this.exec<DbQueryLog[]>(this.withRegion(args), { format: 'json', timeout: 60000 });
353
+ return this.exec<DbQueryLog[]>(args, { format: 'json', timeout: 60000 });
360
354
  }
361
355
 
362
- // Storage methods (require region - pass --region from agentuity.json)
363
356
  async listStorageBuckets(): Promise<CliResult<StorageListResponse>> {
364
- return this.exec<StorageListResponse>(this.withRegion(['cloud', 'storage', 'list']), {
357
+ return this.exec<StorageListResponse>(['cloud', 'storage', 'list'], {
365
358
  format: 'json',
366
359
  });
367
360
  }
@@ -374,7 +367,7 @@ export class CliClient {
374
367
  if (prefix) {
375
368
  args.push(prefix);
376
369
  }
377
- return this.exec<StorageListResponse>(this.withRegion(args), { format: 'json' });
370
+ return this.exec<StorageListResponse>(args, { format: 'json' });
378
371
  }
379
372
 
380
373
  async getStorageFileMetadata(
@@ -382,7 +375,7 @@ export class CliClient {
382
375
  filename: string
383
376
  ): Promise<CliResult<StorageFileMetadataResponse>> {
384
377
  return this.exec<StorageFileMetadataResponse>(
385
- this.withRegion(['cloud', 'storage', 'download', bucket, filename, '--metadata']),
378
+ ['cloud', 'storage', 'download', bucket, filename, '--metadata'],
386
379
  { format: 'json' }
387
380
  );
388
381
  }
@@ -509,17 +502,6 @@ export class CliClient {
509
502
 
510
503
  // ==================== Sandbox Methods ====================
511
504
 
512
- /** Default region for sandbox operations when no agentuity.json is present */
513
- private readonly defaultSandboxRegion = 'usc';
514
-
515
- /**
516
- * Get the region for sandbox operations.
517
- * Uses region from agentuity.json if present, otherwise falls back to default.
518
- */
519
- getSandboxRegion(): string {
520
- return this.getProjectRegion() ?? this.defaultSandboxRegion;
521
- }
522
-
523
505
  /** Default home path in sandboxes */
524
506
  static readonly SANDBOX_HOME = '/home/agentuity';
525
507
 
@@ -527,8 +509,23 @@ export class CliClient {
527
509
  * Create a new sandbox.
528
510
  */
529
511
  async sandboxCreate(options: SandboxCreateOptions = {}): Promise<CliResult<SandboxInfo>> {
530
- const args = ['cloud', 'sandbox', 'create', '--region', this.getSandboxRegion()];
512
+ const region = this.getProjectRegion() ?? 'usc';
513
+ const args = ['cloud', 'sandbox', 'create', '--region', region];
531
514
 
515
+ // New runtime/name/description options
516
+ if (options.runtime) {
517
+ args.push('--runtime', options.runtime);
518
+ }
519
+ if (options.runtimeId) {
520
+ args.push('--runtime-id', options.runtimeId);
521
+ }
522
+ if (options.name) {
523
+ args.push('--name', options.name);
524
+ }
525
+ if (options.description) {
526
+ args.push('--description', options.description);
527
+ }
528
+ // Existing options
532
529
  if (options.memory) {
533
530
  args.push('--memory', options.memory);
534
531
  }
@@ -541,6 +538,9 @@ export class CliClient {
541
538
  if (options.network) {
542
539
  args.push('--network');
543
540
  }
541
+ if (options.port !== undefined) {
542
+ args.push('--port', String(options.port));
543
+ }
544
544
  if (options.idleTimeout) {
545
545
  args.push('--idle-timeout', String(options.idleTimeout));
546
546
  }
@@ -569,11 +569,30 @@ export class CliClient {
569
569
  return this.exec<SandboxInfo>(args, { format: 'json', timeout: 120000 });
570
570
  }
571
571
 
572
+ /**
573
+ * List available sandbox runtimes.
574
+ */
575
+ async sandboxRuntimeList(
576
+ params: SandboxRuntimeListParams = {}
577
+ ): Promise<CliResult<SandboxRuntimeListResponse>> {
578
+ const args = ['cloud', 'sandbox', 'runtime', 'list'];
579
+
580
+ if (params.limit !== undefined) {
581
+ args.push('--limit', String(params.limit));
582
+ }
583
+ if (params.offset !== undefined) {
584
+ args.push('--offset', String(params.offset));
585
+ }
586
+
587
+ return this.exec<SandboxRuntimeListResponse>(args, { format: 'json' });
588
+ }
589
+
572
590
  /**
573
591
  * List sandboxes with optional filtering.
592
+ * Uses --all flag to list all sandboxes regardless of project context.
574
593
  */
575
594
  async sandboxList(filter: SandboxListFilter = {}): Promise<CliResult<SandboxInfo[]>> {
576
- const args = ['cloud', 'sandbox', 'list', '--region', this.getSandboxRegion()];
595
+ const args = ['cloud', 'sandbox', 'list', '--all'];
577
596
 
578
597
  if (filter.status) {
579
598
  args.push('--status', filter.status);
@@ -588,7 +607,6 @@ export class CliClient {
588
607
  args.push('--offset', String(filter.offset));
589
608
  }
590
609
 
591
- // CLI returns { sandboxes: [...], total: N }, extract the array
592
610
  const result = await this.exec<{ sandboxes: SandboxInfo[]; total: number }>(args, {
593
611
  format: 'json',
594
612
  });
@@ -602,28 +620,16 @@ export class CliClient {
602
620
  * Get detailed information about a sandbox.
603
621
  */
604
622
  async sandboxGet(sandboxId: string): Promise<CliResult<SandboxInfo>> {
605
- return this.exec<SandboxInfo>(
606
- ['cloud', 'sandbox', 'get', sandboxId, '--region', this.getSandboxRegion()],
607
- { format: 'json' }
608
- );
623
+ return this.exec<SandboxInfo>(['cloud', 'sandbox', 'get', sandboxId], { format: 'json' });
609
624
  }
610
625
 
611
626
  /**
612
627
  * Delete a sandbox.
613
628
  */
614
629
  async sandboxDelete(sandboxId: string): Promise<CliResult<void>> {
615
- return this.exec<void>(
616
- [
617
- 'cloud',
618
- 'sandbox',
619
- 'delete',
620
- sandboxId,
621
- '--confirm',
622
- '--region',
623
- this.getSandboxRegion(),
624
- ],
625
- { format: 'json' }
626
- );
630
+ return this.exec<void>(['cloud', 'sandbox', 'delete', sandboxId, '--confirm'], {
631
+ format: 'json',
632
+ });
627
633
  }
628
634
 
629
635
  /**
@@ -635,7 +641,7 @@ export class CliClient {
635
641
  command: string[],
636
642
  options: SandboxExecOptions = {}
637
643
  ): Promise<CliResult<ExecutionInfo>> {
638
- const args = ['cloud', 'sandbox', 'exec', sandboxId, '--region', this.getSandboxRegion()];
644
+ const args = ['cloud', 'sandbox', 'exec', sandboxId];
639
645
 
640
646
  if (options.timeout) {
641
647
  args.push('--timeout', String(options.timeout));
@@ -655,13 +661,11 @@ export class CliClient {
655
661
  */
656
662
  async sandboxLs(sandboxId: string, remotePath?: string): Promise<CliResult<SandboxFileInfo[]>> {
657
663
  const args = ['cloud', 'sandbox', 'files', sandboxId];
658
- // Only add path if specified (omit for root listing)
659
664
  if (remotePath) {
660
665
  args.push(remotePath);
661
666
  }
662
- args.push('-l', '--region', this.getSandboxRegion());
667
+ args.push('-l');
663
668
 
664
- // CLI returns { files: [...], total: N }, extract the array and add name from path
665
669
  const result = await this.exec<{
666
670
  files: Array<Omit<SandboxFileInfo, 'name'>>;
667
671
  total: number;
@@ -669,7 +673,7 @@ export class CliClient {
669
673
  if (result.success && result.data) {
670
674
  const files = (result.data.files || []).map((f) => ({
671
675
  ...f,
672
- name: f.path.split('/').pop() || f.path, // Extract filename from path
676
+ name: f.path.split('/').pop() || f.path,
673
677
  }));
674
678
  return { success: true, data: files, exitCode: result.exitCode };
675
679
  }
@@ -685,7 +689,7 @@ export class CliClient {
685
689
  remotePath: string,
686
690
  recursive = false
687
691
  ): Promise<CliResult<SandboxCpResult>> {
688
- const args = ['cloud', 'sandbox', 'cp', '--region', this.getSandboxRegion()];
692
+ const args = ['cloud', 'sandbox', 'cp'];
689
693
  if (recursive) {
690
694
  args.push('-r');
691
695
  }
@@ -702,7 +706,7 @@ export class CliClient {
702
706
  localPath: string,
703
707
  recursive = false
704
708
  ): Promise<CliResult<SandboxCpResult>> {
705
- const args = ['cloud', 'sandbox', 'cp', '--region', this.getSandboxRegion()];
709
+ const args = ['cloud', 'sandbox', 'cp'];
706
710
  if (recursive) {
707
711
  args.push('-r');
708
712
  }
@@ -718,15 +722,7 @@ export class CliClient {
718
722
  archivePath: string,
719
723
  destPath?: string
720
724
  ): Promise<CliResult<void>> {
721
- const args = [
722
- 'cloud',
723
- 'sandbox',
724
- 'upload',
725
- sandboxId,
726
- archivePath,
727
- '--region',
728
- this.getSandboxRegion(),
729
- ];
725
+ const args = ['cloud', 'sandbox', 'upload', sandboxId, archivePath];
730
726
  if (destPath) {
731
727
  args.push('--path', destPath);
732
728
  }
@@ -741,15 +737,7 @@ export class CliClient {
741
737
  outputPath: string,
742
738
  sourcePath?: string
743
739
  ): Promise<CliResult<void>> {
744
- const args = [
745
- 'cloud',
746
- 'sandbox',
747
- 'download',
748
- sandboxId,
749
- outputPath,
750
- '--region',
751
- this.getSandboxRegion(),
752
- ];
740
+ const args = ['cloud', 'sandbox', 'download', sandboxId, outputPath];
753
741
  if (sourcePath) {
754
742
  args.push('--path', sourcePath);
755
743
  }
@@ -764,15 +752,7 @@ export class CliClient {
764
752
  remotePath: string,
765
753
  recursive = false
766
754
  ): Promise<CliResult<void>> {
767
- const args = [
768
- 'cloud',
769
- 'sandbox',
770
- 'mkdir',
771
- sandboxId,
772
- remotePath,
773
- '--region',
774
- this.getSandboxRegion(),
775
- ];
755
+ const args = ['cloud', 'sandbox', 'mkdir', sandboxId, remotePath];
776
756
  if (recursive) {
777
757
  args.push('-p');
778
758
  }
@@ -783,10 +763,7 @@ export class CliClient {
783
763
  * Remove a file from a sandbox.
784
764
  */
785
765
  async sandboxRm(sandboxId: string, remotePath: string): Promise<CliResult<void>> {
786
- return this.exec<void>(
787
- ['cloud', 'sandbox', 'rm', sandboxId, remotePath, '--region', this.getSandboxRegion()],
788
- { format: 'json' }
789
- );
766
+ return this.exec<void>(['cloud', 'sandbox', 'rm', sandboxId, remotePath], { format: 'json' });
790
767
  }
791
768
 
792
769
  /**
@@ -797,15 +774,7 @@ export class CliClient {
797
774
  remotePath: string,
798
775
  recursive = false
799
776
  ): Promise<CliResult<void>> {
800
- const args = [
801
- 'cloud',
802
- 'sandbox',
803
- 'rmdir',
804
- sandboxId,
805
- remotePath,
806
- '--region',
807
- this.getSandboxRegion(),
808
- ];
777
+ const args = ['cloud', 'sandbox', 'rmdir', sandboxId, remotePath];
809
778
  if (recursive) {
810
779
  args.push('-r');
811
780
  }
@@ -819,7 +788,7 @@ export class CliClient {
819
788
  sandboxId: string,
820
789
  vars: Record<string, string>
821
790
  ): Promise<CliResult<SandboxEnvResult>> {
822
- const args = ['cloud', 'sandbox', 'env', sandboxId, '--region', this.getSandboxRegion()];
791
+ const args = ['cloud', 'sandbox', 'env', sandboxId];
823
792
  for (const [key, value] of Object.entries(vars)) {
824
793
  args.push(`${key}=${value}`);
825
794
  }
@@ -833,7 +802,7 @@ export class CliClient {
833
802
  sandboxId: string,
834
803
  varNames: string[]
835
804
  ): Promise<CliResult<SandboxEnvResult>> {
836
- const args = ['cloud', 'sandbox', 'env', sandboxId, '--region', this.getSandboxRegion()];
805
+ const args = ['cloud', 'sandbox', 'env', sandboxId];
837
806
  for (const name of varNames) {
838
807
  args.push('--delete', name);
839
808
  }
@@ -844,10 +813,9 @@ export class CliClient {
844
813
  * Get environment variables from a sandbox.
845
814
  */
846
815
  async sandboxEnvGet(sandboxId: string): Promise<CliResult<SandboxEnvResult>> {
847
- return this.exec<SandboxEnvResult>(
848
- ['cloud', 'sandbox', 'env', sandboxId, '--region', this.getSandboxRegion()],
849
- { format: 'json' }
850
- );
816
+ return this.exec<SandboxEnvResult>(['cloud', 'sandbox', 'env', sandboxId], {
817
+ format: 'json',
818
+ });
851
819
  }
852
820
 
853
821
  // ==================== Snapshot Methods ====================
@@ -856,15 +824,7 @@ export class CliClient {
856
824
  * Create a snapshot of a sandbox.
857
825
  */
858
826
  async snapshotCreate(sandboxId: string, tag?: string): Promise<CliResult<SnapshotInfo>> {
859
- const args = [
860
- 'cloud',
861
- 'sandbox',
862
- 'snapshot',
863
- 'create',
864
- sandboxId,
865
- '--region',
866
- this.getSandboxRegion(),
867
- ];
827
+ const args = ['cloud', 'sandbox', 'snapshot', 'create', sandboxId];
868
828
  if (tag) {
869
829
  args.push('--tag', tag);
870
830
  }
@@ -875,11 +835,10 @@ export class CliClient {
875
835
  * List snapshots with optional sandbox filter.
876
836
  */
877
837
  async snapshotList(sandboxId?: string): Promise<CliResult<SnapshotInfo[]>> {
878
- const args = ['cloud', 'sandbox', 'snapshot', 'list', '--region', this.getSandboxRegion()];
838
+ const args = ['cloud', 'sandbox', 'snapshot', 'list'];
879
839
  if (sandboxId) {
880
840
  args.push('--sandbox', sandboxId);
881
841
  }
882
- // CLI returns { snapshots: [], total: N }
883
842
  const result = await this.exec<{ snapshots: SnapshotInfo[]; total: number }>(args, {
884
843
  format: 'json',
885
844
  });
@@ -893,44 +852,25 @@ export class CliClient {
893
852
  * Get detailed information about a snapshot.
894
853
  */
895
854
  async snapshotGet(snapshotId: string): Promise<CliResult<SnapshotInfo>> {
896
- return this.exec<SnapshotInfo>(
897
- ['cloud', 'sandbox', 'snapshot', 'get', snapshotId, '--region', this.getSandboxRegion()],
898
- { format: 'json' }
899
- );
855
+ return this.exec<SnapshotInfo>(['cloud', 'sandbox', 'snapshot', 'get', snapshotId], {
856
+ format: 'json',
857
+ });
900
858
  }
901
859
 
902
860
  /**
903
861
  * Delete a snapshot.
904
862
  */
905
863
  async snapshotDelete(snapshotId: string): Promise<CliResult<void>> {
906
- return this.exec<void>(
907
- [
908
- 'cloud',
909
- 'sandbox',
910
- 'snapshot',
911
- 'delete',
912
- snapshotId,
913
- '--confirm',
914
- '--region',
915
- this.getSandboxRegion(),
916
- ],
917
- { format: 'json' }
918
- );
864
+ return this.exec<void>(['cloud', 'sandbox', 'snapshot', 'delete', snapshotId, '--confirm'], {
865
+ format: 'json',
866
+ });
919
867
  }
920
868
 
921
869
  /**
922
870
  * Tag or untag a snapshot.
923
871
  */
924
872
  async snapshotTag(snapshotId: string, tag: string | null): Promise<CliResult<void>> {
925
- const args = [
926
- 'cloud',
927
- 'sandbox',
928
- 'snapshot',
929
- 'tag',
930
- snapshotId,
931
- '--region',
932
- this.getSandboxRegion(),
933
- ];
873
+ const args = ['cloud', 'sandbox', 'snapshot', 'tag', snapshotId];
934
874
  if (tag === null) {
935
875
  args.push('--clear');
936
876
  } else {
@@ -945,9 +885,8 @@ export class CliClient {
945
885
  * List executions for a sandbox.
946
886
  */
947
887
  async executionList(sandboxId: string): Promise<CliResult<ExecutionInfo[]>> {
948
- // CLI returns { executions: [] }
949
888
  const result = await this.exec<{ executions: ExecutionInfo[] }>(
950
- ['cloud', 'sandbox', 'execution', 'list', sandboxId, '--region', this.getSandboxRegion()],
889
+ ['cloud', 'sandbox', 'execution', 'list', sandboxId],
951
890
  { format: 'json' }
952
891
  );
953
892
  if (result.success && result.data) {
@@ -960,10 +899,9 @@ export class CliClient {
960
899
  * Get detailed information about an execution.
961
900
  */
962
901
  async executionGet(executionId: string): Promise<CliResult<ExecutionInfo>> {
963
- return this.exec<ExecutionInfo>(
964
- ['cloud', 'sandbox', 'execution', 'get', executionId, '--region', this.getSandboxRegion()],
965
- { format: 'json' }
966
- );
902
+ return this.exec<ExecutionInfo>(['cloud', 'sandbox', 'execution', 'get', executionId], {
903
+ format: 'json',
904
+ });
967
905
  }
968
906
 
969
907
  dispose(): void {
@@ -1026,12 +964,19 @@ export interface KvGetResponse {
1026
964
  export interface DbInfo {
1027
965
  name: string;
1028
966
  url: string;
967
+ description?: string | null;
968
+ region?: string;
1029
969
  }
1030
970
 
1031
971
  export interface DbListResponse {
1032
972
  databases: DbInfo[];
1033
973
  }
1034
974
 
975
+ export interface DbCreateOptions {
976
+ name: string;
977
+ description?: string;
978
+ }
979
+
1035
980
  export interface DbQueryLog {
1036
981
  timestamp: string;
1037
982
  command: string;
@@ -1270,13 +1215,29 @@ export interface SandboxInfo {
1270
1215
  resources?: SandboxResources;
1271
1216
  stdoutStreamUrl?: string;
1272
1217
  stderrStreamUrl?: string;
1218
+ // New fields from sandbox improvements
1219
+ name?: string;
1220
+ description?: string;
1221
+ runtimeId?: string;
1222
+ runtimeName?: string;
1223
+ // Network/URL fields
1224
+ identifier?: string;
1225
+ networkPort?: number;
1226
+ url?: string;
1273
1227
  }
1274
1228
 
1275
1229
  export interface SandboxCreateOptions {
1230
+ // New fields from sandbox improvements
1231
+ runtime?: string;
1232
+ runtimeId?: string;
1233
+ name?: string;
1234
+ description?: string;
1235
+ // Existing fields
1276
1236
  memory?: string;
1277
1237
  cpu?: string;
1278
1238
  disk?: string;
1279
1239
  network?: boolean;
1240
+ port?: number; // 1024-65535, enables network automatically
1280
1241
  idleTimeout?: number;
1281
1242
  execTimeout?: number;
1282
1243
  env?: Record<string, string>;
@@ -1341,6 +1302,26 @@ export interface SandboxEnvResult {
1341
1302
  env: Record<string, string>;
1342
1303
  }
1343
1304
 
1305
+ // Sandbox runtime types
1306
+ export interface SandboxRuntime {
1307
+ id: string;
1308
+ name: string;
1309
+ description?: string;
1310
+ iconUrl?: string;
1311
+ url?: string;
1312
+ tags?: string[];
1313
+ }
1314
+
1315
+ export interface SandboxRuntimeListParams {
1316
+ limit?: number;
1317
+ offset?: number;
1318
+ }
1319
+
1320
+ export interface SandboxRuntimeListResponse {
1321
+ runtimes: SandboxRuntime[];
1322
+ total: number;
1323
+ }
1324
+
1344
1325
  // Singleton
1345
1326
  let _cliClient: CliClient | undefined;
1346
1327
 
@@ -17,6 +17,11 @@ export interface LinkedSandbox {
17
17
  linkedAt: string;
18
18
  lastSyncedAt?: string;
19
19
  remotePath: string;
20
+ // New fields from sandbox improvements
21
+ description?: string;
22
+ runtimeId?: string;
23
+ runtimeName?: string;
24
+ region?: string;
20
25
  }
21
26
 
22
27
  /**
@@ -80,11 +85,12 @@ export class SandboxManager {
80
85
  throw new Error('No workspace folder open');
81
86
  }
82
87
 
83
- // Verify sandbox exists
88
+ // Verify sandbox exists and get its info
84
89
  const result = await this.cliClient.sandboxGet(sandboxId);
85
- if (!result.success) {
90
+ if (!result.success || !result.data) {
86
91
  throw new Error(`Failed to verify sandbox: ${result.error}`);
87
92
  }
93
+ const sandboxInfo = result.data;
88
94
 
89
95
  const allLinked = this.context.workspaceState.get<Record<string, LinkedSandbox[]>>(
90
96
  LINKED_SANDBOXES_KEY,
@@ -100,6 +106,7 @@ export class SandboxManager {
100
106
  ...workspaceLinks[existingIndex],
101
107
  name: options.name ?? workspaceLinks[existingIndex].name,
102
108
  remotePath: options.remotePath ?? workspaceLinks[existingIndex].remotePath,
109
+ region: sandboxInfo.region ?? workspaceLinks[existingIndex].region,
103
110
  };
104
111
  } else {
105
112
  // Add new link
@@ -108,6 +115,7 @@ export class SandboxManager {
108
115
  name: options.name,
109
116
  linkedAt: new Date().toISOString(),
110
117
  remotePath: options.remotePath ?? DEFAULT_SANDBOX_PATH,
118
+ region: sandboxInfo.region,
111
119
  });
112
120
  }
113
121
 
@@ -428,20 +436,53 @@ export class SandboxManager {
428
436
  /**
429
437
  * Refresh sandbox info for all linked sandboxes.
430
438
  * Returns info about which sandboxes are still valid.
439
+ * Also updates linked sandbox metadata with new fields from the API.
431
440
  */
432
441
  async refreshLinkedSandboxes(): Promise<Map<string, SandboxInfo | null>> {
433
- const linked = this.getLinkedSandboxes();
442
+ const workspaceKey = this.getWorkspaceKey();
443
+ if (!workspaceKey) {
444
+ return new Map();
445
+ }
446
+
447
+ const allLinked = this.context.workspaceState.get<Record<string, LinkedSandbox[]>>(
448
+ LINKED_SANDBOXES_KEY,
449
+ {}
450
+ );
451
+ const workspaceLinks = allLinked[workspaceKey] || [];
434
452
  const results = new Map<string, SandboxInfo | null>();
453
+ let needsUpdate = false;
435
454
 
436
- for (const link of linked) {
455
+ for (const link of workspaceLinks) {
437
456
  const result = await this.cliClient.sandboxGet(link.sandboxId);
438
457
  if (result.success && result.data) {
439
- results.set(link.sandboxId, result.data);
458
+ const info = result.data;
459
+ results.set(link.sandboxId, info);
460
+
461
+ // Update linked sandbox with new fields from API
462
+ if (
463
+ info.name !== link.name ||
464
+ info.description !== link.description ||
465
+ info.runtimeId !== link.runtimeId ||
466
+ info.runtimeName !== link.runtimeName
467
+ ) {
468
+ link.name = info.name ?? link.name;
469
+ link.description = info.description;
470
+ link.runtimeId = info.runtimeId;
471
+ link.runtimeName = info.runtimeName;
472
+ needsUpdate = true;
473
+ }
440
474
  } else {
441
475
  results.set(link.sandboxId, null);
442
476
  }
443
477
  }
444
478
 
479
+ // Persist updated metadata if changed
480
+ if (needsUpdate) {
481
+ allLinked[workspaceKey] = workspaceLinks;
482
+ await this.context.workspaceState.update(LINKED_SANDBOXES_KEY, allLinked);
483
+ _onLinkedSandboxesChanged.fire(workspaceLinks);
484
+ }
485
+
445
486
  return results;
446
487
  }
447
488