@minato-aqukin/autodl-cli 0.1.2 → 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/README.md +61 -6
- package/README.zh-CN.md +55 -5
- package/dist/{app-3QXI65BE.js → app-V5GCG3MC.js} +1641 -208
- package/dist/chunk-2E2Y5GFH.js +1704 -0
- package/dist/{chunk-6QR4ZYQR.js → chunk-GHJVQKKC.js} +78 -32
- package/dist/{chunk-DLCDJE6I.js → chunk-JOOIUQLJ.js} +4 -4
- package/dist/{chunk-BMKVXRT7.js → chunk-MWYYK755.js} +4 -4
- package/dist/{chunk-NBWWTYSL.js → chunk-VEDMEYCW.js} +2 -2
- package/dist/{chunk-GJGHX55T.js → chunk-WN2CXPD2.js} +2 -2
- package/dist/cli.js +289 -22
- package/dist/index.d.ts +11 -2
- package/dist/index.js +6 -4
- package/dist/{tui-2JGNYYRA.js → tui-ZT5ZBVSQ.js} +4 -4
- package/package.json +1 -1
|
@@ -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
|
|
1107
|
-
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
17
|
+
} from "./chunk-GHJVQKKC.js";
|
|
18
18
|
|
|
19
19
|
// src/commands/helpers.ts
|
|
20
20
|
import { confirm, isCancel } from "@clack/prompts";
|
|
@@ -112,7 +112,7 @@ function assertInteractive(json) {
|
|
|
112
112
|
}
|
|
113
113
|
async function launchTui(globals) {
|
|
114
114
|
assertInteractive(globals.json === true);
|
|
115
|
-
const { runTui } = await import("./app-
|
|
115
|
+
const { runTui } = await import("./app-V5GCG3MC.js");
|
|
116
116
|
await runTui({
|
|
117
117
|
...globals.token ? { token: globals.token } : {},
|
|
118
118
|
...globals.baseUrl ? { baseUrl: globals.baseUrl } : {}
|
|
@@ -137,4 +137,4 @@ export {
|
|
|
137
137
|
launchTui,
|
|
138
138
|
registerTuiCommand
|
|
139
139
|
};
|
|
140
|
-
//# sourceMappingURL=chunk-
|
|
140
|
+
//# sourceMappingURL=chunk-JOOIUQLJ.js.map
|
|
@@ -15,10 +15,10 @@ import {
|
|
|
15
15
|
parseCudaVersion,
|
|
16
16
|
resolveGpuSpec,
|
|
17
17
|
resolveRegion
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-WN2CXPD2.js";
|
|
19
19
|
import {
|
|
20
20
|
createContext
|
|
21
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
1458
|
+
//# sourceMappingURL=chunk-MWYYK755.js.map
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
resolveToken,
|
|
11
11
|
setLang,
|
|
12
12
|
sweepExpired
|
|
13
|
-
} from "./chunk-
|
|
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-
|
|
46
|
+
//# sourceMappingURL=chunk-VEDMEYCW.js.map
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
note,
|
|
10
10
|
warn,
|
|
11
11
|
yuanToMilli
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-GHJVQKKC.js";
|
|
13
13
|
|
|
14
14
|
// src/core/catalog.ts
|
|
15
15
|
var GPU_SPECS = [
|
|
@@ -435,4 +435,4 @@ export {
|
|
|
435
435
|
chooseRegions,
|
|
436
436
|
VERSION
|
|
437
437
|
};
|
|
438
|
-
//# sourceMappingURL=chunk-
|
|
438
|
+
//# sourceMappingURL=chunk-WN2CXPD2.js.map
|
package/dist/cli.js
CHANGED
|
@@ -13,7 +13,13 @@ import {
|
|
|
13
13
|
saveImage,
|
|
14
14
|
startMcpServer,
|
|
15
15
|
watchIdle
|
|
16
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
41
|
-
import "./chunk-
|
|
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-
|
|
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(
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
[
|
|
813
|
-
|
|
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
|
-
|
|
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-
|
|
1291
|
+
const { launchTui } = await import("./tui-ZT5ZBVSQ.js");
|
|
1025
1292
|
await launchTui({});
|
|
1026
1293
|
return;
|
|
1027
1294
|
} catch (err) {
|