@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.
@@ -1,6 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire as __nodeCreateRequire } from "node:module";
3
3
  const require = __nodeCreateRequire(import.meta.url);
4
+ import {
5
+ FileWorkspace,
6
+ TransferQueue,
7
+ connectTerminal,
8
+ identityFromToken,
9
+ tokenOverrideNote
10
+ } from "./chunk-2E2Y5GFH.js";
4
11
  import {
5
12
  BASE_IMAGES,
6
13
  DEFAULT_BASE_IMAGE,
@@ -11,9 +18,10 @@ import {
11
18
  getBalance,
12
19
  getStockByRegion,
13
20
  parseCudaVersion
14
- } from "./chunk-GJGHX55T.js";
21
+ } from "./chunk-WN2CXPD2.js";
15
22
  import {
16
23
  AutoDLClient,
24
+ DEFAULT_BASE_URL,
17
25
  __commonJS,
18
26
  __export,
19
27
  __require,
@@ -29,6 +37,7 @@ import {
29
37
  formatRate,
30
38
  formatTime,
31
39
  formatYuan,
40
+ getCredentials,
32
41
  getInstanceSnapshot,
33
42
  getInstanceStatus,
34
43
  isAuthError,
@@ -47,7 +56,7 @@ import {
47
56
  tryResolveToken,
48
57
  updateConfig,
49
58
  waitForShutdown
50
- } from "./chunk-6QR4ZYQR.js";
59
+ } from "./chunk-GHJVQKKC.js";
51
60
 
52
61
  // node_modules/react/cjs/react.production.js
53
62
  var require_react_production = __commonJS({
@@ -914,21 +923,21 @@ var require_react_development = __commonJS({
914
923
  );
915
924
  actScopeDepth = prevActScopeDepth;
916
925
  }
917
- function recursivelyFlushAsyncActWork(returnValue, resolve, reject) {
926
+ function recursivelyFlushAsyncActWork(returnValue, resolve2, reject) {
918
927
  var queue = ReactSharedInternals.actQueue;
919
928
  if (null !== queue)
920
929
  if (0 !== queue.length)
921
930
  try {
922
931
  flushActQueue(queue);
923
932
  enqueueTask(function() {
924
- return recursivelyFlushAsyncActWork(returnValue, resolve, reject);
933
+ return recursivelyFlushAsyncActWork(returnValue, resolve2, reject);
925
934
  });
926
935
  return;
927
936
  } catch (error) {
928
937
  ReactSharedInternals.thrownErrors.push(error);
929
938
  }
930
939
  else ReactSharedInternals.actQueue = null;
931
- 0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve(returnValue);
940
+ 0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve2(returnValue);
932
941
  }
933
942
  function flushActQueue(queue) {
934
943
  if (!isFlushing) {
@@ -1115,7 +1124,7 @@ var require_react_development = __commonJS({
1115
1124
  ));
1116
1125
  });
1117
1126
  return {
1118
- then: function(resolve, reject) {
1127
+ then: function(resolve2, reject) {
1119
1128
  didAwaitActCall = true;
1120
1129
  thenable.then(
1121
1130
  function(returnValue) {
@@ -1125,7 +1134,7 @@ var require_react_development = __commonJS({
1125
1134
  flushActQueue(queue), enqueueTask(function() {
1126
1135
  return recursivelyFlushAsyncActWork(
1127
1136
  returnValue,
1128
- resolve,
1137
+ resolve2,
1129
1138
  reject
1130
1139
  );
1131
1140
  });
@@ -1139,7 +1148,7 @@ var require_react_development = __commonJS({
1139
1148
  ReactSharedInternals.thrownErrors.length = 0;
1140
1149
  reject(_thrownError);
1141
1150
  }
1142
- } else resolve(returnValue);
1151
+ } else resolve2(returnValue);
1143
1152
  },
1144
1153
  function(error) {
1145
1154
  popActScope(prevActQueue, prevActScopeDepth);
@@ -1161,15 +1170,15 @@ var require_react_development = __commonJS({
1161
1170
  if (0 < ReactSharedInternals.thrownErrors.length)
1162
1171
  throw callback = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, callback;
1163
1172
  return {
1164
- then: function(resolve, reject) {
1173
+ then: function(resolve2, reject) {
1165
1174
  didAwaitActCall = true;
1166
1175
  0 === prevActScopeDepth ? (ReactSharedInternals.actQueue = queue, enqueueTask(function() {
1167
1176
  return recursivelyFlushAsyncActWork(
1168
1177
  returnValue$jscomp$0,
1169
- resolve,
1178
+ resolve2,
1170
1179
  reject
1171
1180
  );
1172
- })) : resolve(returnValue$jscomp$0);
1181
+ })) : resolve2(returnValue$jscomp$0);
1173
1182
  }
1174
1183
  };
1175
1184
  };
@@ -3253,8 +3262,8 @@ var require_react_reconciler_production = __commonJS({
3253
3262
  currentEntangledActionThenable = {
3254
3263
  status: "pending",
3255
3264
  value: void 0,
3256
- then: function(resolve) {
3257
- entangledListeners.push(resolve);
3265
+ then: function(resolve2) {
3266
+ entangledListeners.push(resolve2);
3258
3267
  }
3259
3268
  };
3260
3269
  }
@@ -3277,8 +3286,8 @@ var require_react_reconciler_production = __commonJS({
3277
3286
  status: "pending",
3278
3287
  value: null,
3279
3288
  reason: null,
3280
- then: function(resolve) {
3281
- listeners.push(resolve);
3289
+ then: function(resolve2) {
3290
+ listeners.push(resolve2);
3282
3291
  }
3283
3292
  };
3284
3293
  thenable.then(
@@ -12877,8 +12886,8 @@ var require_react_reconciler_development = __commonJS({
12877
12886
  currentEntangledActionThenable = {
12878
12887
  status: "pending",
12879
12888
  value: void 0,
12880
- then: function(resolve) {
12881
- entangledListeners.push(resolve);
12889
+ then: function(resolve2) {
12890
+ entangledListeners.push(resolve2);
12882
12891
  }
12883
12892
  };
12884
12893
  }
@@ -12901,8 +12910,8 @@ var require_react_reconciler_development = __commonJS({
12901
12910
  status: "pending",
12902
12911
  value: null,
12903
12912
  reason: null,
12904
- then: function(resolve) {
12905
- listeners.push(resolve);
12913
+ then: function(resolve2) {
12914
+ listeners.push(resolve2);
12906
12915
  }
12907
12916
  };
12908
12917
  thenable.then(
@@ -25031,6 +25040,9 @@ var require_jsx_runtime = __commonJS({
25031
25040
  }
25032
25041
  });
25033
25042
 
25043
+ // src/tui/app.tsx
25044
+ import { createHash } from "crypto";
25045
+
25034
25046
  // node_modules/ink/build/render.js
25035
25047
  import { Stream } from "stream";
25036
25048
  import process13 from "process";
@@ -27678,21 +27690,21 @@ var getClosingSgrSequence = (activeStyles) => [...activeStyles].reverse().map((a
27678
27690
  var getOpeningSgrSequence = (activeStyles) => activeStyles.map((activeStyle) => wrapAnsiCode(activeStyle.open)).join("");
27679
27691
  var wrapWord = (rows, word, columns2, rowWidth2) => {
27680
27692
  const tokens = getTokens(word);
27681
- let visible = rowWidth2;
27693
+ let visible2 = rowWidth2;
27682
27694
  for (let index = 0; index < tokens.length; index++) {
27683
27695
  const token = tokens[index];
27684
- if (token.width > 0 && visible > 0 && visible + token.width > columns2) {
27696
+ if (token.width > 0 && visible2 > 0 && visible2 + token.width > columns2) {
27685
27697
  rows.push("");
27686
- visible = 0;
27698
+ visible2 = 0;
27687
27699
  }
27688
27700
  rows[rows.length - 1] += token.value;
27689
- visible += token.width;
27690
- if (visible === columns2 && index < tokens.length - 1) {
27701
+ visible2 += token.width;
27702
+ if (visible2 === columns2 && index < tokens.length - 1) {
27691
27703
  rows.push("");
27692
- visible = 0;
27704
+ visible2 = 0;
27693
27705
  }
27694
27706
  }
27695
- if (!visible && rows.at(-1).length > 0 && rows.length > 1) {
27707
+ if (!visible2 && rows.at(-1).length > 0 && rows.length > 1) {
27696
27708
  rows[rows.length - 2] += rows.pop();
27697
27709
  }
27698
27710
  return getWidth(rows.at(-1));
@@ -27727,7 +27739,7 @@ var expandTabs = (line) => {
27727
27739
  if (!line.includes(" ")) {
27728
27740
  return line;
27729
27741
  }
27730
- let visible = 0;
27742
+ let visible2 = 0;
27731
27743
  let expandedLine = "";
27732
27744
  let plainTextSinceTab = "";
27733
27745
  const expandPlainText = (plainText) => {
@@ -27736,11 +27748,11 @@ var expandTabs = (line) => {
27736
27748
  expandedLine += segment;
27737
27749
  plainTextSinceTab += segment;
27738
27750
  if (index < segments.length - 1) {
27739
- visible += getStringWidth(plainTextSinceTab);
27751
+ visible2 += getStringWidth(plainTextSinceTab);
27740
27752
  plainTextSinceTab = "";
27741
- const spaces = TAB_SIZE - visible % TAB_SIZE;
27753
+ const spaces = TAB_SIZE - visible2 % TAB_SIZE;
27742
27754
  expandedLine += " ".repeat(spaces);
27743
- visible += spaces;
27755
+ visible2 += spaces;
27744
27756
  }
27745
27757
  }
27746
27758
  };
@@ -29247,19 +29259,19 @@ function cliTruncate(text, columns2, options = {}) {
29247
29259
  }
29248
29260
  return start;
29249
29261
  }
29250
- function appendWithInheritedStyleFromEnd(visible, suffix) {
29251
- const start = trailingSgrSpanStartIndex(visible);
29252
- if (start === visible.length) {
29253
- return visible + suffix;
29262
+ function appendWithInheritedStyleFromEnd(visible2, suffix) {
29263
+ const start = trailingSgrSpanStartIndex(visible2);
29264
+ if (start === visible2.length) {
29265
+ return visible2 + suffix;
29254
29266
  }
29255
- return visible.slice(0, start) + suffix + visible.slice(start);
29267
+ return visible2.slice(0, start) + suffix + visible2.slice(start);
29256
29268
  }
29257
- function prependWithInheritedStyleFromStart(prefix, visible) {
29258
- const end = leadingSgrSpanEndIndex(visible);
29269
+ function prependWithInheritedStyleFromStart(prefix, visible2) {
29270
+ const end = leadingSgrSpanEndIndex(visible2);
29259
29271
  if (end === 0) {
29260
- return prefix + visible;
29272
+ return prefix + visible2;
29261
29273
  }
29262
- return visible.slice(0, end) + prefix + visible.slice(end);
29274
+ return visible2.slice(0, end) + prefix + visible2.slice(end);
29263
29275
  }
29264
29276
  if (position === "start") {
29265
29277
  if (preferTruncationOnSpace) {
@@ -31650,7 +31662,7 @@ function splitCompoundSGRSequences(code) {
31650
31662
  }
31651
31663
  function tokenize(str, endChar = Number.POSITIVE_INFINITY) {
31652
31664
  const ret = [];
31653
- let visible = 0;
31665
+ let visible2 = 0;
31654
31666
  let codeEndIndex = 0;
31655
31667
  for (const { segment, index } of segmenter6.segment(str)) {
31656
31668
  if (index < codeEndIndex)
@@ -31700,8 +31712,8 @@ function tokenize(str, endChar = Number.POSITIVE_INFINITY) {
31700
31712
  value: segment,
31701
31713
  fullWidth
31702
31714
  });
31703
- visible += fullWidth ? 2 : 1;
31704
- if (visible >= endChar) {
31715
+ visible2 += fullWidth ? 2 : 1;
31716
+ if (visible2 >= endChar) {
31705
31717
  break;
31706
31718
  }
31707
31719
  }
@@ -33374,8 +33386,8 @@ var kittyModifiers = {
33374
33386
  var noop = () => {
33375
33387
  };
33376
33388
  var textEncoder = new TextEncoder();
33377
- var yieldImmediate = async () => new Promise((resolve) => {
33378
- setImmediate(resolve);
33389
+ var yieldImmediate = async () => new Promise((resolve2) => {
33390
+ setImmediate(resolve2);
33379
33391
  });
33380
33392
  var kittyQueryEscapeByte = 27;
33381
33393
  var kittyQueryOpenBracketByte = 91;
@@ -33592,8 +33604,8 @@ var Ink = class {
33592
33604
  };
33593
33605
  }
33594
33606
  this.initKittyKeyboard();
33595
- this.exitPromise = new Promise((resolve, reject) => {
33596
- this.resolveExitPromise = resolve;
33607
+ this.exitPromise = new Promise((resolve2, reject) => {
33608
+ this.resolveExitPromise = resolve2;
33597
33609
  this.rejectExitPromise = reject;
33598
33610
  });
33599
33611
  void this.exitPromise.catch(noop);
@@ -33904,9 +33916,9 @@ var Ink = class {
33904
33916
  settleThrottle(this.throttledOnRender, canWriteToStdout);
33905
33917
  settleThrottle(this.throttledLog, canWriteToStdout);
33906
33918
  if (canWriteToStdout && hasWritableState) {
33907
- await new Promise((resolve) => {
33919
+ await new Promise((resolve2) => {
33908
33920
  this.options.stdout.write("", () => {
33909
- resolve();
33921
+ resolve2();
33910
33922
  });
33911
33923
  });
33912
33924
  return;
@@ -33992,8 +34004,8 @@ var Ink = class {
33992
34004
  async awaitNextRender() {
33993
34005
  if (!this.nextRenderCommit) {
33994
34006
  let resolveRender;
33995
- const promise = new Promise((resolve) => {
33996
- resolveRender = resolve;
34007
+ const promise = new Promise((resolve2) => {
34008
+ resolveRender = resolve2;
33997
34009
  });
33998
34010
  this.nextRenderCommit = { promise, resolve: resolveRender };
33999
34011
  }
@@ -34789,36 +34801,10 @@ var import_react32 = __toESM(require_react(), 1);
34789
34801
  var import_react33 = __toESM(require_react(), 1);
34790
34802
 
34791
34803
  // src/tui/app.tsx
34792
- var import_react39 = __toESM(require_react(), 1);
34793
-
34794
- // src/tui/account.ts
34795
- import { Buffer as Buffer2 } from "buffer";
34796
- function identityFromToken(token) {
34797
- const empty = { uid: null, uuid: null, tenant: null };
34798
- const parts = token.split(".");
34799
- if (parts.length < 2) return empty;
34800
- try {
34801
- const segment = parts[1];
34802
- const padded = segment + "=".repeat((4 - segment.length % 4) % 4);
34803
- const payload = JSON.parse(Buffer2.from(padded, "base64url").toString("utf8"));
34804
- return {
34805
- uid: typeof payload.uid === "number" ? payload.uid : null,
34806
- uuid: typeof payload.uuid === "string" ? payload.uuid : null,
34807
- tenant: typeof payload.tenant === "string" ? payload.tenant : null
34808
- };
34809
- } catch {
34810
- return empty;
34811
- }
34812
- }
34813
- function tokenOverrideNote(source) {
34814
- if (source === "env")
34815
- return "\u6CE8\u610F\uFF1A\u73AF\u5883\u53D8\u91CF AUTODL_TOKEN \u4ECD\u7136\u751F\u6548\uFF0C\u5176\u4F18\u5148\u7EA7\u9AD8\u4E8E\u8FD9\u91CC\u4FDD\u5B58\u7684 Token\u3002";
34816
- if (source === "flag") return "\u6CE8\u610F\uFF1A\u672C\u6B21\u542F\u52A8\u5E26\u4E86 --token \u53C2\u6570\uFF0C\u5176\u4F18\u5148\u7EA7\u9AD8\u4E8E\u8FD9\u91CC\u4FDD\u5B58\u7684 Token\u3002";
34817
- return null;
34818
- }
34804
+ var import_react40 = __toESM(require_react(), 1);
34819
34805
 
34820
34806
  // src/tui/clipboard.ts
34821
- import { Buffer as Buffer3 } from "buffer";
34807
+ import { Buffer as Buffer2 } from "buffer";
34822
34808
  import { spawn } from "child_process";
34823
34809
  import { platform as platform2 } from "os";
34824
34810
  function candidates() {
@@ -34837,22 +34823,22 @@ function candidates() {
34837
34823
  }
34838
34824
  }
34839
34825
  function runCopy(command, args, text) {
34840
- return new Promise((resolve) => {
34826
+ return new Promise((resolve2) => {
34841
34827
  let child;
34842
34828
  try {
34843
34829
  child = spawn(command, args, { stdio: ["pipe", "ignore", "ignore"] });
34844
34830
  } catch {
34845
- resolve(false);
34831
+ resolve2(false);
34846
34832
  return;
34847
34833
  }
34848
- child.on("error", () => resolve(false));
34849
- child.on("close", (code) => resolve(code === 0));
34850
- child.stdin?.on("error", () => resolve(false));
34834
+ child.on("error", () => resolve2(false));
34835
+ child.on("close", (code) => resolve2(code === 0));
34836
+ child.stdin?.on("error", () => resolve2(false));
34851
34837
  child.stdin?.end(text);
34852
34838
  });
34853
34839
  }
34854
34840
  function writeOsc52(text) {
34855
- const payload = Buffer3.from(text, "utf8").toString("base64");
34841
+ const payload = Buffer2.from(text, "utf8").toString("base64");
34856
34842
  const ESC3 = String.fromCharCode(27);
34857
34843
  const BEL3 = String.fromCharCode(7);
34858
34844
  process.stdout.write(`${ESC3}]52;c;${payload}${BEL3}`);
@@ -35430,12 +35416,12 @@ function Table({
35430
35416
  if (rows.length === 0) {
35431
35417
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { dimColor: true, children: emptyMessage }) });
35432
35418
  }
35433
- const visible = maxRows && maxRows > 0 ? Math.min(maxRows, rows.length) : rows.length;
35419
+ const visible2 = maxRows && maxRows > 0 ? Math.min(maxRows, rows.length) : rows.length;
35434
35420
  const start = Math.max(
35435
35421
  0,
35436
- Math.min(selectedIndex - Math.floor((visible - 1) / 2), rows.length - visible)
35422
+ Math.min(selectedIndex - Math.floor((visible2 - 1) / 2), rows.length - visible2)
35437
35423
  );
35438
- const window2 = rows.slice(start, start + visible);
35424
+ const window2 = rows.slice(start, start + visible2);
35439
35425
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { flexDirection: "column", children: [
35440
35426
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { paddingX: 1, children: [
35441
35427
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: CURSOR.blank }),
@@ -35809,19 +35795,1213 @@ function SessionExpired({
35809
35795
  ] });
35810
35796
  }
35811
35797
 
35798
+ // src/tui/screens/files.tsx
35799
+ import { basename, dirname, join, posix, resolve } from "path";
35800
+ var import_react37 = __toESM(require_react(), 1);
35801
+ var import_jsx_runtime11 = __toESM(require_jsx_runtime(), 1);
35802
+ var REMOTE_HOME = "/root/autodl-tmp";
35803
+ function isVisibleCode(code) {
35804
+ if (code < 32 || code === 127) return false;
35805
+ if (code >= 128 && code <= 159) return false;
35806
+ return true;
35807
+ }
35808
+ function visible(input) {
35809
+ let out = "";
35810
+ for (const char of input) {
35811
+ if (isVisibleCode(char.codePointAt(0) ?? 0)) out += char;
35812
+ }
35813
+ return out || "?";
35814
+ }
35815
+ function sanitizeInput(input) {
35816
+ let out = "";
35817
+ for (const char of input) {
35818
+ if (isVisibleCode(char.codePointAt(0) ?? 0)) out += char;
35819
+ }
35820
+ return out;
35821
+ }
35822
+ var joinSide = (side, dir, name) => side === "remote" ? posix.join(dir, name) : join(dir, name);
35823
+ function resolveSide(side, base, value) {
35824
+ return side === "remote" ? posix.resolve(base, value) : resolve(base, value);
35825
+ }
35826
+ var parentSide = (side, p) => side === "remote" ? posix.dirname(p) : dirname(p);
35827
+ var baseSide = (side, p) => side === "remote" ? posix.basename(p) : basename(p);
35828
+ function kindMark(kind) {
35829
+ if (kind === "directory") return "/";
35830
+ if (kind === "symlink") return "@";
35831
+ if (kind === "other") return "?";
35832
+ return "";
35833
+ }
35834
+ var directionText = (direction) => direction === "upload" ? "\u4E0A\u4F20" : "\u4E0B\u8F7D";
35835
+ function jobStateText(state) {
35836
+ switch (state) {
35837
+ case "queued":
35838
+ return "\u6392\u961F";
35839
+ case "running":
35840
+ return "\u4F20\u8F93\u4E2D";
35841
+ case "conflict":
35842
+ return "\u5F85\u786E\u8BA4";
35843
+ case "paused":
35844
+ return "\u5DF2\u6682\u505C";
35845
+ case "completed":
35846
+ return "\u5B8C\u6210";
35847
+ case "cancelled":
35848
+ return "\u5DF2\u53D6\u6D88";
35849
+ }
35850
+ }
35851
+ function useQueueJobs(queue) {
35852
+ const subscribe = (0, import_react37.useCallback)((listener) => queue.subscribe(listener), [queue]);
35853
+ const snapshot = (0, import_react37.useCallback)(() => queue.snapshot(), [queue]);
35854
+ return (0, import_react37.useSyncExternalStore)(subscribe, snapshot);
35855
+ }
35856
+ var emptyPane = (p) => ({
35857
+ path: p,
35858
+ entries: [],
35859
+ loading: true,
35860
+ error: null,
35861
+ cursor: 0,
35862
+ checked: []
35863
+ });
35864
+ function dialogRows(mode, selectionCount) {
35865
+ switch (mode.name) {
35866
+ case "none":
35867
+ return 0;
35868
+ case "pathNav":
35869
+ case "mkdir":
35870
+ case "rename":
35871
+ return 4;
35872
+ case "delete":
35873
+ return 5 + Math.min(selectionCount, 3) + (selectionCount > 3 ? 1 : 0);
35874
+ case "transfer":
35875
+ return 6 + Math.min(selectionCount, 2);
35876
+ case "pathTransfer":
35877
+ return 7;
35878
+ }
35879
+ }
35880
+ function useFlash() {
35881
+ const [notice, setNotice] = (0, import_react37.useState)(null);
35882
+ const timer = (0, import_react37.useRef)(void 0);
35883
+ const mounted = (0, import_react37.useRef)(true);
35884
+ (0, import_react37.useEffect)(() => {
35885
+ mounted.current = true;
35886
+ return () => {
35887
+ mounted.current = false;
35888
+ clearTimeout(timer.current);
35889
+ };
35890
+ }, []);
35891
+ const flash = (0, import_react37.useCallback)((message) => {
35892
+ if (!mounted.current) return;
35893
+ setNotice(message);
35894
+ clearTimeout(timer.current);
35895
+ timer.current = setTimeout(() => {
35896
+ if (mounted.current) setNotice(null);
35897
+ }, 6e3);
35898
+ }, []);
35899
+ return { notice, flash };
35900
+ }
35901
+ function fitLine(text, width) {
35902
+ return clip(visible(text), Math.max(1, width));
35903
+ }
35904
+ function skippedPageStart(index) {
35905
+ return Math.floor(Math.max(0, index) / 3) * 3;
35906
+ }
35907
+ function detailRows(job, show, skippedOpen, skippedIndex) {
35908
+ if (!job || !show) return 0;
35909
+ let n = 2;
35910
+ n += Math.min(job.request.sources.length, 2) + (job.request.sources.length > 2 ? 1 : 0);
35911
+ if (job.progress) n += 2;
35912
+ if (job.state === "conflict" && job.conflict) n += 2;
35913
+ if (job.result) {
35914
+ n += 1;
35915
+ const total = job.result.skipped.length;
35916
+ if (total > 0) {
35917
+ if (!skippedOpen) {
35918
+ n += 1;
35919
+ } else {
35920
+ const clamped = Math.min(Math.max(0, skippedIndex), total - 1);
35921
+ n += Math.min(3, total - skippedPageStart(clamped)) + 2;
35922
+ }
35923
+ }
35924
+ }
35925
+ if (job.error) n += 1;
35926
+ return n;
35927
+ }
35928
+ function renderQueueBody(ui) {
35929
+ const { jobs, cursor, listRows } = ui;
35930
+ const width = Math.max(20, ui.width);
35931
+ const rowWidth2 = Math.max(1, width - 2);
35932
+ const start = Math.max(0, Math.min(cursor, Math.max(0, jobs.length - listRows)));
35933
+ const shown = jobs.slice(start, start + listRows);
35934
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", children: [
35935
+ jobs.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, wrap: "truncate", children: fitLine("\u961F\u5217\u4E3A\u7A7A\u3002\u5728\u6D4F\u89C8\u5668\u6309 u \u5C06\u9009\u4E2D\u6587\u4EF6\u52A0\u5165\u4F20\u8F93\u961F\u5217\u3002", rowWidth2) }) }) : shown.map((job, i) => {
35936
+ const index = start + i;
35937
+ const active = index === cursor;
35938
+ const req = job.request;
35939
+ const label = `${req.direction === "upload" ? "\u2191" : "\u2193"} ${req.sources.length}\u9879 \u2192 ${req.destination}`;
35940
+ const pct = job.progress && job.progress.total > 0 ? job.progress.transferred / job.progress.total * 100 : job.state === "completed" ? 100 : 0;
35941
+ const speed = job.state === "running" && job.progress ? ` ${formatBytes(job.progress.bytesPerSecond)}/s` : "";
35942
+ const row = `${active ? "\u203A" : " "} ${label} ${jobStateText(job.state)} ${Math.round(pct)}%${speed}${job.state === "conflict" ? " \u5F85\u786E\u8BA4" : ""}${job.error ? " \u2716" : ""}`;
35943
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: active ? "green" : void 0, inverse: active, wrap: "truncate", children: fitLine(row, rowWidth2) }) }, job.request.id);
35944
+ }),
35945
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(QueueDetail, { ui })
35946
+ ] });
35947
+ }
35948
+ function SkippedBrowser({
35949
+ ui,
35950
+ job,
35951
+ width
35952
+ }) {
35953
+ const skipped = job.result?.skipped;
35954
+ if (!skipped || skipped.length === 0) return null;
35955
+ if (!ui.skippedOpen) {
35956
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, wrap: "truncate", children: fitLine(`\u8DF3\u8FC7 ${skipped.length} \u9879 \xB7 v \u67E5\u770B\u5168\u90E8`, width) });
35957
+ }
35958
+ const total = skipped.length;
35959
+ const clamped = Math.min(ui.skippedIndex, Math.max(0, total - 1));
35960
+ const start = skippedPageStart(clamped);
35961
+ const shown = skipped.slice(start, start + 3);
35962
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
35963
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { bold: true, wrap: "truncate", children: fitLine(`\u8DF3\u8FC7 ${total} \u9879\uFF08${clamped + 1}/${total}\uFF09\xB7 \u2191\u2193 \u9009\u62E9 \xB7 Esc \u6536\u8D77`, width) }),
35964
+ shown.map((entry, i) => {
35965
+ const absolute = start + i;
35966
+ const active = absolute === clamped;
35967
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: !active, inverse: active, wrap: "truncate", children: fitLine(`${active ? "\u203A" : "\xB7"} ${entry}`, width) }, `${absolute}`);
35968
+ }),
35969
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, wrap: "truncate", children: fitLine(
35970
+ total > 3 ? `\u7B2C ${Math.floor(start / 3) + 1}/${Math.ceil(total / 3)} \u9875 \xB7 Esc \u6536\u8D77\u8DF3\u8FC7\u5217\u8868` : "Esc \u6536\u8D77\u8DF3\u8FC7\u5217\u8868",
35971
+ width
35972
+ ) })
35973
+ ] });
35974
+ }
35975
+ function QueueDetail({ ui }) {
35976
+ const job = ui.jobs[Math.min(ui.cursor, Math.max(0, ui.jobs.length - 1))];
35977
+ if (!job || !ui.showDetail) return null;
35978
+ const req = job.request;
35979
+ const width = Math.max(20, ui.width - 4);
35980
+ const lines = [
35981
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { bold: true, wrap: "truncate", children: fitLine(
35982
+ `${directionText(req.direction)} \xB7 ${req.sources.length} \u4E2A\u6765\u6E90${req.sync ? " \xB7 \u540C\u6B65" : ""}${req.checksum ? " \xB7 \u54C8\u5E0C\u6821\u9A8C" : ""}`,
35983
+ width
35984
+ ) }, "h"),
35985
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, wrap: "truncate", children: fitLine(`\u2192 ${req.destination}`, width) }, "d")
35986
+ ];
35987
+ for (const [i, source] of req.sources.slice(0, 2).entries()) {
35988
+ lines.push(
35989
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, wrap: "truncate", children: fitLine(`\xB7 ${source}`, width) }, `s-${i}`)
35990
+ );
35991
+ }
35992
+ if (req.sources.length > 2) {
35993
+ lines.push(
35994
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, wrap: "truncate", children: fitLine(`\xB7 \u2026\u53E6 ${req.sources.length - 2} \u4E2A`, width) }, "more")
35995
+ );
35996
+ }
35997
+ if (job.progress) {
35998
+ const p = job.progress;
35999
+ lines.push(
36000
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { wrap: "truncate", children: fitLine(
36001
+ `${p.file} ${formatBytes(p.transferred)}/${formatBytes(p.total)} \xB7 ${p.filesDone}/${p.filesTotal} \u6587\u4EF6 \xB7 ${formatBytes(p.bytesPerSecond)}/s`,
36002
+ width
36003
+ ) }, "p")
36004
+ );
36005
+ lines.push(
36006
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { wrap: "truncate", children: fitLine(
36007
+ bar(p.total > 0 ? p.transferred / p.total * 100 : 0, Math.min(30, width)),
36008
+ width
36009
+ ) }, "b")
36010
+ );
36011
+ }
36012
+ if (job.state === "conflict" && job.conflict) {
36013
+ lines.push(
36014
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: "yellow", wrap: "truncate", children: fitLine(
36015
+ `\u51B2\u7A81\uFF1A${job.conflict.source} \u2192 ${job.conflict.destination} (${formatBytes(job.conflict.sourceSize)}/${formatBytes(job.conflict.destinationSize)})`,
36016
+ width
36017
+ ) }, "c")
36018
+ );
36019
+ lines.push(
36020
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: "yellow", wrap: "truncate", children: fitLine(
36021
+ `o \u8986\u76D6 \xB7 s \u8DF3\u8FC7 \xB7 b \u4FDD\u7559\u4E24\u8005 \xB7 a \u5E94\u7528\u4E8E\u5168\u90E8[${ui.applyAll ? "\u2713" : " "}] \xB7 \u76EE\u6807\u4E3A\u9009\u4E2D\u4EFB\u52A1`,
36022
+ width
36023
+ ) }, "ck")
36024
+ );
36025
+ }
36026
+ if (job.result) {
36027
+ lines.push(
36028
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: "green", wrap: "truncate", children: fitLine(
36029
+ `\u5B8C\u6210\uFF1A${job.result.files} \u6587\u4EF6 \xB7 ${formatBytes(job.result.bytes)}${job.result.skipped.length > 0 ? ` \xB7 \u8DF3\u8FC7 ${job.result.skipped.length} \u9879` : ""}`,
36030
+ width
36031
+ ) }, "r")
36032
+ );
36033
+ lines.push(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(SkippedBrowser, { ui, job, width }, "skipped"));
36034
+ }
36035
+ if (job.error) {
36036
+ lines.push(
36037
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: "red", wrap: "truncate", children: fitLine(`\u2716 ${job.error}`, width) }, "e")
36038
+ );
36039
+ }
36040
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { flexDirection: "column", paddingX: 1, children: lines });
36041
+ }
36042
+ function handleQueueInput(input, key, ui, queue, onExit) {
36043
+ const job = ui.jobs[Math.min(ui.cursor, Math.max(0, ui.jobs.length - 1))];
36044
+ const skippedTotal = ui.showDetail && !ui.skippedOpen ? job?.result?.skipped.length ?? 0 : 0;
36045
+ if (ui.showDetail && ui.skippedOpen && job?.result) {
36046
+ const total = job.result.skipped.length;
36047
+ if (key.upArrow || input === "k") {
36048
+ ui.setSkippedIndex((v) => Math.max(0, v - 1));
36049
+ return true;
36050
+ }
36051
+ if (key.downArrow || input === "j") {
36052
+ ui.setSkippedIndex((v) => Math.max(0, Math.min(total - 1, v + 1)));
36053
+ return true;
36054
+ }
36055
+ if (input === "q" || key.escape || key.return || input === "v") {
36056
+ ui.setSkippedOpen(() => false);
36057
+ return true;
36058
+ }
36059
+ return false;
36060
+ }
36061
+ if (key.upArrow || input === "k") {
36062
+ ui.setCursor((v) => Math.max(0, v - 1));
36063
+ return true;
36064
+ }
36065
+ if (key.downArrow || input === "j") {
36066
+ ui.setCursor((v) => Math.max(0, Math.min(ui.jobs.length - 1, v + 1)));
36067
+ return true;
36068
+ }
36069
+ if (key.return) {
36070
+ if (ui.showDetail) {
36071
+ ui.setShowDetail(() => false);
36072
+ ui.setSkippedOpen(() => false);
36073
+ } else {
36074
+ ui.setShowDetail(() => true);
36075
+ }
36076
+ return true;
36077
+ }
36078
+ if (input === "v" && ui.showDetail && skippedTotal > 0) {
36079
+ ui.setSkippedIndex(() => 0);
36080
+ ui.setSkippedOpen(() => true);
36081
+ return true;
36082
+ }
36083
+ if (input === "q" || key.escape) {
36084
+ if (ui.showDetail) {
36085
+ ui.setShowDetail(() => false);
36086
+ ui.setSkippedOpen(() => false);
36087
+ } else onExit();
36088
+ return true;
36089
+ }
36090
+ if (input === "r") {
36091
+ if (!job) {
36092
+ ui.flash("\u961F\u5217\u4E3A\u7A7A");
36093
+ return true;
36094
+ }
36095
+ if (job.state === "paused" || job.state === "cancelled") ui.resume(job.request.id);
36096
+ else ui.flash("\u53EA\u6709\u5DF2\u6682\u505C/\u5DF2\u53D6\u6D88\u7684\u4EFB\u52A1\u53EF\u4EE5\u6062\u590D");
36097
+ return true;
36098
+ }
36099
+ if (input === "c") {
36100
+ if (!job) {
36101
+ ui.flash("\u961F\u5217\u4E3A\u7A7A");
36102
+ return true;
36103
+ }
36104
+ if (job.state === "queued" || job.state === "running" || job.state === "paused" || job.state === "conflict") {
36105
+ queue.cancel(job.request.id);
36106
+ } else ui.flash("\u5DF2\u5B8C\u6210/\u5DF2\u53D6\u6D88\u7684\u4EFB\u52A1\u65E0\u9700\u53D6\u6D88");
36107
+ return true;
36108
+ }
36109
+ if (input === "a" && job && job.state === "conflict") {
36110
+ ui.setApplyAll((v) => !v);
36111
+ return true;
36112
+ }
36113
+ if ((input === "o" || input === "s" || input === "b") && job && job.state === "conflict") {
36114
+ queue.resolveConflict(job.request.id, {
36115
+ choice: input === "o" ? "overwrite" : input === "s" ? "skip" : "keep-both",
36116
+ applyToAll: ui.applyAll
36117
+ });
36118
+ return true;
36119
+ }
36120
+ return false;
36121
+ }
36122
+ function queueHints(job, skippedOpen) {
36123
+ const base = "\u2191\u2193/jk \u9009\u62E9 \xB7 Enter \u8BE6\u60C5 \xB7 r \u6062\u590D\u6682\u505C/\u53D6\u6D88 \xB7 c \u53D6\u6D88\u6392\u961F/\u4F20\u8F93";
36124
+ const skipped = job?.result && job.result.skipped.length > 0 ? " \xB7 \u8BE6\u60C5\u4E2D\u6309 v \u67E5\u770B\u8DF3\u8FC7" : "";
36125
+ if (skippedOpen) {
36126
+ return [base, "\u8DF3\u8FC7\u5217\u8868\uFF1A\u2191\u2193 \u9009\u62E9 \xB7 Enter/Esc/q/v \u6536\u8D77"];
36127
+ }
36128
+ const second = job?.state === "conflict" ? "o \u8986\u76D6 \xB7 s \u8DF3\u8FC7 \xB7 b \u4FDD\u7559\u4E24\u8005 \xB7 a \u5E94\u7528\u4E8E\u5168\u90E8 \xB7 Esc/q \u6536\u8D77\u8BE6\u60C5\u6216\u8FD4\u56DE" : "Esc/q \u6536\u8D77\u8BE6\u60C5\u6216\u8FD4\u56DE \xB7 \u8DF3\u8FC7\u5217\u8868\u4E2D \u2191\u2193 \u7FFB\u770B Esc \u6536\u8D77";
36129
+ return [`${base}${skipped}`, second];
36130
+ }
36131
+ function TransferJobs({
36132
+ queue,
36133
+ width,
36134
+ height,
36135
+ onBack,
36136
+ onResume
36137
+ }) {
36138
+ const jobs = useQueueJobs(queue);
36139
+ const [cursor, setCursor] = (0, import_react37.useState)(0);
36140
+ const [showDetail, setShowDetail] = (0, import_react37.useState)(false);
36141
+ const [skippedOpen, setSkippedOpen] = (0, import_react37.useState)(false);
36142
+ const [skippedIndex, setSkippedIndex] = (0, import_react37.useState)(0);
36143
+ const [applyAll, setApplyAll] = (0, import_react37.useState)(false);
36144
+ const { notice, flash } = useFlash();
36145
+ const resume = (0, import_react37.useCallback)(
36146
+ (id) => onResume ? onResume(id) : queue.resume(id),
36147
+ [onResume, queue]
36148
+ );
36149
+ const clamped = Math.min(cursor, Math.max(0, jobs.length - 1));
36150
+ const selected = jobs[clamped];
36151
+ const selectedId = selected?.request.id;
36152
+ const previousSelectedId = (0, import_react37.useRef)(selectedId);
36153
+ (0, import_react37.useEffect)(() => {
36154
+ if (previousSelectedId.current === selectedId) return;
36155
+ previousSelectedId.current = selectedId;
36156
+ setSkippedOpen(false);
36157
+ setSkippedIndex(0);
36158
+ }, [selectedId]);
36159
+ const listRows = Math.max(
36160
+ 1,
36161
+ height - 3 - detailRows(selected, showDetail, skippedOpen, skippedIndex) - (notice ? 1 : 0)
36162
+ );
36163
+ const ui = {
36164
+ jobs,
36165
+ cursor: clamped,
36166
+ setCursor,
36167
+ showDetail,
36168
+ setShowDetail,
36169
+ skippedOpen,
36170
+ setSkippedOpen,
36171
+ skippedIndex,
36172
+ setSkippedIndex,
36173
+ applyAll,
36174
+ setApplyAll,
36175
+ resume,
36176
+ flash,
36177
+ width,
36178
+ listRows
36179
+ };
36180
+ use_input_default((input, key) => {
36181
+ if (key.ctrl) return;
36182
+ handleQueueInput(input, key, ui, queue, onBack);
36183
+ });
36184
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", width, height, overflow: "hidden", children: [
36185
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { paddingX: 1, children: [
36186
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { bold: true, children: "\u4F20\u8F93\u961F\u5217" }),
36187
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
36188
+ jobs.length,
36189
+ " \u4E2A\u4EFB\u52A1\uFF08\u4E32\u884C\uFF09",
36190
+ jobs.length > 0 ? ` \xB7 ${clamped + 1}/${jobs.length}` : ""
36191
+ ] })
36192
+ ] }),
36193
+ renderQueueBody(ui),
36194
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { flexGrow: 1 }),
36195
+ notice ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: "cyan", children: clip(visible(notice), Math.max(10, width - 2)) }) }) : null,
36196
+ queueHints(selected, skippedOpen).map((hint) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, wrap: "truncate", children: fitLine(hint, Math.max(10, width - 2)) }) }, hint))
36197
+ ] });
36198
+ }
36199
+ function FilesScreen({
36200
+ workspace,
36201
+ queue,
36202
+ uuid,
36203
+ width,
36204
+ height,
36205
+ onBack,
36206
+ onResume,
36207
+ whenRunning
36208
+ }) {
36209
+ const [tab2, setTab] = (0, import_react37.useState)("browser");
36210
+ const [panes, setPanes] = (0, import_react37.useState)(() => ({
36211
+ local: emptyPane(process.cwd()),
36212
+ remote: emptyPane(REMOTE_HOME)
36213
+ }));
36214
+ const [activeSide, setActiveSide] = (0, import_react37.useState)("local");
36215
+ const [mode, setMode] = (0, import_react37.useState)({ name: "none" });
36216
+ const [field, setField] = (0, import_react37.useState)("");
36217
+ const [confirmYes, setConfirmYes] = (0, import_react37.useState)(false);
36218
+ const [syncDefault, setSyncDefault] = (0, import_react37.useState)(false);
36219
+ const [checksumDefault, setChecksumDefault] = (0, import_react37.useState)(false);
36220
+ const [pending, setPending] = (0, import_react37.useState)(null);
36221
+ const [pathForm, setPathForm] = (0, import_react37.useState)({
36222
+ direction: "upload",
36223
+ source: "",
36224
+ dest: "",
36225
+ sync: false,
36226
+ checksum: false
36227
+ });
36228
+ const [busy, setBusy] = (0, import_react37.useState)(false);
36229
+ const { notice, flash } = useFlash();
36230
+ const jobs = useQueueJobs(queue);
36231
+ const [queueCursor, setQueueCursor] = (0, import_react37.useState)(0);
36232
+ const [queueDetail, setQueueDetail] = (0, import_react37.useState)(false);
36233
+ const [skippedOpen, setSkippedOpen] = (0, import_react37.useState)(false);
36234
+ const [skippedIndex, setSkippedIndex] = (0, import_react37.useState)(0);
36235
+ const [applyAll, setApplyAll] = (0, import_react37.useState)(false);
36236
+ const resume = (0, import_react37.useCallback)(
36237
+ (id) => onResume ? onResume(id) : queue.resume(id),
36238
+ [onResume, queue]
36239
+ );
36240
+ const mounted = (0, import_react37.useRef)(true);
36241
+ (0, import_react37.useEffect)(
36242
+ () => () => {
36243
+ mounted.current = false;
36244
+ workspace.dispose();
36245
+ },
36246
+ [workspace]
36247
+ );
36248
+ const gen = (0, import_react37.useRef)({ local: 0, remote: 0 });
36249
+ const queueJobId = jobs[Math.min(queueCursor, Math.max(0, jobs.length - 1))]?.request.id;
36250
+ const previousQueueJobId = (0, import_react37.useRef)(queueJobId);
36251
+ (0, import_react37.useEffect)(() => {
36252
+ if (previousQueueJobId.current === queueJobId) return;
36253
+ previousQueueJobId.current = queueJobId;
36254
+ setSkippedOpen(false);
36255
+ setSkippedIndex(0);
36256
+ }, [queueJobId]);
36257
+ const pathRef = (0, import_react37.useRef)({
36258
+ local: process.cwd(),
36259
+ remote: REMOTE_HOME
36260
+ });
36261
+ (0, import_react37.useEffect)(() => {
36262
+ pathRef.current = { local: panes.local.path, remote: panes.remote.path };
36263
+ });
36264
+ const loadPane = (0, import_react37.useCallback)(
36265
+ (side, nextPath) => {
36266
+ gen.current[side] += 1;
36267
+ const g = gen.current[side];
36268
+ if (side === "local") {
36269
+ setPanes((prev) => ({ ...prev, local: { ...emptyPane(nextPath), cursor: 0 } }));
36270
+ } else {
36271
+ setPanes((prev) => ({ ...prev, remote: { ...emptyPane(nextPath), cursor: 0 } }));
36272
+ }
36273
+ const run = () => {
36274
+ if (!mounted.current || gen.current[side] !== g) return;
36275
+ workspace.list(side, nextPath).then((entries) => {
36276
+ if (!mounted.current || gen.current[side] !== g) return;
36277
+ const sorted = [...entries].sort((a, b) => {
36278
+ if (a.kind === "directory" && b.kind !== "directory") return -1;
36279
+ if (a.kind !== "directory" && b.kind === "directory") return 1;
36280
+ return a.name.localeCompare(b.name);
36281
+ });
36282
+ setPanes(
36283
+ (prev) => side === "local" ? { ...prev, local: { ...prev.local, entries: sorted, loading: false } } : { ...prev, remote: { ...prev.remote, entries: sorted, loading: false } }
36284
+ );
36285
+ }).catch((err) => {
36286
+ if (!mounted.current || gen.current[side] !== g) return;
36287
+ const message = err.message;
36288
+ setPanes(
36289
+ (prev) => side === "local" ? { ...prev, local: { ...prev.local, entries: [], loading: false, error: message } } : {
36290
+ ...prev,
36291
+ remote: { ...prev.remote, entries: [], loading: false, error: message }
36292
+ }
36293
+ );
36294
+ });
36295
+ };
36296
+ if (side === "remote" && whenRunning) {
36297
+ void whenRunning.then(
36298
+ () => run(),
36299
+ (err) => {
36300
+ if (!mounted.current || gen.current[side] !== g) return;
36301
+ const message = err.message;
36302
+ setPanes((prev) => ({
36303
+ ...prev,
36304
+ remote: { ...prev.remote, entries: [], loading: false, error: message }
36305
+ }));
36306
+ }
36307
+ );
36308
+ return;
36309
+ }
36310
+ run();
36311
+ },
36312
+ [workspace, whenRunning]
36313
+ );
36314
+ (0, import_react37.useEffect)(() => {
36315
+ loadPane("local", process.cwd());
36316
+ loadPane("remote", REMOTE_HOME);
36317
+ }, [loadPane]);
36318
+ const completedJobs = (0, import_react37.useRef)(
36319
+ new Set(jobs.filter((job) => job.state === "completed").map((job) => job.request.id))
36320
+ );
36321
+ (0, import_react37.useEffect)(() => {
36322
+ for (const job of jobs) {
36323
+ if (job.state !== "completed" || completedJobs.current.has(job.request.id)) continue;
36324
+ completedJobs.current.add(job.request.id);
36325
+ if (job.request.uuid !== uuid) continue;
36326
+ const side = job.request.direction === "upload" ? "remote" : "local";
36327
+ loadPane(side, pathRef.current[side]);
36328
+ }
36329
+ }, [jobs, loadPane, uuid]);
36330
+ const pane = panes[activeSide];
36331
+ const otherSide = activeSide === "local" ? "remote" : "local";
36332
+ const cursorIndex = Math.min(pane.cursor, Math.max(0, pane.entries.length - 1));
36333
+ const cursorEntry = pane.entries[cursorIndex];
36334
+ const selection = (0, import_react37.useMemo)(() => {
36335
+ if (pane.checked.length > 0) return pane.entries.filter((e) => pane.checked.includes(e.path));
36336
+ return cursorEntry ? [cursorEntry] : [];
36337
+ }, [pane.checked, pane.entries, cursorEntry]);
36338
+ const setPaneCursor = (0, import_react37.useCallback)((side, fn) => {
36339
+ setPanes((prev) => {
36340
+ const current = prev[side];
36341
+ const next = fn(current.cursor);
36342
+ return side === "local" ? { ...prev, local: { ...current, cursor: next } } : { ...prev, remote: { ...current, cursor: next } };
36343
+ });
36344
+ }, []);
36345
+ const runOp = (0, import_react37.useCallback)(
36346
+ async (label, fn, side) => {
36347
+ setBusy(true);
36348
+ try {
36349
+ await fn();
36350
+ if (!mounted.current) return;
36351
+ flash(`\u2714 ${label}`);
36352
+ loadPane(side, pathRef.current[side]);
36353
+ } catch (err) {
36354
+ if (!mounted.current) return;
36355
+ flash(`\u2716 ${label}\u5931\u8D25\uFF1A${err.message}`);
36356
+ } finally {
36357
+ if (mounted.current) setBusy(false);
36358
+ }
36359
+ },
36360
+ [flash, loadPane]
36361
+ );
36362
+ const doDelete = (0, import_react37.useCallback)(() => {
36363
+ const targets = selection.map((e) => e.path);
36364
+ const side = activeSide;
36365
+ const count = targets.length;
36366
+ setMode({ name: "none" });
36367
+ setConfirmYes(false);
36368
+ void runOp(
36369
+ `\u5DF2\u5220\u9664 ${count} \u9879`,
36370
+ async () => {
36371
+ const results = await Promise.allSettled(targets.map((t) => workspace.remove(side, t)));
36372
+ const failed = results.filter((r) => r.status === "rejected");
36373
+ if (failed.length > 0) throw new Error(`${failed.length}/${count} \u9879\u5220\u9664\u5931\u8D25`);
36374
+ },
36375
+ side
36376
+ );
36377
+ }, [selection, activeSide, runOp, workspace]);
36378
+ const openTransferConfirm = (0, import_react37.useCallback)(() => {
36379
+ if (selection.length === 0) {
36380
+ flash("\u6CA1\u6709\u53EF\u4F20\u8F93\u7684\u9009\u4E2D\u9879");
36381
+ return;
36382
+ }
36383
+ setPending({
36384
+ sources: selection.map((e) => e.path),
36385
+ kinds: selection.map((e) => e.kind),
36386
+ direction: activeSide === "local" ? "upload" : "download",
36387
+ destDir: panes[otherSide].path
36388
+ });
36389
+ setMode({ name: "transfer" });
36390
+ }, [selection, activeSide, panes, otherSide, flash]);
36391
+ const submitTransfer = (0, import_react37.useCallback)(
36392
+ (sync, checksum) => {
36393
+ if (!pending) return;
36394
+ queue.enqueue({
36395
+ uuid,
36396
+ direction: pending.direction,
36397
+ sources: pending.sources,
36398
+ destination: pending.destDir,
36399
+ sync,
36400
+ checksum
36401
+ });
36402
+ flash(`\u2714 \u5DF2\u52A0\u5165\u961F\u5217\uFF1A${pending.sources.length} \u9879\uFF0C\u6309 Q \u67E5\u770B`);
36403
+ setPending(null);
36404
+ setMode({ name: "none" });
36405
+ },
36406
+ [pending, queue, uuid, flash]
36407
+ );
36408
+ const submitPathForm = (0, import_react37.useCallback)(() => {
36409
+ const rawSource = pathForm.source.trim();
36410
+ const rawDest = pathForm.dest.trim();
36411
+ if (!rawSource || !rawDest) {
36412
+ flash("\u6765\u6E90\u548C\u76EE\u6807\u8DEF\u5F84\u90FD\u4E0D\u80FD\u4E3A\u7A7A");
36413
+ return;
36414
+ }
36415
+ const direction = pathForm.direction;
36416
+ const fromSide = direction === "upload" ? "local" : "remote";
36417
+ const toSide = direction === "upload" ? "remote" : "local";
36418
+ const source = resolveSide(fromSide, panes[fromSide].path, rawSource);
36419
+ const dest = resolveSide(toSide, panes[toSide].path, rawDest);
36420
+ queue.enqueue({
36421
+ uuid,
36422
+ direction,
36423
+ sources: [source],
36424
+ destination: dest,
36425
+ sync: pathForm.sync,
36426
+ checksum: pathForm.checksum
36427
+ });
36428
+ flash("\u2714 \u5DF2\u52A0\u5165\u961F\u5217\uFF0C\u6309 Q \u67E5\u770B");
36429
+ setMode({ name: "none" });
36430
+ }, [pathForm, panes, queue, uuid, flash]);
36431
+ const updatePaneChecked = (0, import_react37.useCallback)((side, fn) => {
36432
+ setPanes((prev) => {
36433
+ const current = prev[side];
36434
+ const next = fn(current.checked);
36435
+ return side === "local" ? { ...prev, local: { ...current, checked: next } } : { ...prev, remote: { ...current, checked: next } };
36436
+ });
36437
+ }, []);
36438
+ use_input_default((input, key) => {
36439
+ if (key.ctrl) return;
36440
+ if (busy) {
36441
+ if (key.escape || input === "q") {
36442
+ if (tab2 === "queue") {
36443
+ if (queueDetail) {
36444
+ setQueueDetail(false);
36445
+ setSkippedOpen(false);
36446
+ } else setTab("browser");
36447
+ } else if (mode.name !== "none") {
36448
+ setMode({ name: "none" });
36449
+ setPending(null);
36450
+ setConfirmYes(false);
36451
+ } else onBack();
36452
+ }
36453
+ return;
36454
+ }
36455
+ if (tab2 === "queue") {
36456
+ const ui = {
36457
+ jobs,
36458
+ cursor: Math.min(queueCursor, Math.max(0, jobs.length - 1)),
36459
+ setCursor: setQueueCursor,
36460
+ showDetail: queueDetail,
36461
+ setShowDetail: setQueueDetail,
36462
+ skippedOpen,
36463
+ setSkippedOpen,
36464
+ skippedIndex,
36465
+ setSkippedIndex,
36466
+ applyAll,
36467
+ setApplyAll,
36468
+ resume,
36469
+ flash,
36470
+ width,
36471
+ listRows: 1
36472
+ };
36473
+ if (handleQueueInput(input, key, ui, queue, () => setTab("browser"))) return;
36474
+ if (input === "Q") {
36475
+ setTab("browser");
36476
+ return;
36477
+ }
36478
+ return;
36479
+ }
36480
+ if (mode.name === "pathNav" || mode.name === "mkdir" || mode.name === "rename") {
36481
+ if (key.escape) {
36482
+ setMode({ name: "none" });
36483
+ return;
36484
+ }
36485
+ if (key.return) {
36486
+ const value = field.trim();
36487
+ if (mode.name === "pathNav") {
36488
+ if (value) loadPane(activeSide, resolveSide(activeSide, pane.path, value));
36489
+ setMode({ name: "none" });
36490
+ } else if (mode.name === "mkdir") {
36491
+ if (!value) {
36492
+ flash("\u76EE\u5F55\u540D\u4E0D\u80FD\u4E3A\u7A7A");
36493
+ return;
36494
+ }
36495
+ const target = resolveSide(activeSide, pane.path, value);
36496
+ setMode({ name: "none" });
36497
+ void runOp("\u5DF2\u521B\u5EFA\u76EE\u5F55", () => workspace.mkdir(activeSide, target), activeSide);
36498
+ } else if (mode.name === "rename") {
36499
+ if (!value) {
36500
+ flash("\u76EE\u6807\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A");
36501
+ return;
36502
+ }
36503
+ const targets = selection;
36504
+ setMode({ name: "none" });
36505
+ if (mode.multi) {
36506
+ const destDir = resolveSide(activeSide, pane.path, value);
36507
+ void runOp(
36508
+ `\u5DF2\u79FB\u52A8 ${targets.length} \u9879`,
36509
+ async () => {
36510
+ const failures = [];
36511
+ for (const t of targets) {
36512
+ try {
36513
+ await workspace.rename(
36514
+ activeSide,
36515
+ t.path,
36516
+ joinSide(activeSide, destDir, baseSide(activeSide, t.path))
36517
+ );
36518
+ } catch {
36519
+ failures.push(t.path);
36520
+ }
36521
+ }
36522
+ if (failures.length > 0) {
36523
+ throw new Error(
36524
+ `${failures.length}/${targets.length} \u9879\u79FB\u52A8\u5931\u8D25\uFF1A${failures[0]}`
36525
+ );
36526
+ }
36527
+ },
36528
+ activeSide
36529
+ );
36530
+ } else {
36531
+ const first = targets[0];
36532
+ if (targets.length === 1 && first) {
36533
+ const from = first.path;
36534
+ void runOp(
36535
+ "\u5DF2\u6539\u540D/\u79FB\u52A8",
36536
+ () => workspace.rename(activeSide, from, resolveSide(activeSide, pane.path, value)),
36537
+ activeSide
36538
+ );
36539
+ }
36540
+ }
36541
+ }
36542
+ return;
36543
+ }
36544
+ if (key.backspace || key.delete) {
36545
+ setField((v) => v.slice(0, -1));
36546
+ return;
36547
+ }
36548
+ if (key.tab) return;
36549
+ const clean = sanitizeInput(input);
36550
+ if (clean) setField((v) => v + clean);
36551
+ return;
36552
+ }
36553
+ if (mode.name === "pathTransfer") {
36554
+ if (key.escape) {
36555
+ setMode({ name: "none" });
36556
+ return;
36557
+ }
36558
+ if (key.tab || key.upArrow || key.downArrow) {
36559
+ const delta = key.upArrow ? -1 : 1;
36560
+ setMode({ name: "pathTransfer", field: (mode.field + delta + 3) % 3 });
36561
+ return;
36562
+ }
36563
+ if (mode.field === 0) {
36564
+ if (key.return) {
36565
+ submitPathForm();
36566
+ return;
36567
+ }
36568
+ if (key.leftArrow || key.rightArrow) {
36569
+ setPathForm((v) => ({
36570
+ ...v,
36571
+ direction: v.direction === "upload" ? "download" : "upload"
36572
+ }));
36573
+ return;
36574
+ }
36575
+ if (input === "u" || input === "d") {
36576
+ setPathForm((v) => ({ ...v, direction: input === "u" ? "upload" : "download" }));
36577
+ return;
36578
+ }
36579
+ if (input === "s") {
36580
+ setPathForm((v) => ({ ...v, sync: !v.sync }));
36581
+ return;
36582
+ }
36583
+ if (input === "c") {
36584
+ setPathForm((v) => ({ ...v, checksum: !v.checksum }));
36585
+ return;
36586
+ }
36587
+ return;
36588
+ }
36589
+ if (key.return) {
36590
+ submitPathForm();
36591
+ return;
36592
+ }
36593
+ if (key.backspace || key.delete) {
36594
+ const f = mode.field;
36595
+ setPathForm(
36596
+ (v) => f === 1 ? { ...v, source: v.source.slice(0, -1) } : { ...v, dest: v.dest.slice(0, -1) }
36597
+ );
36598
+ return;
36599
+ }
36600
+ if (key.leftArrow || key.rightArrow) return;
36601
+ const clean = sanitizeInput(input);
36602
+ if (clean) {
36603
+ const f = mode.field;
36604
+ if (f === 1) setPathForm((v) => ({ ...v, source: v.source + clean }));
36605
+ else setPathForm((v) => ({ ...v, dest: v.dest + clean }));
36606
+ }
36607
+ return;
36608
+ }
36609
+ if (mode.name === "delete") {
36610
+ if (key.leftArrow || key.rightArrow || key.tab || input === "h" || input === "l") {
36611
+ setConfirmYes((v) => !v);
36612
+ return;
36613
+ }
36614
+ if (key.return) {
36615
+ if (!confirmYes) {
36616
+ setMode({ name: "none" });
36617
+ return;
36618
+ }
36619
+ doDelete();
36620
+ return;
36621
+ }
36622
+ if (key.escape || input === "q" || input === "n") {
36623
+ setMode({ name: "none" });
36624
+ setConfirmYes(false);
36625
+ return;
36626
+ }
36627
+ if (input === "y") {
36628
+ doDelete();
36629
+ return;
36630
+ }
36631
+ return;
36632
+ }
36633
+ if (mode.name === "transfer") {
36634
+ if (key.escape || input === "q") {
36635
+ setMode({ name: "none" });
36636
+ setPending(null);
36637
+ return;
36638
+ }
36639
+ if (key.return || input === "y") {
36640
+ submitTransfer(syncDefault, checksumDefault);
36641
+ return;
36642
+ }
36643
+ if (input === "s") {
36644
+ setSyncDefault((v) => !v);
36645
+ return;
36646
+ }
36647
+ if (input === "c") {
36648
+ setChecksumDefault((v) => !v);
36649
+ return;
36650
+ }
36651
+ return;
36652
+ }
36653
+ if (key.tab) {
36654
+ setActiveSide((v) => v === "local" ? "remote" : "local");
36655
+ return;
36656
+ }
36657
+ if (key.upArrow || input === "k") {
36658
+ setPaneCursor(activeSide, (v) => Math.max(0, v - 1));
36659
+ return;
36660
+ }
36661
+ if (key.downArrow || input === "j") {
36662
+ setPaneCursor(activeSide, (v) => Math.max(0, Math.min(pane.entries.length - 1, v + 1)));
36663
+ return;
36664
+ }
36665
+ if (key.return || key.rightArrow || input === "l") {
36666
+ if (cursorEntry?.kind === "directory") loadPane(activeSide, cursorEntry.path);
36667
+ else if (cursorEntry) flash("\u6587\u4EF6\u4E0D\u80FD\u8FDB\u5165\uFF1Au \u4F20\u8F93 \xB7 r \u6539\u540D \xB7 x \u5220\u9664");
36668
+ return;
36669
+ }
36670
+ if (key.leftArrow || key.backspace || key.delete || input === "h") {
36671
+ const parent = parentSide(activeSide, pane.path);
36672
+ if (parent !== pane.path) loadPane(activeSide, parent);
36673
+ return;
36674
+ }
36675
+ if (input === " ") {
36676
+ if (!cursorEntry) return;
36677
+ const p = cursorEntry.path;
36678
+ updatePaneChecked(
36679
+ activeSide,
36680
+ (checked) => checked.includes(p) ? checked.filter((x) => x !== p) : [...checked, p]
36681
+ );
36682
+ setPaneCursor(activeSide, (v) => Math.min(pane.entries.length - 1, v + 1));
36683
+ return;
36684
+ }
36685
+ if (key.escape || input === "q" || input === "Q") {
36686
+ if (input === "Q") {
36687
+ setTab("queue");
36688
+ return;
36689
+ }
36690
+ onBack();
36691
+ return;
36692
+ }
36693
+ if (input === "a") {
36694
+ updatePaneChecked(
36695
+ activeSide,
36696
+ (checked) => checked.length === pane.entries.length && pane.entries.length > 0 ? [] : pane.entries.map((e) => e.path)
36697
+ );
36698
+ return;
36699
+ }
36700
+ if (input === "r" && cursorEntry) {
36701
+ const multi = selection.length > 1;
36702
+ setField(multi ? pane.path : cursorEntry.path);
36703
+ setMode({ name: "rename", multi });
36704
+ return;
36705
+ }
36706
+ if (input === "g") {
36707
+ setField(pane.path);
36708
+ setMode({ name: "pathNav" });
36709
+ return;
36710
+ }
36711
+ if (input === "m") {
36712
+ setField("");
36713
+ setMode({ name: "mkdir" });
36714
+ return;
36715
+ }
36716
+ if (input === "x") {
36717
+ if (selection.length === 0) {
36718
+ flash("\u6CA1\u6709\u53EF\u5220\u9664\u7684\u9009\u4E2D\u9879");
36719
+ return;
36720
+ }
36721
+ setConfirmYes(false);
36722
+ setMode({ name: "delete" });
36723
+ return;
36724
+ }
36725
+ if (input === "u") {
36726
+ openTransferConfirm();
36727
+ return;
36728
+ }
36729
+ if (input === "p") {
36730
+ setPathForm({
36731
+ direction: activeSide === "local" ? "upload" : "download",
36732
+ source: "",
36733
+ dest: panes[otherSide].path,
36734
+ sync: syncDefault,
36735
+ checksum: checksumDefault
36736
+ });
36737
+ setMode({ name: "pathTransfer", field: 1 });
36738
+ return;
36739
+ }
36740
+ if (input === "s") {
36741
+ setSyncDefault((v) => !v);
36742
+ flash(`\u540C\u6B65\u6A21\u5F0F\uFF1A${!syncDefault ? "\u5F00\uFF08\u76EE\u5F55\u6309\u5927\u5C0F+\u65F6\u95F4\u8DF3\u8FC7\u672A\u53D8\u6587\u4EF6\uFF09" : "\u5173\uFF08\u5168\u90E8\u4F20\u8F93\uFF09"}`);
36743
+ return;
36744
+ }
36745
+ if (input === "c") {
36746
+ setChecksumDefault((v) => !v);
36747
+ flash(`\u5185\u5BB9\u54C8\u5E0C\u6821\u9A8C\uFF1A${!checksumDefault ? "\u5F00" : "\u5173"}`);
36748
+ return;
36749
+ }
36750
+ });
36751
+ const narrow = width < 64;
36752
+ const hintLines = mode.name === "none" ? narrow ? [
36753
+ "Tab \u5207\u4FA7 \xB7 \u2191\u2193 \u79FB\u52A8 \xB7 Enter \u8FDB\u5165",
36754
+ "\u2190 \u8FD4\u56DE \xB7 Space \u591A\u9009 \xB7 a \u5168\u9009",
36755
+ "u \u4F20\u8F93 \xB7 p \u8DEF\u5F84\u76F4\u4F20 \xB7 g \u8DF3\u8F6C",
36756
+ "m \u65B0\u5EFA \xB7 r \u79FB\u52A8 \xB7 x \u5220\u9664",
36757
+ `s \u540C\u6B65[${syncDefault ? "\u5F00" : "\u5173"}] c \u6821\u9A8C[${checksumDefault ? "\u5F00" : "\u5173"}] \xB7 Q \u961F\u5217`,
36758
+ "Esc/q \u8FD4\u56DE"
36759
+ ] : [
36760
+ "Tab \u5207\u4FA7 \xB7 \u2191\u2193 \u79FB\u52A8 \xB7 Enter \u8FDB\u5165 \xB7 \u2190 \u8FD4\u56DE \xB7 Space \u591A\u9009 \xB7 a \u5168\u9009",
36761
+ "u \u4F20\u8F93 \xB7 p \u8DEF\u5F84\u76F4\u4F20 \xB7 g \u8DF3\u8F6C \xB7 Q \u961F\u5217 \xB7 Esc/q \u8FD4\u56DE",
36762
+ `m \u65B0\u5EFA \xB7 r \u6539\u540D/\u79FB\u52A8 \xB7 x \u5220\u9664 \xB7 s \u540C\u6B65[${syncDefault ? "\u5F00" : "\u5173"}] c \u6821\u9A8C[${checksumDefault ? "\u5F00" : "\u5173"}]`
36763
+ ] : mode.name === "delete" ? ["\u2190\u2192 \u9009\u62E9 \xB7 Enter \u786E\u5B9A \xB7 y \u5220\u9664 \xB7 Esc/q/n \u53D6\u6D88"] : mode.name === "transfer" ? ["Enter/y \u52A0\u5165\u961F\u5217 \xB7 s \u540C\u6B65 \xB7 c \u6821\u9A8C \xB7 Esc/q \u53D6\u6D88"] : ["\u8F93\u5165\u8DEF\u5F84 \xB7 \u232B \u5220\u9664 \xB7 Enter \u786E\u5B9A \xB7 Esc \u53D6\u6D88"];
36764
+ const dialogH = dialogRows(mode, selection.length);
36765
+ const hintCount = hintLines.length;
36766
+ const listRows = Math.max(1, height - 3 - dialogH - (notice ? 1 : 0) - hintCount);
36767
+ const renderPane = (side, paneWidth) => {
36768
+ const p = panes[side];
36769
+ const activePane = side === activeSide && !(narrow && side !== activeSide);
36770
+ const idx = side === activeSide ? cursorIndex : Math.min(p.cursor, Math.max(0, p.entries.length - 1));
36771
+ const nameWidth = Math.max(8, paneWidth - 14);
36772
+ const start = Math.max(0, Math.min(idx, Math.max(0, p.entries.length - listRows)));
36773
+ const shown = p.entries.slice(start, start + listRows);
36774
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", width: paneWidth, flexShrink: 0, children: [
36775
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { paddingX: 1, children: [
36776
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { bold: activePane, color: activePane ? "green" : void 0, children: side === "local" ? "\u25B8\u672C\u5730" : "\u25B8\u8FDC\u7A0B" }),
36777
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
36778
+ " ",
36779
+ clip(visible(p.path), Math.max(8, paneWidth - 10))
36780
+ ] })
36781
+ ] }),
36782
+ p.loading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: "\u8BFB\u53D6\u4E2D\u2026" }) }) : p.error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { color: "red", children: [
36783
+ "\u2716 ",
36784
+ clip(visible(p.error), Math.max(8, paneWidth - 4))
36785
+ ] }) }) : p.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: "\u7A7A\u76EE\u5F55" }) }) : shown.map((entry, i) => {
36786
+ const absolute = start + i;
36787
+ const isCursor = side === activeSide && absolute === idx;
36788
+ const checked = p.checked.includes(entry.path);
36789
+ const label = `${checked ? "[\u2713]" : "[ ]"}${isCursor ? "\u203A" : " "}${visible(entry.name)}${kindMark(entry.kind)}`;
36790
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
36791
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { inverse: isCursor, children: clip(label, nameWidth) }),
36792
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
36793
+ " ",
36794
+ entry.kind === "directory" ? "-" : formatBytes(entry.size)
36795
+ ] }),
36796
+ entry.kind === "symlink" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: "yellow", children: " \u8DF3\u8FC7" }) : null
36797
+ ] }) }, entry.path);
36798
+ }),
36799
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
36800
+ p.entries.length > listRows ? `${idx + 1}/` : "",
36801
+ p.entries.length,
36802
+ " \u9879",
36803
+ p.checked.length > 0 ? ` \xB7 \u5DF2\u9009 ${p.checked.length}` : "",
36804
+ p.entries.some((e) => e.kind === "symlink") ? " \xB7 @\u7B26\u53F7\u94FE\u63A5\u5C06\u8DF3\u8FC7" : ""
36805
+ ] }) })
36806
+ ] });
36807
+ };
36808
+ const renderDialog = () => {
36809
+ const inner = Math.max(10, width - 4);
36810
+ if (mode.name === "pathNav") {
36811
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, borderStyle: "round", borderColor: "cyan", children: [
36812
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
36813
+ "\u8DF3\u8F6C\uFF08",
36814
+ activeSide === "local" ? "\u672C\u5730" : "\u8FDC\u7A0B",
36815
+ "\uFF09\uFF1A",
36816
+ clip(visible(field), inner - 8),
36817
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: "\u2588" })
36818
+ ] }),
36819
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: "\u76F8\u5BF9\u8DEF\u5F84\u4EE5\u672C\u4FA7\u5F53\u524D\u76EE\u5F55\u4E3A\u57FA\u51C6 \xB7 Enter \u8DF3\u8F6C \xB7 Esc \u53D6\u6D88" })
36820
+ ] });
36821
+ }
36822
+ if (mode.name === "mkdir") {
36823
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, borderStyle: "round", borderColor: "cyan", children: [
36824
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
36825
+ "\u5728 ",
36826
+ clip(visible(pane.path), Math.max(8, inner - 24)),
36827
+ " \u4E0B\u65B0\u5EFA\u76EE\u5F55\uFF1A",
36828
+ clip(visible(field), Math.max(8, inner - 24)),
36829
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: "\u2588" })
36830
+ ] }),
36831
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: "\u76F8\u5BF9\u540D\u5EFA\u4E8E\u5F53\u524D\u76EE\u5F55\uFF0C\u7EDD\u5BF9\u8DEF\u5F84\u4EA6\u53EF \xB7 Enter \u521B\u5EFA \xB7 Esc \u53D6\u6D88" })
36832
+ ] });
36833
+ }
36834
+ if (mode.name === "rename") {
36835
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, borderStyle: "round", borderColor: "cyan", children: [
36836
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
36837
+ mode.multi ? `\u79FB\u52A8 ${selection.length} \u9879\u5230\u76EE\u5F55\uFF1A` : "\u6539\u540D/\u79FB\u52A8\u4E3A\uFF1A",
36838
+ clip(visible(field), inner - 8),
36839
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: "\u2588" })
36840
+ ] }),
36841
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: "\u76F8\u5BF9\u8DEF\u5F84\u4EE5\u5F53\u524D\u76EE\u5F55\u4E3A\u57FA\u51C6 \xB7 Enter \u786E\u5B9A \xB7 Esc \u53D6\u6D88" })
36842
+ ] });
36843
+ }
36844
+ if (mode.name === "delete") {
36845
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, borderStyle: "round", borderColor: "red", children: [
36846
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { bold: true, color: "red", children: [
36847
+ "\u6C38\u4E45\u5220\u9664 ",
36848
+ selection.length,
36849
+ " \u9879\uFF08\u9012\u5F52\uFF0C\u4E0D\u53EF\u6062\u590D\uFF09\uFF1A"
36850
+ ] }),
36851
+ selection.slice(0, 3).map((e) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
36852
+ "\xB7 ",
36853
+ clip(visible(e.path), inner - 4)
36854
+ ] }, e.path)),
36855
+ selection.length > 3 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
36856
+ "\xB7 \u2026\u53E6 ",
36857
+ selection.length - 3,
36858
+ " \u9879"
36859
+ ] }) : null,
36860
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { children: [
36861
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { inverse: !confirmYes, children: " \u53D6\u6D88 " }),
36862
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: " " }),
36863
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { inverse: confirmYes, color: confirmYes ? "red" : void 0, children: " \u5220\u9664 " })
36864
+ ] }),
36865
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: "\u2190\u2192 \u5207\u6362 \xB7 Enter \u786E\u5B9A \xB7 y \u76F4\u63A5\u5220 \xB7 Esc/q/n \u53D6\u6D88" })
36866
+ ] });
36867
+ }
36868
+ if (mode.name === "transfer" && pending) {
36869
+ const hasSymlink = pending.kinds.some((k) => k === "symlink");
36870
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, borderStyle: "round", borderColor: "cyan", children: [
36871
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { bold: true, children: [
36872
+ directionText(pending.direction),
36873
+ " ",
36874
+ pending.sources.length,
36875
+ " \u9879 \u2192",
36876
+ " ",
36877
+ clip(visible(pending.destDir), inner - 16)
36878
+ ] }),
36879
+ pending.sources.slice(0, 2).map((s) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
36880
+ "\xB7 ",
36881
+ clip(visible(s), inner - 4)
36882
+ ] }, s)),
36883
+ pending.sources.length > 2 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
36884
+ "\xB7 \u2026\u53E6 ",
36885
+ pending.sources.length - 2,
36886
+ " \u9879"
36887
+ ] }) : null,
36888
+ hasSymlink ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: "yellow", children: "\u7B26\u53F7\u94FE\u63A5\u5C06\u88AB\u8DF3\u8FC7\u5E76\u8BA1\u5165\u7ED3\u679C\u3002" }) : null,
36889
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
36890
+ "s \u540C\u6B65[",
36891
+ syncDefault ? "\u2713" : " ",
36892
+ "] c \u54C8\u5E0C\u6821\u9A8C[",
36893
+ checksumDefault ? "\u2713" : " ",
36894
+ "] \xB7 Enter \u52A0\u5165\u961F\u5217 \xB7 Esc \u53D6\u6D88"
36895
+ ] })
36896
+ ] });
36897
+ }
36898
+ if (mode.name === "pathTransfer") {
36899
+ const f = mode.field;
36900
+ const marker = (i) => f === i ? "\u203A" : " ";
36901
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, borderStyle: "round", borderColor: "cyan", children: [
36902
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { bold: true, children: "\u6309\u8DEF\u5F84\u76F4\u63A5\u4F20\u8F93" }),
36903
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
36904
+ marker(0),
36905
+ "\u65B9\u5411\uFF1A",
36906
+ directionText(pathForm.direction),
36907
+ "\uFF08\u2190\u2192/u/d \u5207\u6362\uFF09"
36908
+ ] }),
36909
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
36910
+ marker(1),
36911
+ "\u6765\u6E90\uFF1A",
36912
+ clip(visible(pathForm.source), inner - 8),
36913
+ f === 1 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: "\u2588" }) : null
36914
+ ] }),
36915
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { children: [
36916
+ marker(2),
36917
+ "\u76EE\u6807\uFF1A",
36918
+ clip(visible(pathForm.dest), inner - 8),
36919
+ f === 2 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: "\u2588" }) : null
36920
+ ] }),
36921
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
36922
+ "\u6765\u6E90\u76F8\u5BF9\u53D1\u8D77\u4FA7\u5F53\u524D\u76EE\u5F55\uFF0C\u76EE\u6807\u76F8\u5BF9\u63A5\u6536\u4FA7\u5F53\u524D\u76EE\u5F55 \xB7 Tab/\u2191\u2193 \u6362\u884C \xB7",
36923
+ " ",
36924
+ f === 0 ? "s \u540C\u6B65 c \u6821\u9A8C \xB7 " : "",
36925
+ "Enter \u63D0\u4EA4 \xB7 Esc \u53D6\u6D88"
36926
+ ] })
36927
+ ] });
36928
+ }
36929
+ return null;
36930
+ };
36931
+ if (tab2 === "queue") {
36932
+ const clamped = Math.min(queueCursor, Math.max(0, jobs.length - 1));
36933
+ const selected = jobs[clamped];
36934
+ const detail = detailRows(selected, queueDetail, skippedOpen, skippedIndex);
36935
+ const queueListRows = Math.max(1, height - 4 - detail - (notice ? 1 : 0));
36936
+ const ui = {
36937
+ jobs,
36938
+ cursor: clamped,
36939
+ setCursor: setQueueCursor,
36940
+ showDetail: queueDetail,
36941
+ setShowDetail: setQueueDetail,
36942
+ skippedOpen,
36943
+ setSkippedOpen,
36944
+ skippedIndex,
36945
+ setSkippedIndex,
36946
+ applyAll,
36947
+ setApplyAll,
36948
+ resume,
36949
+ flash,
36950
+ width,
36951
+ listRows: queueListRows
36952
+ };
36953
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", width, height, overflow: "hidden", children: [
36954
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { paddingX: 1, children: [
36955
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { bold: true, children: "\u6587\u4EF6" }),
36956
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
36957
+ clip(visible(uuid), 12),
36958
+ " \xB7 \u6D4F\u89C8\u5668 | \u961F\u5217(",
36959
+ jobs.length,
36960
+ ")",
36961
+ jobs.length > 0 ? ` \xB7 ${clamped + 1}/${jobs.length}` : ""
36962
+ ] })
36963
+ ] }),
36964
+ renderQueueBody(ui),
36965
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { flexGrow: 1 }),
36966
+ notice ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: "cyan", children: clip(visible(notice), Math.max(10, width - 2)) }) }) : null,
36967
+ queueHints(selected, skippedOpen).map((hint) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, wrap: "truncate", children: fitLine(hint, Math.max(10, width - 2)) }) }, hint)),
36968
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: clip("Q/Esc \u8FD4\u56DE\u6D4F\u89C8\u5668", Math.max(10, width - 2)) }) })
36969
+ ] });
36970
+ }
36971
+ const half = Math.max(20, Math.floor((width - 1) / 2));
36972
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", width, height, overflow: "hidden", children: [
36973
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { bold: true, wrap: "truncate", children: [
36974
+ "\u6587\u4EF6 \xB7 ",
36975
+ narrow ? activeSide === "local" ? "\u672C\u5730" : "\u8FDC\u7A0B" : "\u672C\u5730 / \u8FDC\u7A0B",
36976
+ " \xB7 Q \u961F\u5217(",
36977
+ jobs.length,
36978
+ ")",
36979
+ busy ? " \xB7 \u5904\u7406\u4E2D\u2026" : ""
36980
+ ] }) }),
36981
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { flexDirection: "row", children: narrow ? renderPane(activeSide, Math.max(20, width - 2)) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
36982
+ renderPane("local", half),
36983
+ renderPane("remote", width - 1 - half)
36984
+ ] }) }),
36985
+ renderDialog(),
36986
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { flexGrow: 1 }),
36987
+ notice ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: "cyan", children: clip(visible(notice), Math.max(10, width - 2)) }) }) : null,
36988
+ hintLines.map((hint) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: clip(hint, Math.max(10, width - 2)) }) }, hint))
36989
+ ] });
36990
+ }
36991
+
35812
36992
  // src/tui/screens/login.tsx
