alink-cli 0.8.4 → 0.8.5

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.
Files changed (2) hide show
  1. package/dist/ui.js +160 -51
  2. package/package.json +1 -1
package/dist/ui.js CHANGED
@@ -48397,7 +48397,7 @@ function SimpleInput({ label, value, onChange, onSubmit, onCancel, mask }) {
48397
48397
  onSubmit();
48398
48398
  return;
48399
48399
  }
48400
- if (key.escape) {
48400
+ if (key.escape || key.ctrl && input === "c") {
48401
48401
  onCancel();
48402
48402
  return;
48403
48403
  }
@@ -48419,8 +48419,10 @@ function SimpleInput({ label, value, onChange, onSubmit, onCancel, mask }) {
48419
48419
  ] });
48420
48420
  }
48421
48421
  function StatusDetail({ status, running, onBack }) {
48422
- use_input_default((_, key) => {
48422
+ const { exit } = use_app_default();
48423
+ use_input_default((input, key) => {
48423
48424
  if (key.return || key.escape) onBack();
48425
+ if (key.ctrl && input === "c") exit();
48424
48426
  });
48425
48427
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
48426
48428
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { bold: true, color: "cyanBright", children: "\u25C6 Daemon \u72B6\u6001\u8BE6\u60C5" }),
@@ -48458,14 +48460,14 @@ function StatusDetail({ status, running, onBack }) {
48458
48460
  function ConfirmDialog({ title, onConfirm, onCancel }) {
48459
48461
  const [selected, setSelected] = (0, import_react22.useState)(0);
48460
48462
  const options = ["\u786E\u8BA4", "\u53D6\u6D88"];
48461
- use_input_default((_, key) => {
48463
+ use_input_default((input, key) => {
48462
48464
  if (key.leftArrow || key.upArrow) setSelected((s) => s === 0 ? 1 : 0);
48463
48465
  if (key.rightArrow || key.downArrow) setSelected((s) => s === 0 ? 1 : 0);
48464
48466
  if (key.return) {
48465
48467
  if (selected === 0) onConfirm();
48466
48468
  else onCancel();
48467
48469
  }
48468
- if (key.escape) onCancel();
48470
+ if (key.escape || key.ctrl && input === "c") onCancel();
48469
48471
  });
48470
48472
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [
48471
48473
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { bold: true, color: "yellow", children: [
@@ -48479,8 +48481,10 @@ function ConfirmDialog({ title, onConfirm, onCancel }) {
48479
48481
  ] });
48480
48482
  }
48481
48483
  function HelpView({ onBack }) {
48482
- use_input_default((_, key) => {
48484
+ const { exit } = use_app_default();
48485
+ use_input_default((input, key) => {
48483
48486
  if (key.return || key.escape) onBack();
48487
+ if (key.ctrl && input === "c") exit();
48484
48488
  });
48485
48489
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
48486
48490
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { bold: true, color: "cyanBright", children: "\u25C6 \u5FEB\u6377\u5E2E\u52A9" }),
@@ -48565,7 +48569,7 @@ function App2() {
48565
48569
  } else if (key.return) {
48566
48570
  const item = menuItems[selectedIndex];
48567
48571
  if (item && !item.disabled) handleMenu(item.value);
48568
- } else if (key.escape || input === "q") {
48572
+ } else if (key.escape || input === "q" || key.ctrl && input === "c") {
48569
48573
  exit();
48570
48574
  }
48571
48575
  });
@@ -48583,7 +48587,7 @@ function App2() {
48583
48587
  }
48584
48588
  const credential = readCredential(cfg.hub);
48585
48589
  if (!credential) {
48586
- setNotice("\u672A\u767B\u5F55\uFF0C\u8BF7\u5148\u767B\u5F55\u6216\u914D\u5BF9");
48590
+ setNotice("\u672A\u767B\u5F55\uFF0C\u8BF7\u5148\u767B\u5F55");
48587
48591
  return;
48588
48592
  }
48589
48593
  if (!existsSync3(daemonBundlePath())) {
@@ -48628,10 +48632,6 @@ function App2() {
48628
48632
  setView("login");
48629
48633
  return;
48630
48634
  }
48631
- case "pair": {
48632
- setView("pair");
48633
- return;
48634
- }
48635
48635
  case "logout": {
48636
48636
  setConfirmAction({ type: "logout" });
48637
48637
  setView("confirm");
@@ -48688,6 +48688,7 @@ function App2() {
48688
48688
  stopDaemon().catch(() => void 0);
48689
48689
  clearCredential();
48690
48690
  setRunning(false);
48691
+ setAuthTick((t) => t + 1);
48691
48692
  setNotice("\u5DF2\u9000\u51FA\u8D26\u53F7\u5E76\u505C\u6B62 daemon");
48692
48693
  }
48693
48694
  setView("menu");
@@ -48738,7 +48739,16 @@ function App2() {
48738
48739
  }
48739
48740
  );
48740
48741
  case "pair":
48741
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PairView, { onDone: () => setView("menu"), onCancel: () => setView("menu") });
48742
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
48743
+ PairView,
48744
+ {
48745
+ onDone: () => {
48746
+ setAuthTick((t) => t + 1);
48747
+ setView("menu");
48748
+ },
48749
+ onCancel: () => setView("menu")
48750
+ }
48751
+ );
48742
48752
  case "machines":
48743
48753
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MachinesView, { onDone: () => setView("menu") });
48744
48754
  case "menu":
@@ -48792,7 +48802,7 @@ function SettingsPanel({
48792
48802
  opt.value === "approvalRenotifyMin" ? String(config.approvalRenotifyMin) : config[opt.value]
48793
48803
  );
48794
48804
  }
48795
- } else if (key.escape) {
48805
+ } else if (key.escape || key.ctrl && input === "c") {
48796
48806
  onBack();
48797
48807
  }
48798
48808
  });
@@ -48831,17 +48841,40 @@ function SettingsPanel({
48831
48841
  function runDetachedScript(args, options = {}) {
48832
48842
  return new Promise((resolve2) => {
48833
48843
  const binPath = fileURLToPath2(new URL("../bin/agentlink.js", import.meta.url));
48834
- const parentRaw = process.stdin.isTTY && process.stdin.isRaw;
48844
+ const stdin = process.stdin;
48845
+ const parentRaw = stdin.isTTY ? stdin.isRaw ?? false : false;
48846
+ let finished = false;
48847
+ const cleanup = (code) => {
48848
+ if (finished) return;
48849
+ finished = true;
48850
+ if (stdin.isTTY && typeof stdin.setRawMode === "function") {
48851
+ try {
48852
+ stdin.setRawMode(parentRaw);
48853
+ } catch {
48854
+ }
48855
+ }
48856
+ for (const s of signalHandlers) process.off(s, forwardSignal);
48857
+ resolve2(code);
48858
+ };
48835
48859
  const child = spawn2(process.execPath, [binPath, ...args, "--no-tui"], {
48836
48860
  stdio: "inherit",
48837
48861
  env: { ...process.env, ...options.env }
48838
48862
  });
48839
- child.on("exit", (code) => {
48840
- if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") {
48841
- process.stdin.setRawMode(parentRaw);
48842
- }
48843
- resolve2(code ?? 1);
48863
+ child.on("exit", (code) => cleanup(code ?? 1));
48864
+ child.on("error", (err) => {
48865
+ console.error(`\u5B50\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`);
48866
+ cleanup(1);
48844
48867
  });
48868
+ const signalHandlers = ["SIGINT", "SIGTERM"];
48869
+ const forwardSignal = (signal) => {
48870
+ if (child.pid) {
48871
+ try {
48872
+ child.kill(signal);
48873
+ } catch {
48874
+ }
48875
+ }
48876
+ };
48877
+ for (const s of signalHandlers) process.on(s, forwardSignal);
48845
48878
  });
48846
48879
  }
48847
48880
  function LoginView({
@@ -48854,13 +48887,13 @@ function LoginView({
48854
48887
  const [identifier, setIdentifier] = (0, import_react22.useState)("");
48855
48888
  const [inputVal, setInputVal] = (0, import_react22.useState)("");
48856
48889
  const [error, setError] = (0, import_react22.useState)("");
48857
- use_input_default((_, key) => {
48890
+ use_input_default((input, key) => {
48858
48891
  if (stage !== "error") return;
48859
48892
  if (key.return) {
48860
48893
  setError("");
48861
48894
  setInputVal("");
48862
48895
  setStage("password");
48863
- } else if (key.escape) {
48896
+ } else if (key.escape || key.ctrl && input === "c") {
48864
48897
  onCancel();
48865
48898
  }
48866
48899
  });
@@ -48935,31 +48968,60 @@ function saveSessionJwt(jwt, hub) {
48935
48968
  const sessionFile = join2(STATE_DIR, "session");
48936
48969
  writeFileSync2(sessionFile, JSON.stringify({ hub: normalizeHub(hub), jwt }), { mode: 384 });
48937
48970
  }
48971
+ function savedSessionJwt(hub = OFFICIAL_HUB) {
48972
+ const sessionFile = join2(STATE_DIR, "session");
48973
+ try {
48974
+ const raw = readFileSync3(sessionFile, "utf8").trim();
48975
+ if (!raw) return void 0;
48976
+ if (!raw.startsWith("{")) {
48977
+ return normalizeHub(hub) === normalizeHub(OFFICIAL_HUB) ? raw : void 0;
48978
+ }
48979
+ const saved = JSON.parse(raw);
48980
+ return saved.hub === normalizeHub(hub) && typeof saved.jwt === "string" ? saved.jwt : void 0;
48981
+ } catch {
48982
+ return void 0;
48983
+ }
48984
+ }
48938
48985
  function PairView({ onDone, onCancel }) {
48939
- const { exit } = use_app_default();
48940
48986
  const [stage, setStage] = (0, import_react22.useState)("confirm");
48941
- use_input_default(async (_, key) => {
48987
+ const [error, setError] = (0, import_react22.useState)("");
48988
+ use_input_default(async (input, key) => {
48942
48989
  if (stage === "confirm") {
48943
48990
  if (key.return) {
48944
48991
  setStage("running");
48945
- await runDetachedScript(["--pair"]);
48946
- setStage("done");
48947
- } else if (key.escape) {
48992
+ const code = await runDetachedScript(["--pair"]);
48993
+ if (code === 0) {
48994
+ onDone();
48995
+ } else {
48996
+ setError(`\u914D\u5BF9\u6D41\u7A0B\u5F02\u5E38\u7ED3\u675F\uFF08\u9000\u51FA\u7801 ${code}\uFF09\u3002`);
48997
+ setStage("error");
48998
+ }
48999
+ } else if (key.escape || key.ctrl && input === "c") {
48948
49000
  onCancel();
48949
49001
  }
48950
- } else if (stage === "done") {
48951
- if (key.return || key.escape) {
48952
- exit();
49002
+ } else if (stage === "error") {
49003
+ if (key.return) {
49004
+ setError("");
49005
+ setStage("confirm");
49006
+ } else if (key.escape || key.ctrl && input === "c") {
49007
+ onCancel();
48953
49008
  }
48954
49009
  }
48955
49010
  });
48956
49011
  if (stage === "running") {
48957
49012
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "cyan", children: "\u23F3 \u6B63\u5728\u6253\u5F00\u626B\u7801\u914D\u5BF9\u6D41\u7A0B..." });
48958
49013
  }
48959
- if (stage === "done") {
48960
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "green", children: "\u2714 \u914D\u5BF9\u6D41\u7A0B\u7ED3\u675F\u3002\u6309 Enter \u9000\u51FA CLI\u3002" }) });
49014
+ if (stage === "error") {
49015
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 1, children: [
49016
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { color: "redBright", children: [
49017
+ "\u2716 ",
49018
+ error
49019
+ ] }),
49020
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "gray", children: "Enter \u91CD\u8BD5 \xB7 Esc \u8FD4\u56DE\u83DC\u5355" })
49021
+ ] });
48961
49022
  }
48962
49023
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
49024
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { bold: true, color: "cyanBright", children: "\u25C6 \u626B\u7801\u914D\u5BF9" }),
48963
49025
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "white", children: "\u5C06\u9000\u51FA TUI \u5E76\u8FDB\u5165\u626B\u7801\u914D\u5BF9\u6D41\u7A0B\u3002" }),
48964
49026
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "gray", children: "\u914D\u5BF9\u540E\u53EF\u5728\u6D4F\u89C8\u5668\u76F4\u63A5\u4F7F\u7528\uFF0C\u65E0\u9700\u6CE8\u518C\u6216\u767B\u5F55\u8D26\u53F7\u3002" }),
48965
49027
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "gray", children: "\u4F1A\u8BDD\u5728\u6D4F\u89C8\u5668\u4E0E\u672C\u673A\u4E4B\u95F4\u7AEF\u5230\u7AEF\u52A0\u5BC6\uFF0C\u670D\u52A1\u5668\u53EA\u4E2D\u8F6C\u3001\u770B\u4E0D\u5230\u660E\u6587\u3002" }),
@@ -48969,31 +49031,78 @@ function PairView({ onDone, onCancel }) {
48969
49031
  }
