agentuity-vscode 0.1.6 → 0.1.7

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,393 @@ 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, { format: 'json' });
593
+ if (result.success && result.data) {
594
+ return { success: true, data: result.data.sandboxes || [], exitCode: result.exitCode };
595
+ }
596
+ return { success: result.success, error: result.error, data: [], exitCode: result.exitCode };
597
+ }
598
+
599
+ /**
600
+ * Get detailed information about a sandbox.
601
+ */
602
+ async sandboxGet(sandboxId: string): Promise<CliResult<SandboxInfo>> {
603
+ return this.exec<SandboxInfo>(
604
+ ['cloud', 'sandbox', 'get', sandboxId, '--region', this.getSandboxRegion()],
605
+ { format: 'json' }
606
+ );
607
+ }
608
+
609
+ /**
610
+ * Delete a sandbox.
611
+ */
612
+ async sandboxDelete(sandboxId: string): Promise<CliResult<void>> {
613
+ return this.exec<void>(
614
+ ['cloud', 'sandbox', 'delete', sandboxId, '--confirm', '--region', this.getSandboxRegion()],
615
+ { format: 'json' }
616
+ );
617
+ }
618
+
619
+ /**
620
+ * Execute a command in a sandbox.
621
+ * Note: For streaming output, use sandboxExecInTerminal instead.
622
+ */
623
+ async sandboxExec(
624
+ sandboxId: string,
625
+ command: string[],
626
+ options: SandboxExecOptions = {}
627
+ ): Promise<CliResult<ExecutionInfo>> {
628
+ const args = ['cloud', 'sandbox', 'exec', sandboxId, '--region', this.getSandboxRegion()];
629
+
630
+ if (options.timeout) {
631
+ args.push('--timeout', String(options.timeout));
632
+ }
633
+ if (options.timestamps) {
634
+ args.push('--timestamps');
635
+ }
636
+
637
+ args.push('--');
638
+ args.push(...command);
639
+
640
+ return this.exec<ExecutionInfo>(args, { format: 'json', timeout: options.timeout || 300000 });
641
+ }
642
+
643
+ /**
644
+ * List files in a sandbox directory.
645
+ */
646
+ async sandboxLs(sandboxId: string, remotePath?: string): Promise<CliResult<SandboxFileInfo[]>> {
647
+ const args = ['cloud', 'sandbox', 'files', sandboxId];
648
+ // Only add path if specified (omit for root listing)
649
+ if (remotePath) {
650
+ args.push(remotePath);
651
+ }
652
+ args.push('-l', '--region', this.getSandboxRegion());
653
+
654
+ // CLI returns { files: [...], total: N }, extract the array and add name from path
655
+ const result = await this.exec<{ files: Array<Omit<SandboxFileInfo, 'name'>>; total: number }>(args, { format: 'json' });
656
+ if (result.success && result.data) {
657
+ const files = (result.data.files || []).map(f => ({
658
+ ...f,
659
+ name: f.path.split('/').pop() || f.path, // Extract filename from path
660
+ }));
661
+ return { success: true, data: files, exitCode: result.exitCode };
662
+ }
663
+ return { success: result.success, error: result.error, data: [], exitCode: result.exitCode };
664
+ }
665
+
666
+ /**
667
+ * Upload a file or directory to a sandbox.
668
+ */
669
+ async sandboxCpToSandbox(
670
+ sandboxId: string,
671
+ localPath: string,
672
+ remotePath: string,
673
+ recursive = false
674
+ ): Promise<CliResult<SandboxCpResult>> {
675
+ const args = ['cloud', 'sandbox', 'cp', '--region', this.getSandboxRegion()];
676
+ if (recursive) {
677
+ args.push('-r');
678
+ }
679
+ args.push(localPath, `${sandboxId}:${remotePath}`);
680
+ return this.exec<SandboxCpResult>(args, { format: 'json', timeout: 300000 });
681
+ }
682
+
683
+ /**
684
+ * Download a file or directory from a sandbox.
685
+ */
686
+ async sandboxCpFromSandbox(
687
+ sandboxId: string,
688
+ remotePath: string,
689
+ localPath: string,
690
+ recursive = false
691
+ ): Promise<CliResult<SandboxCpResult>> {
692
+ const args = ['cloud', 'sandbox', 'cp', '--region', this.getSandboxRegion()];
693
+ if (recursive) {
694
+ args.push('-r');
695
+ }
696
+ args.push(`${sandboxId}:${remotePath}`, localPath);
697
+ return this.exec<SandboxCpResult>(args, { format: 'json', timeout: 300000 });
698
+ }
699
+
700
+ /**
701
+ * Upload an archive (tar.gz or zip) to a sandbox and extract it.
702
+ */
703
+ async sandboxUpload(
704
+ sandboxId: string,
705
+ archivePath: string,
706
+ destPath?: string
707
+ ): Promise<CliResult<void>> {
708
+ const args = ['cloud', 'sandbox', 'upload', sandboxId, archivePath, '--region', this.getSandboxRegion()];
709
+ if (destPath) {
710
+ args.push('--path', destPath);
711
+ }
712
+ return this.exec<void>(args, { format: 'json', timeout: 300000 });
713
+ }
714
+
715
+ /**
716
+ * Download sandbox files as an archive.
717
+ */
718
+ async sandboxDownload(
719
+ sandboxId: string,
720
+ outputPath: string,
721
+ sourcePath?: string
722
+ ): Promise<CliResult<void>> {
723
+ const args = ['cloud', 'sandbox', 'download', sandboxId, outputPath, '--region', this.getSandboxRegion()];
724
+ if (sourcePath) {
725
+ args.push('--path', sourcePath);
726
+ }
727
+ return this.exec<void>(args, { format: 'json', timeout: 300000 });
728
+ }
729
+
730
+ /**
731
+ * Create a directory in a sandbox.
732
+ */
733
+ async sandboxMkdir(
734
+ sandboxId: string,
735
+ remotePath: string,
736
+ recursive = false
737
+ ): Promise<CliResult<void>> {
738
+ const args = ['cloud', 'sandbox', 'mkdir', sandboxId, remotePath, '--region', this.getSandboxRegion()];
739
+ if (recursive) {
740
+ args.push('-p');
741
+ }
742
+ return this.exec<void>(args, { format: 'json' });
743
+ }
744
+
745
+ /**
746
+ * Remove a file from a sandbox.
747
+ */
748
+ async sandboxRm(sandboxId: string, remotePath: string): Promise<CliResult<void>> {
749
+ return this.exec<void>(
750
+ ['cloud', 'sandbox', 'rm', sandboxId, remotePath, '--region', this.getSandboxRegion()],
751
+ { format: 'json' }
752
+ );
753
+ }
754
+
755
+ /**
756
+ * Remove a directory from a sandbox.
757
+ */
758
+ async sandboxRmdir(
759
+ sandboxId: string,
760
+ remotePath: string,
761
+ recursive = false
762
+ ): Promise<CliResult<void>> {
763
+ const args = ['cloud', 'sandbox', 'rmdir', sandboxId, remotePath, '--region', this.getSandboxRegion()];
764
+ if (recursive) {
765
+ args.push('-r');
766
+ }
767
+ return this.exec<void>(args, { format: 'json' });
768
+ }
769
+
770
+ /**
771
+ * Set environment variables in a sandbox.
772
+ */
773
+ async sandboxEnvSet(
774
+ sandboxId: string,
775
+ vars: Record<string, string>
776
+ ): Promise<CliResult<SandboxEnvResult>> {
777
+ const args = ['cloud', 'sandbox', 'env', sandboxId, '--region', this.getSandboxRegion()];
778
+ for (const [key, value] of Object.entries(vars)) {
779
+ args.push(`${key}=${value}`);
780
+ }
781
+ return this.exec<SandboxEnvResult>(args, { format: 'json' });
782
+ }
783
+
784
+ /**
785
+ * Delete environment variables from a sandbox.
786
+ */
787
+ async sandboxEnvDelete(
788
+ sandboxId: string,
789
+ varNames: string[]
790
+ ): Promise<CliResult<SandboxEnvResult>> {
791
+ const args = ['cloud', 'sandbox', 'env', sandboxId, '--region', this.getSandboxRegion()];
792
+ for (const name of varNames) {
793
+ args.push('--delete', name);
794
+ }
795
+ return this.exec<SandboxEnvResult>(args, { format: 'json' });
796
+ }
797
+
798
+ /**
799
+ * Get environment variables from a sandbox.
800
+ */
801
+ async sandboxEnvGet(sandboxId: string): Promise<CliResult<SandboxEnvResult>> {
802
+ return this.exec<SandboxEnvResult>(
803
+ ['cloud', 'sandbox', 'env', sandboxId, '--region', this.getSandboxRegion()],
804
+ { format: 'json' }
805
+ );
806
+ }
807
+
808
+ // ==================== Snapshot Methods ====================
809
+
810
+ /**
811
+ * Create a snapshot of a sandbox.
812
+ */
813
+ async snapshotCreate(sandboxId: string, tag?: string): Promise<CliResult<SnapshotInfo>> {
814
+ const args = ['cloud', 'sandbox', 'snapshot', 'create', sandboxId, '--region', this.getSandboxRegion()];
815
+ if (tag) {
816
+ args.push('--tag', tag);
817
+ }
818
+ return this.exec<SnapshotInfo>(args, { format: 'json', timeout: 120000 });
819
+ }
820
+
821
+ /**
822
+ * List snapshots with optional sandbox filter.
823
+ */
824
+ async snapshotList(sandboxId?: string): Promise<CliResult<SnapshotInfo[]>> {
825
+ const args = ['cloud', 'sandbox', 'snapshot', 'list', '--region', this.getSandboxRegion()];
826
+ if (sandboxId) {
827
+ args.push('--sandbox', sandboxId);
828
+ }
829
+ // CLI returns { snapshots: [], total: N }
830
+ const result = await this.exec<{ snapshots: SnapshotInfo[]; total: number }>(args, { format: 'json' });
831
+ if (result.success && result.data) {
832
+ return { success: true, data: result.data.snapshots || [], exitCode: result.exitCode };
833
+ }
834
+ return { success: result.success, error: result.error, data: [], exitCode: result.exitCode };
835
+ }
836
+
837
+ /**
838
+ * Get detailed information about a snapshot.
839
+ */
840
+ async snapshotGet(snapshotId: string): Promise<CliResult<SnapshotInfo>> {
841
+ return this.exec<SnapshotInfo>(
842
+ ['cloud', 'sandbox', 'snapshot', 'get', snapshotId, '--region', this.getSandboxRegion()],
843
+ { format: 'json' }
844
+ );
845
+ }
846
+
847
+ /**
848
+ * Delete a snapshot.
849
+ */
850
+ async snapshotDelete(snapshotId: string): Promise<CliResult<void>> {
851
+ return this.exec<void>(
852
+ ['cloud', 'sandbox', 'snapshot', 'delete', snapshotId, '--confirm', '--region', this.getSandboxRegion()],
853
+ { format: 'json' }
854
+ );
855
+ }
856
+
857
+ /**
858
+ * Tag or untag a snapshot.
859
+ */
860
+ async snapshotTag(snapshotId: string, tag: string | null): Promise<CliResult<void>> {
861
+ const args = ['cloud', 'sandbox', 'snapshot', 'tag', snapshotId, '--region', this.getSandboxRegion()];
862
+ if (tag === null) {
863
+ args.push('--clear');
864
+ } else {
865
+ args.push(tag);
866
+ }
867
+ return this.exec<void>(args, { format: 'json' });
868
+ }
869
+
870
+ // ==================== Execution Methods ====================
871
+
872
+ /**
873
+ * List executions for a sandbox.
874
+ */
875
+ async executionList(sandboxId: string): Promise<CliResult<ExecutionInfo[]>> {
876
+ // CLI returns { executions: [] }
877
+ const result = await this.exec<{ executions: ExecutionInfo[] }>(
878
+ ['cloud', 'sandbox', 'execution', 'list', sandboxId, '--region', this.getSandboxRegion()],
879
+ { format: 'json' }
880
+ );
881
+ if (result.success && result.data) {
882
+ return { success: true, data: result.data.executions || [], exitCode: result.exitCode };
883
+ }
884
+ return { success: result.success, error: result.error, data: [], exitCode: result.exitCode };
885
+ }
886
+
887
+ /**
888
+ * Get detailed information about an execution.
889
+ */
890
+ async executionGet(executionId: string): Promise<CliResult<ExecutionInfo>> {
891
+ return this.exec<ExecutionInfo>(
892
+ ['cloud', 'sandbox', 'execution', 'get', executionId, '--region', this.getSandboxRegion()],
893
+ { format: 'json' }
894
+ );
895
+ }
896
+
510
897
  dispose(): void {
511
898
  this.outputChannel.dispose();
512
899
  }