35813
- var import_react38 = __toESM(require_react(), 1);
36993
+ var import_react39 = __toESM(require_react(), 1);
35814
36994
 
35815
36995
  // src/tui/useTerminalSize.ts
35816
- var import_react37 = __toESM(require_react(), 1);
36996
+ var import_react38 = __toESM(require_react(), 1);
35817
36997
  function useTerminalSize() {
35818
36998
  const { stdout } = use_stdout_default();
35819
36999
  const read = () => ({
35820
37000
  columns: stdout?.columns || 80,
35821
37001
  rows: stdout?.rows || 24
35822
37002
  });
35823
- const [size, setSize] = (0, import_react37.useState)(read);
35824
- (0, import_react37.useEffect)(() => {
37003
+ const [size, setSize] = (0, import_react38.useState)(read);
37004
+ (0, import_react38.useEffect)(() => {
35825
37005
  if (!stdout) return;
35826
37006
  const onResize = () => setSize({ columns: stdout.columns || 80, rows: stdout.rows || 24 });
35827
37007
  stdout.on("resize", onResize);
@@ -35834,7 +37014,7 @@ function useTerminalSize() {
35834
37014
  }
35835
37015
 
35836
37016
  // src/tui/screens/login.tsx
35837
- var import_jsx_runtime11 = __toESM(require_jsx_runtime(), 1);
37017
+ var import_jsx_runtime12 = __toESM(require_jsx_runtime(), 1);
35838
37018
  function sanitize(input) {
35839
37019
  let out = "";
35840
37020
  for (const char of input) {
@@ -35851,10 +37031,11 @@ function Login({
35851
37031
  notice
35852
37032
  }) {
35853
37033
  const { columns: columns2, rows } = useTerminalSize();
35854
- const [stage, setStage] = (0, import_react38.useState)("menu");
35855
- const [choice, setChoice] = (0, import_react38.useState)(0);
35856
- const [token, setToken] = (0, import_react38.useState)("");
37034
+ const [stage, setStage] = (0, import_react39.useState)("menu");
37035
+ const [choice, setChoice] = (0, import_react39.useState)(0);
37036
+ const [token, setToken] = (0, import_react39.useState)("");
35857
37037
  use_input_default((input, key) => {
37038
+ if (key.ctrl && input.toLowerCase() === "c") return onQuit();
35858
37039
  if (verifying) return;
35859
37040
  if (stage === "menu") {
35860
37041
  if (key.upArrow || key.downArrow || input === "j" || input === "k") {
@@ -35885,37 +37066,37 @@ function Login({
35885
37066
  const clean = sanitize(input);
35886
37067
  if (clean) setToken((v) => v + clean);
35887
37068
  });
35888
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", height: rows, width: columns2, children: [
35889
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(BrandMark, { subtitle: "\u767B\u5165", columns: columns2 }),
35890
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
35891
- stage === "menu" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
35892
- notice ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: "yellow", children: notice }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: "\u5C1A\u672A\u914D\u7F6E\u5F00\u53D1\u8005 Token\uFF0C\u8BF7\u5148\u767B\u5165\u3002" }),
35893
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
35894
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { inverse: choice === 0, children: [
37069
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { flexDirection: "column", height: rows, width: columns2, children: [
37070
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(BrandMark, { subtitle: "\u767B\u5165", columns: columns2 }),
37071
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
37072
+ stage === "menu" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
37073
+ notice ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: "yellow", children: notice }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { dimColor: true, children: "\u5C1A\u672A\u914D\u7F6E\u5F00\u53D1\u8005 Token\uFF0C\u8BF7\u5148\u767B\u5165\u3002" }),
37074
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
37075
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { inverse: choice === 0, children: [
35895
37076
  choice === 0 ? "\u203A " : " ",
35896
37077
  "\u914D\u7F6E Token \u767B\u5165"
35897
37078
  ] }),
35898
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { inverse: choice === 1, children: [
37079
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { inverse: choice === 1, children: [
35899
37080
  choice === 1 ? "\u203A " : " ",
35900
37081
  "\u9000\u51FA"
35901
37082
  ] })
35902
37083
  ] }),
35903
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u786E\u5B9A \xB7 q \u9000\u51FA" }) })
35904
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
35905
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: "\u7C98\u8D34\u4F60\u7684\u5F00\u53D1\u8005 Token\uFF1A" }),
35906
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: "AutoDL \u63A7\u5236\u53F0 \u2192 \u8BBE\u7F6E \u2192 \u5F00\u53D1\u8005 Token\uFF08\u9700\u5148\u5B8C\u6210\u5B9E\u540D\u8BA4\u8BC1\uFF09" }),
35907
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Box_default, { marginTop: 1, children: [
35908
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: "\u203A " }),
35909
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { color: "green", children: token ? "\u2022".repeat(Math.min(token.length, 48)) : "" }),
35910
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: token ? ` (${token.length} \u5B57\u7B26)` : "\u7B49\u5F85\u8F93\u5165\u2026" })
37084
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { dimColor: true, children: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u786E\u5B9A \xB7 q \u9000\u51FA" }) })
37085
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
37086
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { dimColor: true, children: "\u7C98\u8D34\u4F60\u7684\u5F00\u53D1\u8005 Token\uFF1A" }),
37087
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { dimColor: true, children: "AutoDL \u63A7\u5236\u53F0 \u2192 \u8BBE\u7F6E \u2192 \u5F00\u53D1\u8005 Token\uFF08\u9700\u5148\u5B8C\u6210\u5B9E\u540D\u8BA4\u8BC1\uFF09" }),
37088
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { marginTop: 1, children: [
37089
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { children: "\u203A " }),
37090
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: "green", children: token ? "\u2022".repeat(Math.min(token.length, 48)) : "" }),
37091
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { dimColor: true, children: token ? ` (${token.length} \u5B57\u7B26)` : "\u7B49\u5F85\u8F93\u5165\u2026" })
35911
37092
  ] }),
