agentuity-vscode 0.1.6 → 0.1.8

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,6 +507,465 @@ export class CliClient {
507
507
  });
508
508
  }
509
509
 
510
+ // ==================== Sandbox Methods ====================
511
+
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
+ /** Default home path in sandboxes */
524
+ static readonly SANDBOX_HOME = '/home/agentuity/app';
525
+
526
+ /**
527
+ * Create a new sandbox.
528
+ */
529
+ async sandboxCreate(options: SandboxCreateOptions = {}): Promise<CliResult<SandboxInfo>> {
530
+ const args = ['cloud', 'sandbox', 'create', '--region', this.getSandboxRegion()];
531
+
532
+ if (options.memory) {
533
+ args.push('--memory', options.memory);
534
+ }
535
+ if (options.cpu) {
536
+ args.push('--cpu', options.cpu);
537
+ }
538
+ if (options.disk) {
539
+ args.push('--disk', options.disk);
540
+ }
541
+ if (options.network) {
542
+ args.push('--network');
543
+ }
544
+ if (options.idleTimeout) {
545
+ args.push('--idle-timeout', String(options.idleTimeout));
546
+ }
547
+ if (options.execTimeout) {
548
+ args.push('--exec-timeout', String(options.execTimeout));
549
+ }
550
+ if (options.snapshot) {
551
+ args.push('--snapshot', options.snapshot);
552
+ }
553
+ if (options.dependencies && options.dependencies.length > 0) {
554
+ for (const dep of options.dependencies) {
555
+ args.push('--dependency', dep);
556
+ }
557
+ }
558
+ if (options.env) {
559
+ for (const [key, value] of Object.entries(options.env)) {
560
+ args.push('--env', `${key}=${value}`);
561
+ }
562
+ }
563
+ if (options.metadata) {
564
+ for (const [key, value] of Object.entries(options.metadata)) {
565
+ args.push('--metadata', `${key}=${value}`);
566
+ }
567
+ }
568
+
569
+ return this.exec<SandboxInfo>(args, { format: 'json', timeout: 120000 });
570
+ }
571
+
572
+ /**
573
+ * List sandboxes with optional filtering.
574
+ */
575
+ async sandboxList(filter: SandboxListFilter = {}): Promise<CliResult<SandboxInfo[]>> {
576
+ const args = ['cloud', 'sandbox', 'list', '--region', this.getSandboxRegion()];
577
+
578
+ if (filter.status) {
579
+ args.push('--status', filter.status);
580
+ }
581
+ if (filter.projectId) {
582
+ args.push('--project-id', filter.projectId);
583
+ }
584
+ if (filter.limit) {
585
+ args.push('--limit', String(filter.limit));
586
+ }
587
+ if (filter.offset) {
588
+ args.push('--offset', String(filter.offset));
589
+ }
590
+
591
+ // CLI returns { sandboxes: [...], total: N }, extract the array
592
+ const result = await this.exec<{ sandboxes: SandboxInfo[]; total: number }>(args, {
593
+ format: 'json',
594
+ });
595
+ if (result.success && result.data) {
596
+ return { success: true, data: result.data.sandboxes || [], exitCode: result.exitCode };
597
+ }
598
+ return { success: result.success, error: result.error, data: [], exitCode: result.exitCode };
599
+ }
600
+
601
+ /**
602
+ * Get detailed information about a sandbox.
603
+ */
604
+ async sandboxGet(sandboxId: string): Promise<CliResult<SandboxInfo>> {
605
+ return this.exec<SandboxInfo>(
606
+ ['cloud', 'sandbox', 'get', sandboxId, '--region', this.getSandboxRegion()],
607
+ { format: 'json' }
608
+ );
609
+ }
610
+
611
+ /**
612
+ * Delete a sandbox.
613
+ */
614
+ 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
+ );
627
+ }
628
+
629
+ /**
630
+ * Execute a command in a sandbox.
631
+ * Note: For streaming output, use sandboxExecInTerminal instead.
632
+ */
633
+ async sandboxExec(
634
+ sandboxId: string,
635
+ command: string[],
636
+ options: SandboxExecOptions = {}
637
+ ): Promise<CliResult<ExecutionInfo>> {
638
+ const args = ['cloud', 'sandbox', 'exec', sandboxId, '--region', this.getSandboxRegion()];
639
+
640
+ if (options.timeout) {
641
+ args.push('--timeout', String(options.timeout));
642
+ }
643
+ if (options.timestamps) {
644
+ args.push('--timestamps');
645
+ }
646
+
647
+ args.push('--');
648
+ args.push(...command);
649
+
650
+ return this.exec<ExecutionInfo>(args, { format: 'json', timeout: options.timeout || 300000 });
651
+ }
652
+
653
+ /**
654
+ * List files in a sandbox directory.
655
+ */
656
+ async sandboxLs(sandboxId: string, remotePath?: string): Promise<CliResult<SandboxFileInfo[]>> {
657
+ const args = ['cloud', 'sandbox', 'files', sandboxId];
658
+ // Only add path if specified (omit for root listing)
659
+ if (remotePath) {
660
+ args.push(remotePath);
661
+ }
662
+ args.push('-l', '--region', this.getSandboxRegion());
663
+
664
+ // CLI returns { files: [...], total: N }, extract the array and add name from path
665
+ const result = await this.exec<{
666
+ files: Array<Omit<SandboxFileInfo, 'name'>>;
667
+ total: number;
668
+ }>(args, { format: 'json' });
669
+ if (result.success && result.data) {
670
+ const files = (result.data.files || []).map((f) => ({
671
+ ...f,
672
+ name: f.path.split('/').pop() || f.path, // Extract filename from path
673
+ }));
674
+ return { success: true, data: files, exitCode: result.exitCode };
675
+ }
676
+ return { success: result.success, error: result.error, data: [], exitCode: result.exitCode };
677
+ }
678
+
679
+ /**
680
+ * Upload a file or directory to a sandbox.
681
+ */
682
+ async sandboxCpToSandbox(
683
+ sandboxId: string,
684
+ localPath: string,
685
+ remotePath: string,
686
+ recursive = false
687
+ ): Promise<CliResult<SandboxCpResult>> {
688
+ const args = ['cloud', 'sandbox', 'cp', '--region', this.getSandboxRegion()];
689
+ if (recursive) {
690
+ args.push('-r');
691
+ }
692
+ args.push(localPath, `${sandboxId}:${remotePath}`);
693
+ return this.exec<SandboxCpResult>(args, { format: 'json', timeout: 300000 });
694
+ }
695
+
696
+ /**
697
+ * Download a file or directory from a sandbox.
698
+ */
699
+ async sandboxCpFromSandbox(
700
+ sandboxId: string,
701
+ remotePath: string,
702
+ localPath: string,
703
+ recursive = false
704
+ ): Promise<CliResult<SandboxCpResult>> {
705
+ const args = ['cloud', 'sandbox', 'cp', '--region', this.getSandboxRegion()];
706
+ if (recursive) {
707
+ args.push('-r');
708
+ }
709
+ args.push(`${sandboxId}:${remotePath}`, localPath);
710
+ return this.exec<SandboxCpResult>(args, { format: 'json', timeout: 300000 });
711
+ }
712
+
713
+ /**
714
+ * Upload an archive (tar.gz or zip) to a sandbox and extract it.
715
+ */
716
+ async sandboxUpload(
717
+ sandboxId: string,
718
+ archivePath: string,
719
+ destPath?: string
720
+ ): Promise<CliResult<void>> {
721
+ const args = [
722
+ 'cloud',
723
+ 'sandbox',
724
+ 'upload',
725
+ sandboxId,
726
+ archivePath,
727
+ '--region',
728
+ this.getSandboxRegion(),
729
+ ];
730
+ if (destPath) {
731
+ args.push('--path', destPath);
732
+ }
733
+ return this.exec<void>(args, { format: 'json', timeout: 300000 });
734
+ }
735
+
736
+ /**
737
+ * Download sandbox files as an archive.
738
+ */
739
+ async sandboxDownload(
740
+ sandboxId: string,
741
+ outputPath: string,
742
+ sourcePath?: string
743
+ ): Promise<CliResult<void>> {
744
+ const args = [
745
+ 'cloud',
746
+ 'sandbox',
747
+ 'download',
748
+ sandboxId,
749
+ outputPath,
750
+ '--region',
751
+ this.getSandboxRegion(),
752
+ ];
753
+ if (sourcePath) {
754
+ args.push('--path', sourcePath);
755
+ }
756
+ return this.exec<void>(args, { format: 'json', timeout: 300000 });
757
+ }
758
+
759
+ /**
760
+ * Create a directory in a sandbox.
761
+ */
762
+ async sandboxMkdir(
763
+ sandboxId: string,
764
+ remotePath: string,
765
+ recursive = false
766
+ ): Promise<CliResult<void>> {
767
+ const args = [
768
+ 'cloud',
769
+ 'sandbox',
770
+ 'mkdir',
771
+ sandboxId,
772
+ remotePath,
773
+ '--region',
774
+ this.getSandboxRegion(),
775
+ ];
776
+ if (recursive) {
777
+ args.push('-p');
778
+ }
779
+ return this.exec<void>(args, { format: 'json' });
780
+ }
781
+
782
+ /**
783
+ * Remove a file from a sandbox.
784
+ */
785
+ 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
+ );
790
+ }
791
+
792
+ /**
793
+ * Remove a directory from a sandbox.
794
+ */
795
+ async sandboxRmdir(
796
+ sandboxId: string,
797
+ remotePath: string,
798
+ recursive = false
799
+ ): Promise<CliResult<void>> {
800
+ const args = [
801
+ 'cloud',
802
+ 'sandbox',
803
+ 'rmdir',
804
+ sandboxId,
805
+ remotePath,
806
+ '--region',
807
+ this.getSandboxRegion(),
808
+ ];
809
+ if (recursive) {
810
+ args.push('-r');
811
+ }
812
+ return this.exec<void>(args, { format: 'json' });
813
+ }
814
+
815
+ /**
816
+ * Set environment variables in a sandbox.
817
+ */
818
+ async sandboxEnvSet(
819
+ sandboxId: string,
820
+ vars: Record<string, string>
821
+ ): Promise<CliResult<SandboxEnvResult>> {
822
+ const args = ['cloud', 'sandbox', 'env', sandboxId, '--region', this.getSandboxRegion()];
823
+ for (const [key, value] of Object.entries(vars)) {
824
+ args.push(`${key}=${value}`);
825
+ }
826
+ return this.exec<SandboxEnvResult>(args, { format: 'json' });
827
+ }
828
+
829
+ /**
830
+ * Delete environment variables from a sandbox.
831
+ */
832
+ async sandboxEnvDelete(
833
+ sandboxId: string,
834
+ varNames: string[]
835
+ ): Promise<CliResult<SandboxEnvResult>> {
836
+ const args = ['cloud', 'sandbox', 'env', sandboxId, '--region', this.getSandboxRegion()];
837
+ for (const name of varNames) {
838
+ args.push('--delete', name);
839
+ }
840
+ return this.exec<SandboxEnvResult>(args, { format: 'json' });
841
+ }
842
+
843
+ /**
844
+ * Get environment variables from a sandbox.
845
+ */
846
+ async sandboxEnvGet(sandboxId: string): Promise<CliResult<SandboxEnvResult>> {
847
+ return this.exec<SandboxEnvResult>(
848
+ ['cloud', 'sandbox', 'env', sandboxId, '--region', this.getSandboxRegion()],
849
+ { format: 'json' }
850
+ );
851
+ }
852
+
853
+ // ==================== Snapshot Methods ====================
854
+
855
+ /**
856
+ * Create a snapshot of a sandbox.
857
+ */
858
+ 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
+ ];
868
+ if (tag) {
869
+ args.push('--tag', tag);
870
+ }
871
+ return this.exec<SnapshotInfo>(args, { format: 'json', timeout: 120000 });
872
+ }
873
+
874
+ /**
875
+ * List snapshots with optional sandbox filter.
876
+ */
877
+ async snapshotList(sandboxId?: string): Promise<CliResult<SnapshotInfo[]>> {
878
+ const args = ['cloud', 'sandbox', 'snapshot', 'list', '--region', this.getSandboxRegion()];
879
+ if (sandboxId) {
880
+ args.push('--sandbox', sandboxId);
881
+ }
882
+ // CLI returns { snapshots: [], total: N }
883
+ const result = await this.exec<{ snapshots: SnapshotInfo[]; total: number }>(args, {
884
+ format: 'json',
885
+ });
886
+ if (result.success && result.data) {
887
+ return { success: true, data: result.data.snapshots || [], exitCode: result.exitCode };
888
+ }
889
+ return { success: result.success, error: result.error, data: [], exitCode: result.exitCode };
890
+ }
891
+
892
+ /**
893
+ * Get detailed information about a snapshot.
894
+ */
895
+ 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
+ );
900
+ }
901
+
902
+ /**
903
+ * Delete a snapshot.
904
+ */
905
+ 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
+ );
919
+ }
920
+
921
+ /**
922
+ * Tag or untag a snapshot.
923
+ */
924
+ 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
+ ];
934
+ if (tag === null) {
935
+ args.push('--clear');
936
+ } else {
937
+ args.push(tag);
938
+ }
939
+ return this.exec<void>(args, { format: 'json' });
940
+ }
941
+
942
+ // ==================== Execution Methods ====================
943
+
944
+ /**
945
+ * List executions for a sandbox.
946
+ */
947
+ async executionList(sandboxId: string): Promise<CliResult<ExecutionInfo[]>> {
948
+ // CLI returns { executions: [] }
949
+ const result = await this.exec<{ executions: ExecutionInfo[] }>(
950
+ ['cloud', 'sandbox', 'execution', 'list', sandboxId, '--region', this.getSandboxRegion()],
951
+ { format: 'json' }
952
+ );
953
+ if (result.success && result.data) {
954
+ return { success: true, data: result.data.executions || [], exitCode: result.exitCode };
955
+ }
956
+ return { success: result.success, error: result.error, data: [], exitCode: result.exitCode };
957
+ }
958
+
959
+ /**
960
+ * Get detailed information about an execution.
961
+ */
962
+ 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
+ );
967
+ }
968
+
510
969
  dispose(): void {
511
970
  this.outputChannel.dispose();
512
971
  }
