@minato-aqukin/autodl-cli 0.1.1 → 0.2.0

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/dist/cli.js CHANGED
@@ -13,7 +13,13 @@ import {
13
13
  saveImage,
14
14
  startMcpServer,
15
15
  watchIdle
16
- } from "./chunk-5RZ4Q7T4.js";
16
+ } from "./chunk-MWYYK755.js";
17
+ import {
18
+ FileWorkspace,
19
+ TransferQueue,
20
+ connectTerminal,
21
+ identityFromToken
22
+ } from "./chunk-2E2Y5GFH.js";
17
23
  import {
18
24
  BASE_IMAGES,
19
25
  DEFAULT_BASE_IMAGE,
@@ -29,7 +35,7 @@ import {
29
35
  parseCudaVersion,
30
36
  resolveGpuSpec,
31
37
  resolveRegion
32
- } from "./chunk-CWCG3MOC.js";
38
+ } from "./chunk-WN2CXPD2.js";
33
39
  import {
34
40
  action,
35
41
  bareAction,
@@ -37,10 +43,11 @@ import {
37
43
  globalsOf,
38
44
  lazyAction,
39
45
  registerTuiCommand
40
- } from "./chunk-LHDSN4DL.js";
41
- import "./chunk-NBWWTYSL.js";
46
+ } from "./chunk-JOOIUQLJ.js";
47
+ import "./chunk-VEDMEYCW.js";
42
48
  import {
43
49
  AutoDLClient,
50
+ DEFAULT_BASE_URL,
44
51
  ExitCode,
45
52
  UsageError,
46
53
  armTTLOverSSH,
@@ -76,7 +83,9 @@ import {
76
83
  recordTTL,
77
84
  redactToken,
78
85
  releaseInstance,
86
+ resolveBaseUrl,
79
87
  resolveLang,
88
+ resolveToken,
80
89
  setLang,
81
90
  success,
82
91
  sweepExpired,
@@ -87,7 +96,7 @@ import {
87
96
  waitForRunning,
88
97
  waitForShutdown,
89
98
  warn
90
- } from "./chunk-6QR4ZYQR.js";
99
+ } from "./chunk-GHJVQKKC.js";
91
100
 
92
101
  // src/cli.ts
93
102
  import { Command } from "commander";
@@ -356,6 +365,250 @@ function registerDeployCommand(program) {
356
365
  );
357
366
  }
358
367
 