35912
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { dimColor: true, children: verifying ? "\u6B63\u5728\u9A8C\u8BC1\u2026" : "Enter \u9A8C\u8BC1\u5E76\u4FDD\u5B58 \xB7 Esc \u8FD4\u56DE \xB7 \u652F\u6301\u76F4\u63A5\u7C98\u8D34" }) })
37093
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { dimColor: true, children: verifying ? "\u6B63\u5728\u9A8C\u8BC1\u2026" : "Enter \u9A8C\u8BC1\u5E76\u4FDD\u5B58 \xB7 Esc \u8FD4\u56DE \xB7 \u652F\u6301\u76F4\u63A5\u7C98\u8D34" }) })
35913
37094
  ] }),
35914
- error ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { color: "red", children: [
37095
+ error ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { color: "red", children: [
35915
37096
  "\u2716 ",
35916
37097
  error
35917
37098
  ] }) }) : null,
35918
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Text, { dimColor: true, children: [
37099
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { dimColor: true, children: [
35919
37100
  "\u9A8C\u8BC1\u901A\u8FC7\u540E\u4F1A\u4FDD\u5B58\u5230 ",
35920
37101
  configPath(),
35921
37102
  "\uFF08\u6743\u9650 0600\uFF09"
@@ -35925,7 +37106,7 @@ function Login({
35925
37106
  }
35926
37107
 
35927
37108
  // src/tui/screens/stock.tsx
35928
- var import_jsx_runtime12 = __toESM(require_jsx_runtime(), 1);
37109
+ var import_jsx_runtime13 = __toESM(require_jsx_runtime(), 1);
35929
37110
  function toStockRows(snapshots, showEmpty) {
35930
37111
  const rentable = new Map(GPU_SPECS.map((spec) => [spec.stockName, spec.id]));
35931
37112
  const proRegions = new Set(PRO_CREATE_REGIONS.map((region) => region.id));
@@ -35951,7 +37132,7 @@ var columns = [
35951
37132
  header: "\u53EF\u5EFAPro",
35952
37133
  width: 8,
35953
37134
  text: (row) => row.proCreate ? "\u662F" : "\u5426",
35954
- render: (row) => row.proCreate ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: "green", children: "\u662F" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { dimColor: true, children: "\u5426" })
37135
+ render: (row) => row.proCreate ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: "green", children: "\u662F" }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { dimColor: true, children: "\u5426" })
35955
37136
  },
35956
37137
  {
35957
37138
  header: "GPU",
@@ -35962,19 +37143,19 @@ var columns = [
35962
37143
  header: "\u53EF\u79DF\u89C4\u683C",
35963
37144
  width: 12,
35964
37145
  text: (row) => row.gpuSpec ?? "\u2014",
35965
- render: (row, clipped) => row.gpuSpec ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: "green", children: clipped }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { dimColor: true, children: clipped })
37146
+ render: (row, clipped) => row.gpuSpec ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: "green", children: clipped }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { dimColor: true, children: clipped })
35966
37147
  },