48970
49032
  function MachinesView({ onDone }) {
48971
49033
  const { exit } = use_app_default();
48972
- const [stage, setStage] = (0, import_react22.useState)("confirm");
48973
- use_input_default(async (_, key) => {
48974
- if (stage === "confirm") {
48975
- if (key.return) {
48976
- setStage("running");
48977
- await runDetachedScript(["machines"]);
48978
- setStage("done");
48979
- } else if (key.escape) {
48980
- onDone();
48981
- }
48982
- } else if (stage === "done") {
48983
- if (key.return || key.escape) {
48984
- exit();
49034
+ const [state, setState] = (0, import_react22.useState)("loading");
49035
+ const [machines, setMachines] = (0, import_react22.useState)([]);
49036
+ const [error, setError] = (0, import_react22.useState)("");
49037
+ use_input_default((input, key) => {
49038
+ if (key.escape) onDone();
49039
+ if (key.ctrl && input === "c") exit();
49040
+ });
49041
+ (0, import_react22.useEffect)(() => {
49042
+ let cancelled = false;
49043
+ async function load() {
49044
+ try {
49045
+ const hub = loadConfig().hub;
49046
+ const account = await import(new URL("../bin/agentlink-account.js", import.meta.url).href);
49047
+ const config = await account.loadAgentLinkAccountConfig(hub);
49048
+ if (config?.mode !== "multi") throw new Error("\u5F53\u524D Hub \u4E0D\u4F7F\u7528\u8D26\u53F7\u673A\u5668\u76EE\u5F55\u3002");
49049
+ let jwt = savedSessionJwt(hub);
49050
+ if (!jwt) {
49051
+ const credential = readCredential(hub);
49052
+ if (credential) {
49053
+ jwt = await account.exchangeCredentialForSessionJwt({ hub, credential });
49054
+ } else {
49055
+ throw new Error("\u6CA1\u6709\u53EF\u7528\u7684\u767B\u5F55\u4F1A\u8BDD\u6216\u673A\u5668\u51ED\u8BC1\uFF0C\u8BF7\u5148\u767B\u5F55\u3002");
49056
+ }
49057
+ }
49058
+ if (jwt) saveSessionJwt(jwt, hub);
49059
+ const list = await account.listAgentLinkAccountMachinesFromHub({ hub, jwt });
49060
+ if (!cancelled) {
49061
+ setMachines(list);
49062
+ setState("list");
49063
+ }
49064
+ } catch (err) {
49065
+ if (!cancelled) {
49066
+ setError(err instanceof Error ? err.message : String(err));
49067
+ setState("error");
49068
+ }
48985
49069
  }
48986
49070
  }
48987
- });
48988
- if (stage === "running") {
48989
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "cyan", children: "\u23F3 \u6B63\u5728\u6253\u5F00\u673A\u5668\u5217\u8868..." });
49071
+ load();
49072
+ return () => {
49073
+ cancelled = true;
49074
+ };
49075
+ }, []);
49076
+ if (state === "loading") {
49077
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
49078
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { bold: true, color: "cyanBright", children: "\u25C6 \u673A\u5668\u5217\u8868" }),
49079
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "cyan", children: "\u23F3 \u6B63\u5728\u52A0\u8F7D..." }),
49080
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "gray", dimColor: true, children: "\u6309 Esc \u8FD4\u56DE \xB7 Ctrl+C \u9000\u51FA" })
49081
+ ] });
48990
49082
  }