@@ -786,6 +1173,96 @@ export interface SessionLog {
786
1173
  timestamp: string;
787
1174
  }
788
1175
 
1176
+ // Sandbox types
1177
+ export type SandboxStatus = 'creating' | 'idle' | 'running' | 'terminated' | 'failed';
1178
+ export type ExecutionStatus = 'queued' | 'running' | 'completed' | 'failed' | 'timeout' | 'cancelled';
1179
+
1180
+ export interface SandboxResources {
1181
+ memory?: string;
1182
+ cpu?: string;
1183
+ disk?: string;
1184
+ }
1185
+
1186
+ export interface SandboxInfo {
1187
+ sandboxId: string;
1188
+ status: SandboxStatus;
1189
+ createdAt: string;
1190
+ region?: string;
1191
+ executions?: number;
1192
+ resources?: SandboxResources;
1193
+ stdoutStreamUrl?: string;
1194
+ stderrStreamUrl?: string;
1195
+ }
1196
+
1197
+ export interface SandboxCreateOptions {
1198
+ memory?: string;
1199
+ cpu?: string;
1200
+ disk?: string;
1201
+ network?: boolean;
1202
+ idleTimeout?: number;
1203
+ execTimeout?: number;
1204
+ env?: Record<string, string>;
1205
+ dependencies?: string[];
1206
+ metadata?: Record<string, string>;
1207
+ snapshot?: string;
1208
+ }
1209
+
1210
+ export interface SandboxListFilter {
1211
+ status?: SandboxStatus;
1212
+ projectId?: string;
1213
+ limit?: number;
1214
+ offset?: number;
1215
+ }
1216
+
1217
+ export interface SandboxExecOptions {
1218
+ timeout?: number;
1219
+ timestamps?: boolean;
1220
+ }
1221
+
1222
+ export interface SandboxFileInfo {
1223
+ path: string;
1224
+ name: string;
1225
+ size: number;
1226
+ isDir: boolean;
1227
+ mode: string;
1228
+ modTime: string;
1229
+ }
1230
+
1231
+ export interface SnapshotInfo {
1232
+ snapshotId: string;
1233
+ tag?: string | null;
1234
+ sizeBytes: number;
1235
+ fileCount: number;
1236
+ createdAt: string;
1237
+ parentSnapshotId?: string | null;
1238
+ downloadUrl?: string;
1239
+ sandboxId?: string; // Present in list context
1240
+ files?: Array<{ path: string; size: number }>; // Present in get response
1241
+ }
1242
+
1243
+ export interface ExecutionInfo {
1244
+ executionId: string;
1245
+ status: ExecutionStatus;
1246
+ exitCode?: number;
1247
+ durationMs?: number;
1248
+ output?: string;
1249
+ sandboxId?: string;
1250
+ startedAt?: string;
1251
+ completedAt?: string;
1252
+ stdoutStreamUrl?: string;
1253
+ stderrStreamUrl?: string;
1254
+ command?: string;
1255
+ }
1256
+
1257
+ export interface SandboxCpResult {
1258
+ filesTransferred: number;
1259
+ bytesTransferred: number;
1260
+ }
1261
+
1262
+ export interface SandboxEnvResult {
1263
+ env: Record<string, string>;
1264
+ }
1265
+
789
1266
  // Singleton
790
1267
  let _cliClient: CliClient | undefined;
791
1268
 
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
  }