@@ -786,6 +1245,102 @@ export interface SessionLog {
786
1245
  timestamp: string;
787
1246
  }
788
1247
 
1248
+ // Sandbox types
1249
+ export type SandboxStatus = 'creating' | 'idle' | 'running' | 'terminated' | 'failed';
1250
+ export type ExecutionStatus =
1251
+ | 'queued'
1252
+ | 'running'
1253
+ | 'completed'
1254
+ | 'failed'
1255
+ | 'timeout'
1256
+ | 'cancelled';
1257
+
1258
+ export interface SandboxResources {
1259
+ memory?: string;
1260
+ cpu?: string;
1261
+ disk?: string;
1262
+ }
1263
+
1264
+ export interface SandboxInfo {
1265
+ sandboxId: string;
1266
+ status: SandboxStatus;
1267
+ createdAt: string;
1268
+ region?: string;
1269
+ executions?: number;
1270
+ resources?: SandboxResources;
1271
+ stdoutStreamUrl?: string;
1272
+ stderrStreamUrl?: string;
1273
+ }
1274
+
1275
+ export interface SandboxCreateOptions {
1276
+ memory?: string;
1277
+ cpu?: string;
1278
+ disk?: string;
1279
+ network?: boolean;
1280
+ idleTimeout?: number;
1281
+ execTimeout?: number;
1282
+ env?: Record<string, string>;
1283
+ dependencies?: string[];
1284
+ metadata?: Record<string, string>;
1285
+ snapshot?: string;
1286
+ }
1287
+
1288
+ export interface SandboxListFilter {
1289
+ status?: SandboxStatus;
1290
+ projectId?: string;
1291
+ limit?: number;
1292
+ offset?: number;
1293
+ }
1294
+
1295
+ export interface SandboxExecOptions {
1296
+ timeout?: number;
1297
+ timestamps?: boolean;
1298
+ }
1299
+
1300
+ export interface SandboxFileInfo {
1301
+ path: string;
1302
+ name: string;
1303
+ size: number;
1304
+ isDir: boolean;
1305
+ mode: string;
1306
+ modTime: string;
1307
+ }
1308
+
1309
+ export interface SnapshotInfo {
1310
+ snapshotId: string;
1311
+ tag?: string | null;
1312
+ sizeBytes: number;
1313
+ fileCount: number;
1314
+ createdAt: string;
1315
+ parentSnapshotId?: string | null;
1316
+ downloadUrl?: string;
1317
+ sandboxId?: string; // Present in list context
1318
+ files?: Array<{ path: string; size: number }>; // Present in get response
1319
+ }
1320
+
1321
+ export interface ExecutionInfo {
1322
+ executionId: string;
1323
+ status: ExecutionStatus;
1324
+ exitCode?: number;
1325
+ durationMs?: number;
1326
+ output?: string;
1327
+ sandboxId?: string;
1328
+ startedAt?: string;
1329
+ completedAt?: string;
1330
+ stdoutStreamUrl?: string;
1331
+ stderrStreamUrl?: string;
1332
+ command?: string;
1333
+ }
1334
+
1335
+ export interface SandboxCpResult {
1336
+ filesTransferred: number;
1337
+ bytesTransferred: number;
1338
+ }
1339
+
1340
+ export interface SandboxEnvResult {
1341
+ env: Record<string, string>;
1342
+ }
1343
+
789
1344
  // Singleton