48991
- if (stage === "done") {
48992
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "green", children: "\u2714 \u673A\u5668\u5217\u8868\u5DF2\u5173\u95ED\u3002\u6309 Enter \u9000\u51FA CLI\u3002" }) });
49083
+ if (state === "error") {
49084
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 1, children: [
49085
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { bold: true, color: "redBright", children: "\u2716 \u52A0\u8F7D\u5931\u8D25" }),
49086
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "white", children: error }),
49087
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "gray", dimColor: true, children: "\u6309 Esc \u8FD4\u56DE \xB7 Ctrl+C \u9000\u51FA" })
49088
+ ] });
48993
49089
  }
48994
49090
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
48995
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "white", children: "\u5C06\u9000\u51FA TUI \u5E76\u663E\u793A\u8D26\u53F7\u4E0B\u7684\u673A\u5668\u5217\u8868\u3002" }),
48996
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "gray", children: "Enter \u7EE7\u7EED \xB7 Esc \u53D6\u6D88" })
49091
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { bold: true, color: "cyanBright", children: "\u25C6 \u673A\u5668\u5217\u8868" }),
49092
+ machines.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "gray", children: "\u8D26\u53F7\u4E0B\u8FD8\u6CA1\u6709\u673A\u5668\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { flexDirection: "column", marginTop: 1, children: machines.map((m) => {
49093
+ const label = m.name || m.hostname || m.machineId;
49094
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
49095
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: m.online ? "green" : "gray", children: m.online ? "\u25CF" : "\u25CB" }),
49096
+ " ",
49097
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "white", children: label }),
49098
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { color: "gray", children: [
49099
+ " (#",
49100
+ m.machineId.slice(-6),
49101
+ ")"
49102
+ ] })
49103
+ ] }, m.machineId);
49104
+ }) }),
49105
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "gray", dimColor: true, children: "\u6309 Esc \u8FD4\u56DE \xB7 Ctrl+C \u9000\u51FA" })
48997
49106
  ] });
48998
49107
  }
48999
49108
  if (process.env.AGENTLINK_RENDER_TUI === "1") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alink-cli",
3
- "version": "0.8.4",
3
+ "version": "0.8.5",
4
4
  "description": "一条命令把工作机接入 AgentLink,随时随地遥控本机的编码 agent。One command to link your machine to AgentLink and control your coding agents from anywhere.",
5
5
  "keywords": [
6
6
  "acp",