35967
37148
  {
35968
37149
  header: "\u7A7A\u95F2",
35969
37150
  width: 7,
35970
37151
  text: (row) => String(row.idle),
35971
- render: (row, clipped) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { bold: row.idle > 0, children: clipped })
37152
+ render: (row, clipped) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { bold: row.idle > 0, children: clipped })
35972
37153
  },
35973
37154
  {
35974
37155
  header: "\u603B\u6570",
35975
37156
  width: 7,
35976
37157
  text: (row) => String(row.total),
35977
- render: (_row, clipped) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { dimColor: true, children: clipped })
37158
+ render: (_row, clipped) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { dimColor: true, children: clipped })
35978
37159
  }
35979
37160
  ];
35980
37161
  function StockScreen({
@@ -35982,8 +37163,8 @@ function StockScreen({
35982
37163
  selectedIndex,
35983
37164
  loading
35984
37165
  }) {
35985
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { flexDirection: "column", children: [
35986
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
37166
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Box_default, { flexDirection: "column", children: [
37167
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
35987
37168
  Table,
35988
37169
  {
35989
37170
  columns,
@@ -35993,27 +37174,29 @@ function StockScreen({
35993
37174
  emptyMessage: loading ? "\u6B63\u5728\u67E5\u8BE2\u5E93\u5B58\u2026" : "\u67E5\u8BE2\u8303\u56F4\u5185\u6CA1\u6709\u7A7A\u95F2 GPU"
35994
37175
  }
35995
37176
  ),
35996
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
35997
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { dimColor: true, children: [
37177
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
37178
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Text, { dimColor: true, children: [
35998
37179
  "\u300C\u53EF\u5EFAPro\u300D\u53EA\u6709 ",
35999
37180
  PRO_CREATE_REGIONS.map((r) => r.displayName).join(" / "),
36000
37181
  " \u4E3A\u662F\uFF0C \u5176\u4F59\u5730\u533A\u4EC5\u7528\u4E8E\u5F39\u6027\u90E8\u7F72\u3002"
36001
37182
  ] }),
36002
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Text, { color: "yellow", children: "\u6570\u5B57\u6765\u81EA\u300C\u5F39\u6027\u90E8\u7F72 GPU \u5E93\u5B58\u300D\uFF0C\u4E0D\u4EE3\u8868 Pro \u5B9E\u4F8B\u53EF\u7528\u91CF\u2014\u2014\u5B9E\u6D4B\u51FA\u73B0\u8FC7\u663E\u793A 140 \u5F20\u7A7A\u95F2\u3001\u521B\u5EFA\u5374\u63D0\u793A\u6682\u65E0\u5E93\u5B58\u7684\u60C5\u51B5\u3002" })
37183
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { color: "yellow", children: "\u6570\u5B57\u6765\u81EA\u300C\u5F39\u6027\u90E8\u7F72 GPU \u5E93\u5B58\u300D\uFF0C\u4E0D\u4EE3\u8868 Pro \u5B9E\u4F8B\u53EF\u7528\u91CF\u2014\u2014\u5B9E\u6D4B\u51FA\u73B0\u8FC7\u663E\u793A 140 \u5F20\u7A7A\u95F2\u3001\u521B\u5EFA\u5374\u63D0\u793A\u6682\u65E0\u5E93\u5B58\u7684\u60C5\u51B5\u3002" })
36003
37184
  ] })
36004
37185
  ] });
36005
37186
  }
