@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.
@@ -976,6 +976,7 @@ var waitForRunning = (client, uuid, options2) => waitForStatus(client, uuid, ["r
976
976
  var waitForShutdown = (client, uuid, options2) => waitForStatus(client, uuid, ["shutdown"], options2);
977
977
 
978
978
  // src/ssh/credentials.ts
979
+ import { setTimeout as delay } from "timers/promises";
979
980
  import { Client } from "ssh2";
980
981
 
981
982
  // src/output/i18n.ts
@@ -1065,7 +1066,9 @@ function t(key) {
1065
1066
 
1066
1067
  // src/ssh/credentials.ts
1067
1068
  async function getCredentials(client, uuid, options2 = {}) {
1069
+ assertNotAborted(options2.signal);
1068
1070
  let status = await getInstanceStatus(client, uuid);
1071
+ assertNotAborted(options2.signal);
1069
1072
  if (status !== "running") {
1070
1073
  if (!options2.autoStart) {
1071
1074
  throw new SSHError(`\u5B9E\u4F8B ${uuid} \u5F53\u524D\u72B6\u6001\u4E3A "${status}"\uFF0C\u65E0\u6CD5\u5EFA\u7ACB SSH \u8FDE\u63A5`, {
@@ -1082,6 +1085,7 @@ async function getCredentials(client, uuid, options2 = {}) {
1082
1085
  });
1083
1086
  }
1084
1087
  if (status === "shutdown") {
1088
+ assertNotAborted(options2.signal);
1085
1089
  note(t("instance.poweringOn"));
1086
1090
  await powerOnInstance(client, uuid);
1087
1091
  }
@@ -1093,6 +1097,7 @@ async function getCredentials(client, uuid, options2 = {}) {
1093
1097
  });
1094
1098
  }
1095
1099
  const snapshot = await getInstanceSnapshot(client, uuid);
1100
+ assertNotAborted(options2.signal);
1096
1101
  const { host, port, password } = snapshot.ssh;
1097
1102
  if (!host || !port || !password) {
1098
1103
  throw new SSHError(`\u5B9E\u4F8B ${uuid} \u5C1A\u672A\u8FD4\u56DE\u5B8C\u6574\u7684 SSH \u4FE1\u606F`, {
@@ -1103,57 +1108,92 @@ async function getCredentials(client, uuid, options2 = {}) {
1103
1108
  return { uuid, host, port, user: "root", password };
1104
1109
  }
1105
1110
  var RETRY_DELAYS_MS = [2e3, 5e3, 8e3];
1106
- function delay(ms) {
1107
- return new Promise((resolve) => setTimeout(resolve, ms));
1108
- }
1109
- function connectOnce(creds, options2) {
1110
- return new Promise((resolve, reject) => {
1111
- const conn = new Client();
1112
- const settle = (err) => {
1113
- conn.removeAllListeners("ready");
1114
- conn.removeAllListeners("error");
1115
- if (err) reject(err);
1116
- else resolve(conn);
1117
- };
1118
- conn.once("ready", () => settle());
1119
- conn.once("error", (err) => {
1120
- conn.end();
1121
- settle(err);
1122
- });
1111
+ async function connectOnce(creds, options2) {
1112
+ assertNotAborted(options2.signal);
1113
+ const { promise, resolve, reject } = Promise.withResolvers();
1114
+ const conn = new Client();
1115
+ let settled = false;
1116
+ const settle = (err) => {
1117
+ if (settled) return;
1118
+ settled = true;
1119
+ conn.off("ready", onReady);
1120
+ if (err) {
1121
+ conn.once("close", () => conn.off("error", onError));
1122
+ } else {
1123
+ conn.off("error", onError);
1124
+ }
1125
+ conn.off("close", onClose);
1126
+ options2.signal?.removeEventListener("abort", onAbort);
1127
+ if (err) reject(err);
1128
+ else resolve(conn);
1129
+ };
1130
+ const onReady = () => settle();
1131
+ const onError = (err) => {
1132
+ if (settled) return;
1133
+ settle(err);
1134
+ conn.end();
1135
+ };
1136
+ const onClose = () => {
1137
+ settle(new SSHError("SSH \u8FDE\u63A5\u5728\u63E1\u624B\u5B8C\u6210\u524D\u5173\u95ED"));
1138
+ conn.off("error", onError);
1139
+ };
1140
+ const onAbort = () => {
1141
+ settle(new AutoDLError("\u64CD\u4F5C\u5DF2\u53D6\u6D88"));
1142
+ conn.destroy();
1143
+ };
1144
+ conn.once("ready", onReady);
1145
+ conn.on("error", onError);
1146
+ conn.once("close", onClose);
1147
+ options2.signal?.addEventListener("abort", onAbort, { once: true });
1148
+ try {
1123
1149
  conn.connect({
1124
1150
  host: creds.host,
1125
1151
  port: creds.port,
1126
1152
  username: creds.user,
1127
1153
  password: creds.password,
1128
1154
  readyTimeout: options2.connectTimeoutMs ?? 3e4,
1129
- keepaliveInterval: options2.keepaliveIntervalMs ?? 15e3,
1130
- // AutoDL's proxy hosts reuse addresses across instances, so a known_hosts
1131
- // check would fail constantly and teach users to ignore warnings.
1132
- algorithms: void 0
1155
+ keepaliveInterval: options2.keepaliveIntervalMs ?? 15e3
1133
1156
  });
1134
- });
1157
+ } catch (err) {
1158
+ onError(err instanceof Error ? err : new Error(String(err)));
1159
+ }
1160
+ return promise;
1135
1161
  }
1136
1162
  async function withSSH(client, uuid, fn, options2 = {}) {
1163
+ const conn = await connectSSH(client, uuid, options2);
1164
+ const creds = conn._creds;
1165
+ try {
1166
+ if (!creds) throw new SSHError("SSH \u8FDE\u63A5\u7F3A\u5C11\u51ED\u8BC1\u4E0A\u4E0B\u6587", { details: { uuid } });
1167
+ return await fn(conn, creds);
1168
+ } finally {
1169
+ delete conn._creds;
1170
+ conn.end();
1171
+ }
1172
+ }
1173
+ async function connectSSH(client, uuid, options2 = {}) {
1137
1174
  let lastError;
1138
1175
  const attempts = Math.max(1, options2.connectAttempts ?? 3);
1139
1176
  for (let attempt = 0; attempt < attempts; attempt++) {
1177
+ assertNotAborted(options2.signal);
1140
1178
  if (attempt > 0) {
1141
1179
  note(t("ssh.refreshing"));
1142
- await delay(RETRY_DELAYS_MS[attempt - 1] ?? RETRY_DELAYS_MS.at(-1) ?? 5e3);
1180
+ await delay(RETRY_DELAYS_MS[attempt - 1] ?? RETRY_DELAYS_MS.at(-1) ?? 5e3, void 0, {
1181
+ signal: options2.signal
1182
+ });
1143
1183
  }
1144
1184
  const creds = await getCredentials(client, uuid, options2);
1145
- let conn;
1185
+ assertNotAborted(options2.signal);
1146
1186
  try {
1147
- conn = await connectOnce(creds, options2);
1187
+ const conn = await connectOnce(creds, options2);
1188
+ conn._creds = creds;
1189
+ return conn;
1148
1190
  } catch (err) {
1191
+ assertNotAborted(options2.signal);
1192
+ if (err instanceof Error && "level" in err && err.level === "client-authentication") {
1193
+ throw new SSHError("SSH \u8BA4\u8BC1\u5931\u8D25\uFF0C\u8BF7\u91CD\u65B0\u83B7\u53D6\u5B9E\u4F8B\u51ED\u8BC1", { cause: err });
1194
+ }
1149
1195
  lastError = err;
1150
1196
  debug(`SSH \u8FDE\u63A5 ${creds.host}:${creds.port} \u5931\u8D25\uFF1A${err.message}`);
1151
- continue;
1152
- }
1153
- try {
1154
- return await fn(conn, creds);
1155
- } finally {
1156
- conn.end();
1157
1197
  }
1158
1198
  }
1159
1199
  throw new SSHError(`\u65E0\u6CD5\u8FDE\u63A5\u5230\u5B9E\u4F8B ${uuid}\uFF08\u5DF2\u5C1D\u8BD5 ${attempts} \u6B21\uFF09`, {
@@ -1164,6 +1204,9 @@ async function withSSH(client, uuid, fn, options2 = {}) {
1164
1204
  function shellQuote(value) {
1165
1205
  return `'${value.replace(/'/g, `'\\''`)}'`;
1166
1206
  }
1207
+ function assertNotAborted(signal) {
1208
+ if (signal?.aborted) throw new AutoDLError("\u64CD\u4F5C\u5DF2\u53D6\u6D88");
1209
+ }
1167
1210
 
1168
1211
  // src/ssh/exec.ts
1169
1212
  function buildCommand(command, options2) {
@@ -1362,6 +1405,7 @@ export {
1362
1405
  SSHError,
1363
1406
  toAutoDLError,
1364
1407
  isAuthError,
1408
+ configDir,
1365
1409
  configPath,
1366
1410
  readConfig,
1367
1411
  writeConfig,
@@ -1420,6 +1464,8 @@ export {
1420
1464
  waitForShutdown,
1421
1465
  getCredentials,
1422
1466
  withSSH,
1467
+ connectSSH,
1468
+ assertNotAborted,
1423
1469
  execOnConnection,
1424
1470
  execCommand,
1425
1471
  buildTTLSnippet,
@@ -1429,4 +1475,4 @@ export {
1429
1475
  recordTTL,
1430
1476
  sweepExpired
1431
1477
  };
1432
- //# sourceMappingURL=chunk-6QR4ZYQR.js.map
1478
+ //# sourceMappingURL=chunk-GHJVQKKC.js.map
@@ -4,7 +4,7 @@ const require = __nodeCreateRequire(import.meta.url);
4
4
  import {
5
5
  createContext,
6
6
  runOpportunisticSweep
7
- } from "./chunk-NBWWTYSL.js";
7
+ } from "./chunk-VEDMEYCW.js";
8
8
  import {
9
9
  ExitCode,
10
10
  UsageError,
@@ -14,7 +14,7 @@ import {
14
14
  resolveLang,
15
15
  setLang,
16
16
  toAutoDLError
17
- } from "./chunk-6QR4ZYQR.js";
17
+ } from "./chunk-GHJVQKKC.js";
18
18
 
19
19
  // src/commands/helpers.ts
20
20
  import { confirm, isCancel } from "@clack/prompts";
@@ -70,6 +70,12 @@ function bareAction(handler) {
70
70
  const command = args[args.length - 1];
71
71
  const globals = globalsOf(command);
72
72
  try {
73
+ configureOutput({
74
+ json: globals.json ?? false,
75
+ color: globals.color ?? true,
76
+ verbose: globals.verbose ?? false
77
+ });
78
+ setLang(resolveLang(globals.lang));
73
79
  const code = await handler(globals, ...args);
74
80
  process.exitCode = typeof code === "number" ? code : ExitCode.OK;
75
81
  } catch (err) {
@@ -106,7 +112,7 @@ function assertInteractive(json) {
106
112
  }
107
113
  async function launchTui(globals) {
108
114
  assertInteractive(globals.json === true);
109
- const { runTui } = await import("./app-4PY7NE53.js");
115
+ const { runTui } = await import("./app-V5GCG3MC.js");
110
116
  await runTui({
111
117
  ...globals.token ? { token: globals.token } : {},
112
118
  ...globals.baseUrl ? { baseUrl: globals.baseUrl } : {}
@@ -131,4 +137,4 @@ export {
131
137
  launchTui,
132
138
  registerTuiCommand
133
139
  };
134
- //# sourceMappingURL=chunk-LHDSN4DL.js.map
140
+ //# sourceMappingURL=chunk-JOOIUQLJ.js.map
@@ -15,10 +15,10 @@ import {
15
15
  parseCudaVersion,
16
16
  resolveGpuSpec,
17
17
  resolveRegion
18
- } from "./chunk-CWCG3MOC.js";
18
+ } from "./chunk-WN2CXPD2.js";
19
19
  import {
20
20
  createContext
21
- } from "./chunk-NBWWTYSL.js";
21
+ } from "./chunk-VEDMEYCW.js";
22
22
  import {
23
23
  AutoDLError,
24
24
  BudgetError,
@@ -56,7 +56,7 @@ import {
56
56
  waitForShutdown,
57
57
  warn,
58
58
  withSSH
59
- } from "./chunk-6QR4ZYQR.js";
59
+ } from "./chunk-GHJVQKKC.js";
60
60
 
61
61
  // src/core/endpoints/image.ts
62
62
  async function saveImage(client, instanceUuid, imageName) {
@@ -1455,4 +1455,4 @@ export {
1455
1455
  connectInteractive,
1456
1456
  formatSSHCommand
1457
1457
  };
1458
- //# sourceMappingURL=chunk-5RZ4Q7T4.js.map
1458
+ //# sourceMappingURL=chunk-MWYYK755.js.map
@@ -10,7 +10,7 @@ import {
10
10
  resolveToken,
11
11
  setLang,
12
12
  sweepExpired
13
- } from "./chunk-6QR4ZYQR.js";
13
+ } from "./chunk-GHJVQKKC.js";
14
14
 
15
15
  // src/context.ts
16
16
  function createContext(options = {}) {
@@ -43,4 +43,4 @@ export {
43
43
  createContext,
44
44
  runOpportunisticSweep
45
45
  };
46
- //# sourceMappingURL=chunk-NBWWTYSL.js.map
46
+ //# sourceMappingURL=chunk-VEDMEYCW.js.map
@@ -4,11 +4,12 @@ const require = __nodeCreateRequire(import.meta.url);
4
4
  import {
5
5
  UsageError,
6
6
  debug,
7
+ isAuthError,
7
8
  normalizeBalance,
8
9
  note,
9
10
  warn,
10
11
  yuanToMilli
11
- } from "./chunk-6QR4ZYQR.js";
12
+ } from "./chunk-GHJVQKKC.js";
12
13
 
13
14
  // src/core/catalog.ts
14
15
  var GPU_SPECS = [
@@ -335,6 +336,10 @@ async function getStockByRegion(client, options = {}) {
335
336
  );
336
337
  const snapshots = [];
337
338
  const failures = [];
339
+ const authFailure = settled.find(
340
+ (result) => result.status === "rejected" && isAuthError(result.reason)
341
+ );
342
+ if (authFailure?.status === "rejected") throw authFailure.reason;
338
343
  settled.forEach((result, index) => {
339
344
  const region = targets[index];
340
345
  if (!region) return;
@@ -430,4 +435,4 @@ export {
430
435
  chooseRegions,
431
436
  VERSION
432
437
  };
433
- //# sourceMappingURL=chunk-CWCG3MOC.js.map
438
+ //# sourceMappingURL=chunk-WN2CXPD2.js.map