368
+ // src/commands/files.ts
369
+ import { createHash } from "crypto";
370
+ function queueNamespace(token, baseUrl) {
371
+ const identity = identityFromToken(token);
372
+ const account = [identity.tenant, identity.uid ?? identity.uuid ?? token];
373
+ return createHash("sha256").update(JSON.stringify([(baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, ""), account])).digest("hex");
374
+ }
375
+ function queueFor(context, command) {
376
+ const globals = globalsOf(command);
377
+ const resolved = resolveToken(globals.token);
378
+ const baseUrl = resolveBaseUrl(globals.baseUrl);
379
+ const queue = new TransferQueue(context.client, queueNamespace(resolved.token, baseUrl));
380
+ let released = false;
381
+ return {
382
+ queue,
383
+ release: async () => {
384
+ if (!released) {
385
+ released = true;
386
+ await queue.dispose();
387
+ }
388
+ }
389
+ };
390
+ }
391
+ async function withWorkspace(context, id, fn) {
392
+ const workspace = new FileWorkspace(context.client, id);
393
+ try {
394
+ return await fn(workspace);
395
+ } finally {
396
+ workspace.dispose();
397
+ }
398
+ }
399
+ async function ensureStarted(context, id, start) {
400
+ if (start) await getCredentials(context.client, id, { autoStart: true });
401
+ }
402
+ var STATE_LABEL = {
403
+ queued: "\u6392\u961F",
404
+ running: "\u4F20\u8F93\u4E2D",
405
+ conflict: "\u5F85\u786E\u8BA4",
406
+ paused: "\u5DF2\u6682\u505C",
407
+ completed: "\u5B8C\u6210",
408
+ cancelled: "\u5DF2\u53D6\u6D88"
409
+ };
410
+ function registerFileCommands(program) {
411
+ const files = program.command("files").description("\u6D4F\u89C8\u4E0E\u7BA1\u7406\u5B9E\u4F8B\u6587\u4EF6\uFF08SFTP\uFF0C\u8FDE\u63A5\u590D\u7528\uFF09");
412
+ files.command("ls <id> [remote]").description("\u5217\u51FA\u8FDC\u7A0B\u76EE\u5F55\uFF08\u5F53\u524D\u5C42\uFF0C\u65E0\u9012\u5F52\uFF09").option("--start", "\u5B9E\u4F8B\u672A\u8FD0\u884C\u65F6\u81EA\u52A8\u5F00\u673A", false).action(
413
+ action(
414
+ async (context, id, remote, options) => {
415
+ const dir = remote ?? "/root/autodl-tmp";
416
+ const entries = await withWorkspace(context, id, async (workspace) => {
417
+ await ensureStarted(context, id, options.start);
418
+ return workspace.list("remote", dir);
419
+ });
420
+ emit({ uuid: id, path: dir, entries }, () => {
421
+ for (const entry of entries) {
422
+ printKeyValues([[entry.name, `${entry.kind} ${entry.size} ${entry.path}`]]);
423
+ }
424
+ if (entries.length === 0) note("\u7A7A\u76EE\u5F55");
425
+ });
426
+ return 0;
427
+ }
428
+ )
429
+ );
430
+ files.command("mkdir <id> <remote>").description("\u5728\u5B9E\u4F8B\u4E0A\u521B\u5EFA\u76EE\u5F55").option("--start", "\u5B9E\u4F8B\u672A\u8FD0\u884C\u65F6\u81EA\u52A8\u5F00\u673A", false).action(
431
+ action(async (context, id, remote, options) => {
432
+ await withWorkspace(context, id, async (workspace) => {
433
+ await ensureStarted(context, id, options.start);
434
+ return workspace.mkdir("remote", remote);
435
+ });
436
+ emit({ uuid: id, path: remote, created: true }, () => success(`\u5DF2\u521B\u5EFA ${remote}`));
437
+ return 0;
438
+ })
439
+ );
440
+ files.command("mv <id> <from> <to>").description("\u5728\u5B9E\u4F8B\u4E0A\u6539\u540D\u6216\u79FB\u52A8\uFF08\u4E0D\u8986\u76D6\u5DF2\u5B58\u5728\u76EE\u6807\uFF09").option("--start", "\u5B9E\u4F8B\u672A\u8FD0\u884C\u65F6\u81EA\u52A8\u5F00\u673A", false).action(
441
+ action(async (context, id, from, to, options) => {
442
+ await withWorkspace(context, id, async (workspace) => {
443
+ await ensureStarted(context, id, options.start);
444
+ return workspace.rename("remote", from, to);
445
+ });
446
+ emit({ uuid: id, from, to, moved: true }, () => success(`\u5DF2\u79FB\u52A8 ${from} \u2192 ${to}`));
447
+ return 0;
448
+ })
449
+ );
450
+ files.command("rm <id> <remote>").description("\u5220\u9664\u5B9E\u4F8B\u6587\u4EF6\u6216\u76EE\u5F55\uFF08\u9012\u5F52\uFF0C\u4E0D\u53EF\u6062\u590D\uFF09").option("--yes", "\u786E\u8BA4\u5220\u9664", false).option("--start", "\u5B9E\u4F8B\u672A\u8FD0\u884C\u65F6\u81EA\u52A8\u5F00\u673A", false).action(
451
+ action(
452
+ async (context, id, remote, options) => {
453
+ const confirmed = await confirmDestructive(
454
+ `\u6C38\u4E45\u5220\u9664\u5B9E\u4F8B\u6587\u4EF6 ${id}:${remote}`,
455
+ options.yes
456
+ );
457
+ if (!confirmed) {
458
+ emit({ uuid: id, path: remote, removed: false, cancelled: true }, () => note("\u5DF2\u53D6\u6D88"));
459
+ return 0;
460
+ }
461
+ await withWorkspace(context, id, async (workspace) => {
462
+ await ensureStarted(context, id, options.start);
463
+ return workspace.remove("remote", remote);
464
+ });
465
+ emit({ uuid: id, path: remote, removed: true }, () => success(`\u5DF2\u5220\u9664 ${remote}`));
466
+ return 0;
467
+ }
468
+ )
469
+ );
470
+ const queue = program.command("queue").description("\u4E32\u884C\u4F20\u8F93\u961F\u5217\uFF08\u4E0E\u770B\u677F\u5171\u7528\u540C\u4E00\u961F\u5217\uFF09");
471
+ queue.command("add <id> <source> <destination>").description("\u52A0\u5165\u4E0A\u4F20/\u4E0B\u8F7D\u4EFB\u52A1\uFF08\u6765\u6E90\u5728\u672C\u5730\u5219\u4E0A\u4F20\uFF0C\u5728\u8FDC\u7AEF\u9700 --download\uFF09").option("--download", "\u4ECE\u5B9E\u4F8B\u4E0B\u8F7D\u5230\u672C\u5730", false).option("--sync", "\u5355\u5411\u589E\u91CF\u540C\u6B65\uFF0C\u4E0D\u5220\u9664\u76EE\u6807\u72EC\u6709\u6587\u4EF6", false).option("--checksum", "SHA-256 \u5185\u5BB9\u6821\u9A8C", false).option("--wait", "\u7B49\u5F85\u4EFB\u52A1\u5B8C\u6210", false).action(
472
+ action(
473
+ async (context, id, source, destination, options, command) => {
474
+ const { queue: transferQueue, release } = queueFor(context, command);
475
+ try {
476
+ const jobId = transferQueue.enqueue({
477
+ uuid: id,
478
+ direction: options.download ? "download" : "upload",
479
+ sources: [source],
480
+ destination,
481
+ sync: options.sync,
482
+ checksum: options.checksum
483
+ });
484
+ if (options.wait) {
485
+ const done = await waitForJob(transferQueue, jobId);
486
+ emit(
487
+ { jobId, ...done },
488
+ () => success(`\u4EFB\u52A1 ${jobId}\uFF1A${STATE_LABEL[done.state] ?? done.state}`)
489
+ );
490
+ return 0;
491
+ }
492
+ emit({ jobId, uuid: id }, () => success(`\u5DF2\u52A0\u5165\u961F\u5217\uFF1A${jobId}`));
493
+ return 0;
494
+ } finally {
495
+ await release();
496
+ }
497
+ }
498
+ )
499
+ );
500
+ queue.command("ls").description("\u5217\u51FA\u5F53\u524D\u8D26\u53F7\u7684\u4F20\u8F93\u4EFB\u52A1").action(
501
+ action(async (context, _options, command) => {
502
+ const { queue: transferQueue, release } = queueFor(context, command);
503
+ try {
504
+ const jobs = transferQueue.snapshot().map((job) => ({
505
+ id: job.request.id,
506
+ uuid: job.request.uuid,
507
+ direction: job.request.direction,
508
+ sources: job.request.sources,
509
+ destination: job.request.destination,
510
+ sync: job.request.sync,
511
+ checksum: job.request.checksum,
512
+ state: job.state,
513
+ ...job.error !== void 0 ? { error: job.error } : {},
514
+ ...job.result ? { result: job.result } : {}
515
+ }));
516
+ emit({ jobs }, () => {
517
+ if (jobs.length === 0) {
518
+ note("\u961F\u5217\u4E3A\u7A7A");
519
+ return;
520
+ }
521
+ for (const job of jobs) {
522
+ printKeyValues([
523
+ [
524
+ job.id,
525
+ `${job.direction} ${STATE_LABEL[job.state] ?? job.state} \u2192 ${job.destination}`
526
+ ]
527
+ ]);
528
+ }
529
+ });
530
+ return 0;
531
+ } finally {
532
+ await release();
533
+ }
534
+ })
535
+ );
536
+ queue.command("resume <jobId>").description("\u6062\u590D\u5DF2\u6682\u505C/\u5DF2\u53D6\u6D88\u7684\u4EFB\u52A1\uFF08\u9700\u5B9E\u4F8B\u8FD0\u884C\u4E2D\uFF0C\u4E0D\u81EA\u52A8\u5F00\u673A\uFF09").action(
537
+ action(async (context, jobId, _options, command) => {
538
+ const { queue: transferQueue, release } = queueFor(context, command);
539
+ try {
540
+ transferQueue.resume(jobId);
541
+ emit({ jobId, resumed: true }, () => success(`\u5DF2\u6062\u590D ${jobId}`));
542
+ return 0;
543
+ } finally {
544
+ await release();
545
+ }
546
+ })
547
+ );
548
+ queue.command("cancel <jobId>").description("\u53D6\u6D88\u6392\u961F/\u4F20\u8F93\u4E2D\u7684\u4EFB\u52A1\uFF08\u4FDD\u7559\u7EED\u4F20\u6570\u636E\uFF09").action(
549
+ action(async (context, jobId, _options, command) => {
550
+ const { queue: transferQueue, release } = queueFor(context, command);
551
+ try {
552
+ transferQueue.cancel(jobId);
553
+ emit({ jobId, cancelled: true }, () => success(`\u5DF2\u53D6\u6D88 ${jobId}`));
554
+ return 0;
555
+ } finally {
556
+ await release();
557
+ }
558
+ })
559
+ );
560
+ queue.command("resolve <jobId> <choice>").description("\u5904\u7406\u4EFB\u52A1\u7684\u6587\u4EF6\u51B2\u7A81\uFF1Aoverwrite / skip / keep-both").option("--all", "\u5E94\u7528\u4E8E\u672C\u4EFB\u52A1\u5176\u4F59\u51B2\u7A81", false).action(
561
+ action(
562
+ async (context, jobId, choice, options, command) => {
563
+ if (choice !== "overwrite" && choice !== "skip" && choice !== "keep-both") {
564
+ throw new UsageError("choice \u5FC5\u987B\u662F overwrite / skip / keep-both");
565
+ }
566
+ const { queue: transferQueue, release } = queueFor(context, command);
567
+ try {
568
+ transferQueue.resolveConflict(jobId, { choice, applyToAll: options.all });
569
+ emit(
570
+ { jobId, choice, applyToAll: options.all },
571
+ () => success(`\u5DF2\u5904\u7406\u51B2\u7A81\uFF1A${choice}`)
572
+ );
573
+ return 0;
574
+ } finally {
575
+ await release();
576
+ }
577
+ }
578
+ )
579
+ );
580
+ }
581
+ async function waitForJob(queue, jobId) {
582
+ const { promise, resolve } = Promise.withResolvers();
583
+ const done = (state, error) => resolve(error === void 0 ? { state } : { state, error });
584
+ const check = () => {
585
+ const job = queue.snapshot().find((entry) => entry.request.id === jobId);
586
+ if (!job) {
587
+ done("missing", "\u4EFB\u52A1\u4E0D\u5B58\u5728");
588
+ return true;
589
+ }
590
+ if (job.state === "completed" || job.state === "cancelled") {
591
+ done(job.state, job.error);
592
+ return true;
593
+ }
594
+ if (job.state === "paused") {
595
+ done(job.state, job.error ?? "\u4EFB\u52A1\u5DF2\u6682\u505C");
596
+ return true;
597
+ }
598
+ if (job.state === "conflict" && isJson()) {
599
+ done(job.state, job.error ?? "\u4EFB\u52A1\u7B49\u5F85\u51B2\u7A81\u786E\u8BA4\uFF08\u975E\u4EA4\u4E92\u6A21\u5F0F\u65E0\u6CD5\u56DE\u7B54\uFF09");
600
+ return true;
601
+ }
602
+ return false;
603
+ };
604
+ if (check()) return promise;
605
+ const unsubscribe = queue.subscribe(() => {
606
+ if (check()) unsubscribe();
607
+ });
608
+ if (check()) unsubscribe();
609
+ return promise;
610
+ }
611
+
359
612
  // src/commands/guard.ts