36006
37187
 
36007
37188
  // src/tui/app.tsx
36008
- var import_jsx_runtime13 = __toESM(require_jsx_runtime(), 1);
37189
+ var import_jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
36009
37190
  var SCREEN_TITLES = {
36010
37191
  dashboard: "\u5B9E\u4F8B\u770B\u677F",
36011
37192
  detail: "\u5B9E\u4F8B\u8BE6\u60C5",
36012
37193
  stock: "GPU \u5E93\u5B58",
36013
37194
  create: "\u65B0\u5EFA\u5B9E\u4F8B",
36014
- help: "\u5FEB\u6377\u952E"
37195
+ help: "\u5FEB\u6377\u952E",
37196
+ files: "\u6587\u4EF6\u7BA1\u7406",
37197
+ transfers: "\u4F20\u8F93\u961F\u5217"
36015
37198
  };
36016
- var DASHBOARD_KEYS = "\u2191\u2193 \u79FB\u52A8 \xB7 Enter \u8BE6\u60C5 \xB7 s \u5F00\u673A \xB7 x \u5173\u673A \xB7 c \u590D\u5236SSH \xB7 ctrl+d \u91CA\u653E \xB7 g \u5E93\u5B58 \xB7 n \u65B0\u5EFA \xB7 r \u5237\u65B0 \xB7 ctrl+l \u9000\u51FA\u767B\u5F55 \xB7 ? \u5E2E\u52A9 \xB7 q \u9000\u51FA";
37199
+ var DASHBOARD_KEYS = "\u2191\u2193 \u79FB\u52A8 \xB7 Enter \u8BE6\u60C5 \xB7 s \u5F00\u673A \xB7 x \u5173\u673A \xB7 ctrl+d \u91CA\u653E \xB7 g \u5E93\u5B58 \xB7 r \u5237\u65B0 \xB7 ctrl+l \u9000\u51FA\u767B\u5F55 \xB7 ? \u5E2E\u52A9 \xB7 q \u9000\u51FA";
36017
37200
  var isCtrl = (input, key, letter) => key.ctrl && input.toLowerCase() === letter;
