@cli-remote/local 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2265,7 +2265,7 @@ var require_websocket = __commonJS({
2265
2265
  var http = require("http");
2266
2266
  var net = require("net");
2267
2267
  var tls = require("tls");
2268
- var { randomBytes: randomBytes3, createHash } = require("crypto");
2268
+ var { randomBytes: randomBytes3, createHash: createHash2 } = require("crypto");
2269
2269
  var { Duplex, Readable } = require("stream");
2270
2270
  var { URL: URL2 } = require("url");
2271
2271
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -2933,7 +2933,7 @@ var require_websocket = __commonJS({
2933
2933
  abortHandshake(websocket, socket, "Invalid Upgrade header");
2934
2934
  return;
2935
2935
  }
2936
- const digest = createHash("sha1").update(key + GUID).digest("base64");
2936
+ const digest = createHash2("sha1").update(key + GUID).digest("base64");
2937
2937
  if (res.headers["sec-websocket-accept"] !== digest) {
2938
2938
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
2939
2939
  return;
@@ -3302,7 +3302,7 @@ var require_websocket_server = __commonJS({
3302
3302
  var EventEmitter = require("events");
3303
3303
  var http = require("http");
3304
3304
  var { Duplex } = require("stream");
3305
- var { createHash } = require("crypto");
3305
+ var { createHash: createHash2 } = require("crypto");
3306
3306
  var extension2 = require_extension();
3307
3307
  var PerMessageDeflate2 = require_permessage_deflate();
3308
3308
  var subprotocol2 = require_subprotocol();
@@ -3609,7 +3609,7 @@ var require_websocket_server = __commonJS({
3609
3609
  );
3610
3610
  }
3611
3611
  if (this._state > RUNNING) return abortHandshake(socket, 503);
3612
- const digest = createHash("sha1").update(key + GUID).digest("base64");
3612
+ const digest = createHash2("sha1").update(key + GUID).digest("base64");
3613
3613
  const headers = [
3614
3614
  "HTTP/1.1 101 Switching Protocols",
3615
3615
  "Upgrade: websocket",
@@ -9940,9 +9940,9 @@ var require_lib = __commonJS({
9940
9940
 
9941
9941
  // src/index.ts
9942
9942
  var import_node_fs5 = require("node:fs");
9943
- var import_node_child_process3 = require("node:child_process");
9944
- var import_node_os7 = require("node:os");
9945
- var import_node_path7 = require("node:path");
9943
+ var import_node_child_process4 = require("node:child_process");
9944
+ var import_node_os8 = require("node:os");
9945
+ var import_node_path8 = require("node:path");
9946
9946
  var import_node_url = require("node:url");
9947
9947
 
9948
9948
  // ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/wrapper.mjs
@@ -9957,11 +9957,11 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
9957
9957
  var wrapper_default = import_websocket.default;
9958
9958
 
9959
9959
  // src/client.ts
9960
- var import_node_child_process2 = require("node:child_process");
9960
+ var import_node_child_process3 = require("node:child_process");
9961
9961
  var import_node_readline = require("node:readline");
9962
9962
  var import_node_fs4 = require("node:fs");
9963
- var import_node_os6 = require("node:os");
9964
- var import_node_path6 = require("node:path");
9963
+ var import_node_os7 = require("node:os");
9964
+ var import_node_path7 = require("node:path");
9965
9965
 
9966
9966
  // ../shared/src/codec.ts
9967
9967
  var import_msgpack = __toESM(require_dist(), 1);
@@ -10042,6 +10042,10 @@ function buildEnv() {
10042
10042
  }
10043
10043
  delete env.NO_COLOR;
10044
10044
  env.COLORTERM = "truecolor";
10045
+ if (!env.LC_ALL && !env.LANG) {
10046
+ env.LANG = process.env.PTY_LANG || "zh_CN.UTF-8";
10047
+ }
10048
+ env.TERM ||= "xterm-256color";
10045
10049
  if (process.env.PTY_HOME) env.HOME = process.env.PTY_HOME;
10046
10050
  return env;
10047
10051
  }
@@ -10602,18 +10606,148 @@ function startSetupCallback(timeoutMs = 3 * 6e4) {
10602
10606
  }
10603
10607
 
10604
10608
  // src/auth.ts
10609
+ var import_node_child_process2 = require("node:child_process");
10610
+ var import_node_crypto3 = require("node:crypto");
10611
+ var import_promises3 = require("node:fs/promises");
10612
+ var import_node_os6 = require("node:os");
10613
+ var import_node_path6 = require("node:path");
10614
+ var import_qrcode = __toESM(require_lib(), 1);
10615
+
10616
+ // src/machine-id.ts
10605
10617
  var import_node_child_process = require("node:child_process");
10606
10618
  var import_node_crypto2 = require("node:crypto");
10607
10619
  var import_promises2 = require("node:fs/promises");
10608
10620
  var import_node_os5 = require("node:os");
10609
10621
  var import_node_path5 = require("node:path");
10610
- var import_qrcode = __toESM(require_lib(), 1);
10622
+ var import_node_util = require("node:util");
10623
+ var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
10624
+ var MACHINE_ID_DOMAIN = "cli-remote.machineid.v1";
10625
+ function normalizeHardwareId(raw) {
10626
+ return raw.trim().toLowerCase().replace(/\s+/g, " ");
10627
+ }
10628
+ var GARBAGE_VALUES = /* @__PURE__ */ new Set([
10629
+ "",
10630
+ "none",
10631
+ "null",
10632
+ "default string",
10633
+ "system serial number",
10634
+ "to be filled by o.e.m.",
10635
+ "to be filled by oem",
10636
+ "not specified",
10637
+ "not available",
10638
+ "unknown",
10639
+ "chassis manufacturer",
10640
+ "no enclosure",
10641
+ "empty"
10642
+ ]);
10643
+ function isValidHardwareId(raw) {
10644
+ const v = normalizeHardwareId(raw);
10645
+ if (v.length < 4) return false;
10646
+ if (GARBAGE_VALUES.has(v)) return false;
10647
+ const digits = v.replace(/[^0-9a-f]/g, "");
10648
+ if (digits.length >= 8 && /^([0-9a-f])\1*$/.test(digits)) return false;
10649
+ return true;
10650
+ }
10651
+ function deriveMachineId(tag, raw) {
10652
+ const h = (0, import_node_crypto2.createHash)("sha256").update(`${MACHINE_ID_DOMAIN}|${tag}|${normalizeHardwareId(raw)}`).digest("hex");
10653
+ return "m_" + h.slice(0, 32);
10654
+ }
10655
+ async function ioregValue(exec, key) {
10656
+ try {
10657
+ const { stdout } = await exec("ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"]);
10658
+ const m = stdout.match(new RegExp(`"${key}"\\s*=\\s*"([^"]*)"`));
10659
+ return m?.[1] ? normalizeHardwareId(m[1]) : null;
10660
+ } catch {
10661
+ return null;
10662
+ }
10663
+ }
10664
+ async function firstPhysicalMacMac(deps) {
10665
+ try {
10666
+ const names = (await deps.readDir("/sys/class/net")).sort();
10667
+ for (const name of names) {
10668
+ const base = (0, import_node_path5.join)("/sys/class/net", name);
10669
+ if (!await deps.statOrNull((0, import_node_path5.join)(base, "device"))) continue;
10670
+ const mac = await deps.readFile((0, import_node_path5.join)(base, "address"));
10671
+ return normalizeHardwareId(mac);
10672
+ }
10673
+ } catch {
10674
+ }
10675
+ return null;
10676
+ }
10677
+ async function winMachineGuid(exec) {
10678
+ try {
10679
+ const { stdout } = await exec("reg", [
10680
+ "query",
10681
+ "HKLM\\SOFTWARE\\Microsoft\\Cryptography",
10682
+ "/v",
10683
+ "MachineGuid"
10684
+ ]);
10685
+ const m = stdout.match(/MachineGuid\s+REG_SZ\s+(\S+)/i);
10686
+ return m?.[1] ? normalizeHardwareId(m[1]) : null;
10687
+ } catch {
10688
+ return null;
10689
+ }
10690
+ }
10691
+ async function resolveHardwareMachineId(deps = {}) {
10692
+ const plat = deps.platform ?? (0, import_node_os5.platform)();
10693
+ const readFileDeps = deps.readFile ?? ((p) => (0, import_promises2.readFile)(p, "utf-8"));
10694
+ const execDeps = deps.exec ?? (async (cmd, args) => {
10695
+ const { stdout } = await execFileAsync(cmd, args, { timeout: 3e3 });
10696
+ return { stdout };
10697
+ });
10698
+ const candidates = [];
10699
+ if (plat === "darwin") {
10700
+ candidates.push(
10701
+ { tag: "mac:serial", raw: await ioregValue(execDeps, "IOPlatformSerialNumber") },
10702
+ { tag: "mac:platform-uuid", raw: await ioregValue(execDeps, "IOPlatformUUID") }
10703
+ );
10704
+ } else if (plat === "linux") {
10705
+ const readFileOrNull = async (p) => {
10706
+ try {
10707
+ return await readFileDeps(p);
10708
+ } catch {
10709
+ return null;
10710
+ }
10711
+ };
10712
+ candidates.push(
10713
+ { tag: "linux:product-uuid", raw: await readFileOrNull("/sys/class/dmi/id/product_uuid") },
10714
+ { tag: "linux:board-serial", raw: await readFileOrNull("/sys/class/dmi/id/board_serial") },
10715
+ { tag: "linux:machine-id", raw: await readFileOrNull("/etc/machine-id") },
10716
+ { tag: "linux:machine-id", raw: await readFileOrNull("/var/lib/dbus/machine-id") },
10717
+ {
10718
+ tag: "linux:nic-mac",
10719
+ raw: await firstPhysicalMacMac({
10720
+ readFile: readFileDeps,
10721
+ readDir: deps.readDir ?? ((p) => (0, import_promises2.readdir)(p)),
10722
+ statOrNull: deps.statOrNull ?? (async (p) => {
10723
+ try {
10724
+ await (0, import_promises2.stat)(p);
10725
+ return true;
10726
+ } catch {
10727
+ return false;
10728
+ }
10729
+ })
10730
+ })
10731
+ }
10732
+ );
10733
+ } else if (plat === "win32") {
10734
+ candidates.push({ tag: "win:machine-guid", raw: await winMachineGuid(execDeps) });
10735
+ }
10736
+ for (const c of candidates) {
10737
+ if (c.raw !== null && isValidHardwareId(c.raw)) {
10738
+ return { id: deriveMachineId(c.tag, c.raw), source: c.tag };
10739
+ }
10740
+ }
10741
+ return null;
10742
+ }
10743
+
10744
+ // src/auth.ts
10611
10745
  function credentialsPath() {
10612
- return (0, import_node_path5.join)((0, import_node_os5.homedir)(), DEFAULT_CONFIG_DIR, CREDENTIALS_FILE);
10746
+ return (0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR, CREDENTIALS_FILE);
10613
10747
  }
10614
10748
  async function loadCredentials() {
10615
10749
  try {
10616
- const raw = JSON.parse(await (0, import_promises2.readFile)(credentialsPath(), "utf-8"));
10750
+ const raw = JSON.parse(await (0, import_promises3.readFile)(credentialsPath(), "utf-8"));
10617
10751
  if (!raw.deviceId || !raw.accessToken || !raw.refreshToken) return null;
10618
10752
  return raw;
10619
10753
  } catch {
@@ -10622,10 +10756,10 @@ async function loadCredentials() {
10622
10756
  }
10623
10757
  async function saveCredentials(creds) {
10624
10758
  const path = credentialsPath();
10625
- await (0, import_promises2.mkdir)((0, import_node_path5.join)(path, ".."), { recursive: true });
10626
- await (0, import_promises2.writeFile)(path, JSON.stringify(creds, null, 2) + "\n", { mode: 384 });
10759
+ await (0, import_promises3.mkdir)((0, import_node_path6.join)(path, ".."), { recursive: true });
10760
+ await (0, import_promises3.writeFile)(path, JSON.stringify(creds, null, 2) + "\n", { mode: 384 });
10627
10761
  try {
10628
- await (0, import_promises2.chmod)(path, 384);
10762
+ await (0, import_promises3.chmod)(path, 384);
10629
10763
  } catch {
10630
10764
  }
10631
10765
  }
@@ -10651,7 +10785,7 @@ function openBrowser(url) {
10651
10785
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
10652
10786
  const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
10653
10787
  try {
10654
- const child = (0, import_node_child_process.spawn)(cmd, args, { stdio: "ignore", detached: true });
10788
+ const child = (0, import_node_child_process2.spawn)(cmd, args, { stdio: "ignore", detached: true });
10655
10789
  child.unref();
10656
10790
  } catch {
10657
10791
  }
@@ -10673,15 +10807,15 @@ async function loginAndFetchRegistrationKey(baseUrl, email, password) {
10673
10807
  return { registrationKey: data.registrationKey, user: data.user?.email ?? email };
10674
10808
  }
10675
10809
  async function mergeLocalConfig(patch) {
10676
- const dir = (0, import_node_path5.join)((0, import_node_os5.homedir)(), DEFAULT_CONFIG_DIR);
10677
- const path = (0, import_node_path5.join)(dir, CONFIG_FILE);
10810
+ const dir = (0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR);
10811
+ const path = (0, import_node_path6.join)(dir, CONFIG_FILE);
10678
10812
  let existing = {};
10679
10813
  try {
10680
- existing = JSON.parse(await (0, import_promises2.readFile)(path, "utf-8"));
10814
+ existing = JSON.parse(await (0, import_promises3.readFile)(path, "utf-8"));
10681
10815
  } catch {
10682
10816
  }
10683
- await (0, import_promises2.mkdir)(dir, { recursive: true });
10684
- await (0, import_promises2.writeFile)(path, JSON.stringify({ ...existing, ...patch }, null, 2) + "\n");
10817
+ await (0, import_promises3.mkdir)(dir, { recursive: true });
10818
+ await (0, import_promises3.writeFile)(path, JSON.stringify({ ...existing, ...patch }, null, 2) + "\n");
10685
10819
  }
10686
10820
  var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
10687
10821
  var AuthError = class extends Error {
@@ -10695,7 +10829,7 @@ async function deviceFlowAuthorize(baseUrl, registrationKey, deviceId) {
10695
10829
  const res = await postJson(`${baseUrl}/oauth/device_authorization`, {
10696
10830
  registrationKey,
10697
10831
  device_id: deviceId,
10698
- device_name: (0, import_node_os5.hostname)(),
10832
+ device_name: (0, import_node_os6.hostname)(),
10699
10833
  platform: process.platform
10700
10834
  });
10701
10835
  if (res.status !== 200) {
@@ -10785,14 +10919,19 @@ async function ensureFreshAccessToken(baseUrl, creds) {
10785
10919
  return refreshInFlight;
10786
10920
  }
10787
10921
  async function ensureMachineId() {
10788
- const path = (0, import_node_path5.join)((0, import_node_os5.homedir)(), DEFAULT_CONFIG_DIR, CONFIG_FILE);
10922
+ const path = (0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR, CONFIG_FILE);
10789
10923
  try {
10790
- const cfg = JSON.parse(await (0, import_promises2.readFile)(path, "utf-8"));
10924
+ const cfg = JSON.parse(await (0, import_promises3.readFile)(path, "utf-8"));
10791
10925
  if (typeof cfg.machineId === "string" && cfg.machineId) return cfg.machineId;
10792
10926
  } catch {
10793
10927
  }
10794
- const machineId = "m_" + (0, import_node_crypto2.randomBytes)(16).toString("hex");
10795
- await mergeLocalConfig({ machineId });
10928
+ const hw = await resolveHardwareMachineId();
10929
+ if (hw) {
10930
+ await mergeLocalConfig({ machineId: hw.id, machineIdSource: hw.source });
10931
+ return hw.id;
10932
+ }
10933
+ const machineId = "m_" + (0, import_node_crypto3.randomBytes)(16).toString("hex");
10934
+ await mergeLocalConfig({ machineId, machineIdSource: "random" });
10796
10935
  return machineId;
10797
10936
  }
10798
10937
  async function stableDeviceId() {
@@ -10815,7 +10954,7 @@ function isPermanentAuthFailure(e) {
10815
10954
  return e instanceof AuthError && e.message.includes("invalid_grant");
10816
10955
  }
10817
10956
  async function loadConfig() {
10818
- const path = (0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR, CONFIG_FILE);
10957
+ const path = (0, import_node_path7.join)((0, import_node_os7.homedir)(), DEFAULT_CONFIG_DIR, CONFIG_FILE);
10819
10958
  try {
10820
10959
  return JSON.parse((0, import_node_fs4.readFileSync)(path, "utf-8"));
10821
10960
  } catch {
@@ -10847,10 +10986,10 @@ function detectCommands() {
10847
10986
  for (const [cmd, bin] of bins) {
10848
10987
  let ok = false;
10849
10988
  if (process.platform === "win32") {
10850
- ok = (0, import_node_child_process2.spawnSync)("where", [bin], { timeout: 5e3 }).status === 0;
10989
+ ok = (0, import_node_child_process3.spawnSync)("where", [bin], { timeout: 5e3 }).status === 0;
10851
10990
  } else {
10852
10991
  const shell = process.env.SHELL ?? "/bin/bash";
10853
- ok = (0, import_node_child_process2.spawnSync)(shell, ["-l", "-c", `command -v ${bin}`], {
10992
+ ok = (0, import_node_child_process3.spawnSync)(shell, ["-l", "-c", `command -v ${bin}`], {
10854
10993
  timeout: 5e3
10855
10994
  }).status === 0;
10856
10995
  }
@@ -11425,10 +11564,10 @@ async function startLocal(opts = {}) {
11425
11564
  }
11426
11565
  }
11427
11566
  function credentialsPathOf() {
11428
- return (0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR, "credentials.json");
11567
+ return (0, import_node_path7.join)((0, import_node_os7.homedir)(), DEFAULT_CONFIG_DIR, "credentials.json");
11429
11568
  }
11430
11569
  function instanceLockPath() {
11431
- return (0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR, "instance.lock");
11570
+ return (0, import_node_path7.join)((0, import_node_os7.homedir)(), DEFAULT_CONFIG_DIR, "instance.lock");
11432
11571
  }
11433
11572
  function processAlive(pid) {
11434
11573
  try {
@@ -11448,7 +11587,7 @@ function acquireSingleInstanceLock() {
11448
11587
  } catch {
11449
11588
  }
11450
11589
  try {
11451
- (0, import_node_fs4.mkdirSync)((0, import_node_path6.join)(path, ".."), { recursive: true });
11590
+ (0, import_node_fs4.mkdirSync)((0, import_node_path7.join)(path, ".."), { recursive: true });
11452
11591
  (0, import_node_fs4.writeFileSync)(path, String(process.pid));
11453
11592
  } catch {
11454
11593
  }
@@ -11478,10 +11617,10 @@ function toUint8(raw) {
11478
11617
  throw new Error("unexpected raw type");
11479
11618
  }
11480
11619
  function gatewayPidFile() {
11481
- return (0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR, "gateway.pid");
11620
+ return (0, import_node_path7.join)((0, import_node_os7.homedir)(), DEFAULT_CONFIG_DIR, "gateway.pid");
11482
11621
  }
11483
11622
  function writeGatewayPid() {
11484
- (0, import_node_fs4.mkdirSync)((0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR), { recursive: true });
11623
+ (0, import_node_fs4.mkdirSync)((0, import_node_path7.join)((0, import_node_os7.homedir)(), DEFAULT_CONFIG_DIR), { recursive: true });
11485
11624
  (0, import_node_fs4.writeFileSync)(gatewayPidFile(), String(process.pid));
11486
11625
  }
11487
11626
  function removeGatewayPid() {
@@ -11526,11 +11665,11 @@ function resolveHere() {
11526
11665
  if (DEBUG) console.error("[debug] cannot resolve script path");
11527
11666
  return;
11528
11667
  }
11529
- const hereDir = (0, import_node_path7.dirname)(here);
11668
+ const hereDir = (0, import_node_path8.dirname)(here);
11530
11669
  const candidates = [
11531
- (0, import_node_path7.join)(hereDir, "..", "node_modules", "node-pty", "prebuilds"),
11532
- (0, import_node_path7.join)(hereDir, "..", "..", "node-pty", "prebuilds"),
11533
- (0, import_node_path7.join)(hereDir, "..", "..", "..", "node_modules", "node-pty", "prebuilds")
11670
+ (0, import_node_path8.join)(hereDir, "..", "node_modules", "node-pty", "prebuilds"),
11671
+ (0, import_node_path8.join)(hereDir, "..", "..", "node-pty", "prebuilds"),
11672
+ (0, import_node_path8.join)(hereDir, "..", "..", "..", "node_modules", "node-pty", "prebuilds")
11534
11673
  ];
11535
11674
  let prebuildsDir = null;
11536
11675
  for (const c of candidates) {
@@ -11545,7 +11684,7 @@ function resolveHere() {
11545
11684
  }
11546
11685
  let n = 0;
11547
11686
  for (const sub of (0, import_node_fs5.readdirSync)(prebuildsDir)) {
11548
- const helper = (0, import_node_path7.join)(prebuildsDir, sub, "spawn-helper");
11687
+ const helper = (0, import_node_path8.join)(prebuildsDir, sub, "spawn-helper");
11549
11688
  if ((0, import_node_fs5.existsSync)(helper)) {
11550
11689
  try {
11551
11690
  (0, import_node_fs5.chmodSync)(helper, 493);
@@ -11559,7 +11698,7 @@ function resolveHere() {
11559
11698
  var argUrl = process.argv[2];
11560
11699
  var argMatch = argUrl && (argUrl.startsWith("ws://") || argUrl.startsWith("wss://"));
11561
11700
  function readVersion() {
11562
- if (true) return "0.3.2";
11701
+ if (true) return "0.3.4";
11563
11702
  try {
11564
11703
  const pkg = JSON.parse(
11565
11704
  (0, import_node_fs5.readFileSync)(new URL("../package.json", import_meta.url), "utf-8")
@@ -11619,7 +11758,7 @@ example:
11619
11758
  }
11620
11759
  var LAUNCHD_LABEL = "net.cli-remote.local";
11621
11760
  function launchdPlistPath() {
11622
- return (0, import_node_path7.join)((0, import_node_os7.homedir)(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
11761
+ return (0, import_node_path8.join)((0, import_node_os8.homedir)(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
11623
11762
  }
11624
11763
  function entryScript() {
11625
11764
  const a1 = process.argv[1];
@@ -11632,7 +11771,7 @@ function entryScript() {
11632
11771
  }
11633
11772
  function run(cmd, args, okToFail = false) {
11634
11773
  try {
11635
- return (0, import_node_child_process3.execFileSync)(cmd, args, { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
11774
+ return (0, import_node_child_process4.execFileSync)(cmd, args, { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
11636
11775
  } catch (e) {
11637
11776
  if (okToFail) return null;
11638
11777
  throw e;
@@ -11643,7 +11782,7 @@ function launchdInstall(urlArg) {
11643
11782
  console.error(`[local] launchd \u5E38\u9A7B\u4EC5\u652F\u6301 macOS\uFF08\u5F53\u524D ${process.platform}\uFF09\uFF1BLinux \u8BF7\u7528 systemd --user \u81EA\u884C\u6258\u7BA1`);
11644
11783
  process.exit(1);
11645
11784
  }
11646
- const home = (0, import_node_os7.homedir)();
11785
+ const home = (0, import_node_os8.homedir)();
11647
11786
  const entry = entryScript();
11648
11787
  if (!entry) {
11649
11788
  console.error("[local] \u5F53\u524D\u4EE5\u6E90\u7801\uFF08tsx\uFF09\u6A21\u5F0F\u8FD0\u884C\uFF0C\u65E0\u6CD5\u5B89\u88C5\u5E38\u9A7B\u670D\u52A1\u3002");
@@ -11651,9 +11790,9 @@ function launchdInstall(urlArg) {
11651
11790
  process.exit(1);
11652
11791
  }
11653
11792
  if (urlArg) {
11654
- const cfgDir = (0, import_node_path7.join)(home, DEFAULT_CONFIG_DIR);
11793
+ const cfgDir = (0, import_node_path8.join)(home, DEFAULT_CONFIG_DIR);
11655
11794
  (0, import_node_fs5.mkdirSync)(cfgDir, { recursive: true });
11656
- const cfgPath = (0, import_node_path7.join)(cfgDir, "config.json");
11795
+ const cfgPath = (0, import_node_path8.join)(cfgDir, "config.json");
11657
11796
  let cfg = {};
11658
11797
  try {
11659
11798
  cfg = JSON.parse((0, import_node_fs5.readFileSync)(cfgPath, "utf-8"));
@@ -11663,15 +11802,15 @@ function launchdInstall(urlArg) {
11663
11802
  (0, import_node_fs5.writeFileSync)(cfgPath, JSON.stringify(cfg, null, 2) + "\n");
11664
11803
  console.log(`[local] relayUrl \u5DF2\u5199\u5165 ${cfgPath}`);
11665
11804
  }
11666
- const credPath = (0, import_node_path7.join)(home, DEFAULT_CONFIG_DIR, CREDENTIALS_FILE);
11805
+ const credPath = (0, import_node_path8.join)(home, DEFAULT_CONFIG_DIR, CREDENTIALS_FILE);
11667
11806
  if (!(0, import_node_fs5.existsSync)(credPath)) {
11668
11807
  console.error("[local] \u5C1A\u672A\u6388\u6743\uFF08\u627E\u4E0D\u5230 credentials.json\uFF09\u3002");
11669
11808
  console.error("[local] \u8BF7\u5148\u4EA4\u4E92\u5F0F\u8FD0\u884C\u4E00\u6B21 cli-local \u5B8C\u6210\u914D\u7F6E\u4E0E\u6388\u6743\uFF0C\u518D\u5B89\u88C5\u5E38\u9A7B\u670D\u52A1\u3002");
11670
11809
  process.exit(1);
11671
11810
  }
11672
11811
  const plist = launchdPlistPath();
11673
- const logDir = (0, import_node_path7.join)(home, DEFAULT_CONFIG_DIR, "logs");
11674
- (0, import_node_fs5.mkdirSync)((0, import_node_path7.dirname)(plist), { recursive: true });
11812
+ const logDir = (0, import_node_path8.join)(home, DEFAULT_CONFIG_DIR, "logs");
11813
+ (0, import_node_fs5.mkdirSync)((0, import_node_path8.dirname)(plist), { recursive: true });
11675
11814
  (0, import_node_fs5.mkdirSync)(logDir, { recursive: true });
11676
11815
  const xml = `<?xml version="1.0" encoding="UTF-8"?>
11677
11816
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -11686,8 +11825,8 @@ function launchdInstall(urlArg) {
11686
11825
  <key>RunAtLoad</key><true/>
11687
11826
  <key>KeepAlive</key><true/>
11688
11827
  <key>ThrottleInterval</key><integer>30</integer>
11689
- <key>StandardOutPath</key><string>${(0, import_node_path7.join)(logDir, "daemon.out.log")}</string>
11690
- <key>StandardErrorPath</key><string>${(0, import_node_path7.join)(logDir, "daemon.err.log")}</string>
11828
+ <key>StandardOutPath</key><string>${(0, import_node_path8.join)(logDir, "daemon.out.log")}</string>
11829
+ <key>StandardErrorPath</key><string>${(0, import_node_path8.join)(logDir, "daemon.err.log")}</string>
11691
11830
  </dict>
11692
11831
  </plist>
11693
11832
  `;
@@ -11730,8 +11869,8 @@ if (argUrl && !argMatch && argUrl === "uninstall") {
11730
11869
  }
11731
11870
  if (argUrl === "pair") {
11732
11871
  void (async () => {
11733
- const home = (0, import_node_os7.homedir)();
11734
- const cfgPath = (0, import_node_path7.join)(home, DEFAULT_CONFIG_DIR, "config.json");
11872
+ const home = (0, import_node_os8.homedir)();
11873
+ const cfgPath = (0, import_node_path8.join)(home, DEFAULT_CONFIG_DIR, "config.json");
11735
11874
  let cfg = {};
11736
11875
  try {
11737
11876
  cfg = JSON.parse((0, import_node_fs5.readFileSync)(cfgPath, "utf-8"));
@@ -11769,7 +11908,7 @@ if (argUrl === "pair") {
11769
11908
  }
11770
11909
  if (argUrl === "gateway") {
11771
11910
  let gatewayLogFile = function() {
11772
- return (0, import_node_path7.join)((0, import_node_os7.homedir)(), DEFAULT_CONFIG_DIR, "logs", "gateway.log");
11911
+ return (0, import_node_path8.join)((0, import_node_os8.homedir)(), DEFAULT_CONFIG_DIR, "logs", "gateway.log");
11773
11912
  }, gatewayLogs = function() {
11774
11913
  const follow = rest.includes("-f") || rest.includes("--follow");
11775
11914
  const li = rest.findIndex((a) => a === "-n" || a === "--lines");
@@ -11813,10 +11952,10 @@ if (argUrl === "gateway") {
11813
11952
  console.log(`[gateway] already running (pid ${gatewayPid()})`);
11814
11953
  process.exit(0);
11815
11954
  }
11816
- const home = (0, import_node_os7.homedir)();
11955
+ const home = (0, import_node_os8.homedir)();
11817
11956
  const entry = entryScript();
11818
- const cfgDir = (0, import_node_path7.join)(home, DEFAULT_CONFIG_DIR);
11819
- const cfgPath = (0, import_node_path7.join)(cfgDir, "config.json");
11957
+ const cfgDir = (0, import_node_path8.join)(home, DEFAULT_CONFIG_DIR);
11958
+ const cfgPath = (0, import_node_path8.join)(cfgDir, "config.json");
11820
11959
  const hasCfg = (0, import_node_fs5.existsSync)(cfgPath);
11821
11960
  if (!entry) {
11822
11961
  console.error("[gateway] \u6E90\u7801\uFF08tsx\uFF09\u6A21\u5F0F\u4E0D\u652F\u6301\u540E\u53F0\u8FD0\u884C\uFF1B\u8BF7\u7528 npm \u5B89\u88C5\u7248\uFF1Anpm i -g @cli-remote/local");
@@ -11827,13 +11966,13 @@ if (argUrl === "gateway") {
11827
11966
  console.error("[gateway] \uFF08\u65E0\u51ED\u8BC1\u4E5F\u53EF\u4EE5\u542F\u52A8\uFF1Agateway \u4F1A\u8FDB\u5165 needs-pairing\uFF0C\u7B49 cli-local pair\uFF09");
11828
11967
  process.exit(1);
11829
11968
  }
11830
- (0, import_node_fs5.mkdirSync)((0, import_node_path7.dirname)(gatewayLogFile()), { recursive: true });
11969
+ (0, import_node_fs5.mkdirSync)((0, import_node_path8.dirname)(gatewayLogFile()), { recursive: true });
11831
11970
  const out = (0, import_node_fs5.openSync)(gatewayLogFile(), "a");
11832
11971
  const env = {};
11833
11972
  for (const [k, v] of Object.entries(process.env)) if (v !== void 0) env[k] = v;
11834
11973
  const args = [entry, "gateway", "run"];
11835
11974
  if (hubArg) args.push("--hub", hubArg);
11836
- const child = (0, import_node_child_process3.spawn)(process.execPath, args, {
11975
+ const child = (0, import_node_child_process4.spawn)(process.execPath, args, {
11837
11976
  detached: true,
11838
11977
  stdio: ["ignore", out, out],
11839
11978
  env
@@ -11859,12 +11998,12 @@ if (argUrl === "gateway") {
11859
11998
  while (Date.now() - t02 < 5e3 && gatewayAlive()) {
11860
11999
  await new Promise((res) => setTimeout(res, 150));
11861
12000
  }
11862
- (0, import_node_fs5.rmSync)((0, import_node_path7.join)((0, import_node_os7.homedir)(), DEFAULT_CONFIG_DIR, "gateway.pid"), { force: true });
12001
+ (0, import_node_fs5.rmSync)((0, import_node_path8.join)((0, import_node_os8.homedir)(), DEFAULT_CONFIG_DIR, "gateway.pid"), { force: true });
11863
12002
  return;
11864
12003
  }
11865
12004
  const pid = gatewayPid();
11866
12005
  if (pid === null || !gatewayAlive()) {
11867
- (0, import_node_fs5.rmSync)((0, import_node_path7.join)((0, import_node_os7.homedir)(), DEFAULT_CONFIG_DIR, "gateway.pid"), { force: true });
12006
+ (0, import_node_fs5.rmSync)((0, import_node_path8.join)((0, import_node_os8.homedir)(), DEFAULT_CONFIG_DIR, "gateway.pid"), { force: true });
11868
12007
  return;
11869
12008
  }
11870
12009
  try {
@@ -11875,7 +12014,7 @@ if (argUrl === "gateway") {
11875
12014
  while (Date.now() - t0 < 5e3 && gatewayAlive()) {
11876
12015
  await new Promise((res) => setTimeout(res, 150));
11877
12016
  }
11878
- (0, import_node_fs5.rmSync)((0, import_node_path7.join)((0, import_node_os7.homedir)(), DEFAULT_CONFIG_DIR, "gateway.pid"), { force: true });
12017
+ (0, import_node_fs5.rmSync)((0, import_node_path8.join)((0, import_node_os8.homedir)(), DEFAULT_CONFIG_DIR, "gateway.pid"), { force: true });
11879
12018
  }
11880
12019
  async function gatewayStop() {
11881
12020
  const pid = gatewayPid();
@@ -11916,7 +12055,7 @@ if (argUrl === "gateway") {
11916
12055
  const pid = gatewayPid();
11917
12056
  let hub = "(\u672A\u914D\u7F6E)";
11918
12057
  try {
11919
- const cfg = JSON.parse((0, import_node_fs5.readFileSync)((0, import_node_path7.join)((0, import_node_os7.homedir)(), DEFAULT_CONFIG_DIR, "config.json"), "utf-8"));
12058
+ const cfg = JSON.parse((0, import_node_fs5.readFileSync)((0, import_node_path8.join)((0, import_node_os8.homedir)(), DEFAULT_CONFIG_DIR, "config.json"), "utf-8"));
11920
12059
  hub = cfg.relayUrl ?? hub;
11921
12060
  } catch {
11922
12061
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cli-remote/local",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Local PTY agent for cli-remote — exposes your local CLI tools (claude/codex/kimi-cli/shell) to the mobile web client via a self-hosted hub.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -22,12 +22,15 @@
22
22
  "cli-local": "./dist/index.cjs"
23
23
  },
24
24
  "files": [
25
- "dist"
25
+ "dist",
26
+ "scripts"
26
27
  ],
27
28
  "scripts": {
28
29
  "dev": "tsx watch src/index.ts",
29
30
  "build": "node scripts/build.mjs",
31
+ "postinstall": "node scripts/fix-spawn-helper.mjs",
30
32
  "typecheck": "tsc --noEmit",
33
+ "test": "vitest run",
31
34
  "prepublishOnly": "pnpm build"
32
35
  },
33
36
  "dependencies": {
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ // esbuild wrapper: bundles src/index.ts → dist/index.cjs, injecting the
3
+ // package version as CLI_LOCAL_VERSION (consumed by the -v/--version
4
+ // handler in src/index.ts — single source of truth stays package.json).
5
+ import { spawnSync } from 'node:child_process'
6
+ import { readFileSync } from 'node:fs'
7
+
8
+ const version = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8')).version
9
+
10
+ const res = spawnSync(
11
+ 'esbuild',
12
+ [
13
+ 'src/index.ts',
14
+ '--bundle',
15
+ '--platform=node',
16
+ '--format=cjs',
17
+ '--outfile=dist/index.cjs',
18
+ `--define:CLI_LOCAL_VERSION=${JSON.stringify(version)}`,
19
+ '--external:node-pty',
20
+ // NOTE: argv array goes straight to esbuild (no shell except win32), so
21
+ // the banner text must NOT be wrapped in quotes — literal quotes end up
22
+ // in the output and break the shebang.
23
+ '--banner:js=#!/usr/bin/env node',
24
+ ],
25
+ { stdio: 'inherit', shell: process.platform === 'win32' },
26
+ )
27
+ process.exit(res.status ?? 1)
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+ // Install-time fix: npm's tarball unpack sometimes strips the exec bit from
3
+ // node-pty's spawn-helper, which makes every PTY spawn fail on macOS with
4
+ // "posix_spawnp failed." (pit #1 in docs/HANDOFF.md). The runtime fallback in
5
+ // src/index.ts (fixSpawnHelperPerms) only runs when the gateway process
6
+ // starts — it cannot cover "npm i -g 升级后老 daemon 还在跑" — so this script
7
+ // runs as the package's own postinstall to chmod +x at install time.
8
+ import { chmodSync, existsSync, readdirSync } from 'node:fs'
9
+ import { dirname, join } from 'node:path'
10
+ import { fileURLToPath } from 'node:url'
11
+
12
+ const here = dirname(fileURLToPath(import.meta.url))
13
+
14
+ // <pkg>/scripts → node-pty, for npm -g (deps nest under the package) and
15
+ // plain `npm install <pkg>` (deps hoist to the project root).
16
+ const candidates = [
17
+ join(here, '..', 'node_modules', 'node-pty', 'prebuilds'),
18
+ join(here, '..', '..', 'node_modules', 'node-pty', 'prebuilds'),
19
+ join(here, '..', '..', '..', 'node-pty', 'prebuilds'),
20
+ ]
21
+
22
+ // pnpm keeps deps under .pnpm — scan both likely locations.
23
+ for (const base of [join(here, '..', 'node_modules'), join(here, '..', '..', 'node_modules')]) {
24
+ const pnpmDir = join(base, '.pnpm')
25
+ if (!existsSync(pnpmDir)) continue
26
+ for (const d of readdirSync(pnpmDir)) {
27
+ if (d.startsWith('node-pty@')) {
28
+ candidates.push(join(pnpmDir, d, 'node_modules', 'node-pty', 'prebuilds'))
29
+ }
30
+ }
31
+ }
32
+
33
+ let n = 0
34
+ for (const dir of candidates) {
35
+ if (!existsSync(dir)) continue
36
+ for (const sub of readdirSync(dir)) {
37
+ const helper = join(dir, sub, 'spawn-helper')
38
+ if (!existsSync(helper)) continue
39
+ try {
40
+ chmodSync(helper, 0o755)
41
+ n++
42
+ } catch {
43
+ // best effort — runtime chmod in index.ts is the second line of defense
44
+ }
45
+ }
46
+ }
47
+ if (n) console.log(`[cli-local postinstall] chmod +x spawn-helper (${n} files)`)