@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 +202 -63
- package/package.json +5 -2
- package/scripts/build.mjs +27 -0
- package/scripts/fix-spawn-helper.mjs +47 -0
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 =
|
|
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 =
|
|
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
|
|
9944
|
-
var
|
|
9945
|
-
var
|
|
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
|
|
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
|
|
9964
|
-
var
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
10626
|
-
await (0,
|
|
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,
|
|
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,
|
|
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,
|
|
10677
|
-
const path = (0,
|
|
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,
|
|
10814
|
+
existing = JSON.parse(await (0, import_promises3.readFile)(path, "utf-8"));
|
|
10681
10815
|
} catch {
|
|
10682
10816
|
}
|
|
10683
|
-
await (0,
|
|
10684
|
-
await (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
10795
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
11668
|
+
const hereDir = (0, import_node_path8.dirname)(here);
|
|
11530
11669
|
const candidates = [
|
|
11531
|
-
(0,
|
|
11532
|
-
(0,
|
|
11533
|
-
(0,
|
|
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,
|
|
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.
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
11674
|
-
(0, import_node_fs5.mkdirSync)((0,
|
|
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,
|
|
11690
|
-
<key>StandardErrorPath</key><string>${(0,
|
|
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,
|
|
11734
|
-
const cfgPath = (0,
|
|
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,
|
|
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,
|
|
11955
|
+
const home = (0, import_node_os8.homedir)();
|
|
11817
11956
|
const entry = entryScript();
|
|
11818
|
-
const cfgDir = (0,
|
|
11819
|
-
const cfgPath = (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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.
|
|
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)`)
|