790
1345
  let _cliClient: CliClient | undefined;
791
1346
 
package/src/core/index.ts CHANGED
@@ -4,3 +4,4 @@ export * from './project';
4
4
  export * from './service';
5
5
  export * from './baseTreeDataProvider';
6
6
  export * from './logger';
7
+ export * from './sandboxManager';
@@ -21,6 +21,15 @@ export function registerReadonlyDocumentProvider(context: vscode.ExtensionContex
21
21
  context.subscriptions.push(
22
22
  vscode.workspace.registerTextDocumentContentProvider(SCHEME, new ReadonlyDocumentProvider())
23
23
  );
24
+
25
+ // Clean up content when documents are closed
26
+ context.subscriptions.push(
27
+ vscode.workspace.onDidCloseTextDocument((doc) => {
28
+ if (doc.uri.scheme === SCHEME) {
29
+ contentMap.delete(doc.uri.path);
30
+ }
31
+ })
32
+ );
24
33
  }
25
34
 
26
35
  export async function openReadonlyDocument(
@@ -52,6 +61,8 @@ function getExtension(language: string): string {
52
61
  markdown: 'md',
53
62
  log: 'log',
54
63
  plaintext: 'txt',
64
+ properties: 'env',
65
+ ini: 'ini',
55
66
  };
56
67
  return extensions[language] || 'txt';
57
68
  }