11import { Command } from "@cliffy/command" ;
22import { Sandbox } from "@deno/sandbox" ;
33import { green , magenta , red } from "@std/fmt/colors" ;
4+ import { pooledMap } from "@std/async" ;
5+ import { expandGlob } from "@std/fs" ;
6+ import { join } from "@std/path" ;
47
58import { getAppFromConfig , readConfig , writeConfig } from "./config.ts" ;
69import { error , renderTemporalTimestamp , withApp } from "./util.ts" ;
@@ -47,6 +50,8 @@ export const sandboxCreateCommand = new Command<SandboxContext>()
4750 }
4851
4952 if ( options . lifetime === "session" ) {
53+ console . log ( `Created sandbox with id '${ sandbox . id } '` ) ;
54+
5055 const success = await sshIntoSandbox ( sandbox ) ;
5156 const stopMessage = "Stopping the sandbox..." ;
5257 if ( success ) {
@@ -174,18 +179,18 @@ export const sandboxSshCommand = new Command<SandboxContext>()
174179
175180export const sandboxCopyCommand = new Command < SandboxContext > ( )
176181 . description ( "Copy files from or to a running sandbox" )
177- /* .example(
182+ . example (
178183 "Copy a file from a sandbox to the local machine" ,
179184 "copy someSandboxId:/app/remote-file.txt ./local-file.txt" ,
180- )*/
185+ )
181186 . example (
182187 "Copy a file from the local machine to a sandbox" ,
183188 "copy ./local-file.txt someSandboxId:/app/remote-file.txt" ,
184189 )
185- /* .example(
190+ . example (
186191 "Copy multiple files from a sandbox to the local machine" ,
187192 "copy someSandboxId:/app/remote-file.txt someSandboxId:/app/another-remote-file.txt ./" ,
188- )*/
193+ )
189194 . example (
190195 "Copy multiple files from the local machine to a sandbox" ,
191196 "copy ./local-file.txt ./another-local-file.txt someSandboxId:/app/" ,
@@ -194,10 +199,14 @@ export const sandboxCopyCommand = new Command<SandboxContext>()
194199 "Copy a directory from the local machine to a sandbox" ,
195200 "copy ./ ./another-local-file.txt someSandboxId:/app/" ,
196201 )
197- /* .example(
202+ . example (
198203 "Copy files from a sandbox to another sandbox" ,
199204 "copy someSandboxId:/app/remote-file.txt anotherSandboxId:/app/remote-file.txt" ,
200- )*/
205+ )
206+ . example (
207+ "Copy a all files from a directory in a sandbox to the local machine" ,
208+ "copy someSandboxId:/app/* ./" ,
209+ )
201210 . arguments ( "<paths...:string>" )
202211 . action ( async ( options , ...paths ) => {
203212 if ( paths . length < 2 ) {
@@ -207,56 +216,70 @@ export const sandboxCopyCommand = new Command<SandboxContext>()
207216 const target = paths . pop ( ) ! ;
208217
209218 if ( target . includes ( ":" ) ) {
210- const [ sandboxId , sandboxPath ] = target . split ( ":" ) ;
211- await using sandbox = await connectToSandbox ( options , sandboxId ) ;
219+ const [ sandboxId , targetSandboxPath ] = target . split ( ":" ) ;
220+ await using targetSandbox = await connectToSandbox ( options , sandboxId ) ;
212221
213- // const sourceSandboxPaths = [];
222+ const sourceSandboxPaths = [ ] ;
214223 const localPaths = [ ] ;
215224
216225 for ( const path of paths ) {
217226 if ( path . includes ( ":" ) ) {
218- error (
219- options . debug ,
220- "Copying between sandboxes is currently not supported" ,
221- ) ;
222-
223- //sourceSandboxPaths.push(path);
227+ sourceSandboxPaths . push ( path ) ;
224228 } else {
225229 localPaths . push ( path ) ;
226230 }
227231 }
228232
229- /* const sourceSandboxGroups = groupPathsBySandbox(sourceSandboxPaths);
233+ const sourceSandboxGroups = groupPathsBySandbox ( sourceSandboxPaths ) ;
230234 const sourceSandboxes : Record < string , Sandbox > = { } ;
231235
232236 for ( const sandboxId of Object . keys ( sourceSandboxGroups ) ) {
233237 sourceSandboxes [ sandboxId ] = await connectToSandbox ( options , sandboxId ) ;
234- }*/
238+ }
235239
236240 await Promise . all ( [
237241 ...localPaths . map ( ( path ) => {
238- return sandbox . upload ( path , sandboxPath ) ;
242+ return targetSandbox . upload ( path , targetSandboxPath ) ;
239243 } ) ,
240- /* ...Object.entries(sourceSandboxGroups).map(
244+ ...Object . entries ( sourceSandboxGroups ) . map (
241245 async ( [ sandboxId , sourceSandboxPaths ] ) => {
242246 const sourceSandbox = sourceSandboxes [ sandboxId ] ;
243247
244248 await Promise . all (
245249 sourceSandboxPaths . map ( async ( sourceSandboxPath ) => {
246250 const tempDir = await Deno . makeTempDir ( ) ;
247- await sourceSandbox.download(sourceSandboxPath, tempDir);
248- await sandbox.upload(tempDir, target);
251+
252+ await Array . fromAsync ( pooledMap (
253+ Infinity ,
254+ sourceSandbox . expandGlob ( sourceSandboxPath ) ,
255+ async ( sandboxEntry ) => {
256+ await sourceSandbox . download ( sandboxEntry . path , tempDir ) ;
257+
258+ await Array . fromAsync ( pooledMap (
259+ Infinity ,
260+ expandGlob ( `${ tempDir } /*` ) ,
261+ ( localEntry ) =>
262+ targetSandbox . upload (
263+ localEntry . path ,
264+ join (
265+ targetSandboxPath ,
266+ localEntry . isDirectory
267+ ? "./"
268+ : `./${ localEntry . name } ` ,
269+ ) ,
270+ ) ,
271+ ) ) ;
272+ } ,
273+ ) ) ;
249274 } ) ,
250275 ) ;
251276
252- await sandbox .close();
277+ await sourceSandbox . close ( ) ;
253278 } ,
254- ),*/
279+ ) ,
255280 ] ) ;
256281 } else {
257- error ( options . debug , "Copying from sandboxes is currently not supported" ) ;
258-
259- /*for (const path of paths) {
282+ for ( const path of paths ) {
260283 if ( ! path . includes ( ":" ) ) {
261284 error (
262285 options . debug ,
@@ -276,13 +299,17 @@ export const sandboxCopyCommand = new Command<SandboxContext>()
276299 Object . entries ( groups ) . map ( async ( [ sandboxId , sandboxPaths ] ) => {
277300 const sandbox = sandboxes [ sandboxId ] ;
278301
279- await Promise.all(sandboxPaths.map((sandboxPath) => {
280- return sandbox.download(sandboxPath, target);
302+ await Promise . all ( sandboxPaths . map ( async ( sandboxPath ) => {
303+ await Array . fromAsync ( pooledMap (
304+ Infinity ,
305+ sandbox . expandGlob ( sandboxPath ) ,
306+ ( entry ) => sandbox . download ( entry . path , target ) ,
307+ ) ) ;
281308 } ) ) ;
282309
283310 await sandbox . close ( ) ;
284311 } ) ,
285- );*/
312+ ) ;
286313 }
287314 } ) ;
288315
@@ -379,20 +406,17 @@ export const sandboxRunCommand = new Command<SandboxContext>()
379406 Deno . exit ( status . code ) ;
380407 } ) ;
381408
382- /*
383409export const sandboxExtendCommand = new Command < SandboxContext > ( )
384410 . description ( "Extend the lifetime of a running sandbox" )
385411 . arguments ( "<sandbox-id:string> <lifetime:string>" )
386412 . action ( async ( options , sandboxId , lifetime ) => {
387413 await using sandbox = await connectToSandbox ( options , sandboxId ) ;
388414
389- console.log(await sandbox.extendLifetime(lifetime));
415+ console . log ( await sandbox . extendLifetime ( lifetime as any ) ) ;
390416 } ) ;
391- */
392417
393- /*
394418function groupPathsBySandbox ( paths : string [ ] ) : Record < string , string [ ] > {
395- const groups = {};
419+ const groups : Record < string , string [ ] > = { } ;
396420
397421 for ( const path of paths ) {
398422 const [ sandboxId , sandboxPath ] = path . split ( ":" ) ;
@@ -403,7 +427,7 @@ function groupPathsBySandbox(paths: string[]): Record<string, string[]> {
403427 }
404428
405429 return groups ;
406- }*/
430+ }
407431
408432async function ensureOrg ( options : SandboxContext , quiet : boolean = true ) {
409433 const config = await readConfig ( Deno . cwd ( ) , options . config ) ;
@@ -440,6 +464,7 @@ async function connectToSandbox(
440464
441465 return await Sandbox . connect ( {
442466 id : sandboxId ,
467+ apiEndpoint : options . endpoint ,
443468 region : cluster . region ,
444469 debug : options . debug ,
445470 token,
@@ -597,5 +622,5 @@ export const sandboxCommand = new Command<GlobalOptions>()
597622 . alias ( "cp" )
598623 . command ( "exec" , sandboxExecCommand )
599624 . command ( "run" , sandboxRunCommand )
600- // .command("extend", sandboxExtendCommand)
625+ . command ( "extend" , sandboxExtendCommand )
601626 . command ( "ssh" , sandboxSshCommand ) ;
0 commit comments