36018
37201
  var HELP_COLUMNS = ((items) => {
36019
37202
  const half = Math.ceil(items.length / 2);
@@ -36023,25 +37206,35 @@ function App2({
36023
37206
  client,
36024
37207
  token,
36025
37208
  tokenSource,
36026
- onLogout
37209
+ onLogout,
37210
+ baseUrl
36027
37211
  }) {
36028
- const { exit } = use_app_default();
36029
- const [view, setView] = (0, import_react39.useState)("dashboard");
36030
- const [selected, setSelected] = (0, import_react39.useState)(0);
36031
- const [notice, setNotice] = (0, import_react39.useState)(null);
36032
- const [pending, setPending] = (0, import_react39.useState)(null);
36033
- const [busy, setBusy] = (0, import_react39.useState)(false);
36034
- const [reveal, setReveal] = (0, import_react39.useState)(false);
36035
- const [stock, setStock] = (0, import_react39.useState)([]);
36036
- const [stockLoading, setStockLoading] = (0, import_react39.useState)(false);
36037
- const [stockIndex, setStockIndex] = (0, import_react39.useState)(0);
36038
- const [balance, setBalance] = (0, import_react39.useState)(null);
36039
- const [balanceError, setBalanceError] = (0, import_react39.useState)(null);
36040
- const [expired, setExpired] = (0, import_react39.useState)(null);
36041
- const [watched, setWatched] = (0, import_react39.useState)(void 0);
37212
+ const { exit, suspendTerminal } = use_app_default();
37213
+ const [view, setView] = (0, import_react40.useState)("dashboard");
37214
+ const [selected, setSelected] = (0, import_react40.useState)(0);
37215
+ const [notice, setNotice] = (0, import_react40.useState)(null);
37216
+ const [pending, setPending] = (0, import_react40.useState)(null);
37217
+ const [busy, setBusy] = (0, import_react40.useState)(false);
37218
+ const [reveal, setReveal] = (0, import_react40.useState)(false);
37219
+ const [stock, setStock] = (0, import_react40.useState)([]);
37220
+ const [stockLoading, setStockLoading] = (0, import_react40.useState)(false);
37221
+ const [stockIndex, setStockIndex] = (0, import_react40.useState)(0);
37222
+ const [balance, setBalance] = (0, import_react40.useState)(null);
37223
+ const [balanceError, setBalanceError] = (0, import_react40.useState)(null);
37224
+ const [expired, setExpired] = (0, import_react40.useState)(null);
37225
+ const [terminalActive, setTerminalActive] = (0, import_react40.useState)(false);
37226
+ const [workspace, setWorkspace] = (0, import_react40.useState)(null);
37227
+ const [fileUuid, setFileUuid] = (0, import_react40.useState)("");
37228
+ const [fileGate, setFileGate] = (0, import_react40.useState)(void 0);
37229
+ const queueRef = (0, import_react40.useRef)(null);
37230
+ const accessController = (0, import_react40.useRef)(null);
37231
+ const fileUuidRef = (0, import_react40.useRef)("");
37232
+ const queueUnsubscribe = (0, import_react40.useRef)(null);
37233
+ const [queueNotice, setQueueNotice] = (0, import_react40.useState)(null);
37234
+ const [watched, setWatched] = (0, import_react40.useState)(void 0);
36042
37235
  const { columns: columns2, rows: terminalRows } = useTerminalSize();
36043
- const identity = (0, import_react39.useMemo)(() => identityFromToken(token), [token]);
36044
- const paused = view === "create" || pending !== null || expired !== null;
37236
+ const identity = (0, import_react40.useMemo)(() => identityFromToken(token), [token]);
37237
+ const paused = view === "create" || pending !== null || expired !== null || terminalActive;
36045
37238
  const {
36046
37239
  rows,
36047
37240
  loading,
@@ -36053,24 +37246,24 @@ function App2({
36053
37246
  historyFor,
36054
37247
  loadSnapshot
36055
37248
  } = useInstances(client, { paused, watch: watched });
36056
- const noteAuthFailure = (0, import_react39.useCallback)((err) => {
37249
+ const noteAuthFailure = (0, import_react40.useCallback)((err) => {
36057
37250
  if (isAuthError(err)) setExpired((current) => current ?? err.message);
36058
37251
  }, []);
36059
- (0, import_react39.useEffect)(() => {
37252
+ (0, import_react40.useEffect)(() => {
36060
37253
  if (authError) setExpired((current) => current ?? authError);
36061
37254
  }, [authError]);
36062
37255
  const selectedIndex = Math.min(selected, Math.max(0, rows.length - 1));
36063
37256
  const row = rows[selectedIndex];
36064
- (0, import_react39.useEffect)(() => {
37257
+ (0, import_react40.useEffect)(() => {
36065
37258
  if (view === "detail" && !row) setView("dashboard");
36066
37259
  }, [view, row]);
36067
37260
  const selectedUuid = row?.instance.uuid;
36068
37261
  const selectedRunning = row?.instance.status === "running";
36069
- (0, import_react39.useEffect)(() => {
37262
+ (0, import_react40.useEffect)(() => {
36070
37263
  setWatched(selectedRunning ? selectedUuid : void 0);
36071
37264
  if (selectedUuid && selectedRunning) loadSnapshot(selectedUuid);
36072
37265
  }, [selectedUuid, selectedRunning, loadSnapshot]);
36073
- (0, import_react39.useEffect)(() => {
37266
+ (0, import_react40.useEffect)(() => {
36074
37267
  if (expired) return;
36075
37268
  let cancelled = false;
36076
37269
  const load = () => {
@@ -36092,19 +37285,87 @@ function App2({
36092
37285
  clearInterval(timer);
36093
37286
  };
36094
37287
  }, [client, expired, noteAuthFailure]);
36095
- const noticeTimer = (0, import_react39.useRef)(null);
36096
- const flash = (0, import_react39.useCallback)((message) => {
37288
+ const noticeTimer = (0, import_react40.useRef)(null);
37289
+ const flash = (0, import_react40.useCallback)((message) => {
36097
37290
  setNotice(message);
36098
37291
  if (noticeTimer.current) clearTimeout(noticeTimer.current);
36099
37292
  noticeTimer.current = setTimeout(() => setNotice(null), 6e3);
36100
37293
  }, []);
36101
- (0, import_react39.useEffect)(
37294
+ (0, import_react40.useEffect)(
36102
37295
  () => () => {
36103
37296
  if (noticeTimer.current) clearTimeout(noticeTimer.current);
36104
37297
  },
36105
37298
  []
36106
37299
  );
36107
- const act = (0, import_react39.useCallback)(
37300
+ const getQueue = (0, import_react40.useCallback)(() => {
37301
+ if (!queueRef.current) {
37302
+ const account = [identity.tenant, identity.uid ?? identity.uuid ?? token];
37303
+ const namespace = createHash("sha256").update(
37304
+ JSON.stringify([
37305
+ (baseUrl ?? resolveBaseUrl() ?? DEFAULT_BASE_URL).replace(/\/+$/, ""),
37306
+ account
37307
+ ])
37308
+ ).digest("hex");
37309
+ queueRef.current = new TransferQueue(client, namespace);
37310
+ const queue = queueRef.current;
37311
+ const updateNotice = () => {
37312
+ const jobs = queue.snapshot();
37313
+ const unfinished = jobs.filter(
37314
+ (job) => job.state !== "completed" && job.state !== "cancelled"
37315
+ );
37316
+ setQueueNotice(
37317
+ unfinished.length ? `\u4F20\u8F93 ${unfinished.length} \u9879\u672A\u5B8C\u6210${jobs.some((job) => job.state === "conflict") ? " \xB7 \u7B49\u5F85\u51B2\u7A81\u5904\u7406" : ""} \xB7 t \u67E5\u770B\u961F\u5217` : null
37318
+ );
37319
+ };
37320
+ queueUnsubscribe.current = queue.subscribe(updateNotice);
37321
+ updateNotice();
37322
+ }
37323
+ return queueRef.current;
37324
+ }, [client, baseUrl, identity, token]);
37325
+ (0, import_react40.useEffect)(
37326
+ () => () => {
37327
+ accessController.current?.abort();
37328
+ queueUnsubscribe.current?.();
37329
+ void queueRef.current?.dispose().catch((err) => {
37330
+ process.stderr.write(`\u4F20\u8F93\u961F\u5217\u5173\u95ED\u5931\u8D25\uFF1A${err.message}
37331
+ `);
37332
+ });
37333
+ },
37334
+ []
37335
+ );
37336
+ (0, import_react40.useEffect)(() => {
37337
+ if (expired) {
37338
+ void queueRef.current?.pauseAll().catch((err) => flash(err.message));
37339
+ }
37340
+ }, [expired, flash]);
37341
+ const requestExit = (0, import_react40.useCallback)(() => {
37342
+ accessController.current?.abort();
37343
+ if (queueRef.current?.hasPending()) setPending({ kind: "exit" });
37344
+ else exit();
37345
+ }, [exit]);
37346
+ const finishLogout = async (reason) => {
37347
+ if (busy) return;
37348
+ setBusy(true);
37349
+ try {
37350
+ if (queueRef.current) await queueRef.current.pauseAll();
37351
+ onLogout(reason);
37352
+ } catch (err) {
37353
+ flash(err.message);
37354
+ setBusy(false);
37355
+ }
37356
+ };
37357
+ const finishExit = async () => {
37358
+ if (busy) return;
37359
+ setBusy(true);
37360
+ try {
37361
+ if (queueRef.current) await queueRef.current.pauseAll();
37362
+ exit();
37363
+ } catch (err) {
37364
+ flash(err.message);
37365
+ setBusy(false);
37366
+ }
37367
+ };
37368
+ const act = (0, import_react40.useCallback)(
36108
37369
  async (label, fn) => {
36109
37370
  setBusy(true);
36110
37371
  setNotice(`${label}\u2026`);
@@ -36120,7 +37381,7 @@ function App2({
36120
37381
  },
36121
37382
  [flash, refresh, noteAuthFailure]
36122
37383
  );
36123
- const loadStock = (0, import_react39.useCallback)(async () => {
37384
+ const loadStock = (0, import_react40.useCallback)(async () => {
36124
37385
  setStockLoading(true);
36125
37386
  try {
36126
37387
  const { snapshots } = await getStockByRegion(client);
@@ -36132,7 +37393,106 @@ function App2({
36132
37393
  setStockLoading(false);
36133
37394
  }
36134
37395
  }, [client, flash, noteAuthFailure]);
36135
- const submitCreate = (0, import_react39.useCallback)(
37396
+ const copySSH = (0, import_react40.useCallback)(
37397
+ (uuid) => {
37398
+ const snapshot = snapshotFor(uuid);
37399
+ if (!snapshot?.ssh.host || !snapshot.ssh.port) {
37400
+ flash("\u5B9E\u4F8B\u672A\u8FD0\u884C\u6216 SSH \u4FE1\u606F\u5C1A\u672A\u5C31\u7EEA");
37401
+ return;
37402
+ }
37403
+ const command = snapshot.ssh.command ?? `ssh -p ${snapshot.ssh.port} root@${snapshot.ssh.host}`;
37404
+ void copyToClipboard(command).then((result) => {
37405
+ flash(
37406
+ result.method === "native" ? `\u2714 \u5DF2\u590D\u5236\uFF1A${command}\u3000\u5BC6\u7801 ${snapshot.ssh.password}` : `${command}\u3000\u5BC6\u7801 ${snapshot.ssh.password}\u3000\uFF08${result.note}\uFF09`
37407
+ );
37408
+ });
37409
+ },
37410
+ [flash, snapshotFor]
37411
+ );
37412
+ const performAccess = (0, import_react40.useCallback)(
37413
+ async (action, signal) => {
37414
+ if (signal?.aborted) return;
37415
+ if (action.kind === "files") {
37416
+ setWorkspace(
37417
+ (prev) => prev && fileUuidRef.current === action.uuid ? prev : new FileWorkspace(client, action.uuid)
37418
+ );
37419
+ fileUuidRef.current = action.uuid;
37420
+ setFileUuid(action.uuid);
37421
+ getQueue();
37422
+ setView("files");
37423
+ if (signal) return;
37424
+ const gate = (async () => {
37425
+ const status = await getInstanceStatus(client, action.uuid);
37426
+ if (status !== "running") {
37427
+ setPending({ kind: "power", action });
37428
+ throw new Error(`\u5B9E\u4F8B ${action.uuid} \u5F53\u524D\u672A\u8FD0\u884C\uFF0C\u8FDC\u7A0B\u76EE\u5F55\u9700\u5F00\u673A\u540E\u67E5\u770B`);
37429
+ }
37430
+ })();
37431
+ gate.catch((err) => {
37432
+ flash(err.message);
37433
+ noteAuthFailure(err);
37434
+ });
37435
+ setFileGate(gate.then(() => void 0));
37436
+ return;
37437
+ }
37438
+ if (action.kind === "resume") {
37439
+ getQueue().resume(action.jobId);
37440
+ return;
37441
+ }
37442
+ setTerminalActive(true);
37443
+ try {
37444
+ let code = 0;
37445
+ await suspendTerminal(async () => {
37446
+ process.stdout.write(LEAVE_ALT_SCREEN);
37447
+ try {
37448
+ code = await connectTerminal(client, action.uuid, signal ? { signal } : {});
37449
+ } finally {
37450
+ process.stdout.write(signal?.aborted ? LEAVE_ALT_SCREEN : ENTER_ALT_SCREEN + CLEAR);
37451
+ }
37452
+ });
37453
+ flash(`SSH \u4F1A\u8BDD\u5DF2\u7ED3\u675F\uFF08\u9000\u51FA\u7801 ${code}\uFF09`);
37454
+ } finally {
37455
+ setTerminalActive(false);
37456
+ }
37457
+ },
37458
+ [client, flash, getQueue, noteAuthFailure, suspendTerminal]
37459
+ );
37460
+ const requestAccess = (0, import_react40.useCallback)(
37461
+ async (action) => {
37462
+ if (action.kind === "files") {
37463
+ if (busy) return;
37464
+ await performAccess(action);
37465
+ return;
37466
+ }
37467
+ if (busy) return;
37468
+ setBusy(true);
37469
+ const controller = new AbortController();
37470
+ accessController.current = controller;
37471
+ try {
37472
+ const status = await getInstanceStatus(client, action.uuid);
37473
+ if (controller.signal.aborted) return;
37474
+ if (status !== "running") {
37475
+ setPending({ kind: "power", action });
37476
+ return;
37477
+ }
37478
+ await performAccess(action, controller.signal);
37479
+ } catch (err) {
37480
+ flash(err.message);
37481
+ noteAuthFailure(err);
37482
+ } finally {
37483
+ setBusy(false);
37484
+ }
37485
+ },
37486
+ [busy, client, flash, noteAuthFailure, performAccess]
37487
+ );
37488
+ const resumeTransfer = (0, import_react40.useCallback)(
37489
+ (jobId) => {
37490
+ const job = queueRef.current?.snapshot().find((entry) => entry.request.id === jobId);
37491
+ if (job) void requestAccess({ kind: "resume", uuid: job.request.uuid, jobId });
37492
+ },
37493
+ [requestAccess]
37494
+ );
37495
+ const submitCreate = (0, import_react40.useCallback)(
36136
37496
  async (draft) => {
36137
37497
  setBusy(true);
36138
37498
  try {
@@ -36162,6 +37522,8 @@ function App2({
36162
37522
  );
36163
37523
  use_input_default(
36164
37524
  (input, key) => {
37525
+ if (isCtrl(input, key, "c")) return requestExit();
37526
+ if (view === "files" || view === "transfers" || view === "create" || expired) return;
36165
37527
  if (view === "help") {
36166
37528
  setView("dashboard");
36167
37529
  return;
@@ -36179,15 +37541,30 @@ function App2({
36179
37541
  }
36180
37542
  if (view === "detail") {
36181
37543
  if (input === "p") return setReveal((v) => !v);
37544
+ if (input === "r") return refresh();
37545
+ if (!row) return;
37546
+ if (input === "h") return void requestAccess({ kind: "ssh", uuid: row.instance.uuid });
37547
+ if (input === "f") return void requestAccess({ kind: "files", uuid: row.instance.uuid });
37548
+ if (input === "c") return copySSH(row.instance.uuid);
37549
+ if (input === "t") {
37550
+ try {
37551
+ getQueue();
37552
+ setView("transfers");
37553
+ } catch (err) {
37554
+ flash(err.message);
37555
+ }
37556
+ return;
37557
+ }
37558
+ if (input === "n") return setView("create");
36182
37559
  return;
36183
37560
  }
36184
- if (input === "q" || isCtrl(input, key, "c")) return exit();
37561
+ if (input === "q") return requestExit();
37562
+ if (isCtrl(input, key, "c")) return;
36185
37563
  if (input === "?") return setView("help");
36186
37564
  if (key.upArrow || input === "k") return setSelected((v) => Math.max(0, v - 1));
36187
37565
  if (key.downArrow || input === "j")
36188
37566
  return setSelected((v) => Math.max(0, Math.min(rows.length - 1, v + 1)));
36189
37567
  if (input === "r") return refresh();
36190
- if (input === "n") return setView("create");
36191
37568
  if (isCtrl(input, key, "l")) return setPending({ kind: "logout" });
36192
37569
  if (input === "g") {
36193
37570
  setView("stock");
@@ -36213,37 +37590,31 @@ function App2({
36213
37590
  return stopped ? `\u2714 ${row.instance.uuid} \u5DF2\u5173\u673A\uFF0C\u8BA1\u8D39\u5DF2\u505C\u6B62` : `\u26A0 \u5173\u673A\u672A\u751F\u6548\uFF0C\u72B6\u6001\u4ECD\u4E3A ${status}\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5`;
36214
37591
  });
36215
37592
  }
36216
- if (input === "c") {
36217
- const snapshot = snapshotFor(row.instance.uuid);
36218
- if (!snapshot?.ssh.host || !snapshot.ssh.port) {
36219
- return flash("\u5B9E\u4F8B\u672A\u8FD0\u884C\u6216 SSH \u4FE1\u606F\u5C1A\u672A\u5C31\u7EEA");
36220
- }
36221
- const command = snapshot.ssh.command ?? `ssh -p ${snapshot.ssh.port} root@${snapshot.ssh.host}`;
36222
- void copyToClipboard(command).then((result) => {
36223
- flash(
36224
- result.method === "native" ? `\u2714 \u5DF2\u590D\u5236\uFF1A${command}\u3000\u5BC6\u7801 ${snapshot.ssh.password}` : `${command}\u3000\u5BC6\u7801 ${snapshot.ssh.password}\u3000\uFF08${result.note}\uFF09`
36225
- );
36226
- });
36227
- return;
36228
- }
36229
37593
  if (isCtrl(input, key, "d")) {
36230
37594
  if (busy) return;
36231
37595
  return setPending({ kind: "release", row });
36232
37596
  }
36233
37597
  },
36234
- { isActive: view !== "create" && pending === null && expired === null }
37598
+ {
37599
+ isActive: pending === null && !terminalActive
37600
+ }
36235
37601
  );
36236
- const hints = expired ? "Enter \u91CD\u65B0\u767B\u5165 \xB7 q \u9000\u51FA" : pending ? CONFIRM_KEYS : view === "create" ? (
37602
+ const hints = expired && !pending ? "Enter \u91CD\u65B0\u767B\u5165 \xB7 q \u9000\u51FA" : pending ? CONFIRM_KEYS : view === "files" || view === "transfers" ? "Esc \u8FD4\u56DE \xB7 ctrl+c \u9000\u51FA\uFF08\u672A\u5B8C\u6210\u4EFB\u52A1\u5148\u786E\u8BA4\uFF09" : view === "create" ? (
36237
37603
  // Step-agnostic on purpose: the wizard's own line says what Enter does at this
36238
37604
  // step, and only Esc is true at every one of them.
36239
37605
  "Esc \u53D6\u6D88"
36240
- ) : view === "help" ? "\u6309\u4EFB\u610F\u952E\u8FD4\u56DE" : view === "detail" ? "p \u663E\u793A/\u9690\u85CF\u5BC6\u7801 \xB7 Esc \u8FD4\u56DE" : view === "stock" ? "\u2191\u2193 \u79FB\u52A8 \xB7 r \u5237\u65B0 \xB7 Esc \u8FD4\u56DE" : DASHBOARD_KEYS;
37606
+ ) : view === "help" ? "\u6309\u4EFB\u610F\u952E\u8FD4\u56DE" : view === "detail" ? "h SSH\u767B\u5F55 \xB7 f \u6587\u4EF6 \xB7 t \u4F20\u8F93 \xB7 c \u590D\u5236SSH \xB7 n \u65B0\u5EFA \xB7 p \u663E\u793A/\u9690\u85CF\u5BC6\u7801 \xB7 Esc \u8FD4\u56DE" : view === "stock" ? "\u2191\u2193 \u79FB\u52A8 \xB7 r \u5237\u65B0 \xB7 Esc \u8FD4\u56DE" : DASHBOARD_KEYS;
36241
37607
  const showsDashboard = !expired && !pending && view === "dashboard";
37608
+ const showsFiles = !expired && !pending && (view === "files" || view === "transfers");
36242
37609
  return (
36243
37610
  // Claim the entire terminal so the dashboard is a fixed full-screen surface rather
36244
37611
  // than a block that grows and shrinks with its content.
36245
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Box_default, { flexDirection: "column", height: terminalRows, width: columns2, children: [
36246
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
37612
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Box_default, { flexDirection: "column", height: terminalRows, width: columns2, children: [
37613
+ showsFiles ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Box_default, { height: 1, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Text, { bold: true, wrap: "truncate", children: [
37614
+ "AutoDL \xB7 ",
37615
+ SCREEN_TITLES[view],
37616
+ view === "files" ? ` \xB7 ${fileUuid}` : ""
37617
+ ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
36247
37618
  Header,
36248
37619
  {
36249
37620
  subtitle: SCREEN_TITLES[view],
@@ -36253,27 +37624,56 @@ function App2({
36253
37624
  columns: columns2
36254
37625
  }
36255
37626
  ),
36256
- expired ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
37627
+ expired && !pending ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
36257
37628
  SessionExpired,
36258
37629
  {
36259
37630
  message: expired,
36260
- onRelogin: () => onLogout("\u4E0A\u4E00\u6B21\u4F1A\u8BDD\u7684 Token \u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5165\u3002"),
36261
- onQuit: exit
37631
+ onRelogin: () => void finishLogout("\u4E0A\u4E00\u6B21\u4F1A\u8BDD\u7684 Token \u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5165\u3002"),
37632
+ onQuit: requestExit
36262
37633
  }
36263
- ) : pending?.kind === "logout" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
37634
+ ) : pending?.kind === "logout" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
36264
37635
  Confirm,
36265
37636
  {
36266
37637
  title: "\u9000\u51FA\u767B\u5F55\uFF1F",
36267
- detail: `\u4F1A\u6E05\u9664\u4FDD\u5B58\u5728 ${configPath()} \u7684 Token\uFF0C\u5E76\u8FD4\u56DE\u767B\u5165\u754C\u9762\u3002\u5B9E\u4F8B\u4E0D\u53D7\u5F71\u54CD\uFF0C\u8FD0\u884C\u4E2D\u7684\u5B9E\u4F8B\u4F1A\u7EE7\u7EED\u8BA1\u8D39\u3002`,
37638
+ detail: `\u4F1A\u5148\u6682\u505C\u672A\u5B8C\u6210\u4F20\u8F93\uFF0C\u6E05\u9664\u4FDD\u5B58\u5728 ${configPath()} \u7684 Token\uFF0C\u5E76\u8FD4\u56DE\u767B\u5165\u754C\u9762\u3002\u5B9E\u4F8B\u7EE7\u7EED\u8BA1\u8D39\uFF1B\u4F20\u8F93\u8BB0\u5F55\u4FDD\u7559\uFF0C\u91CD\u65B0\u767B\u5F55\u540E\u53EF\u624B\u52A8\u6062\u590D\u3002`,
36268
37639
  danger: tokenOverrideNote(tokenSource) ?? void 0,
36269
37640
  confirmLabel: "\u9000\u51FA\u767B\u5F55",
37641
+ onConfirm: () => void finishLogout(),
37642
+ onCancel: () => setPending(null)
37643
+ }
37644
+ ) : pending?.kind === "exit" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
37645
+ Confirm,
37646
+ {
37647
+ title: "\u6682\u505C\u4F20\u8F93\u5E76\u9000\u51FA\uFF1F",
37648
+ detail: "\u5F53\u524D\u548C\u6392\u961F\u4EFB\u52A1\u5C06\u6682\u505C\uFF0C\u4FDD\u7559\u7EED\u4F20\u6570\u636E\u3002\u4E0B\u6B21\u6253\u5F00\u4F20\u8F93\u961F\u5217\u540E\u53EF\u624B\u52A8\u6062\u590D\uFF1B\u9000\u51FA\u540E\u4E0D\u4F1A\u540E\u53F0\u4F20\u8F93\u3002",
37649
+ confirmLabel: "\u6682\u505C\u5E76\u9000\u51FA",
37650
+ onConfirm: () => void finishExit(),
37651
+ onCancel: () => setPending(null)
37652
+ }
37653
+ ) : pending?.kind === "power" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
37654
+ Confirm,
37655
+ {
37656
+ title: `\u542F\u52A8\u5B9E\u4F8B ${pending.action.uuid}\uFF1F`,
37657
+ detail: "\u5B9E\u4F8B\u5C1A\u672A\u8FD0\u884C\u3002\u786E\u8BA4\u540E\u7B49\u5F85\u5F00\u673A\u548C SSH \u5C31\u7EEA\uFF0C\u518D\u7EE7\u7EED\u521A\u624D\u7684\u64CD\u4F5C\u3002",
37658
+ danger: "\u5F00\u673A\u4F1A\u4EA7\u751F\u5B9E\u4F8B\u8D39\u7528\u3002\u81EA\u52A8\u91CD\u8FDE\u4E0D\u4F1A\u81EA\u52A8\u5F00\u673A\u3002",
37659
+ confirmLabel: "\u5F00\u673A\u5E76\u7EE7\u7EED",
36270
37660
  onConfirm: () => {
37661
+ const action = pending.action;
36271
37662
  setPending(null);
36272
- onLogout();
37663
+ void act("\u5F00\u673A\u5E76\u8FDE\u63A5", async () => {
37664
+ const controller = new AbortController();
37665
+ accessController.current = controller;
37666
+ await getCredentials(client, action.uuid, {
37667
+ autoStart: true,
37668
+ signal: controller.signal
37669
+ });
37670
+ await performAccess(action, controller.signal);
37671
+ return "\u64CD\u4F5C\u5DF2\u7EE7\u7EED";
37672
+ });
36273
37673
  },
36274
37674
  onCancel: () => setPending(null)
36275
37675
  }
36276
- ) : pending ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
37676
+ ) : pending?.kind === "release" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
36277
37677
  Confirm,
36278
37678
  {
36279
37679
  title: `\u91CA\u653E\u5B9E\u4F8B ${pending.row.instance.name || pending.row.instance.uuid}\uFF1F`,
@@ -36290,18 +37690,39 @@ function App2({
36290
37690
  },
36291
37691
  onCancel: () => setPending(null)
36292
37692
  }
36293
- ) : view === "create" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CreateWizard, { busy, onSubmit: submitCreate, onCancel: () => setView("dashboard") }) : view === "detail" && row ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Detail, { row, snapshot: snapshotFor(row.instance.uuid), revealSecrets: reveal }) : view === "stock" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
37693
+ ) : view === "files" && workspace && queueRef.current ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
37694
+ FilesScreen,
37695
+ {
37696
+ workspace,
37697
+ queue: queueRef.current,
37698
+ uuid: fileUuid,
37699
+ width: columns2,
37700
+ height: Math.max(6, terminalRows - 2),
37701
+ onBack: () => setView("dashboard"),
37702
+ onResume: resumeTransfer,
37703
+ ...fileGate ? { whenRunning: fileGate } : {}
37704
+ }
37705
+ ) : view === "transfers" && queueRef.current ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
37706
+ TransferJobs,
37707
+ {
37708
+ queue: queueRef.current,
37709
+ width: columns2,
37710
+ height: Math.max(6, terminalRows - 2),
37711
+ onBack: () => setView("dashboard"),
37712
+ onResume: resumeTransfer
37713
+ }
37714
+ ) : view === "create" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CreateWizard, { busy, onSubmit: submitCreate, onCancel: () => setView("dashboard") }) : view === "detail" && row ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Detail, { row, snapshot: snapshotFor(row.instance.uuid), revealSecrets: reveal }) : view === "stock" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
36294
37715
  StockScreen,
36295
37716
  {
36296
37717
  rows: toStockRows(stock, false),
36297
37718
  selectedIndex: stockIndex,
36298
37719
  loading: stockLoading
36299
37720
  }
36300
- ) : view === "help" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", paddingX: 1, children: [
36301
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { bold: true, children: "\u5FEB\u6377\u952E" }),
36302
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Box_default, { children: HELP_COLUMNS.map((column) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Box_default, { flexDirection: "column", width: 22, children: column.map((item) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { children: item }, item)) }, column[0])) }),
36303
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Text, { dimColor: true, children: "\u6309\u4EFB\u610F\u952E\u8FD4\u56DE" })
36304
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
37721
+ ) : view === "help" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", paddingX: 1, children: [
37722
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { bold: true, children: "\u5FEB\u6377\u952E" }),
37723
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Box_default, { children: HELP_COLUMNS.map((column) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Box_default, { flexDirection: "column", width: 22, children: column.map((item) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { children: item }, item)) }, column[0])) }),
37724
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { dimColor: true, children: "\u6309\u4EFB\u610F\u952E\u8FD4\u56DE" })
37725
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
36305
37726
  Dashboard,
36306
37727
  {
36307
37728
  rows,
@@ -36314,13 +37735,13 @@ function App2({
36314
37735
  height: terminalRows
36315
37736
  }
36316
37737
  ),
36317
- showsDashboard ? null : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Box_default, { flexGrow: 1 }),
36318
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
37738
+ showsDashboard || showsFiles ? null : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Box_default, { flexGrow: 1 }),
37739
+ showsFiles ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Box_default, { height: 1, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: "cyan", wrap: "truncate", children: notice ?? "ctrl+c \u9000\u51FA\uFF08\u672A\u5B8C\u6210\u4F20\u8F93\u5148\u786E\u8BA4\uFF09" }) }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
36319
37740
  StatusBar,
36320
37741
  {
36321
37742
  rows,
36322
37743
  error: expired ? null : error,
36323
- notice,
37744
+ notice: notice ?? queueNotice,
36324
37745
  lastUpdated,
36325
37746
  hints
36326
37747
  }
@@ -36330,20 +37751,21 @@ function App2({
36330
37751
  }
36331
37752
  function Root({ globals }) {
36332
37753
  const { exit } = use_app_default();
36333
- const [session, setSession] = (0, import_react39.useState)(() => {
37754
+ const [session, setSession] = (0, import_react40.useState)(() => {
36334
37755
  const resolved = tryResolveToken(globals.token);
36335
37756
  if (!resolved) return null;
36336
37757
  const baseUrl = resolveBaseUrl(globals.baseUrl);
36337
37758
  return {
36338
37759
  client: new AutoDLClient({ token: resolved.token, ...baseUrl ? { baseUrl } : {} }),
36339
37760
  token: resolved.token,
36340
- source: resolved.source
37761
+ source: resolved.source,
37762
+ baseUrl: baseUrl ?? DEFAULT_BASE_URL
36341
37763
  };
36342
37764
  });
36343
- const [error, setError] = (0, import_react39.useState)(null);
36344
- const [notice, setNotice] = (0, import_react39.useState)(null);
36345
- const [verifying, setVerifying] = (0, import_react39.useState)(false);
36346
- const submit = (0, import_react39.useCallback)(
37765
+ const [error, setError] = (0, import_react40.useState)(null);
37766
+ const [notice, setNotice] = (0, import_react40.useState)(null);
37767
+ const [verifying, setVerifying] = (0, import_react40.useState)(false);
37768
+ const submit = (0, import_react40.useCallback)(
36347
37769
  async (token) => {
36348
37770
  setVerifying(true);
36349
37771
  setError(null);
@@ -36359,7 +37781,12 @@ function Root({ globals }) {
36359
37781
  });
36360
37782
  if (client !== candidate) await getBalance(client);
36361
37783
  setNotice(null);
36362
- setSession({ client, token: resolved.token, source: resolved.source });
37784
+ setSession({
37785
+ client,
37786
+ token: resolved.token,
37787
+ source: resolved.source,
37788
+ baseUrl: baseUrl ?? DEFAULT_BASE_URL
37789
+ });
36363
37790
  } catch (err) {
36364
37791
  setError(err.message);
36365
37792
  } finally {
@@ -36368,7 +37795,7 @@ function Root({ globals }) {
36368
37795
  },
36369
37796
  [globals.baseUrl, globals.token]
36370
37797
  );
36371
- const logout = (0, import_react39.useCallback)(
37798
+ const logout = (0, import_react40.useCallback)(
36372
37799
  (reason) => {
36373
37800
  clearToken();
36374
37801
  const override = session ? tokenOverrideNote(session.source) : null;
@@ -36379,18 +37806,19 @@ function Root({ globals }) {
36379
37806
  [session]
36380
37807
  );
36381
37808
  if (!session) {
36382
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Login, { onSubmit: submit, onQuit: exit, error, verifying, notice });
37809
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Login, { onSubmit: submit, onQuit: exit, error, verifying, notice });
36383
37810
  }
36384
37811
  return (
36385
37812
  // Keyed on the token so a re-login remounts: the previous session's instances,
36386
37813
  // balance and error state must not bleed into the new account's dashboard.
36387
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
37814
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
36388
37815
  App2,
36389
37816
  {
36390
37817
  client: session.client,
36391
37818
  token: session.token,
36392
37819
  tokenSource: session.source,
36393
- onLogout: logout
37820
+ onLogout: logout,
37821
+ baseUrl: session.baseUrl
36394
37822
  },
36395
37823
  session.token
36396
37824
  )
@@ -36403,24 +37831,29 @@ async function runTui(globals = {}) {
36403
37831
  const previous = { json: isJson(), verbose: isVerbose() };
36404
37832
  configureOutput({ quiet: true });
36405
37833
  let restored = false;
37834
+ let app;
36406
37835
  const restore = () => {
36407
37836
  if (restored) return;
36408
37837
  restored = true;
36409
37838
  process.stdout.write(LEAVE_ALT_SCREEN);
36410
37839
  configureOutput({ quiet: false, json: previous.json, verbose: previous.verbose });
36411
37840
  };
37841
+ const stop = () => {
37842
+ app?.unmount();
37843
+ restore();
37844
+ };
36412
37845
  process.once("exit", restore);
36413
- process.once("SIGINT", restore);
36414
- process.once("SIGTERM", restore);
37846
+ process.once("SIGINT", stop);
37847
+ process.once("SIGTERM", stop);
36415
37848
  process.stdout.write(ENTER_ALT_SCREEN + CLEAR);
36416
37849
  try {
36417
- const app = render_default(/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Root, { globals }));
37850
+ app = render_default(/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Root, { globals }), { exitOnCtrlC: false });
36418
37851
  await app.waitUntilExit();
36419
37852
  } finally {
36420
37853
  restore();
36421
37854
  process.off("exit", restore);
36422
- process.off("SIGINT", restore);
36423
- process.off("SIGTERM", restore);
37855
+ process.off("SIGINT", stop);
37856
+ process.off("SIGTERM", stop);
36424
37857
  }
36425
37858
  }
36426
37859
  export {
@@ -36540,4 +37973,4 @@ react/cjs/react-jsx-runtime.development.js:
36540
37973
  * LICENSE file in the root directory of this source tree.
36541
37974
  *)
36542
37975
  */
36543
- //# sourceMappingURL=app-3QXI65BE.js.map
37976
+ //# sourceMappingURL=app-V5GCG3MC.js.map