@cli-remote/local 0.0.6 → 0.1.1
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 +571 -183
- package/package.json +1 -1
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, createHash } = require("crypto");
|
|
2268
|
+
var { randomBytes: randomBytes2, createHash } = require("crypto");
|
|
2269
2269
|
var { Duplex, Readable } = require("stream");
|
|
2270
2270
|
var { URL: URL2 } = require("url");
|
|
2271
2271
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -2803,7 +2803,7 @@ var require_websocket = __commonJS({
|
|
|
2803
2803
|
}
|
|
2804
2804
|
}
|
|
2805
2805
|
const defaultPort = isSecure ? 443 : 80;
|
|
2806
|
-
const key =
|
|
2806
|
+
const key = randomBytes2(16).toString("base64");
|
|
2807
2807
|
const request = isSecure ? https.request : http.request;
|
|
2808
2808
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
2809
2809
|
let perMessageDeflate;
|
|
@@ -9939,10 +9939,10 @@ var require_lib = __commonJS({
|
|
|
9939
9939
|
});
|
|
9940
9940
|
|
|
9941
9941
|
// src/index.ts
|
|
9942
|
-
var
|
|
9942
|
+
var import_node_fs5 = require("node:fs");
|
|
9943
9943
|
var import_node_child_process3 = require("node:child_process");
|
|
9944
|
-
var
|
|
9945
|
-
var
|
|
9944
|
+
var import_node_os7 = require("node:os");
|
|
9945
|
+
var import_node_path7 = 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
|
|
@@ -9959,9 +9959,9 @@ var wrapper_default = import_websocket.default;
|
|
|
9959
9959
|
// src/client.ts
|
|
9960
9960
|
var import_node_child_process2 = require("node:child_process");
|
|
9961
9961
|
var import_node_readline = require("node:readline");
|
|
9962
|
-
var
|
|
9963
|
-
var
|
|
9964
|
-
var
|
|
9962
|
+
var import_node_fs4 = require("node:fs");
|
|
9963
|
+
var import_node_os6 = require("node:os");
|
|
9964
|
+
var import_node_path6 = require("node:path");
|
|
9965
9965
|
|
|
9966
9966
|
// ../shared/src/codec.ts
|
|
9967
9967
|
var import_msgpack = __toESM(require_dist(), 1);
|
|
@@ -10510,15 +10510,100 @@ async function exists(p) {
|
|
|
10510
10510
|
}
|
|
10511
10511
|
}
|
|
10512
10512
|
|
|
10513
|
+
// src/gateway-api.ts
|
|
10514
|
+
var import_node_http = require("node:http");
|
|
10515
|
+
var import_node_fs3 = require("node:fs");
|
|
10516
|
+
var import_node_os4 = require("node:os");
|
|
10517
|
+
var import_node_path4 = require("node:path");
|
|
10518
|
+
var DEFAULT_API_PORT = 18790;
|
|
10519
|
+
function apiPortFromEnv() {
|
|
10520
|
+
return Number(process.env.CLI_LOCAL_API_PORT) || DEFAULT_API_PORT;
|
|
10521
|
+
}
|
|
10522
|
+
function readConfiguredPort() {
|
|
10523
|
+
if (process.env.CLI_LOCAL_API_PORT) return apiPortFromEnv();
|
|
10524
|
+
try {
|
|
10525
|
+
const cfg = JSON.parse(
|
|
10526
|
+
(0, import_node_fs3.readFileSync)((0, import_node_path4.join)((0, import_node_os4.homedir)(), DEFAULT_CONFIG_DIR, "config.json"), "utf-8")
|
|
10527
|
+
);
|
|
10528
|
+
return Number(cfg.gateway?.apiPort) || DEFAULT_API_PORT;
|
|
10529
|
+
} catch {
|
|
10530
|
+
return DEFAULT_API_PORT;
|
|
10531
|
+
}
|
|
10532
|
+
}
|
|
10533
|
+
function startControlApi(opts) {
|
|
10534
|
+
const server = (0, import_node_http.createServer)((req, res) => {
|
|
10535
|
+
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
10536
|
+
const route = `${req.method} ${url.pathname}`;
|
|
10537
|
+
const json = (code, body) => {
|
|
10538
|
+
res.writeHead(code, { "content-type": "application/json" });
|
|
10539
|
+
res.end(JSON.stringify(body));
|
|
10540
|
+
};
|
|
10541
|
+
if (route === "GET /healthz") return json(200, { ok: true });
|
|
10542
|
+
if (route === "GET /status") return json(200, opts.getStatus());
|
|
10543
|
+
if (route === "GET /sessions") {
|
|
10544
|
+
return json(200, { sessions: opts.getStatus().sessions });
|
|
10545
|
+
}
|
|
10546
|
+
if (route === "GET /logs") {
|
|
10547
|
+
const n = Math.min(500, Math.max(1, Number(url.searchParams.get("n")) || 200));
|
|
10548
|
+
const lines = opts.getLogs().slice(-n);
|
|
10549
|
+
return json(200, { lines });
|
|
10550
|
+
}
|
|
10551
|
+
if (route === "POST /stop") {
|
|
10552
|
+
json(200, { ok: true });
|
|
10553
|
+
opts.onStop();
|
|
10554
|
+
return;
|
|
10555
|
+
}
|
|
10556
|
+
const killMatch = /^POST \/sessions\/([\w-]+)\/kill$/.exec(`${req.method} ${url.pathname}`);
|
|
10557
|
+
if (killMatch) {
|
|
10558
|
+
const ok = opts.killSession(killMatch[1]);
|
|
10559
|
+
return json(ok ? 200 : 404, ok ? { ok: true } : { ok: false, error: "no such session" });
|
|
10560
|
+
}
|
|
10561
|
+
json(404, { error: "not found" });
|
|
10562
|
+
});
|
|
10563
|
+
server.listen(opts.port, "127.0.0.1");
|
|
10564
|
+
return {
|
|
10565
|
+
port: opts.port,
|
|
10566
|
+
close: () => server.close()
|
|
10567
|
+
};
|
|
10568
|
+
}
|
|
10569
|
+
async function apiGet(port, path, timeoutMs = 800) {
|
|
10570
|
+
try {
|
|
10571
|
+
const ctrl = new AbortController();
|
|
10572
|
+
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
10573
|
+
const res = await fetch(`http://127.0.0.1:${port}${path}`, {
|
|
10574
|
+
signal: ctrl.signal
|
|
10575
|
+
});
|
|
10576
|
+
clearTimeout(timer);
|
|
10577
|
+
if (!res.ok) return null;
|
|
10578
|
+
return await res.json();
|
|
10579
|
+
} catch {
|
|
10580
|
+
return null;
|
|
10581
|
+
}
|
|
10582
|
+
}
|
|
10583
|
+
async function apiPost(port, path, timeoutMs = 800) {
|
|
10584
|
+
try {
|
|
10585
|
+
const ctrl = new AbortController();
|
|
10586
|
+
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
10587
|
+
const res = await fetch(`http://127.0.0.1:${port}${path}`, {
|
|
10588
|
+
method: "POST",
|
|
10589
|
+
signal: ctrl.signal
|
|
10590
|
+
});
|
|
10591
|
+
clearTimeout(timer);
|
|
10592
|
+
return res.ok;
|
|
10593
|
+
} catch {
|
|
10594
|
+
return false;
|
|
10595
|
+
}
|
|
10596
|
+
}
|
|
10597
|
+
|
|
10513
10598
|
// src/auth.ts
|
|
10514
10599
|
var import_node_child_process = require("node:child_process");
|
|
10515
10600
|
var import_node_crypto = require("node:crypto");
|
|
10516
10601
|
var import_promises2 = require("node:fs/promises");
|
|
10517
|
-
var
|
|
10518
|
-
var
|
|
10602
|
+
var import_node_os5 = require("node:os");
|
|
10603
|
+
var import_node_path5 = require("node:path");
|
|
10519
10604
|
var import_qrcode = __toESM(require_lib(), 1);
|
|
10520
10605
|
function credentialsPath() {
|
|
10521
|
-
return (0,
|
|
10606
|
+
return (0, import_node_path5.join)((0, import_node_os5.homedir)(), DEFAULT_CONFIG_DIR, CREDENTIALS_FILE);
|
|
10522
10607
|
}
|
|
10523
10608
|
async function loadCredentials() {
|
|
10524
10609
|
try {
|
|
@@ -10531,19 +10616,13 @@ async function loadCredentials() {
|
|
|
10531
10616
|
}
|
|
10532
10617
|
async function saveCredentials(creds) {
|
|
10533
10618
|
const path = credentialsPath();
|
|
10534
|
-
await (0, import_promises2.mkdir)((0,
|
|
10619
|
+
await (0, import_promises2.mkdir)((0, import_node_path5.join)(path, ".."), { recursive: true });
|
|
10535
10620
|
await (0, import_promises2.writeFile)(path, JSON.stringify(creds, null, 2) + "\n", { mode: 384 });
|
|
10536
10621
|
try {
|
|
10537
10622
|
await (0, import_promises2.chmod)(path, 384);
|
|
10538
10623
|
} catch {
|
|
10539
10624
|
}
|
|
10540
10625
|
}
|
|
10541
|
-
async function clearCredentials() {
|
|
10542
|
-
try {
|
|
10543
|
-
await (0, import_promises2.writeFile)(credentialsPath(), "");
|
|
10544
|
-
} catch {
|
|
10545
|
-
}
|
|
10546
|
-
}
|
|
10547
10626
|
function baseUrlFromWsUrl(wsUrl) {
|
|
10548
10627
|
const http = wsUrl.replace(/^ws/, "http");
|
|
10549
10628
|
try {
|
|
@@ -10588,8 +10667,8 @@ async function loginAndFetchRegistrationKey(baseUrl, email, password) {
|
|
|
10588
10667
|
return { registrationKey: data.registrationKey, user: data.user?.email ?? email };
|
|
10589
10668
|
}
|
|
10590
10669
|
async function mergeLocalConfig(patch) {
|
|
10591
|
-
const dir = (0,
|
|
10592
|
-
const path = (0,
|
|
10670
|
+
const dir = (0, import_node_path5.join)((0, import_node_os5.homedir)(), DEFAULT_CONFIG_DIR);
|
|
10671
|
+
const path = (0, import_node_path5.join)(dir, CONFIG_FILE);
|
|
10593
10672
|
let existing = {};
|
|
10594
10673
|
try {
|
|
10595
10674
|
existing = JSON.parse(await (0, import_promises2.readFile)(path, "utf-8"));
|
|
@@ -10610,7 +10689,7 @@ async function deviceFlowAuthorize(baseUrl, registrationKey, deviceId) {
|
|
|
10610
10689
|
const res = await postJson(`${baseUrl}/oauth/device_authorization`, {
|
|
10611
10690
|
registrationKey,
|
|
10612
10691
|
device_id: deviceId,
|
|
10613
|
-
device_name: (0,
|
|
10692
|
+
device_name: (0, import_node_os5.hostname)(),
|
|
10614
10693
|
platform: process.platform
|
|
10615
10694
|
});
|
|
10616
10695
|
if (res.status !== 200) {
|
|
@@ -10699,6 +10778,20 @@ async function ensureFreshAccessToken(baseUrl, creds) {
|
|
|
10699
10778
|
}
|
|
10700
10779
|
return refreshInFlight;
|
|
10701
10780
|
}
|
|
10781
|
+
async function ensureMachineId() {
|
|
10782
|
+
const path = (0, import_node_path5.join)((0, import_node_os5.homedir)(), DEFAULT_CONFIG_DIR, CONFIG_FILE);
|
|
10783
|
+
try {
|
|
10784
|
+
const cfg = JSON.parse(await (0, import_promises2.readFile)(path, "utf-8"));
|
|
10785
|
+
if (typeof cfg.machineId === "string" && cfg.machineId) return cfg.machineId;
|
|
10786
|
+
} catch {
|
|
10787
|
+
}
|
|
10788
|
+
const machineId = "m_" + (0, import_node_crypto.randomBytes)(16).toString("hex");
|
|
10789
|
+
await mergeLocalConfig({ machineId });
|
|
10790
|
+
return machineId;
|
|
10791
|
+
}
|
|
10792
|
+
async function stableDeviceId() {
|
|
10793
|
+
return "dev_" + await ensureMachineId();
|
|
10794
|
+
}
|
|
10702
10795
|
async function ensureCredentials(baseUrl, registrationKey) {
|
|
10703
10796
|
const existing = await loadCredentials();
|
|
10704
10797
|
if (existing) {
|
|
@@ -10708,14 +10801,17 @@ async function ensureCredentials(baseUrl, registrationKey) {
|
|
|
10708
10801
|
console.error(`[local] ${e.message}\uFF0C\u91CD\u65B0\u8D70\u6388\u6743\u6D41\u7A0B`);
|
|
10709
10802
|
}
|
|
10710
10803
|
}
|
|
10711
|
-
return deviceFlowAuthorize(baseUrl, registrationKey,
|
|
10804
|
+
return deviceFlowAuthorize(baseUrl, registrationKey, await stableDeviceId());
|
|
10712
10805
|
}
|
|
10713
10806
|
|
|
10714
10807
|
// src/client.ts
|
|
10808
|
+
function isPermanentAuthFailure(e) {
|
|
10809
|
+
return e instanceof AuthError && e.message.includes("invalid_grant");
|
|
10810
|
+
}
|
|
10715
10811
|
async function loadConfig() {
|
|
10716
|
-
const path = (0,
|
|
10812
|
+
const path = (0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR, CONFIG_FILE);
|
|
10717
10813
|
try {
|
|
10718
|
-
return JSON.parse((0,
|
|
10814
|
+
return JSON.parse((0, import_node_fs4.readFileSync)(path, "utf-8"));
|
|
10719
10815
|
} catch {
|
|
10720
10816
|
return {};
|
|
10721
10817
|
}
|
|
@@ -10852,17 +10948,123 @@ async function startLocal(opts = {}) {
|
|
|
10852
10948
|
(data) => safeSend(data),
|
|
10853
10949
|
(msg) => console.log(`[local] ${msg}`)
|
|
10854
10950
|
);
|
|
10855
|
-
|
|
10951
|
+
const other = acquireSingleInstanceLock();
|
|
10952
|
+
if (other !== null) {
|
|
10953
|
+
console.error(`[local] \u53E6\u4E00\u4E2A cli-local \u6B63\u5728\u8FD0\u884C (pid ${other})\uFF0C\u62D2\u7EDD\u53CC\u5F00\u3002`);
|
|
10954
|
+
console.error("[local] \u53CC\u5F00\u4F1A\u4E92\u76F8\u5237\u65B0 token \u5BFC\u81F4\u8BBE\u5907\u88AB\u670D\u52A1\u7AEF\u540A\u9500\u3002");
|
|
10955
|
+
console.error("[local] \u540E\u53F0\u5E38\u9A7B\u8BF7\u7528: cli-local gateway start / restart / status");
|
|
10956
|
+
process.exit(1);
|
|
10957
|
+
}
|
|
10958
|
+
const startedAt = Date.now();
|
|
10959
|
+
const logRing = [];
|
|
10960
|
+
if (opts.daemon) {
|
|
10961
|
+
writeGatewayPid();
|
|
10962
|
+
patchConsoleIntoRing(logRing);
|
|
10963
|
+
}
|
|
10964
|
+
let credentials;
|
|
10965
|
+
if (opts.daemon) {
|
|
10966
|
+
const existing = await loadCredentials();
|
|
10967
|
+
if (!existing) {
|
|
10968
|
+
credentials = {
|
|
10969
|
+
deviceId: "",
|
|
10970
|
+
accessToken: "",
|
|
10971
|
+
refreshToken: "",
|
|
10972
|
+
accessTokenExpiresAt: 0
|
|
10973
|
+
};
|
|
10974
|
+
} else {
|
|
10975
|
+
credentials = existing;
|
|
10976
|
+
try {
|
|
10977
|
+
credentials = await ensureFreshAccessToken(baseUrl, existing);
|
|
10978
|
+
} catch {
|
|
10979
|
+
}
|
|
10980
|
+
}
|
|
10981
|
+
} else {
|
|
10982
|
+
credentials = await ensureCredentials(baseUrl, registrationKey);
|
|
10983
|
+
}
|
|
10856
10984
|
const commands = detectCommands();
|
|
10857
|
-
if (opts.daemon) writeGatewayPid();
|
|
10858
10985
|
console.log(`[local] connecting to ${url}`);
|
|
10859
|
-
console.log(`[local] deviceId=${credentials.deviceId}`);
|
|
10986
|
+
console.log(`[local] deviceId=${credentials.deviceId || "(\u672A\u6388\u6743)"}`);
|
|
10860
10987
|
console.log(`[local] whitelist: ${whitelist.size} commands`);
|
|
10861
10988
|
console.log(`[local] available CLIs: ${commands.join(", ")}`);
|
|
10989
|
+
let phase = "starting";
|
|
10990
|
+
let phaseReason = "";
|
|
10991
|
+
let reconnectAttempts = 0;
|
|
10992
|
+
const getStatus = () => ({
|
|
10993
|
+
phase,
|
|
10994
|
+
...phaseReason ? { reason: phaseReason } : {},
|
|
10995
|
+
deviceId: credentials.deviceId || null,
|
|
10996
|
+
hubUrl: url,
|
|
10997
|
+
uptimeMs: Date.now() - startedAt,
|
|
10998
|
+
reconnectAttempts,
|
|
10999
|
+
accessTokenExpiresAt: credentials.accessTokenExpiresAt || null,
|
|
11000
|
+
sessions: pty.list().map((s) => ({
|
|
11001
|
+
sessionId: s.id,
|
|
11002
|
+
cmd: s.cmd,
|
|
11003
|
+
startedAt: s.startedAt
|
|
11004
|
+
}))
|
|
11005
|
+
});
|
|
11006
|
+
function setPhase(p, reason = "") {
|
|
11007
|
+
phase = p;
|
|
11008
|
+
phaseReason = reason;
|
|
11009
|
+
}
|
|
11010
|
+
let pairingTimer = null;
|
|
11011
|
+
let pairingCredMtime = 0;
|
|
11012
|
+
function enterNeedsPairing(reason) {
|
|
11013
|
+
setPhase("needs-pairing", reason);
|
|
11014
|
+
console.error(`[local] \u9700\u8981\u91CD\u65B0\u6388\u6743: ${reason}`);
|
|
11015
|
+
console.error("[local] \u5728\u4EFB\u610F\u7EC8\u7AEF\u8FD0\u884C: cli-local pair");
|
|
11016
|
+
console.error("[local] \uFF08\u672C\u8FDB\u7A0B\u4FDD\u6301\u8FD0\u884C\uFF0CPTY \u4F1A\u8BDD\u4FDD\u6D3B\uFF1B\u914D\u5BF9\u5B8C\u6210\u540E\u81EA\u52A8\u91CD\u8FDE\uFF09");
|
|
11017
|
+
if (pairingTimer) return;
|
|
11018
|
+
try {
|
|
11019
|
+
pairingCredMtime = (0, import_node_fs4.statSync)(credentialsPathOf()).mtimeMs;
|
|
11020
|
+
} catch {
|
|
11021
|
+
pairingCredMtime = 0;
|
|
11022
|
+
}
|
|
11023
|
+
pairingTimer = setInterval(async () => {
|
|
11024
|
+
let mtime;
|
|
11025
|
+
try {
|
|
11026
|
+
mtime = (0, import_node_fs4.statSync)(credentialsPathOf()).mtimeMs;
|
|
11027
|
+
} catch {
|
|
11028
|
+
return;
|
|
11029
|
+
}
|
|
11030
|
+
if (mtime <= pairingCredMtime) return;
|
|
11031
|
+
const fresh = await loadCredentials();
|
|
11032
|
+
if (!fresh?.deviceId || !fresh?.refreshToken) return;
|
|
11033
|
+
pairingCredMtime = mtime;
|
|
11034
|
+
credentials = fresh;
|
|
11035
|
+
console.log("[local] \u68C0\u6D4B\u5230\u65B0\u51ED\u8BC1\uFF0C\u91CD\u8FDE\u4E2D\u2026");
|
|
11036
|
+
if (pairingTimer) clearInterval(pairingTimer);
|
|
11037
|
+
pairingTimer = null;
|
|
11038
|
+
reconnectAttempts = 0;
|
|
11039
|
+
setPhase("connecting");
|
|
11040
|
+
connect();
|
|
11041
|
+
}, 2e3);
|
|
11042
|
+
}
|
|
11043
|
+
let controlApi = null;
|
|
11044
|
+
if (opts.daemon) {
|
|
11045
|
+
const port = readConfiguredPort();
|
|
11046
|
+
controlApi = startControlApi({
|
|
11047
|
+
port,
|
|
11048
|
+
getStatus,
|
|
11049
|
+
getLogs: () => [...logRing],
|
|
11050
|
+
onStop: () => {
|
|
11051
|
+
console.log("[local] \u6536\u5230\u505C\u6B62\u6307\u4EE4\uFF08\u63A7\u5236 API\uFF09");
|
|
11052
|
+
exitProcess(0);
|
|
11053
|
+
},
|
|
11054
|
+
killSession: (id) => {
|
|
11055
|
+
try {
|
|
11056
|
+
pty.kill(id);
|
|
11057
|
+
return true;
|
|
11058
|
+
} catch {
|
|
11059
|
+
return false;
|
|
11060
|
+
}
|
|
11061
|
+
}
|
|
11062
|
+
});
|
|
11063
|
+
console.log(`[local] control api: http://127.0.0.1:${port} (\u4EC5\u672C\u673A)`);
|
|
11064
|
+
}
|
|
10862
11065
|
let ws = null;
|
|
10863
11066
|
let heartbeatTimer = null;
|
|
10864
11067
|
let reconnectTimer = null;
|
|
10865
|
-
let reconnectAttempts = 0;
|
|
10866
11068
|
let userExitCode = null;
|
|
10867
11069
|
function stopHeartbeat() {
|
|
10868
11070
|
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
@@ -10885,6 +11087,7 @@ async function startLocal(opts = {}) {
|
|
|
10885
11087
|
ws = sock;
|
|
10886
11088
|
sock.on("open", () => {
|
|
10887
11089
|
console.log("[local] connected");
|
|
11090
|
+
if (phase !== "needs-pairing") setPhase("connecting");
|
|
10888
11091
|
sendAuth();
|
|
10889
11092
|
stopHeartbeat();
|
|
10890
11093
|
heartbeatTimer = setInterval(() => {
|
|
@@ -10901,6 +11104,7 @@ async function startLocal(opts = {}) {
|
|
|
10901
11104
|
process.exit(userExitCode);
|
|
10902
11105
|
}
|
|
10903
11106
|
console.log("[local] disconnected; PTY sessions kept alive, reconnecting\u2026");
|
|
11107
|
+
if (phase !== "needs-pairing") setPhase("reconnecting");
|
|
10904
11108
|
scheduleReconnect();
|
|
10905
11109
|
});
|
|
10906
11110
|
sock.on("error", (err) => {
|
|
@@ -10909,6 +11113,7 @@ async function startLocal(opts = {}) {
|
|
|
10909
11113
|
}
|
|
10910
11114
|
async function scheduleReconnect() {
|
|
10911
11115
|
if (reconnectTimer || userExitCode !== null) return;
|
|
11116
|
+
if (phase === "needs-pairing") return;
|
|
10912
11117
|
const idx = Math.min(reconnectAttempts, RECONNECT_DELAYS_MS.length - 1);
|
|
10913
11118
|
const delay = RECONNECT_DELAYS_MS[idx];
|
|
10914
11119
|
reconnectAttempts++;
|
|
@@ -10918,11 +11123,13 @@ async function startLocal(opts = {}) {
|
|
|
10918
11123
|
ensureFreshAccessToken(baseUrl, credentials).then((creds) => {
|
|
10919
11124
|
credentials = creds;
|
|
10920
11125
|
connect();
|
|
10921
|
-
}).catch(
|
|
10922
|
-
|
|
10923
|
-
|
|
10924
|
-
|
|
10925
|
-
|
|
11126
|
+
}).catch((e) => {
|
|
11127
|
+
if (isPermanentAuthFailure(e)) {
|
|
11128
|
+
enterNeedsPairing(e.message);
|
|
11129
|
+
return;
|
|
11130
|
+
}
|
|
11131
|
+
console.error(`[local] token \u5237\u65B0\u6682\u65F6\u5931\u8D25: ${e.message}`);
|
|
11132
|
+
scheduleReconnect();
|
|
10926
11133
|
});
|
|
10927
11134
|
}, delay);
|
|
10928
11135
|
}
|
|
@@ -10930,6 +11137,9 @@ async function startLocal(opts = {}) {
|
|
|
10930
11137
|
userExitCode = code;
|
|
10931
11138
|
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
10932
11139
|
reconnectTimer = null;
|
|
11140
|
+
if (pairingTimer) clearInterval(pairingTimer);
|
|
11141
|
+
pairingTimer = null;
|
|
11142
|
+
controlApi?.close();
|
|
10933
11143
|
if (opts.daemon) removeGatewayPid();
|
|
10934
11144
|
transfers.abortAll();
|
|
10935
11145
|
pty.killAll();
|
|
@@ -10959,6 +11169,7 @@ async function startLocal(opts = {}) {
|
|
|
10959
11169
|
console.log(`[local] reconnected after ${reconnectAttempts} attempt(s); ${pty.list().length} PTY session(s) alive`);
|
|
10960
11170
|
}
|
|
10961
11171
|
reconnectAttempts = 0;
|
|
11172
|
+
setPhase("connected");
|
|
10962
11173
|
console.log(`[local] auth ok, deviceId=${frame.channelId}`);
|
|
10963
11174
|
break;
|
|
10964
11175
|
case "auth-err":
|
|
@@ -11001,20 +11212,26 @@ async function startLocal(opts = {}) {
|
|
|
11001
11212
|
}
|
|
11002
11213
|
}
|
|
11003
11214
|
async function handleAuthErr(reason) {
|
|
11004
|
-
if (reason === "access token expired") {
|
|
11215
|
+
if (reason === "access token expired" || reason === "invalid access token") {
|
|
11005
11216
|
try {
|
|
11006
11217
|
credentials = await ensureFreshAccessToken(baseUrl, credentials);
|
|
11007
11218
|
console.log("[local] token refreshed, retrying auth");
|
|
11008
11219
|
sendAuth();
|
|
11009
11220
|
return;
|
|
11010
11221
|
} catch (e) {
|
|
11011
|
-
|
|
11222
|
+
if (isPermanentAuthFailure(e)) {
|
|
11223
|
+
enterNeedsPairing(e.message);
|
|
11224
|
+
return;
|
|
11225
|
+
}
|
|
11226
|
+
console.error(`[local] token \u5237\u65B0\u6682\u65F6\u5931\u8D25\uFF08${e.message}\uFF09\uFF0C\u8F6C\u5165\u91CD\u8FDE`);
|
|
11227
|
+
try {
|
|
11228
|
+
ws?.close();
|
|
11229
|
+
} catch {
|
|
11230
|
+
}
|
|
11231
|
+
return;
|
|
11012
11232
|
}
|
|
11013
11233
|
}
|
|
11014
|
-
|
|
11015
|
-
console.error("[local] auth failed:", reason);
|
|
11016
|
-
console.error("[local] \u51ED\u8BC1\u5DF2\u6E05\u9664\uFF0C\u8BF7\u91CD\u65B0\u8FD0\u884C cli-local \u5B8C\u6210\u6388\u6743");
|
|
11017
|
-
exitProcess(1);
|
|
11234
|
+
enterNeedsPairing(reason);
|
|
11018
11235
|
}
|
|
11019
11236
|
function handleSpawn(frame) {
|
|
11020
11237
|
const sessionId = frame.sessionId;
|
|
@@ -11160,7 +11377,57 @@ async function startLocal(opts = {}) {
|
|
|
11160
11377
|
}
|
|
11161
11378
|
process.on("SIGINT", () => cleanup(0));
|
|
11162
11379
|
process.on("SIGTERM", () => cleanup(0));
|
|
11163
|
-
|
|
11380
|
+
if (credentials.deviceId) {
|
|
11381
|
+
setPhase("connecting");
|
|
11382
|
+
connect();
|
|
11383
|
+
} else {
|
|
11384
|
+
enterNeedsPairing("\u65E0\u51ED\u8BC1\uFF08credentials.json \u7F3A\u5931\u6216\u4E3A\u7A7A\uFF09");
|
|
11385
|
+
}
|
|
11386
|
+
}
|
|
11387
|
+
function credentialsPathOf() {
|
|
11388
|
+
return (0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR, "credentials.json");
|
|
11389
|
+
}
|
|
11390
|
+
function instanceLockPath() {
|
|
11391
|
+
return (0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR, "instance.lock");
|
|
11392
|
+
}
|
|
11393
|
+
function processAlive(pid) {
|
|
11394
|
+
try {
|
|
11395
|
+
process.kill(pid, 0);
|
|
11396
|
+
return true;
|
|
11397
|
+
} catch {
|
|
11398
|
+
return false;
|
|
11399
|
+
}
|
|
11400
|
+
}
|
|
11401
|
+
function acquireSingleInstanceLock() {
|
|
11402
|
+
const path = instanceLockPath();
|
|
11403
|
+
try {
|
|
11404
|
+
const pid = Number((0, import_node_fs4.readFileSync)(path, "utf-8").trim());
|
|
11405
|
+
if (Number.isFinite(pid) && pid !== process.pid && processAlive(pid)) {
|
|
11406
|
+
return pid;
|
|
11407
|
+
}
|
|
11408
|
+
} catch {
|
|
11409
|
+
}
|
|
11410
|
+
try {
|
|
11411
|
+
(0, import_node_fs4.mkdirSync)((0, import_node_path6.join)(path, ".."), { recursive: true });
|
|
11412
|
+
(0, import_node_fs4.writeFileSync)(path, String(process.pid));
|
|
11413
|
+
} catch {
|
|
11414
|
+
}
|
|
11415
|
+
return null;
|
|
11416
|
+
}
|
|
11417
|
+
function patchConsoleIntoRing(ring) {
|
|
11418
|
+
const CAP = 500;
|
|
11419
|
+
const push = (line) => {
|
|
11420
|
+
ring.push(line);
|
|
11421
|
+
if (ring.length > CAP) ring.splice(0, ring.length - CAP);
|
|
11422
|
+
};
|
|
11423
|
+
const wrap = (orig) => {
|
|
11424
|
+
return (...args) => {
|
|
11425
|
+
push(args.map((a) => typeof a === "string" ? a : String(a)).join(" "));
|
|
11426
|
+
orig(...args);
|
|
11427
|
+
};
|
|
11428
|
+
};
|
|
11429
|
+
console.log = wrap(console.log.bind(console));
|
|
11430
|
+
console.error = wrap(console.error.bind(console));
|
|
11164
11431
|
}
|
|
11165
11432
|
function toUint8(raw) {
|
|
11166
11433
|
if (raw instanceof Buffer) return new Uint8Array(raw.buffer, raw.byteOffset, raw.byteLength);
|
|
@@ -11171,21 +11438,21 @@ function toUint8(raw) {
|
|
|
11171
11438
|
throw new Error("unexpected raw type");
|
|
11172
11439
|
}
|
|
11173
11440
|
function gatewayPidFile() {
|
|
11174
|
-
return (0,
|
|
11441
|
+
return (0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR, "gateway.pid");
|
|
11175
11442
|
}
|
|
11176
11443
|
function writeGatewayPid() {
|
|
11177
|
-
(0,
|
|
11178
|
-
(0,
|
|
11444
|
+
(0, import_node_fs4.mkdirSync)((0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR), { recursive: true });
|
|
11445
|
+
(0, import_node_fs4.writeFileSync)(gatewayPidFile(), String(process.pid));
|
|
11179
11446
|
}
|
|
11180
11447
|
function removeGatewayPid() {
|
|
11181
11448
|
try {
|
|
11182
|
-
(0,
|
|
11449
|
+
(0, import_node_fs4.rmSync)(gatewayPidFile());
|
|
11183
11450
|
} catch {
|
|
11184
11451
|
}
|
|
11185
11452
|
}
|
|
11186
11453
|
function gatewayPid() {
|
|
11187
11454
|
try {
|
|
11188
|
-
const pid = Number((0,
|
|
11455
|
+
const pid = Number((0, import_node_fs4.readFileSync)(gatewayPidFile(), "utf-8").trim());
|
|
11189
11456
|
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
11190
11457
|
} catch {
|
|
11191
11458
|
return null;
|
|
@@ -11219,15 +11486,15 @@ function resolveHere() {
|
|
|
11219
11486
|
if (DEBUG) console.error("[debug] cannot resolve script path");
|
|
11220
11487
|
return;
|
|
11221
11488
|
}
|
|
11222
|
-
const hereDir = (0,
|
|
11489
|
+
const hereDir = (0, import_node_path7.dirname)(here);
|
|
11223
11490
|
const candidates = [
|
|
11224
|
-
(0,
|
|
11225
|
-
(0,
|
|
11226
|
-
(0,
|
|
11491
|
+
(0, import_node_path7.join)(hereDir, "..", "node_modules", "node-pty", "prebuilds"),
|
|
11492
|
+
(0, import_node_path7.join)(hereDir, "..", "..", "node-pty", "prebuilds"),
|
|
11493
|
+
(0, import_node_path7.join)(hereDir, "..", "..", "..", "node_modules", "node-pty", "prebuilds")
|
|
11227
11494
|
];
|
|
11228
11495
|
let prebuildsDir = null;
|
|
11229
11496
|
for (const c of candidates) {
|
|
11230
|
-
if ((0,
|
|
11497
|
+
if ((0, import_node_fs5.existsSync)(c)) {
|
|
11231
11498
|
prebuildsDir = c;
|
|
11232
11499
|
break;
|
|
11233
11500
|
}
|
|
@@ -11237,11 +11504,11 @@ function resolveHere() {
|
|
|
11237
11504
|
return;
|
|
11238
11505
|
}
|
|
11239
11506
|
let n = 0;
|
|
11240
|
-
for (const sub of (0,
|
|
11241
|
-
const helper = (0,
|
|
11242
|
-
if ((0,
|
|
11507
|
+
for (const sub of (0, import_node_fs5.readdirSync)(prebuildsDir)) {
|
|
11508
|
+
const helper = (0, import_node_path7.join)(prebuildsDir, sub, "spawn-helper");
|
|
11509
|
+
if ((0, import_node_fs5.existsSync)(helper)) {
|
|
11243
11510
|
try {
|
|
11244
|
-
(0,
|
|
11511
|
+
(0, import_node_fs5.chmodSync)(helper, 493);
|
|
11245
11512
|
n++;
|
|
11246
11513
|
} catch {
|
|
11247
11514
|
}
|
|
@@ -11252,49 +11519,56 @@ function resolveHere() {
|
|
|
11252
11519
|
var argUrl = process.argv[2];
|
|
11253
11520
|
var argMatch = argUrl && (argUrl.startsWith("ws://") || argUrl.startsWith("wss://"));
|
|
11254
11521
|
if (argUrl && !argMatch && (argUrl === "-h" || argUrl === "--help" || argUrl === "help")) {
|
|
11255
|
-
console.log(`cli-local \u2014 local PTY agent for cli-remote
|
|
11522
|
+
console.log(`cli-local \u2014 local PTY agent for cli-remote (gateway \u5E38\u9A7B\u670D\u52A1)
|
|
11256
11523
|
|
|
11257
11524
|
usage:
|
|
11258
|
-
cli-local
|
|
11259
|
-
cli-local
|
|
11260
|
-
cli-local
|
|
11525
|
+
cli-local \u65E0 gateway \u4E14\u5DF2\u914D\u7F6E\u65F6\u524D\u53F0\u8FD0\u884C\uFF1B\u5426\u5219\u663E\u793A\u72B6\u6001
|
|
11526
|
+
cli-local pair \u91CD\u65B0\u6388\u6743\uFF08\u540A\u9500/\u8FC7\u671F\u540E\u91CD\u914D\u5BF9\uFF1Bgateway \u70ED\u52A0\u8F7D\uFF09
|
|
11527
|
+
cli-local gateway start \u540E\u53F0\u542F\u52A8\u5E38\u9A7B gateway\uFF08\u63A8\u8350\uFF09
|
|
11528
|
+
cli-local gateway stop|restart|status|logs [-f] [-n N]
|
|
11529
|
+
cli-local install [HUB_URL] \u5B89\u88C5 macOS launchd \u5E38\u9A7B\u670D\u52A1
|
|
11530
|
+
cli-local uninstall \u5378\u8F7D launchd \u5E38\u9A7B\u670D\u52A1
|
|
11531
|
+
|
|
11532
|
+
\u9996\u6B21\u8FD0\u884C\uFF08\u65E0\u914D\u7F6E\uFF09\u4F1A\u8FDB\u5165\u5F15\u5BFC\uFF1Ahub \u5730\u5740 \u2192 registrationKey \u2192 Device Flow \u6388\u6743\u3002
|
|
11533
|
+
\u6388\u6743\u540E\u5EFA\u8BAE\u7528 cli-local gateway start \u5E38\u9A7B\uFF08\u65AD\u7EBF\u81EA\u52A8\u91CD\u8FDE\u3001PTY \u4FDD\u6D3B\uFF09\u3002
|
|
11261
11534
|
|
|
11262
|
-
\
|
|
11263
|
-
|
|
11264
|
-
2. \u81EA\u52A8\u6253\u5F00\u6D4F\u89C8\u5668 \u2192 \u767B\u5F55 hub\uFF0C\u4ECE\u300C\u6211\u7684\u8BBE\u5907\u300D\u9875\u590D\u5236 registrationKey
|
|
11265
|
-
3. \u56DE\u5230\u7EC8\u7AEF\u7C98\u8D34 key \u2192 \u81EA\u52A8\u5199\u5165 ~/.cli-remote/config.json
|
|
11266
|
-
4. \u968F\u540E\u8D70 OAuth Device Flow\uFF08RFC 8628\uFF09\uFF1A\u81EA\u52A8\u6253\u5F00\u6388\u6743\u9875 \u2192 \u6279\u51C6\u672C\u8BBE\u5907
|
|
11267
|
-
5. \u51ED\u8BC1\u5B58\u5165 ~/.cli-remote/credentials.json\uFF080600\uFF09\uFF0C\u4E4B\u540E\u81EA\u52A8\u7EED\u671F
|
|
11535
|
+
\u88AB\u540A\u9500/\u51ED\u8BC1\u8FC7\u671F\u65F6 gateway \u4E0D\u4F1A\u9000\u51FA\uFF0C\u800C\u662F\u8FDB\u5165 needs-pairing \u7B49\u5F85\uFF1B
|
|
11536
|
+
\u53E6\u5F00\u7EC8\u7AEF\u8DD1 cli-local pair \u5199\u5165\u65B0\u51ED\u8BC1\u540E\u81EA\u52A8\u91CD\u8FDE\uFF08\u65E0\u9700\u91CD\u542F gateway\uFF09\u3002
|
|
11268
11537
|
|
|
11269
|
-
\
|
|
11538
|
+
\u672C\u5730\u63A7\u5236 API\uFF08\u4EC5 127.0.0.1\uFF0Cgateway \u8FD0\u884C\u65F6\uFF09\uFF1A
|
|
11539
|
+
GET /status /healthz /logs /sessions POST /stop
|
|
11540
|
+
\u7AEF\u53E3\u9ED8\u8BA4 18790\uFF0Cconfig.json gateway.apiPort \u6216 CLI_LOCAL_API_PORT \u53EF\u6539
|
|
11270
11541
|
|
|
11271
11542
|
environment:
|
|
11272
11543
|
RELAY_URL hub ws url (overrides argv and config)
|
|
11273
11544
|
REGISTRATION_KEY registration key (fallback of config.json)
|
|
11274
11545
|
CLI_LOCAL_NO_BROWSER=1 \u4E0D\u81EA\u52A8\u6253\u5F00\u6D4F\u89C8\u5668\uFF08\u53EA\u6253\u5370\u94FE\u63A5\uFF09
|
|
11546
|
+
CLI_LOCAL_API_PORT gateway \u63A7\u5236API\u7AEF\u53E3
|
|
11275
11547
|
HOME where ~/.cli-remote/ lives
|
|
11276
11548
|
|
|
11277
11549
|
config (~/.cli-remote/config.json):
|
|
11278
11550
|
{
|
|
11279
11551
|
"relayUrl": "wss://your-hub/ws",
|
|
11280
11552
|
"registrationKey": "from-the-web-devices-page",
|
|
11553
|
+
"gateway": { "apiPort": 18790 },
|
|
11281
11554
|
"whitelist": { "add": [], "remove": [] }
|
|
11282
11555
|
}
|
|
11283
11556
|
|
|
11284
11557
|
example:
|
|
11285
|
-
cli-local
|
|
11286
|
-
cli-local
|
|
11558
|
+
cli-local pair # \u9996\u6B21/\u91CD\u65B0\u6388\u6743
|
|
11559
|
+
cli-local gateway start # \u540E\u53F0\u5E38\u9A7B
|
|
11560
|
+
cli-local gateway status # \u67E5\u770B\u8FDE\u63A5\u72B6\u6001`);
|
|
11287
11561
|
process.exit(0);
|
|
11288
11562
|
}
|
|
11289
11563
|
var LAUNCHD_LABEL = "net.cli-remote.local";
|
|
11290
11564
|
function launchdPlistPath() {
|
|
11291
|
-
return (0,
|
|
11565
|
+
return (0, import_node_path7.join)((0, import_node_os7.homedir)(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
|
|
11292
11566
|
}
|
|
11293
11567
|
function entryScript() {
|
|
11294
11568
|
const a1 = process.argv[1];
|
|
11295
11569
|
if (!a1 || a1.endsWith(".ts")) return null;
|
|
11296
11570
|
try {
|
|
11297
|
-
return (0,
|
|
11571
|
+
return (0, import_node_fs5.realpathSync)(a1);
|
|
11298
11572
|
} catch {
|
|
11299
11573
|
return a1;
|
|
11300
11574
|
}
|
|
@@ -11312,7 +11586,7 @@ function launchdInstall(urlArg) {
|
|
|
11312
11586
|
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`);
|
|
11313
11587
|
process.exit(1);
|
|
11314
11588
|
}
|
|
11315
|
-
const home = (0,
|
|
11589
|
+
const home = (0, import_node_os7.homedir)();
|
|
11316
11590
|
const entry = entryScript();
|
|
11317
11591
|
if (!entry) {
|
|
11318
11592
|
console.error("[local] \u5F53\u524D\u4EE5\u6E90\u7801\uFF08tsx\uFF09\u6A21\u5F0F\u8FD0\u884C\uFF0C\u65E0\u6CD5\u5B89\u88C5\u5E38\u9A7B\u670D\u52A1\u3002");
|
|
@@ -11320,28 +11594,28 @@ function launchdInstall(urlArg) {
|
|
|
11320
11594
|
process.exit(1);
|
|
11321
11595
|
}
|
|
11322
11596
|
if (urlArg) {
|
|
11323
|
-
const cfgDir = (0,
|
|
11324
|
-
(0,
|
|
11325
|
-
const cfgPath = (0,
|
|
11597
|
+
const cfgDir = (0, import_node_path7.join)(home, DEFAULT_CONFIG_DIR);
|
|
11598
|
+
(0, import_node_fs5.mkdirSync)(cfgDir, { recursive: true });
|
|
11599
|
+
const cfgPath = (0, import_node_path7.join)(cfgDir, "config.json");
|
|
11326
11600
|
let cfg = {};
|
|
11327
11601
|
try {
|
|
11328
|
-
cfg = JSON.parse((0,
|
|
11602
|
+
cfg = JSON.parse((0, import_node_fs5.readFileSync)(cfgPath, "utf-8"));
|
|
11329
11603
|
} catch {
|
|
11330
11604
|
}
|
|
11331
11605
|
cfg.relayUrl = urlArg;
|
|
11332
|
-
(0,
|
|
11606
|
+
(0, import_node_fs5.writeFileSync)(cfgPath, JSON.stringify(cfg, null, 2) + "\n");
|
|
11333
11607
|
console.log(`[local] relayUrl \u5DF2\u5199\u5165 ${cfgPath}`);
|
|
11334
11608
|
}
|
|
11335
|
-
const credPath = (0,
|
|
11336
|
-
if (!(0,
|
|
11609
|
+
const credPath = (0, import_node_path7.join)(home, DEFAULT_CONFIG_DIR, CREDENTIALS_FILE);
|
|
11610
|
+
if (!(0, import_node_fs5.existsSync)(credPath)) {
|
|
11337
11611
|
console.error("[local] \u5C1A\u672A\u6388\u6743\uFF08\u627E\u4E0D\u5230 credentials.json\uFF09\u3002");
|
|
11338
11612
|
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");
|
|
11339
11613
|
process.exit(1);
|
|
11340
11614
|
}
|
|
11341
11615
|
const plist = launchdPlistPath();
|
|
11342
|
-
const logDir = (0,
|
|
11343
|
-
(0,
|
|
11344
|
-
(0,
|
|
11616
|
+
const logDir = (0, import_node_path7.join)(home, DEFAULT_CONFIG_DIR, "logs");
|
|
11617
|
+
(0, import_node_fs5.mkdirSync)((0, import_node_path7.dirname)(plist), { recursive: true });
|
|
11618
|
+
(0, import_node_fs5.mkdirSync)(logDir, { recursive: true });
|
|
11345
11619
|
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
11346
11620
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
11347
11621
|
<plist version="1.0">
|
|
@@ -11355,12 +11629,12 @@ function launchdInstall(urlArg) {
|
|
|
11355
11629
|
<key>RunAtLoad</key><true/>
|
|
11356
11630
|
<key>KeepAlive</key><true/>
|
|
11357
11631
|
<key>ThrottleInterval</key><integer>30</integer>
|
|
11358
|
-
<key>StandardOutPath</key><string>${(0,
|
|
11359
|
-
<key>StandardErrorPath</key><string>${(0,
|
|
11632
|
+
<key>StandardOutPath</key><string>${(0, import_node_path7.join)(logDir, "daemon.out.log")}</string>
|
|
11633
|
+
<key>StandardErrorPath</key><string>${(0, import_node_path7.join)(logDir, "daemon.err.log")}</string>
|
|
11360
11634
|
</dict>
|
|
11361
11635
|
</plist>
|
|
11362
11636
|
`;
|
|
11363
|
-
(0,
|
|
11637
|
+
(0, import_node_fs5.writeFileSync)(plist, xml);
|
|
11364
11638
|
const uid = process.getuid?.();
|
|
11365
11639
|
if (uid === void 0) {
|
|
11366
11640
|
console.error("[local] \u65E0\u6CD5\u786E\u5B9A uid\uFF0Claunchd \u5B89\u88C5\u5931\u8D25");
|
|
@@ -11378,7 +11652,7 @@ function launchdInstall(urlArg) {
|
|
|
11378
11652
|
}
|
|
11379
11653
|
function launchdUninstall() {
|
|
11380
11654
|
const plist = launchdPlistPath();
|
|
11381
|
-
if (!(0,
|
|
11655
|
+
if (!(0, import_node_fs5.existsSync)(plist)) {
|
|
11382
11656
|
console.log("[local] \u672A\u627E\u5230\u5DF2\u5B89\u88C5\u7684\u5E38\u9A7B\u670D\u52A1\uFF08nothing to uninstall\uFF09");
|
|
11383
11657
|
process.exit(0);
|
|
11384
11658
|
}
|
|
@@ -11386,7 +11660,7 @@ function launchdUninstall() {
|
|
|
11386
11660
|
const guiDomain = `gui/${uid ?? 0}`;
|
|
11387
11661
|
run("launchctl", ["bootout", guiDomain, plist], true);
|
|
11388
11662
|
run("launchctl", ["unload", "-w", plist], true);
|
|
11389
|
-
(0,
|
|
11663
|
+
(0, import_node_fs5.rmSync)(plist);
|
|
11390
11664
|
console.log(`[local] launchd \u670D\u52A1\u5DF2\u505C\u6B62\u5E76\u5378\u8F7D\uFF1A${LAUNCHD_LABEL}`);
|
|
11391
11665
|
}
|
|
11392
11666
|
if (argUrl && !argMatch && argUrl === "install") {
|
|
@@ -11397,70 +11671,70 @@ if (argUrl && !argMatch && argUrl === "uninstall") {
|
|
|
11397
11671
|
launchdUninstall();
|
|
11398
11672
|
process.exit(0);
|
|
11399
11673
|
}
|
|
11400
|
-
if (argUrl === "
|
|
11401
|
-
|
|
11402
|
-
|
|
11403
|
-
|
|
11404
|
-
|
|
11405
|
-
if (pid === null || !gatewayAlive()) {
|
|
11406
|
-
(0, import_node_fs4.rmSync)((0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR, "gateway.pid"), { force: true });
|
|
11407
|
-
return;
|
|
11408
|
-
}
|
|
11674
|
+
if (argUrl === "pair") {
|
|
11675
|
+
void (async () => {
|
|
11676
|
+
const home = (0, import_node_os7.homedir)();
|
|
11677
|
+
const cfgPath = (0, import_node_path7.join)(home, DEFAULT_CONFIG_DIR, "config.json");
|
|
11678
|
+
let cfg = {};
|
|
11409
11679
|
try {
|
|
11410
|
-
|
|
11680
|
+
cfg = JSON.parse((0, import_node_fs5.readFileSync)(cfgPath, "utf-8"));
|
|
11411
11681
|
} catch {
|
|
11412
11682
|
}
|
|
11413
|
-
const
|
|
11414
|
-
|
|
11415
|
-
|
|
11416
|
-
|
|
11417
|
-
}
|
|
11683
|
+
const url = process.argv[4] ?? process.env.RELAY_URL ?? cfg.relayUrl;
|
|
11684
|
+
if (!url) {
|
|
11685
|
+
console.error("[pair] \u7F3A\u5C11 hub \u5730\u5740\uFF1A\u5148\u8FD0\u884C\u4E00\u6B21 cli-local \u5B8C\u6210\u914D\u7F6E\uFF0C\u6216 cli-local pair wss://your-hub/ws");
|
|
11686
|
+
process.exit(1);
|
|
11418
11687
|
}
|
|
11419
|
-
|
|
11420
|
-
|
|
11421
|
-
const pid = gatewayPid();
|
|
11422
|
-
const wasRunning = gatewayAlive();
|
|
11423
|
-
stopWorker();
|
|
11424
|
-
console.log(wasRunning ? `[gateway] stopped (pid ${pid ?? "?"})` : "[gateway] not running");
|
|
11425
|
-
process.exit(0);
|
|
11426
|
-
}, gatewayStatus = function() {
|
|
11427
|
-
if (!gatewayAlive()) {
|
|
11428
|
-
console.log("[gateway] stopped");
|
|
11688
|
+
if (!cfg.registrationKey && !process.env.REGISTRATION_KEY) {
|
|
11689
|
+
console.error("[pair] \u7F3A\u5C11 registrationKey\uFF1A\u5199\u5165 ~/.cli-remote/config.json \u6216\u8BBE\u7F6E REGISTRATION_KEY");
|
|
11429
11690
|
process.exit(1);
|
|
11430
11691
|
}
|
|
11431
|
-
const
|
|
11432
|
-
|
|
11433
|
-
|
|
11434
|
-
|
|
11435
|
-
|
|
11436
|
-
|
|
11692
|
+
const prev = await loadCredentials();
|
|
11693
|
+
const deviceId = prev?.deviceId || await stableDeviceId();
|
|
11694
|
+
console.log(`[pair] hub: ${url}\uFF08\u8BBE\u5907 ${deviceId}${prev ? "\uFF0C\u590D\u7528" : "\uFF0C\u7531\u673A\u5668\u7801\u6D3E\u751F"}\uFF09`);
|
|
11695
|
+
const creds = await deviceFlowAuthorize(
|
|
11696
|
+
baseUrlFromWsUrl(url),
|
|
11697
|
+
cfg.registrationKey ?? process.env.REGISTRATION_KEY,
|
|
11698
|
+
deviceId
|
|
11699
|
+
);
|
|
11700
|
+
console.log(`[pair] \u6388\u6743\u5B8C\u6210 deviceId=${creds.deviceId}`);
|
|
11701
|
+
const port = readConfiguredPort();
|
|
11702
|
+
const health = await apiGet(port, "/healthz");
|
|
11703
|
+
if (health?.ok) {
|
|
11704
|
+
console.log("[pair] gateway \u5728\u8FD0\u884C\uFF0C\u65B0\u51ED\u8BC1\u5DF2\u5199\u5165 \u2014 \u6570\u79D2\u5185\u81EA\u52A8\u91CD\u8FDE\uFF08\u65E0\u9700\u91CD\u542F\uFF09");
|
|
11705
|
+
} else {
|
|
11706
|
+
console.log("[pair] gateway \u672A\u8FD0\u884C\uFF1Acli-local gateway start \u542F\u52A8\u5E38\u9A7B\u670D\u52A1");
|
|
11437
11707
|
}
|
|
11438
|
-
|
|
11439
|
-
console.
|
|
11440
|
-
|
|
11441
|
-
|
|
11708
|
+
})().catch((e) => {
|
|
11709
|
+
console.error(`[pair] ${e.message}`);
|
|
11710
|
+
process.exit(1);
|
|
11711
|
+
});
|
|
11712
|
+
}
|
|
11713
|
+
if (argUrl === "gateway") {
|
|
11714
|
+
let gatewayLogFile = function() {
|
|
11715
|
+
return (0, import_node_path7.join)((0, import_node_os7.homedir)(), DEFAULT_CONFIG_DIR, "logs", "gateway.log");
|
|
11442
11716
|
}, gatewayLogs = function() {
|
|
11443
11717
|
const follow = rest.includes("-f") || rest.includes("--follow");
|
|
11444
11718
|
const li = rest.findIndex((a) => a === "-n" || a === "--lines");
|
|
11445
11719
|
const lines = li >= 0 ? Number(rest[li + 1]) || 50 : 50;
|
|
11446
11720
|
const file = gatewayLogFile();
|
|
11447
|
-
if (!(0,
|
|
11721
|
+
if (!(0, import_node_fs5.existsSync)(file)) {
|
|
11448
11722
|
console.log(`[gateway] \u6682\u65E0\u65E5\u5FD7\uFF08${file} \u4E0D\u5B58\u5728\uFF09`);
|
|
11449
11723
|
process.exit(0);
|
|
11450
11724
|
}
|
|
11451
|
-
const tail = (0,
|
|
11725
|
+
const tail = (0, import_node_fs5.readFileSync)(file, "utf-8").trimEnd().split("\n").slice(-lines);
|
|
11452
11726
|
console.log(tail.join("\n"));
|
|
11453
11727
|
if (!follow) process.exit(0);
|
|
11454
|
-
let size = (0,
|
|
11728
|
+
let size = (0, import_node_fs5.statSync)(file).size;
|
|
11455
11729
|
console.log("--- following (Ctrl+C \u9000\u51FA) ---");
|
|
11456
11730
|
setInterval(() => {
|
|
11457
11731
|
try {
|
|
11458
|
-
const st = (0,
|
|
11732
|
+
const st = (0, import_node_fs5.statSync)(file);
|
|
11459
11733
|
if (st.size > size) {
|
|
11460
|
-
const fd = (0,
|
|
11734
|
+
const fd = (0, import_node_fs5.openSync)(file, "r");
|
|
11461
11735
|
const buf = Buffer.alloc(st.size - size);
|
|
11462
|
-
(0,
|
|
11463
|
-
(0,
|
|
11736
|
+
(0, import_node_fs5.readSync)(fd, buf, 0, buf.length, size);
|
|
11737
|
+
(0, import_node_fs5.closeSync)(fd);
|
|
11464
11738
|
size = st.size;
|
|
11465
11739
|
process.stdout.write(buf.toString("utf-8"));
|
|
11466
11740
|
} else if (st.size < size) {
|
|
@@ -11470,7 +11744,7 @@ if (argUrl === "gateway") {
|
|
|
11470
11744
|
}
|
|
11471
11745
|
}, 1e3);
|
|
11472
11746
|
};
|
|
11473
|
-
gatewayLogFile2 = gatewayLogFile,
|
|
11747
|
+
gatewayLogFile2 = gatewayLogFile, gatewayLogs2 = gatewayLogs;
|
|
11474
11748
|
const action = process.argv[3] ?? "";
|
|
11475
11749
|
const rest = process.argv.slice(4);
|
|
11476
11750
|
const hubArg = (() => {
|
|
@@ -11482,22 +11756,22 @@ if (argUrl === "gateway") {
|
|
|
11482
11756
|
console.log(`[gateway] already running (pid ${gatewayPid()})`);
|
|
11483
11757
|
process.exit(0);
|
|
11484
11758
|
}
|
|
11485
|
-
const home = (0,
|
|
11759
|
+
const home = (0, import_node_os7.homedir)();
|
|
11486
11760
|
const entry = entryScript();
|
|
11487
|
-
const cfgDir = (0,
|
|
11488
|
-
const
|
|
11489
|
-
const
|
|
11490
|
-
const hasCfg = (0, import_node_fs4.existsSync)(cfgPath);
|
|
11761
|
+
const cfgDir = (0, import_node_path7.join)(home, DEFAULT_CONFIG_DIR);
|
|
11762
|
+
const cfgPath = (0, import_node_path7.join)(cfgDir, "config.json");
|
|
11763
|
+
const hasCfg = (0, import_node_fs5.existsSync)(cfgPath);
|
|
11491
11764
|
if (!entry) {
|
|
11492
11765
|
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");
|
|
11493
11766
|
process.exit(1);
|
|
11494
11767
|
}
|
|
11495
|
-
if (!hasCfg
|
|
11496
|
-
console.error("[gateway] \u5C1A\u672A\u914D\u7F6E
|
|
11768
|
+
if (!hasCfg) {
|
|
11769
|
+
console.error("[gateway] \u5C1A\u672A\u914D\u7F6E hub \u5730\u5740\u3002\u8BF7\u5148\u4EA4\u4E92\u5F0F\u8FD0\u884C\u4E00\u6B21 cli-local \u5B8C\u6210\u521D\u59CB\u914D\u7F6E\u3002");
|
|
11770
|
+
console.error("[gateway] \uFF08\u65E0\u51ED\u8BC1\u4E5F\u53EF\u4EE5\u542F\u52A8\uFF1Agateway \u4F1A\u8FDB\u5165 needs-pairing\uFF0C\u7B49 cli-local pair\uFF09");
|
|
11497
11771
|
process.exit(1);
|
|
11498
11772
|
}
|
|
11499
|
-
(0,
|
|
11500
|
-
const out = (0,
|
|
11773
|
+
(0, import_node_fs5.mkdirSync)((0, import_node_path7.dirname)(gatewayLogFile()), { recursive: true });
|
|
11774
|
+
const out = (0, import_node_fs5.openSync)(gatewayLogFile(), "a");
|
|
11501
11775
|
const env = {};
|
|
11502
11776
|
for (const [k, v] of Object.entries(process.env)) if (v !== void 0) env[k] = v;
|
|
11503
11777
|
const args = [entry, "gateway", "run"];
|
|
@@ -11509,41 +11783,124 @@ if (argUrl === "gateway") {
|
|
|
11509
11783
|
});
|
|
11510
11784
|
child.unref();
|
|
11511
11785
|
console.log(`[gateway] started (pid ${child.pid})`);
|
|
11786
|
+
const port = readConfiguredPort();
|
|
11787
|
+
for (let i = 0; i < 20; i++) {
|
|
11788
|
+
await new Promise((res) => setTimeout(res, 250));
|
|
11789
|
+
const h = await apiGet(port, "/healthz", 400);
|
|
11790
|
+
if (h?.ok) {
|
|
11791
|
+
console.log(`[gateway] \u63A7\u5236API\u5C31\u7EEA: http://127.0.0.1:${port}\uFF08\u4EC5\u672C\u673A\uFF09`);
|
|
11792
|
+
break;
|
|
11793
|
+
}
|
|
11794
|
+
}
|
|
11512
11795
|
console.log(`[gateway] \u65E5\u5FD7: ${gatewayLogFile()}`);
|
|
11513
11796
|
console.log("[gateway] \u72B6\u6001: cli-local gateway status");
|
|
11514
11797
|
}
|
|
11515
|
-
|
|
11516
|
-
|
|
11517
|
-
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
|
|
11521
|
-
|
|
11522
|
-
|
|
11523
|
-
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
|
|
11527
|
-
|
|
11528
|
-
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
11539
|
-
|
|
11540
|
-
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
|
|
11798
|
+
async function stopWorker() {
|
|
11799
|
+
const port = readConfiguredPort();
|
|
11800
|
+
if (await apiPost(port, "/stop", 1500)) {
|
|
11801
|
+
const t02 = Date.now();
|
|
11802
|
+
while (Date.now() - t02 < 5e3 && gatewayAlive()) {
|
|
11803
|
+
await new Promise((res) => setTimeout(res, 150));
|
|
11804
|
+
}
|
|
11805
|
+
(0, import_node_fs5.rmSync)((0, import_node_path7.join)((0, import_node_os7.homedir)(), DEFAULT_CONFIG_DIR, "gateway.pid"), { force: true });
|
|
11806
|
+
return;
|
|
11807
|
+
}
|
|
11808
|
+
const pid = gatewayPid();
|
|
11809
|
+
if (pid === null || !gatewayAlive()) {
|
|
11810
|
+
(0, import_node_fs5.rmSync)((0, import_node_path7.join)((0, import_node_os7.homedir)(), DEFAULT_CONFIG_DIR, "gateway.pid"), { force: true });
|
|
11811
|
+
return;
|
|
11812
|
+
}
|
|
11813
|
+
try {
|
|
11814
|
+
process.kill(pid, "SIGTERM");
|
|
11815
|
+
} catch {
|
|
11816
|
+
}
|
|
11817
|
+
const t0 = Date.now();
|
|
11818
|
+
while (Date.now() - t0 < 5e3 && gatewayAlive()) {
|
|
11819
|
+
await new Promise((res) => setTimeout(res, 150));
|
|
11820
|
+
}
|
|
11821
|
+
(0, import_node_fs5.rmSync)((0, import_node_path7.join)((0, import_node_os7.homedir)(), DEFAULT_CONFIG_DIR, "gateway.pid"), { force: true });
|
|
11822
|
+
}
|
|
11823
|
+
async function gatewayStop() {
|
|
11824
|
+
const pid = gatewayPid();
|
|
11825
|
+
const wasRunning = gatewayAlive();
|
|
11826
|
+
await stopWorker();
|
|
11827
|
+
console.log(wasRunning ? `[gateway] stopped (pid ${pid ?? "?"})` : "[gateway] not running");
|
|
11828
|
+
process.exit(0);
|
|
11829
|
+
}
|
|
11830
|
+
async function gatewayStatus() {
|
|
11831
|
+
const port = readConfiguredPort();
|
|
11832
|
+
const st = await apiGet(port, "/status", 600);
|
|
11833
|
+
if (st) {
|
|
11834
|
+
const phaseLabel = {
|
|
11835
|
+
starting: "\u542F\u52A8\u4E2D",
|
|
11836
|
+
connecting: "\u8FDE\u63A5\u4E2D",
|
|
11837
|
+
connected: "\u5DF2\u8FDE\u63A5",
|
|
11838
|
+
reconnecting: "\u91CD\u8FDE\u4E2D",
|
|
11839
|
+
"needs-pairing": "\u9700\u8981\u91CD\u65B0\u6388\u6743"
|
|
11840
|
+
};
|
|
11841
|
+
const up = Math.floor(st.uptimeMs / 1e3);
|
|
11842
|
+
const upStr = up > 3600 ? `${Math.floor(up / 3600)}h${Math.floor(up % 3600 / 60)}m` : up > 60 ? `${Math.floor(up / 60)}m${up % 60}s` : `${up}s`;
|
|
11843
|
+
const exp = st.accessTokenExpiresAt ? new Date(st.accessTokenExpiresAt).toLocaleTimeString() : "\u2014";
|
|
11844
|
+
console.log(`[gateway] running (pid ${gatewayPid() ?? "?"}, api :${port})`);
|
|
11845
|
+
console.log(`[gateway] \u72B6\u6001: ${phaseLabel[st.phase] ?? st.phase}${st.reason ? ` (${st.reason})` : ""}`);
|
|
11846
|
+
console.log(`[gateway] hub: ${st.hubUrl}`);
|
|
11847
|
+
console.log(`[gateway] deviceId: ${st.deviceId ?? "\u2014"}`);
|
|
11848
|
+
console.log(`[gateway] uptime: ${upStr}, token \u5230\u671F: ${exp}`);
|
|
11849
|
+
console.log(`[gateway] PTY \u4F1A\u8BDD: ${st.sessions.length} \u4E2A${st.sessions.length ? ` (${st.sessions.map((s) => s.cmd).join(", ")})` : ""}`);
|
|
11850
|
+
if (st.phase === "needs-pairing") {
|
|
11851
|
+
console.log("[gateway] \u26A0 \u8FD0\u884C cli-local pair \u91CD\u65B0\u6388\u6743\uFF08\u5B8C\u6210\u540E\u81EA\u52A8\u91CD\u8FDE\uFF09");
|
|
11852
|
+
}
|
|
11853
|
+
process.exit(0);
|
|
11854
|
+
}
|
|
11855
|
+
if (!gatewayAlive()) {
|
|
11856
|
+
console.log("[gateway] stopped");
|
|
11857
|
+
process.exit(1);
|
|
11858
|
+
}
|
|
11859
|
+
const pid = gatewayPid();
|
|
11860
|
+
let hub = "(\u672A\u914D\u7F6E)";
|
|
11861
|
+
try {
|
|
11862
|
+
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"));
|
|
11863
|
+
hub = cfg.relayUrl ?? hub;
|
|
11864
|
+
} catch {
|
|
11865
|
+
}
|
|
11866
|
+
console.log(`[gateway] running (pid ${pid}) \u2014 \u63A7\u5236API\u672A\u54CD\u5E94\uFF08\u65E7\u7248\u672C\uFF1F\uFF09`);
|
|
11867
|
+
console.log(`[gateway] hub: ${hub}`);
|
|
11868
|
+
console.log(`[gateway] \u65E5\u5FD7: ${gatewayLogFile()}`);
|
|
11869
|
+
process.exit(0);
|
|
11870
|
+
}
|
|
11871
|
+
void (async () => {
|
|
11872
|
+
switch (action) {
|
|
11873
|
+
case "start":
|
|
11874
|
+
gatewayStart().then(() => process.exit(0)).catch((e) => {
|
|
11875
|
+
console.error(`[gateway] ${e.message}`);
|
|
11876
|
+
process.exit(1);
|
|
11877
|
+
});
|
|
11878
|
+
break;
|
|
11879
|
+
case "stop":
|
|
11880
|
+
gatewayStop();
|
|
11881
|
+
break;
|
|
11882
|
+
case "restart":
|
|
11883
|
+
await stopWorker();
|
|
11884
|
+
gatewayStart().then(() => process.exit(0)).catch((e) => {
|
|
11885
|
+
console.error(`[gateway] ${e.message}`);
|
|
11886
|
+
process.exit(1);
|
|
11887
|
+
});
|
|
11888
|
+
break;
|
|
11889
|
+
case "status":
|
|
11890
|
+
void gatewayStatus();
|
|
11891
|
+
break;
|
|
11892
|
+
case "logs":
|
|
11893
|
+
gatewayLogs();
|
|
11894
|
+
break;
|
|
11895
|
+
case "run":
|
|
11896
|
+
startLocal({ url: hubArg, daemon: true }).catch((err) => {
|
|
11897
|
+
if (err instanceof AuthError) console.error(`[local] ${err.message}`);
|
|
11898
|
+
else console.error("[local] fatal:", err);
|
|
11899
|
+
process.exit(1);
|
|
11900
|
+
});
|
|
11901
|
+
break;
|
|
11902
|
+
default:
|
|
11903
|
+
console.log(`cli-local gateway \u2014 \u540E\u53F0\u8FD0\u884C\u7BA1\u7406
|
|
11547
11904
|
|
|
11548
11905
|
usage:
|
|
11549
11906
|
cli-local gateway start [--hub URL] \u540E\u53F0\u542F\u52A8\uFF08\u81EA\u52A8\u91CD\u8FDE\uFF09
|
|
@@ -11553,24 +11910,55 @@ usage:
|
|
|
11553
11910
|
cli-local gateway logs [-f] [-n N] \u67E5\u770B\u65E5\u5FD7\uFF08-f \u8DDF\u968F\uFF09
|
|
11554
11911
|
cli-local gateway run [--hub URL] \u524D\u53F0\u8FD0\u884C\uFF08\u4F9B start \u62C9\u8D77\uFF09
|
|
11555
11912
|
|
|
11556
|
-
\u8BF4\u660E\uFF1A\u540E\u53F0\u8FDB\u7A0B\u7531 pid \u6587\u4EF6\u7BA1\u7406\uFF08~/.cli-remote/gateway.pid\uFF09\uFF0C\
|
|
11557
|
-
|
|
11558
|
-
\
|
|
11559
|
-
|
|
11560
|
-
|
|
11913
|
+
\u8BF4\u660E\uFF1A\u540E\u53F0\u8FDB\u7A0B\u7531 pid \u6587\u4EF6\u7BA1\u7406\uFF08~/.cli-remote/gateway.pid\uFF09\uFF0C\u672C\u5730\u63A7\u5236API
|
|
11914
|
+
\u4EC5 127.0.0.1\uFF08\u9ED8\u8BA4 18790\uFF0Cconfig.json gateway.apiPort \u53EF\u6539\uFF09\uFF0C\u65E5\u5FD7\u5728
|
|
11915
|
+
~/.cli-remote/logs/gateway.log\u3002\u65E0\u51ED\u8BC1\u4E5F\u80FD\u542F\u52A8\uFF08\u8FDB\u5165 needs-pairing \u7B49
|
|
11916
|
+
cli-local pair\uFF09\u3002macOS \u60F3\u5F00\u673A\u81EA\u542F\u7528 launchd\uFF1Acli-local install\u3002`);
|
|
11917
|
+
process.exit(action ? 1 : 0);
|
|
11918
|
+
}
|
|
11919
|
+
})().catch((e) => {
|
|
11920
|
+
console.error(`[gateway] ${e.message}`);
|
|
11921
|
+
process.exit(1);
|
|
11922
|
+
});
|
|
11561
11923
|
}
|
|
11562
11924
|
var gatewayLogFile2;
|
|
11563
|
-
var stopWorker2;
|
|
11564
|
-
var gatewayStop2;
|
|
11565
|
-
var gatewayStatus2;
|
|
11566
11925
|
var gatewayLogs2;
|
|
11567
|
-
|
|
11568
|
-
if (
|
|
11569
|
-
|
|
11926
|
+
if (!argUrl || argMatch) {
|
|
11927
|
+
if (!argUrl) {
|
|
11928
|
+
void (async () => {
|
|
11929
|
+
const port = readConfiguredPort();
|
|
11930
|
+
const health = await apiGet(port, "/healthz", 400);
|
|
11931
|
+
if (health?.ok) {
|
|
11932
|
+
console.log("[local] gateway \u5E38\u9A7B\u670D\u52A1\u8FD0\u884C\u4E2D\u3002\u5B9E\u65F6\u72B6\u6001:");
|
|
11933
|
+
const entry = process.argv[1];
|
|
11934
|
+
if (entry && !entry.endsWith(".ts")) {
|
|
11935
|
+
const { execFileSync: ex } = await import("node:child_process");
|
|
11936
|
+
try {
|
|
11937
|
+
ex(process.execPath, [entry, "gateway", "status"], { stdio: "inherit" });
|
|
11938
|
+
} catch {
|
|
11939
|
+
console.log(`[local] \u63A7\u5236API: http://127.0.0.1:${port}/status`);
|
|
11940
|
+
}
|
|
11941
|
+
} else {
|
|
11942
|
+
console.log(`[local] \u63A7\u5236API: http://127.0.0.1:${port}/status`);
|
|
11943
|
+
}
|
|
11944
|
+
process.exit(0);
|
|
11945
|
+
}
|
|
11946
|
+
await startLocal({});
|
|
11947
|
+
})().catch((err) => {
|
|
11948
|
+
if (err instanceof AuthError) console.error(`[local] ${err.message}`);
|
|
11949
|
+
else console.error("[local] fatal:", err);
|
|
11950
|
+
process.exit(1);
|
|
11951
|
+
});
|
|
11570
11952
|
} else {
|
|
11571
|
-
|
|
11953
|
+
startLocal({ url: argUrl }).catch((err) => {
|
|
11954
|
+
if (err instanceof AuthError) {
|
|
11955
|
+
console.error(`[local] ${err.message}`);
|
|
11956
|
+
} else {
|
|
11957
|
+
console.error("[local] fatal:", err);
|
|
11958
|
+
}
|
|
11959
|
+
process.exit(1);
|
|
11960
|
+
});
|
|
11572
11961
|
}
|
|
11573
|
-
|
|
11574
|
-
});
|
|
11962
|
+
}
|
|
11575
11963
|
process.on("SIGINT", () => process.exit(0));
|
|
11576
11964
|
process.on("SIGTERM", () => process.exit(0));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cli-remote/local",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
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": {
|