360
613
  import pc4 from "picocolors";
361
614
  function registerGuardCommands(program) {
@@ -803,24 +1056,37 @@ function registerRunCommand(program) {
803
1056
  // src/commands/ssh.ts
804
1057
  import pc6 from "picocolors";
805
1058
  function registerSSHCommands(program) {
806
- program.command("ssh <id>").description("\u4EA4\u4E92\u5F0F SSH \u767B\u5F55\u5B9E\u4F8B").option("--start", "\u5B9E\u4F8B\u672A\u8FD0\u884C\u65F6\u81EA\u52A8\u5F00\u673A", false).option("--print", "\u53EA\u6253\u5370\u8FDE\u63A5\u4FE1\u606F\uFF0C\u4E0D\u5EFA\u7ACB\u8FDE\u63A5", false).allowUnknownOption().action(
807
- action(async (context, id, options, command) => {
808
- if (options.print) {
809
- const creds = await getCredentials(context.client, id, { autoStart: options.start });
810
- emit(creds, () => {
811
- printKeyValues([
812
- ["\u547D\u4EE4", `ssh -p ${creds.port} ${creds.user}@${creds.host}`],
813
- ["\u5BC6\u7801", creds.password]
814
- ]);
1059
+ program.command("ssh <id>").description("\u4EA4\u4E92\u5F0F SSH \u767B\u5F55\u5B9E\u4F8B").option("--start", "\u5B9E\u4F8B\u672A\u8FD0\u884C\u65F6\u81EA\u52A8\u5F00\u673A", false).option("--print", "\u53EA\u6253\u5370\u8FDE\u63A5\u4FE1\u606F\uFF0C\u4E0D\u5EFA\u7ACB\u8FDE\u63A5", false).option("--auto-auth", "\u4F7F\u7528\u5B9E\u4F8B\u5BC6\u7801\u81EA\u52A8\u8BA4\u8BC1\uFF08\u72EC\u5360\u5F53\u524D\u7EC8\u7AEF\uFF0C\u4E0D\u8BFB\u53D6 OpenSSH \u914D\u7F6E\uFF09", false).allowUnknownOption().action(
1060
+ action(
1061
+ async (context, id, options, command) => {
1062
+ if (options.print) {
1063
+ const creds = await getCredentials(context.client, id, { autoStart: options.start });
1064
+ emit(creds, () => {
1065
+ printKeyValues([
1066
+ ["\u547D\u4EE4", `ssh -p ${creds.port} ${creds.user}@${creds.host}`],
1067
+ ["\u5BC6\u7801", creds.password]
1068
+ ]);
1069
+ });
1070
+ return 0;
1071
+ }
1072
+ const extraArgs = command.args.slice(1);
1073
+ if (options.autoAuth) {
1074
+ if (isJson() || !process.stdin.isTTY || !process.stdout.isTTY) {
1075
+ throw new UsageError("--auto-auth \u9700\u8981\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\uFF0C\u4E0D\u652F\u6301 --json");
1076
+ }
1077
+ if (extraArgs.length) {
1078
+ throw new UsageError(
1079
+ "--auto-auth \u4E0D\u63A5\u53D7 OpenSSH \u53C2\u6570\uFF1B\u7AEF\u53E3\u8F6C\u53D1\u7B49\u573A\u666F\u8BF7\u4F7F\u7528\u666E\u901A autodl ssh"
1080
+ );
1081
+ }
1082
+ return connectTerminal(context.client, id, { autoStart: options.start });
1083
+ }
1084
+ return connectInteractive(context.client, id, {
1085
+ autoStart: options.start,
1086
+ extraArgs
815
1087
  });
816
- return 0;
817
1088
  }
818
- const extraArgs = command.args.slice(1);
819
- return connectInteractive(context.client, id, {
820
- autoStart: options.start,
821
- extraArgs
822
- });
823
- })
1089
+ )
824
1090
  );
825
1091
  program.command("exec <id> <command...>").description("\u5728\u5B9E\u4F8B\u4E0A\u6267\u884C\u547D\u4EE4\uFF0C\u6D41\u5F0F\u56DE\u4F20\u8F93\u51FA\uFF0C\u900F\u4F20\u8FDC\u7A0B\u9000\u51FA\u7801").option("--start", "\u5B9E\u4F8B\u672A\u8FD0\u884C\u65F6\u81EA\u52A8\u5F00\u673A", false).option("--cwd <dir>", "\u8FDC\u7A0B\u5DE5\u4F5C\u76EE\u5F55").option("--timeout <duration>", "\u547D\u4EE4\u8D85\u65F6\u65F6\u95F4\uFF0C\u5982 30m").option("--env <key=value...>", "\u6CE8\u5165\u73AF\u5883\u53D8\u91CF").option("--pty", "\u5206\u914D\u4F2A\u7EC8\u7AEF\uFF08\u9700\u8981 isatty \u7684\u7A0B\u5E8F\u7528\u5F97\u4E0A\uFF09", false).action(
826
1092
  action(
@@ -983,6 +1249,7 @@ function buildProgram() {
983
1249
  registerAccountCommands(program);
984
1250
  registerInstanceCommands(program);
985
1251
  registerSSHCommands(program);
1252
+ registerFileCommands(program);
986
1253
  registerRunCommand(program);
987
1254
  registerDeployCommand(program);
988
1255
  registerGuardCommands(program);
@@ -1021,7 +1288,7 @@ function shouldLaunchBareTui(argv) {
1021
1288
  async function main(argv = process.argv) {
1022
1289
  if (shouldLaunchBareTui(argv)) {
1023
1290
  try {
1024
- const { launchTui } = await import("./tui-LEZV34YH.js");
1291
+ const { launchTui } = await import("./tui-ZT5ZBVSQ.js");
1025
1292
  await launchTui({});
1026
1293
  return;
1027
1294
  } catch (err) {
@@ -1032,12 +1299,39 @@ async function main(argv = process.argv) {
1032
1299
  }
1033
1300
  }
1034
1301
  const program = buildProgram();
1302
+ const parseDiagnostics = [];
1303
+ const bufferDiagnostic = (message) => {
1304
+ parseDiagnostics.push(message);
1305
+ };
1306
+ const installParseHandling = (command) => {
1307
+ command.configureOutput({ outputError: bufferDiagnostic, writeErr: bufferDiagnostic });
1308
+ command.exitOverride();
1309
+ for (const subcommand of command.commands) installParseHandling(subcommand);
1310
+ };
1311
+ installParseHandling(program);
1035
1312
  try {
1036
1313
  await program.parseAsync(argv);
1037
1314
  } catch (err) {
1038
- const asCommanderError = err;
1039
- if (typeof asCommanderError?.code === "string" && asCommanderError.code.startsWith("commander.")) {
1040
- process.exitCode = asCommanderError.exitCode ?? ExitCode.OK;
1315
+ const commanderError = err;
1316
+ if (typeof commanderError?.code === "string" && commanderError.code.startsWith("commander.")) {
1317
+ if (commanderError.code === "commander.helpDisplayed" || commanderError.code === "commander.version") {
1318
+ process.exitCode = commanderError.exitCode ?? ExitCode.OK;
1319
+ return;
1320
+ }
1321
+ const jsonRequested = program.opts().json === true;
1322
+ if (jsonRequested) {
1323
+ configureOutput({ json: true });
1324
+ emitError(
1325
+ new UsageError(
1326
+ commanderError.code === "commander.help" ? "\u672A\u6307\u5B9A\u5B50\u547D\u4EE4" : (commanderError.message ?? "").replace(/^error:\s*/, ""),
1327
+ { hint: "\u8FD0\u884C `autodl --help` \u67E5\u770B\u7528\u6CD5" }
1328
+ )
1329
+ );
1330
+ process.exitCode = ExitCode.USAGE;
1331
+ return;
1332
+ }
1333
+ if (parseDiagnostics.length > 0) process.stderr.write(parseDiagnostics.join(""));
1334
+ process.exitCode = commanderError.code === "commander.help" ? commanderError.exitCode ?? ExitCode.GENERIC : ExitCode.USAGE;
1041
1335
  return;
1042
1336
  }
1043
1337
  const error = toAutoDLError(err);
package/dist/index.d.ts CHANGED
@@ -745,7 +745,7 @@ interface ConnectOptions extends CredentialOptions {
745
745
  connectAttempts?: number;
746
746
  }
747
747
  /**
748
- * The single funnel for every SSH operation.
748
+ * The single funnel for every one-shot SSH operation.
749
749
  *
750
750
  * Each attempt re-reads the credentials, which covers both ways a connection can fail:
751
751
  * the port may have been reassigned since the snapshot was taken, and sshd may simply
@@ -753,6 +753,15 @@ interface ConnectOptions extends CredentialOptions {
753
753
  * second only by waiting — so attempts are spaced out rather than fired back to back.
754
754
  */
755
755
  declare function withSSH<T>(client: AutoDLClient, uuid: string, fn: (conn: Client, creds: SSHCredentials) => Promise<T>, options?: ConnectOptions): Promise<T>;
756
+ /**
757
+ * Build one authenticated connection and hand ownership to the caller.
758
+ *
759
+ * Same retry policy as `withSSH` (fresh credentials per attempt, spaced retries
760
+ * for sshd-not-up-yet and port reassignment), except nothing is closed: the
761
+ * caller MUST eventually call `conn.end()`. Used for the file view's reusable
762
+ * browsing connection; one-shot operations should keep using `withSSH`.
763
+ */
764
+ declare function connectSSH(client: AutoDLClient, uuid: string, options?: ConnectOptions): Promise<Client>;
756
765
 
757
766
  /**
758
767
  * Interactive login hands off to the system `ssh` binary rather than ssh2: the user
@@ -945,4 +954,4 @@ interface RunResult {
945
954
  */
946
955
  declare function runWorkflow(client: AutoDLClient, options: RunOptions): Promise<RunResult>;
947
956
 
948
- export { AuthError, AutoDLClient, AutoDLError, BASE_IMAGES, type Balance, type BaseImage, BudgetError, type ClientOptions, type Context, type CreateInstanceInput, DEFAULT_BASE_IMAGE, DEFAULT_BASE_URL, type DeployOptions, type DeployResult, type ErrorCode, type ExecOptions, type ExecResult, ExitCode, GPU_SPECS, type GlobalOptions, type GpuSpec, type GpuStockEntry, type IdleOptions, type IdleResult, type Instance, type InstanceSnapshot, type InstanceStatus, NoStockError, NotFoundError, type Pagination, type ParsedRepo, type PrivateImage, REGIONS, type Region, type RegionChoice, type RegionStock, type RunOptions, type RunResult, type SSHCredentials, SSHError, type ServiceEndpoint, type StockQuery, type StockSnapshot, type StoredConfig, TimeoutError, type TransferOptions, type TransferSummary, UsageError, VERSION, type WaitOptions, armTTLOverSSH, assertBudget, assertStockRegion, buildServer, buildTTLSnippet, chooseRegions, composeStartCommand, connectInteractive, createContext, createInstance, deployWorkflow, disarmTTLOverSSH, estimateCost, execCommand, execOnConnection, findBaseImage, findInstance, findRegionsWithStock, formatCudaVersion, formatDuration, formatRate, formatSSHCommand, formatYuan, getBalance, getCredentials, getInstanceSnapshot, getInstanceStatus, getRegionGpuStock, getStockByRegion, listAllInstances, listInstancesPage, listPrivateImages, milliToYuan, normalizeBalance, normalizeInstance, normalizeSnapshot, parseCudaVersion, parseDuration, parseRepo, parseUtilisation, powerOffInstance, powerOnInstance, pull, push, readConfig, recordTTL, redactCredentials, redactSnapshot, redactToken, releaseInstance, resolveGitToken, resolveGpuSpec, resolveMinBalance, resolveRegion, resolveToken, runWorkflow, saveImage, setNfsMount, specForStockName, startMcpServer, sweepExpired, toAutoDLError, updateConfig, waitForRunning, waitForShutdown, waitForStatus, watchIdle, withCredentials, withSSH, writeConfig, yuanToMilli };
957
+ export { AuthError, AutoDLClient, AutoDLError, BASE_IMAGES, type Balance, type BaseImage, BudgetError, type ClientOptions, type Context, type CreateInstanceInput, DEFAULT_BASE_IMAGE, DEFAULT_BASE_URL, type DeployOptions, type DeployResult, type ErrorCode, type ExecOptions, type ExecResult, ExitCode, GPU_SPECS, type GlobalOptions, type GpuSpec, type GpuStockEntry, type IdleOptions, type IdleResult, type Instance, type InstanceSnapshot, type InstanceStatus, NoStockError, NotFoundError, type Pagination, type ParsedRepo, type PrivateImage, REGIONS, type Region, type RegionChoice, type RegionStock, type RunOptions, type RunResult, type SSHCredentials, SSHError, type ServiceEndpoint, type StockQuery, type StockSnapshot, type StoredConfig, TimeoutError, type TransferOptions, type TransferSummary, UsageError, VERSION, type WaitOptions, armTTLOverSSH, assertBudget, assertStockRegion, buildServer, buildTTLSnippet, chooseRegions, composeStartCommand, connectInteractive, connectSSH, createContext, createInstance, deployWorkflow, disarmTTLOverSSH, estimateCost, execCommand, execOnConnection, findBaseImage, findInstance, findRegionsWithStock, formatCudaVersion, formatDuration, formatRate, formatSSHCommand, formatYuan, getBalance, getCredentials, getInstanceSnapshot, getInstanceStatus, getRegionGpuStock, getStockByRegion, listAllInstances, listInstancesPage, listPrivateImages, milliToYuan, normalizeBalance, normalizeInstance, normalizeSnapshot, parseCudaVersion, parseDuration, parseRepo, parseUtilisation, powerOffInstance, powerOnInstance, pull, push, readConfig, recordTTL, redactCredentials, redactSnapshot, redactToken, releaseInstance, resolveGitToken, resolveGpuSpec, resolveMinBalance, resolveRegion, resolveToken, runWorkflow, saveImage, setNfsMount, specForStockName, startMcpServer, sweepExpired, toAutoDLError, updateConfig, waitForRunning, waitForShutdown, waitForStatus, watchIdle, withCredentials, withSSH, writeConfig, yuanToMilli };
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  startMcpServer,
21
21
  watchIdle,
22
22
  withCredentials
23
- } from "./chunk-5RZ4Q7T4.js";
23
+ } from "./chunk-MWYYK755.js";
24
24
  import {
25
25
  BASE_IMAGES,
26
26
  DEFAULT_BASE_IMAGE,
@@ -40,10 +40,10 @@ import {
40
40
  resolveRegion,
41
41
  setNfsMount,
42
42
  specForStockName
43
- } from "./chunk-CWCG3MOC.js";
43
+ } from "./chunk-WN2CXPD2.js";
44
44
  import {
45
45
  createContext
46
- } from "./chunk-NBWWTYSL.js";
46
+ } from "./chunk-VEDMEYCW.js";
47
47
  import {
48
48
  AuthError,
49
49
  AutoDLClient,
@@ -59,6 +59,7 @@ import {
59
59
  armTTLOverSSH,
60
60
  buildTTLSnippet,
61
61
  composeStartCommand,
62
+ connectSSH,
62
63
  createInstance,
63
64
  disarmTTLOverSSH,
64
65
  estimateCost,
@@ -95,7 +96,7 @@ import {
95
96
  withSSH,
96
97
  writeConfig,
97
98
  yuanToMilli
98
- } from "./chunk-6QR4ZYQR.js";
99
+ } from "./chunk-GHJVQKKC.js";
99
100
  export {
100
101
  AuthError,
101
102
  AutoDLClient,
@@ -121,6 +122,7 @@ export {
121
122
  chooseRegions,
122
123
  composeStartCommand,
123
124
  connectInteractive,
125
+ connectSSH,
124
126
  createContext,
125
127
  createInstance,
126
128
  deployWorkflow,
@@ -5,12 +5,12 @@ import {
5
5
  assertInteractive,
6
6
  launchTui,
7
7
  registerTuiCommand
8
- } from "./chunk-LHDSN4DL.js";
9
- import "./chunk-NBWWTYSL.js";
10
- import "./chunk-6QR4ZYQR.js";
8
+ } from "./chunk-JOOIUQLJ.js";
9
+ import "./chunk-VEDMEYCW.js";
10
+ import "./chunk-GHJVQKKC.js";
11
11
  export {
12
12
  assertInteractive,
13
13
  launchTui,
14
14
  registerTuiCommand
15
15
  };
16
- //# sourceMappingURL=tui-LEZV34YH.js.map
16
+ //# sourceMappingURL=tui-ZT5ZBVSQ.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minato-aqukin/autodl-cli",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Agent-friendly CLI, MCP server and SDK for managing AutoDL GPU instances — create, boot, SSH, run and auto-shutdown, all scriptable.",
5
5
  "keywords": [
6
6
  "autodl",