@cli-remote/local 0.0.6 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +777 -349
- 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: randomBytes3, 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 = randomBytes3(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);
|
|
@@ -9981,6 +9981,7 @@ var ACCESS_TOKEN_TTL_MS = 60 * 6e4;
|
|
|
9981
9981
|
var REFRESH_TOKEN_TTL_MS = 30 * 24 * 60 * 6e4;
|
|
9982
9982
|
var REFRESH_MARGIN_MS = 5 * 6e4;
|
|
9983
9983
|
var OUTPUT_BUFFER_SIZE = 500;
|
|
9984
|
+
var AUDIT_RETENTION_DAYS = 14;
|
|
9984
9985
|
var FILE_CHUNK_SIZE = 64 * 1024;
|
|
9985
9986
|
var MAX_TRANSFER_BYTES = 500 * 1024 * 1024;
|
|
9986
9987
|
var UPLOAD_DIR = "uploads";
|
|
@@ -9991,59 +9992,6 @@ var CONFIG_FILE = "config.json";
|
|
|
9991
9992
|
var LOG_DIR = "logs";
|
|
9992
9993
|
|
|
9993
9994
|
// ../shared/src/whitelist.ts
|
|
9994
|
-
var DEFAULT_WHITELIST = [
|
|
9995
|
-
"ls",
|
|
9996
|
-
"cd",
|
|
9997
|
-
"pwd",
|
|
9998
|
-
"cat",
|
|
9999
|
-
"less",
|
|
10000
|
-
"head",
|
|
10001
|
-
"tail",
|
|
10002
|
-
"mkdir",
|
|
10003
|
-
"rmdir",
|
|
10004
|
-
"rm",
|
|
10005
|
-
"cp",
|
|
10006
|
-
"mv",
|
|
10007
|
-
"touch",
|
|
10008
|
-
"find",
|
|
10009
|
-
"stat",
|
|
10010
|
-
"tree",
|
|
10011
|
-
"grep",
|
|
10012
|
-
"awk",
|
|
10013
|
-
"sed",
|
|
10014
|
-
"sort",
|
|
10015
|
-
"uniq",
|
|
10016
|
-
"wc",
|
|
10017
|
-
"cut",
|
|
10018
|
-
"tr",
|
|
10019
|
-
"xargs",
|
|
10020
|
-
"git",
|
|
10021
|
-
"ps",
|
|
10022
|
-
"top",
|
|
10023
|
-
"htop",
|
|
10024
|
-
"kill",
|
|
10025
|
-
"df",
|
|
10026
|
-
"du",
|
|
10027
|
-
"free",
|
|
10028
|
-
"uname",
|
|
10029
|
-
"whoami",
|
|
10030
|
-
"env",
|
|
10031
|
-
"echo",
|
|
10032
|
-
"node",
|
|
10033
|
-
"npm",
|
|
10034
|
-
"pnpm",
|
|
10035
|
-
"yarn",
|
|
10036
|
-
"python",
|
|
10037
|
-
"python3",
|
|
10038
|
-
"pip",
|
|
10039
|
-
"go",
|
|
10040
|
-
"cargo",
|
|
10041
|
-
"make",
|
|
10042
|
-
"docker",
|
|
10043
|
-
"claude",
|
|
10044
|
-
"codex",
|
|
10045
|
-
"kimi-cli"
|
|
10046
|
-
];
|
|
10047
9995
|
var DANGEROUS_PATTERNS = [
|
|
10048
9996
|
/\brm\s+-[rfRF]+\s+\/(\s|$)/,
|
|
10049
9997
|
/\bsudo\b/,
|
|
@@ -10054,6 +10002,9 @@ var DANGEROUS_PATTERNS = [
|
|
|
10054
10002
|
/\bshutdown\b/,
|
|
10055
10003
|
/\breboot\b/
|
|
10056
10004
|
];
|
|
10005
|
+
function findDangerousPattern(line) {
|
|
10006
|
+
return DANGEROUS_PATTERNS.find((p) => p.test(line));
|
|
10007
|
+
}
|
|
10057
10008
|
|
|
10058
10009
|
// src/pty-manager.ts
|
|
10059
10010
|
var import_node_pty = require("node-pty");
|
|
@@ -10186,20 +10137,55 @@ var PtyManager = class {
|
|
|
10186
10137
|
var import_node_fs = require("node:fs");
|
|
10187
10138
|
var import_node_os2 = require("node:os");
|
|
10188
10139
|
var import_node_path2 = require("node:path");
|
|
10140
|
+
function logRoot() {
|
|
10141
|
+
return (0, import_node_path2.join)((0, import_node_os2.homedir)(), DEFAULT_CONFIG_DIR, LOG_DIR);
|
|
10142
|
+
}
|
|
10189
10143
|
function todayDir() {
|
|
10190
10144
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
10191
10145
|
}
|
|
10146
|
+
function pruneAuditLogs(retentionDays) {
|
|
10147
|
+
if (retentionDays <= 0) return 0;
|
|
10148
|
+
const cutoff = new Date(Date.now() - retentionDays * 24 * 60 * 6e4).toISOString().slice(0, 10);
|
|
10149
|
+
let removed = 0;
|
|
10150
|
+
let entries;
|
|
10151
|
+
try {
|
|
10152
|
+
entries = (0, import_node_fs.readdirSync)(logRoot());
|
|
10153
|
+
} catch {
|
|
10154
|
+
return 0;
|
|
10155
|
+
}
|
|
10156
|
+
for (const name of entries) {
|
|
10157
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(name)) continue;
|
|
10158
|
+
if (name >= cutoff) continue;
|
|
10159
|
+
try {
|
|
10160
|
+
(0, import_node_fs.rmSync)((0, import_node_path2.join)(logRoot(), name), { recursive: true, force: true });
|
|
10161
|
+
removed++;
|
|
10162
|
+
} catch {
|
|
10163
|
+
}
|
|
10164
|
+
}
|
|
10165
|
+
return removed;
|
|
10166
|
+
}
|
|
10192
10167
|
var SessionLogger = class {
|
|
10193
|
-
|
|
10194
|
-
|
|
10195
|
-
|
|
10196
|
-
|
|
10197
|
-
|
|
10168
|
+
constructor(sessionId, cmd, mode = "full") {
|
|
10169
|
+
this.mode = mode;
|
|
10170
|
+
if (mode === "off") {
|
|
10171
|
+
this.path = "";
|
|
10172
|
+
return;
|
|
10173
|
+
}
|
|
10174
|
+
const dir = (0, import_node_path2.join)(logRoot(), todayDir());
|
|
10175
|
+
(0, import_node_fs.mkdirSync)(dir, { recursive: true, mode: 448 });
|
|
10176
|
+
try {
|
|
10177
|
+
(0, import_node_fs.chmodSync)(dir, 448);
|
|
10178
|
+
} catch {
|
|
10179
|
+
}
|
|
10198
10180
|
this.path = (0, import_node_path2.join)(dir, `${sessionId}.log`);
|
|
10199
|
-
|
|
10181
|
+
(0, import_node_fs.appendFileSync)(this.path, "", { mode: 384 });
|
|
10182
|
+
(0, import_node_fs.chmodSync)(this.path, 384);
|
|
10183
|
+
this.write({ ts: Date.now(), event: "spawn", cmd, mode });
|
|
10200
10184
|
}
|
|
10185
|
+
path;
|
|
10186
|
+
closed = false;
|
|
10201
10187
|
logInput(data) {
|
|
10202
|
-
if (this.closed) return;
|
|
10188
|
+
if (this.closed || this.mode === "off") return;
|
|
10203
10189
|
this.write({
|
|
10204
10190
|
ts: Date.now(),
|
|
10205
10191
|
dir: "in",
|
|
@@ -10208,7 +10194,7 @@ var SessionLogger = class {
|
|
|
10208
10194
|
});
|
|
10209
10195
|
}
|
|
10210
10196
|
logOutput(data) {
|
|
10211
|
-
if (this.closed) return;
|
|
10197
|
+
if (this.closed || this.mode !== "full") return;
|
|
10212
10198
|
this.write({
|
|
10213
10199
|
ts: Date.now(),
|
|
10214
10200
|
dir: "out",
|
|
@@ -10217,119 +10203,40 @@ var SessionLogger = class {
|
|
|
10217
10203
|
});
|
|
10218
10204
|
}
|
|
10219
10205
|
logRejected(cmd, reason, dangerous) {
|
|
10220
|
-
if (this.closed) return;
|
|
10206
|
+
if (this.closed || this.mode === "off") return;
|
|
10221
10207
|
this.write({ ts: Date.now(), event: "rejected", cmd, reason, dangerous });
|
|
10222
10208
|
}
|
|
10223
10209
|
logForced(cmd) {
|
|
10224
|
-
if (this.closed) return;
|
|
10210
|
+
if (this.closed || this.mode === "off") return;
|
|
10225
10211
|
this.write({ ts: Date.now(), event: "forced", cmd });
|
|
10226
10212
|
}
|
|
10227
10213
|
logExit(code) {
|
|
10228
|
-
if (this.closed) return;
|
|
10214
|
+
if (this.closed || this.mode === "off") return;
|
|
10229
10215
|
this.write({ ts: Date.now(), event: "exit", code });
|
|
10230
10216
|
this.closed = true;
|
|
10231
10217
|
}
|
|
10232
10218
|
write(record) {
|
|
10219
|
+
if (!this.path) return;
|
|
10233
10220
|
try {
|
|
10234
|
-
(0, import_node_fs.appendFileSync)(this.path, JSON.stringify(record) + "\n", { encoding: "utf-8" });
|
|
10221
|
+
(0, import_node_fs.appendFileSync)(this.path, JSON.stringify(record) + "\n", { encoding: "utf-8", mode: 384 });
|
|
10235
10222
|
} catch {
|
|
10236
10223
|
}
|
|
10237
10224
|
}
|
|
10238
10225
|
};
|
|
10239
10226
|
|
|
10240
10227
|
// src/whitelist-checker.ts
|
|
10241
|
-
function
|
|
10242
|
-
const parts = [];
|
|
10243
|
-
let current = "";
|
|
10244
|
-
let inSingle = false;
|
|
10245
|
-
let inDouble = false;
|
|
10246
|
-
for (let i = 0; i < line.length; i++) {
|
|
10247
|
-
const c = line[i];
|
|
10248
|
-
if (c === "'" && !inDouble) {
|
|
10249
|
-
inSingle = !inSingle;
|
|
10250
|
-
current += c;
|
|
10251
|
-
continue;
|
|
10252
|
-
}
|
|
10253
|
-
if (c === '"' && !inSingle) {
|
|
10254
|
-
inDouble = !inDouble;
|
|
10255
|
-
current += c;
|
|
10256
|
-
continue;
|
|
10257
|
-
}
|
|
10258
|
-
if (!inSingle && !inDouble) {
|
|
10259
|
-
const next = line[i + 1];
|
|
10260
|
-
if (c === "&" && next === "&") {
|
|
10261
|
-
parts.push(current);
|
|
10262
|
-
current = "";
|
|
10263
|
-
i++;
|
|
10264
|
-
continue;
|
|
10265
|
-
}
|
|
10266
|
-
if (c === "|" && next === "|") {
|
|
10267
|
-
parts.push(current);
|
|
10268
|
-
current = "";
|
|
10269
|
-
i++;
|
|
10270
|
-
continue;
|
|
10271
|
-
}
|
|
10272
|
-
if (c === ";" || c === "|") {
|
|
10273
|
-
parts.push(current);
|
|
10274
|
-
current = "";
|
|
10275
|
-
continue;
|
|
10276
|
-
}
|
|
10277
|
-
}
|
|
10278
|
-
current += c;
|
|
10279
|
-
}
|
|
10280
|
-
if (current.trim()) parts.push(current);
|
|
10281
|
-
return parts;
|
|
10282
|
-
}
|
|
10283
|
-
function firstToken(cmd) {
|
|
10284
|
-
const trimmed = cmd.trim();
|
|
10285
|
-
if (!trimmed) return null;
|
|
10286
|
-
const tokens = trimmed.split(/\s+/);
|
|
10287
|
-
for (const t of tokens) {
|
|
10288
|
-
if (!t) continue;
|
|
10289
|
-
if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(t)) continue;
|
|
10290
|
-
return t;
|
|
10291
|
-
}
|
|
10292
|
-
return null;
|
|
10293
|
-
}
|
|
10294
|
-
function checkCommand(line, whitelist, dangerousPatterns = DANGEROUS_PATTERNS) {
|
|
10228
|
+
function checkCommand(line, dangerConfirm) {
|
|
10295
10229
|
const trimmed = line.trim();
|
|
10296
|
-
if (!trimmed) return { allowed: true };
|
|
10297
|
-
|
|
10298
|
-
|
|
10299
|
-
allowed: false,
|
|
10300
|
-
rejectedCmd: trimmed,
|
|
10301
|
-
reason: "\u5305\u542B\u547D\u4EE4\u66FF\u6362 ($() \u6216 `)\uFF0C\u5DF2\u88AB\u7981\u6B62"
|
|
10302
|
-
};
|
|
10303
|
-
}
|
|
10304
|
-
if (trimmed.startsWith("(")) {
|
|
10230
|
+
if (!trimmed || !dangerConfirm) return { allowed: true };
|
|
10231
|
+
const pattern = findDangerousPattern(trimmed);
|
|
10232
|
+
if (pattern) {
|
|
10305
10233
|
return {
|
|
10306
10234
|
allowed: false,
|
|
10307
10235
|
rejectedCmd: trimmed,
|
|
10308
|
-
reason: "\
|
|
10236
|
+
reason: "\u5339\u914D\u5371\u9669\u547D\u4EE4\u6A21\u5F0F\uFF0C\u8BF7\u4E8C\u6B21\u786E\u8BA4",
|
|
10237
|
+
dangerous: true
|
|
10309
10238
|
};
|
|
10310
10239
|
}
|
|
10311
|
-
for (const p of dangerousPatterns) {
|
|
10312
|
-
if (p.test(line)) {
|
|
10313
|
-
return {
|
|
10314
|
-
allowed: false,
|
|
10315
|
-
rejectedCmd: trimmed,
|
|
10316
|
-
reason: "\u5339\u914D\u5371\u9669\u547D\u4EE4\u6A21\u5F0F",
|
|
10317
|
-
dangerous: true
|
|
10318
|
-
};
|
|
10319
|
-
}
|
|
10320
|
-
}
|
|
10321
|
-
const segments = splitCommands(line);
|
|
10322
|
-
for (const seg of segments) {
|
|
10323
|
-
const cmd = firstToken(seg);
|
|
10324
|
-
if (!cmd) continue;
|
|
10325
|
-
if (!whitelist.has(cmd)) {
|
|
10326
|
-
return {
|
|
10327
|
-
allowed: false,
|
|
10328
|
-
rejectedCmd: cmd,
|
|
10329
|
-
reason: `"${cmd}" \u4E0D\u5728\u767D\u540D\u5355`
|
|
10330
|
-
};
|
|
10331
|
-
}
|
|
10332
|
-
}
|
|
10333
10240
|
return { allowed: true };
|
|
10334
10241
|
}
|
|
10335
10242
|
|
|
@@ -10510,15 +10417,199 @@ async function exists(p) {
|
|
|
10510
10417
|
}
|
|
10511
10418
|
}
|
|
10512
10419
|
|
|
10420
|
+
// src/gateway-api.ts
|
|
10421
|
+
var import_node_http = require("node:http");
|
|
10422
|
+
var import_node_fs3 = require("node:fs");
|
|
10423
|
+
var import_node_os4 = require("node:os");
|
|
10424
|
+
var import_node_path4 = require("node:path");
|
|
10425
|
+
var DEFAULT_API_PORT = 18790;
|
|
10426
|
+
function apiPortFromEnv() {
|
|
10427
|
+
return Number(process.env.CLI_LOCAL_API_PORT) || DEFAULT_API_PORT;
|
|
10428
|
+
}
|
|
10429
|
+
function readConfiguredPort() {
|
|
10430
|
+
if (process.env.CLI_LOCAL_API_PORT) return apiPortFromEnv();
|
|
10431
|
+
try {
|
|
10432
|
+
const cfg = JSON.parse(
|
|
10433
|
+
(0, import_node_fs3.readFileSync)((0, import_node_path4.join)((0, import_node_os4.homedir)(), DEFAULT_CONFIG_DIR, "config.json"), "utf-8")
|
|
10434
|
+
);
|
|
10435
|
+
return Number(cfg.gateway?.apiPort) || DEFAULT_API_PORT;
|
|
10436
|
+
} catch {
|
|
10437
|
+
return DEFAULT_API_PORT;
|
|
10438
|
+
}
|
|
10439
|
+
}
|
|
10440
|
+
function startControlApi(opts) {
|
|
10441
|
+
const server = (0, import_node_http.createServer)((req, res) => {
|
|
10442
|
+
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
10443
|
+
const route = `${req.method} ${url.pathname}`;
|
|
10444
|
+
const json = (code, body) => {
|
|
10445
|
+
res.writeHead(code, { "content-type": "application/json" });
|
|
10446
|
+
res.end(JSON.stringify(body));
|
|
10447
|
+
};
|
|
10448
|
+
if (route === "GET /healthz") return json(200, { ok: true });
|
|
10449
|
+
if (route === "GET /status") return json(200, opts.getStatus());
|
|
10450
|
+
if (route === "GET /sessions") {
|
|
10451
|
+
return json(200, { sessions: opts.getStatus().sessions });
|
|
10452
|
+
}
|
|
10453
|
+
if (route === "GET /logs") {
|
|
10454
|
+
const n = Math.min(500, Math.max(1, Number(url.searchParams.get("n")) || 200));
|
|
10455
|
+
const lines = opts.getLogs().slice(-n);
|
|
10456
|
+
return json(200, { lines });
|
|
10457
|
+
}
|
|
10458
|
+
if (route === "POST /stop") {
|
|
10459
|
+
json(200, { ok: true });
|
|
10460
|
+
opts.onStop();
|
|
10461
|
+
return;
|
|
10462
|
+
}
|
|
10463
|
+
const killMatch = /^POST \/sessions\/([\w-]+)\/kill$/.exec(`${req.method} ${url.pathname}`);
|
|
10464
|
+
if (killMatch) {
|
|
10465
|
+
const ok = opts.killSession(killMatch[1]);
|
|
10466
|
+
return json(ok ? 200 : 404, ok ? { ok: true } : { ok: false, error: "no such session" });
|
|
10467
|
+
}
|
|
10468
|
+
json(404, { error: "not found" });
|
|
10469
|
+
});
|
|
10470
|
+
server.listen(opts.port, "127.0.0.1");
|
|
10471
|
+
return {
|
|
10472
|
+
port: opts.port,
|
|
10473
|
+
close: () => server.close()
|
|
10474
|
+
};
|
|
10475
|
+
}
|
|
10476
|
+
async function apiGet(port, path, timeoutMs = 800) {
|
|
10477
|
+
try {
|
|
10478
|
+
const ctrl = new AbortController();
|
|
10479
|
+
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
10480
|
+
const res = await fetch(`http://127.0.0.1:${port}${path}`, {
|
|
10481
|
+
signal: ctrl.signal
|
|
10482
|
+
});
|
|
10483
|
+
clearTimeout(timer);
|
|
10484
|
+
if (!res.ok) return null;
|
|
10485
|
+
return await res.json();
|
|
10486
|
+
} catch {
|
|
10487
|
+
return null;
|
|
10488
|
+
}
|
|
10489
|
+
}
|
|
10490
|
+
async function apiPost(port, path, timeoutMs = 800) {
|
|
10491
|
+
try {
|
|
10492
|
+
const ctrl = new AbortController();
|
|
10493
|
+
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
10494
|
+
const res = await fetch(`http://127.0.0.1:${port}${path}`, {
|
|
10495
|
+
method: "POST",
|
|
10496
|
+
signal: ctrl.signal
|
|
10497
|
+
});
|
|
10498
|
+
clearTimeout(timer);
|
|
10499
|
+
return res.ok;
|
|
10500
|
+
} catch {
|
|
10501
|
+
return false;
|
|
10502
|
+
}
|
|
10503
|
+
}
|
|
10504
|
+
|
|
10505
|
+
// src/setup-callback.ts
|
|
10506
|
+
var import_node_http2 = require("node:http");
|
|
10507
|
+
var import_node_crypto = require("node:crypto");
|
|
10508
|
+
var BODY_LIMIT = 64 * 1024;
|
|
10509
|
+
function startSetupCallback(timeoutMs = 3 * 6e4) {
|
|
10510
|
+
return new Promise((resolveStart, rejectStart) => {
|
|
10511
|
+
const chan = (0, import_node_crypto.randomBytes)(8).toString("hex");
|
|
10512
|
+
let timer = null;
|
|
10513
|
+
let settled = false;
|
|
10514
|
+
let server = null;
|
|
10515
|
+
const cors = {
|
|
10516
|
+
"access-control-allow-origin": "*",
|
|
10517
|
+
"access-control-allow-methods": "POST, OPTIONS",
|
|
10518
|
+
"access-control-allow-headers": "content-type",
|
|
10519
|
+
"access-control-allow-private-network": "true",
|
|
10520
|
+
"access-control-max-age": "600"
|
|
10521
|
+
};
|
|
10522
|
+
const done = new Promise((resolve2, reject) => {
|
|
10523
|
+
timer = setTimeout(() => {
|
|
10524
|
+
if (settled) return;
|
|
10525
|
+
settled = true;
|
|
10526
|
+
stop();
|
|
10527
|
+
reject(new Error("\u7B49\u5F85\u7F51\u9875\u56DE\u8C03\u8D85\u65F6"));
|
|
10528
|
+
}, timeoutMs);
|
|
10529
|
+
function stop() {
|
|
10530
|
+
if (timer) clearTimeout(timer);
|
|
10531
|
+
server?.close();
|
|
10532
|
+
}
|
|
10533
|
+
function finish(fn) {
|
|
10534
|
+
if (settled) return;
|
|
10535
|
+
settled = true;
|
|
10536
|
+
if (timer) clearTimeout(timer);
|
|
10537
|
+
server?.close();
|
|
10538
|
+
fn();
|
|
10539
|
+
}
|
|
10540
|
+
const tryPort = (port) => {
|
|
10541
|
+
if (port > 18802) {
|
|
10542
|
+
rejectStart(new Error("no free port for the setup callback"));
|
|
10543
|
+
return;
|
|
10544
|
+
}
|
|
10545
|
+
const s = (0, import_node_http2.createServer)((req, res) => {
|
|
10546
|
+
const reply = (code, body) => {
|
|
10547
|
+
res.writeHead(code, { "content-type": "application/json", ...cors });
|
|
10548
|
+
res.end(JSON.stringify(body));
|
|
10549
|
+
};
|
|
10550
|
+
if (req.method === "OPTIONS") {
|
|
10551
|
+
reply(204, {});
|
|
10552
|
+
return;
|
|
10553
|
+
}
|
|
10554
|
+
if (req.method !== "POST" || !req.url?.startsWith("/setup")) {
|
|
10555
|
+
reply(404, { ok: false, error: "not found" });
|
|
10556
|
+
return;
|
|
10557
|
+
}
|
|
10558
|
+
let size = 0;
|
|
10559
|
+
const chunks = [];
|
|
10560
|
+
req.on("data", (c) => {
|
|
10561
|
+
size += c.length;
|
|
10562
|
+
if (size > BODY_LIMIT) {
|
|
10563
|
+
req.destroy();
|
|
10564
|
+
return;
|
|
10565
|
+
}
|
|
10566
|
+
chunks.push(c);
|
|
10567
|
+
});
|
|
10568
|
+
req.on("end", () => {
|
|
10569
|
+
try {
|
|
10570
|
+
const body = JSON.parse(Buffer.concat(chunks).toString("utf-8"));
|
|
10571
|
+
if (body.chan !== chan || !body.registrationKey) {
|
|
10572
|
+
reply(400, { ok: false, error: "bad request" });
|
|
10573
|
+
return;
|
|
10574
|
+
}
|
|
10575
|
+
reply(200, { ok: true });
|
|
10576
|
+
const key = body.registrationKey;
|
|
10577
|
+
finish(() => resolve2(key));
|
|
10578
|
+
} catch {
|
|
10579
|
+
reply(400, { ok: false, error: "bad request" });
|
|
10580
|
+
}
|
|
10581
|
+
});
|
|
10582
|
+
});
|
|
10583
|
+
s.once("error", () => {
|
|
10584
|
+
s.close();
|
|
10585
|
+
tryPort(port + 1);
|
|
10586
|
+
});
|
|
10587
|
+
s.listen(port, "127.0.0.1", () => {
|
|
10588
|
+
server = s;
|
|
10589
|
+
resolveStart({
|
|
10590
|
+
port,
|
|
10591
|
+
chan,
|
|
10592
|
+
done,
|
|
10593
|
+
stop: () => {
|
|
10594
|
+
finish(() => reject(new Error("callback stopped")));
|
|
10595
|
+
}
|
|
10596
|
+
});
|
|
10597
|
+
});
|
|
10598
|
+
};
|
|
10599
|
+
tryPort(18793);
|
|
10600
|
+
});
|
|
10601
|
+
});
|
|
10602
|
+
}
|
|
10603
|
+
|
|
10513
10604
|
// src/auth.ts
|
|
10514
10605
|
var import_node_child_process = require("node:child_process");
|
|
10515
|
-
var
|
|
10606
|
+
var import_node_crypto2 = require("node:crypto");
|
|
10516
10607
|
var import_promises2 = require("node:fs/promises");
|
|
10517
|
-
var
|
|
10518
|
-
var
|
|
10608
|
+
var import_node_os5 = require("node:os");
|
|
10609
|
+
var import_node_path5 = require("node:path");
|
|
10519
10610
|
var import_qrcode = __toESM(require_lib(), 1);
|
|
10520
10611
|
function credentialsPath() {
|
|
10521
|
-
return (0,
|
|
10612
|
+
return (0, import_node_path5.join)((0, import_node_os5.homedir)(), DEFAULT_CONFIG_DIR, CREDENTIALS_FILE);
|
|
10522
10613
|
}
|
|
10523
10614
|
async function loadCredentials() {
|
|
10524
10615
|
try {
|
|
@@ -10531,19 +10622,13 @@ async function loadCredentials() {
|
|
|
10531
10622
|
}
|
|
10532
10623
|
async function saveCredentials(creds) {
|
|
10533
10624
|
const path = credentialsPath();
|
|
10534
|
-
await (0, import_promises2.mkdir)((0,
|
|
10625
|
+
await (0, import_promises2.mkdir)((0, import_node_path5.join)(path, ".."), { recursive: true });
|
|
10535
10626
|
await (0, import_promises2.writeFile)(path, JSON.stringify(creds, null, 2) + "\n", { mode: 384 });
|
|
10536
10627
|
try {
|
|
10537
10628
|
await (0, import_promises2.chmod)(path, 384);
|
|
10538
10629
|
} catch {
|
|
10539
10630
|
}
|
|
10540
10631
|
}
|
|
10541
|
-
async function clearCredentials() {
|
|
10542
|
-
try {
|
|
10543
|
-
await (0, import_promises2.writeFile)(credentialsPath(), "");
|
|
10544
|
-
} catch {
|
|
10545
|
-
}
|
|
10546
|
-
}
|
|
10547
10632
|
function baseUrlFromWsUrl(wsUrl) {
|
|
10548
10633
|
const http = wsUrl.replace(/^ws/, "http");
|
|
10549
10634
|
try {
|
|
@@ -10588,8 +10673,8 @@ async function loginAndFetchRegistrationKey(baseUrl, email, password) {
|
|
|
10588
10673
|
return { registrationKey: data.registrationKey, user: data.user?.email ?? email };
|
|
10589
10674
|
}
|
|
10590
10675
|
async function mergeLocalConfig(patch) {
|
|
10591
|
-
const dir = (0,
|
|
10592
|
-
const path = (0,
|
|
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);
|
|
10593
10678
|
let existing = {};
|
|
10594
10679
|
try {
|
|
10595
10680
|
existing = JSON.parse(await (0, import_promises2.readFile)(path, "utf-8"));
|
|
@@ -10610,7 +10695,7 @@ async function deviceFlowAuthorize(baseUrl, registrationKey, deviceId) {
|
|
|
10610
10695
|
const res = await postJson(`${baseUrl}/oauth/device_authorization`, {
|
|
10611
10696
|
registrationKey,
|
|
10612
10697
|
device_id: deviceId,
|
|
10613
|
-
device_name: (0,
|
|
10698
|
+
device_name: (0, import_node_os5.hostname)(),
|
|
10614
10699
|
platform: process.platform
|
|
10615
10700
|
});
|
|
10616
10701
|
if (res.status !== 200) {
|
|
@@ -10699,6 +10784,20 @@ async function ensureFreshAccessToken(baseUrl, creds) {
|
|
|
10699
10784
|
}
|
|
10700
10785
|
return refreshInFlight;
|
|
10701
10786
|
}
|
|
10787
|
+
async function ensureMachineId() {
|
|
10788
|
+
const path = (0, import_node_path5.join)((0, import_node_os5.homedir)(), DEFAULT_CONFIG_DIR, CONFIG_FILE);
|
|
10789
|
+
try {
|
|
10790
|
+
const cfg = JSON.parse(await (0, import_promises2.readFile)(path, "utf-8"));
|
|
10791
|
+
if (typeof cfg.machineId === "string" && cfg.machineId) return cfg.machineId;
|
|
10792
|
+
} catch {
|
|
10793
|
+
}
|
|
10794
|
+
const machineId = "m_" + (0, import_node_crypto2.randomBytes)(16).toString("hex");
|
|
10795
|
+
await mergeLocalConfig({ machineId });
|
|
10796
|
+
return machineId;
|
|
10797
|
+
}
|
|
10798
|
+
async function stableDeviceId() {
|
|
10799
|
+
return "dev_" + await ensureMachineId();
|
|
10800
|
+
}
|
|
10702
10801
|
async function ensureCredentials(baseUrl, registrationKey) {
|
|
10703
10802
|
const existing = await loadCredentials();
|
|
10704
10803
|
if (existing) {
|
|
@@ -10708,23 +10807,31 @@ async function ensureCredentials(baseUrl, registrationKey) {
|
|
|
10708
10807
|
console.error(`[local] ${e.message}\uFF0C\u91CD\u65B0\u8D70\u6388\u6743\u6D41\u7A0B`);
|
|
10709
10808
|
}
|
|
10710
10809
|
}
|
|
10711
|
-
return deviceFlowAuthorize(baseUrl, registrationKey,
|
|
10810
|
+
return deviceFlowAuthorize(baseUrl, registrationKey, await stableDeviceId());
|
|
10712
10811
|
}
|
|
10713
10812
|
|
|
10714
10813
|
// src/client.ts
|
|
10814
|
+
function isPermanentAuthFailure(e) {
|
|
10815
|
+
return e instanceof AuthError && e.message.includes("invalid_grant");
|
|
10816
|
+
}
|
|
10715
10817
|
async function loadConfig() {
|
|
10716
|
-
const path = (0,
|
|
10818
|
+
const path = (0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR, CONFIG_FILE);
|
|
10717
10819
|
try {
|
|
10718
|
-
return JSON.parse((0,
|
|
10820
|
+
return JSON.parse((0, import_node_fs4.readFileSync)(path, "utf-8"));
|
|
10719
10821
|
} catch {
|
|
10720
10822
|
return {};
|
|
10721
10823
|
}
|
|
10722
10824
|
}
|
|
10723
|
-
function
|
|
10724
|
-
|
|
10725
|
-
|
|
10726
|
-
|
|
10727
|
-
|
|
10825
|
+
function applyConfigDeprecations(cfg) {
|
|
10826
|
+
if (cfg.whitelist) {
|
|
10827
|
+
console.log(
|
|
10828
|
+
'[local] config "whitelist" \u5DF2\u5E9F\u5F03\uFF08v0.4\uFF09\uFF1Abash \u5168\u900F\u4F20\uFF0C\u4E0D\u518D\u6709\u767D\u540D\u5355\u3002\u5982\u9700\u5371\u9669\u547D\u4EE4\u4E8C\u6B21\u786E\u8BA4\uFF0C\u8BBE\u7F6E dangerConfirm: true'
|
|
10829
|
+
);
|
|
10830
|
+
}
|
|
10831
|
+
if (cfg.auditMode && !["full", "commands", "off"].includes(cfg.auditMode)) {
|
|
10832
|
+
console.log(`[local] \u65E0\u6548\u7684 auditMode "${cfg.auditMode}"\uFF0C\u56DE\u9000 full`);
|
|
10833
|
+
cfg.auditMode = "full";
|
|
10834
|
+
}
|
|
10728
10835
|
}
|
|
10729
10836
|
function isTTY() {
|
|
10730
10837
|
return process.stdin.isTTY === true;
|
|
@@ -10794,19 +10901,18 @@ async function setupWizard(config, opts) {
|
|
|
10794
10901
|
console.log("[local] \u5F53\u524D\u975E\u4EA4\u4E92\u7EC8\u7AEF\uFF1A\u62FF\u5230 key \u540E\u5199\u5165 ~/.cli-remote/config.json \u518D\u8FD0\u884C\u3002");
|
|
10795
10902
|
return null;
|
|
10796
10903
|
}
|
|
10797
|
-
console.log(" 1) \
|
|
10798
|
-
console.log(" 2) \
|
|
10904
|
+
console.log(" 1) \u901A\u8FC7\u7F51\u9875\u767B\u5F55\u83B7\u53D6\uFF08\u63A8\u8350\uFF0C\u81EA\u52A8\u5B8C\u6210\uFF0C\u65E0\u9700\u590D\u5236\u7C98\u8D34\uFF09");
|
|
10905
|
+
console.log(" 2) \u76F4\u63A5\u8F93\u5165 registrationKey");
|
|
10906
|
+
console.log(" 3) \u5728\u6B64\u8F93\u5165\u8D26\u53F7\u5BC6\u7801\u83B7\u53D6");
|
|
10799
10907
|
const mode = (await readInput("\u9009\u62E9 [1]: ")).trim();
|
|
10800
10908
|
if (mode === "2") {
|
|
10801
|
-
openBrowser(base);
|
|
10802
|
-
console.log(`[local] \u5DF2\u6253\u5F00\u6D4F\u89C8\u5668\uFF0C\u8BF7\u767B\u5F55 ${base} \u5E76\u5728\u300C\u63A7\u5236\u9762\u677F\u300D\u9875\u590D\u5236 registrationKey`);
|
|
10803
10909
|
const ans = (await readInput("\u7C98\u8D34 registrationKey: ")).trim();
|
|
10804
10910
|
if (!ans) {
|
|
10805
10911
|
console.log("[local] \u672A\u63D0\u4F9B registrationKey\uFF0C\u9000\u51FA\u3002");
|
|
10806
10912
|
return null;
|
|
10807
10913
|
}
|
|
10808
10914
|
registrationKey = ans;
|
|
10809
|
-
} else {
|
|
10915
|
+
} else if (mode === "3") {
|
|
10810
10916
|
for (; ; ) {
|
|
10811
10917
|
const email = (await readInput("\u90AE\u7BB1: ")).trim();
|
|
10812
10918
|
const password = (await readInput("\u5BC6\u7801: ")).trim();
|
|
@@ -10824,6 +10930,26 @@ async function setupWizard(config, opts) {
|
|
|
10824
10930
|
console.log("[local] \u91CD\u8BD5\uFF08\u6216 Ctrl+C \u9000\u51FA\uFF09\u3002");
|
|
10825
10931
|
}
|
|
10826
10932
|
}
|
|
10933
|
+
} else {
|
|
10934
|
+
const cb = await startSetupCallback();
|
|
10935
|
+
const setupUrl = `${base}/local-setup?port=${cb.port}&chan=${cb.chan}`;
|
|
10936
|
+
console.log(`[local] \u5DF2\u6253\u5F00\u6D4F\u89C8\u5668\uFF1A${setupUrl}`);
|
|
10937
|
+
console.log("[local] \u672A\u767B\u5F55\u65F6\u4F1A\u5148\u8FDB\u5165\u767B\u5F55\u9875\uFF0C\u767B\u5F55\u540E\u81EA\u52A8\u7EE7\u7EED\uFF0C\u65E0\u9700\u56DE\u5230\u7EC8\u7AEF");
|
|
10938
|
+
openBrowser(setupUrl);
|
|
10939
|
+
try {
|
|
10940
|
+
registrationKey = await cb.done;
|
|
10941
|
+
console.log("[local] \u5DF2\u4ECE\u7F51\u9875\u83B7\u53D6 registrationKey");
|
|
10942
|
+
} catch {
|
|
10943
|
+
console.log("[local] \u672A\u6536\u5230\u7F51\u9875\u56DE\u8C03\uFF0C\u53EF\u624B\u52A8\u7C98\u8D34 registrationKey\uFF08\u56DE\u8F66\u8DF3\u8FC7\u9000\u51FA\uFF09\uFF1A");
|
|
10944
|
+
const ans = (await readInput("registrationKey: ")).trim();
|
|
10945
|
+
if (!ans) {
|
|
10946
|
+
cb.stop();
|
|
10947
|
+
console.log("[local] \u672A\u63D0\u4F9B registrationKey\uFF0C\u9000\u51FA\u3002");
|
|
10948
|
+
return null;
|
|
10949
|
+
}
|
|
10950
|
+
registrationKey = ans;
|
|
10951
|
+
}
|
|
10952
|
+
cb.stop();
|
|
10827
10953
|
}
|
|
10828
10954
|
changed = true;
|
|
10829
10955
|
}
|
|
@@ -10844,7 +10970,10 @@ async function startLocal(opts = {}) {
|
|
|
10844
10970
|
registrationKey = setup.registrationKey;
|
|
10845
10971
|
}
|
|
10846
10972
|
const baseUrl = baseUrlFromWsUrl(url);
|
|
10847
|
-
|
|
10973
|
+
applyConfigDeprecations(config);
|
|
10974
|
+
const dangerConfirm = config.dangerConfirm === true;
|
|
10975
|
+
const auditMode = config.auditMode ?? "full";
|
|
10976
|
+
const retentionDays = config.auditRetentionDays ?? AUDIT_RETENTION_DAYS;
|
|
10848
10977
|
const pty = new PtyManager();
|
|
10849
10978
|
const loggers = /* @__PURE__ */ new Map();
|
|
10850
10979
|
const lineBuffers = /* @__PURE__ */ new Map();
|
|
@@ -10852,17 +10981,130 @@ async function startLocal(opts = {}) {
|
|
|
10852
10981
|
(data) => safeSend(data),
|
|
10853
10982
|
(msg) => console.log(`[local] ${msg}`)
|
|
10854
10983
|
);
|
|
10855
|
-
|
|
10984
|
+
const other = acquireSingleInstanceLock();
|
|
10985
|
+
if (other !== null) {
|
|
10986
|
+
console.error(`[local] \u53E6\u4E00\u4E2A cli-local \u6B63\u5728\u8FD0\u884C (pid ${other})\uFF0C\u62D2\u7EDD\u53CC\u5F00\u3002`);
|
|
10987
|
+
console.error("[local] \u53CC\u5F00\u4F1A\u4E92\u76F8\u5237\u65B0 token \u5BFC\u81F4\u8BBE\u5907\u88AB\u670D\u52A1\u7AEF\u540A\u9500\u3002");
|
|
10988
|
+
console.error("[local] \u540E\u53F0\u5E38\u9A7B\u8BF7\u7528: cli-local gateway start / restart / status");
|
|
10989
|
+
process.exit(1);
|
|
10990
|
+
}
|
|
10991
|
+
const startedAt = Date.now();
|
|
10992
|
+
const logRing = [];
|
|
10993
|
+
if (opts.daemon) {
|
|
10994
|
+
writeGatewayPid();
|
|
10995
|
+
patchConsoleIntoRing(logRing);
|
|
10996
|
+
}
|
|
10997
|
+
let credentials;
|
|
10998
|
+
if (opts.daemon) {
|
|
10999
|
+
const existing = await loadCredentials();
|
|
11000
|
+
if (!existing) {
|
|
11001
|
+
credentials = {
|
|
11002
|
+
deviceId: "",
|
|
11003
|
+
accessToken: "",
|
|
11004
|
+
refreshToken: "",
|
|
11005
|
+
accessTokenExpiresAt: 0
|
|
11006
|
+
};
|
|
11007
|
+
} else {
|
|
11008
|
+
credentials = existing;
|
|
11009
|
+
try {
|
|
11010
|
+
credentials = await ensureFreshAccessToken(baseUrl, existing);
|
|
11011
|
+
} catch {
|
|
11012
|
+
}
|
|
11013
|
+
}
|
|
11014
|
+
} else {
|
|
11015
|
+
credentials = await ensureCredentials(baseUrl, registrationKey);
|
|
11016
|
+
}
|
|
10856
11017
|
const commands = detectCommands();
|
|
10857
|
-
if (opts.daemon) writeGatewayPid();
|
|
10858
11018
|
console.log(`[local] connecting to ${url}`);
|
|
10859
|
-
console.log(`[local] deviceId=${credentials.deviceId}`);
|
|
10860
|
-
console.log(`[local]
|
|
11019
|
+
console.log(`[local] deviceId=${credentials.deviceId || "(\u672A\u6388\u6743)"}`);
|
|
11020
|
+
console.log(`[local] bash: passthrough (audit: ${auditMode}, retention: ${retentionDays === 0 ? "forever" : retentionDays + "d"}${dangerConfirm ? ", dangerConfirm: on" : ""})`);
|
|
10861
11021
|
console.log(`[local] available CLIs: ${commands.join(", ")}`);
|
|
11022
|
+
const pruned = pruneAuditLogs(retentionDays);
|
|
11023
|
+
if (pruned > 0) console.log(`[local] pruned ${pruned} expired audit log dir(s)`);
|
|
11024
|
+
const pruneTimer = setInterval(() => {
|
|
11025
|
+
const n = pruneAuditLogs(retentionDays);
|
|
11026
|
+
if (n > 0) console.log(`[local] pruned ${n} expired audit log dir(s)`);
|
|
11027
|
+
}, 24 * 60 * 6e4);
|
|
11028
|
+
pruneTimer.unref?.();
|
|
11029
|
+
let phase = "starting";
|
|
11030
|
+
let phaseReason = "";
|
|
11031
|
+
let reconnectAttempts = 0;
|
|
11032
|
+
const getStatus = () => ({
|
|
11033
|
+
phase,
|
|
11034
|
+
...phaseReason ? { reason: phaseReason } : {},
|
|
11035
|
+
deviceId: credentials.deviceId || null,
|
|
11036
|
+
hubUrl: url,
|
|
11037
|
+
uptimeMs: Date.now() - startedAt,
|
|
11038
|
+
reconnectAttempts,
|
|
11039
|
+
accessTokenExpiresAt: credentials.accessTokenExpiresAt || null,
|
|
11040
|
+
sessions: pty.list().map((s) => ({
|
|
11041
|
+
sessionId: s.id,
|
|
11042
|
+
cmd: s.cmd,
|
|
11043
|
+
startedAt: s.startedAt
|
|
11044
|
+
}))
|
|
11045
|
+
});
|
|
11046
|
+
function setPhase(p, reason = "") {
|
|
11047
|
+
phase = p;
|
|
11048
|
+
phaseReason = reason;
|
|
11049
|
+
}
|
|
11050
|
+
let pairingTimer = null;
|
|
11051
|
+
let pairingCredMtime = 0;
|
|
11052
|
+
function enterNeedsPairing(reason) {
|
|
11053
|
+
setPhase("needs-pairing", reason);
|
|
11054
|
+
console.error(`[local] \u9700\u8981\u91CD\u65B0\u6388\u6743: ${reason}`);
|
|
11055
|
+
console.error("[local] \u5728\u4EFB\u610F\u7EC8\u7AEF\u8FD0\u884C: cli-local pair");
|
|
11056
|
+
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");
|
|
11057
|
+
if (pairingTimer) return;
|
|
11058
|
+
try {
|
|
11059
|
+
pairingCredMtime = (0, import_node_fs4.statSync)(credentialsPathOf()).mtimeMs;
|
|
11060
|
+
} catch {
|
|
11061
|
+
pairingCredMtime = 0;
|
|
11062
|
+
}
|
|
11063
|
+
pairingTimer = setInterval(async () => {
|
|
11064
|
+
let mtime;
|
|
11065
|
+
try {
|
|
11066
|
+
mtime = (0, import_node_fs4.statSync)(credentialsPathOf()).mtimeMs;
|
|
11067
|
+
} catch {
|
|
11068
|
+
return;
|
|
11069
|
+
}
|
|
11070
|
+
if (mtime <= pairingCredMtime) return;
|
|
11071
|
+
const fresh = await loadCredentials();
|
|
11072
|
+
if (!fresh?.deviceId || !fresh?.refreshToken) return;
|
|
11073
|
+
pairingCredMtime = mtime;
|
|
11074
|
+
credentials = fresh;
|
|
11075
|
+
console.log("[local] \u68C0\u6D4B\u5230\u65B0\u51ED\u8BC1\uFF0C\u91CD\u8FDE\u4E2D\u2026");
|
|
11076
|
+
if (pairingTimer) clearInterval(pairingTimer);
|
|
11077
|
+
pairingTimer = null;
|
|
11078
|
+
reconnectAttempts = 0;
|
|
11079
|
+
setPhase("connecting");
|
|
11080
|
+
connect();
|
|
11081
|
+
}, 2e3);
|
|
11082
|
+
}
|
|
11083
|
+
let controlApi = null;
|
|
11084
|
+
if (opts.daemon) {
|
|
11085
|
+
const port = readConfiguredPort();
|
|
11086
|
+
controlApi = startControlApi({
|
|
11087
|
+
port,
|
|
11088
|
+
getStatus,
|
|
11089
|
+
getLogs: () => [...logRing],
|
|
11090
|
+
onStop: () => {
|
|
11091
|
+
console.log("[local] \u6536\u5230\u505C\u6B62\u6307\u4EE4\uFF08\u63A7\u5236 API\uFF09");
|
|
11092
|
+
exitProcess(0);
|
|
11093
|
+
},
|
|
11094
|
+
killSession: (id) => {
|
|
11095
|
+
try {
|
|
11096
|
+
pty.kill(id);
|
|
11097
|
+
return true;
|
|
11098
|
+
} catch {
|
|
11099
|
+
return false;
|
|
11100
|
+
}
|
|
11101
|
+
}
|
|
11102
|
+
});
|
|
11103
|
+
console.log(`[local] control api: http://127.0.0.1:${port} (\u4EC5\u672C\u673A)`);
|
|
11104
|
+
}
|
|
10862
11105
|
let ws = null;
|
|
10863
11106
|
let heartbeatTimer = null;
|
|
10864
11107
|
let reconnectTimer = null;
|
|
10865
|
-
let reconnectAttempts = 0;
|
|
10866
11108
|
let userExitCode = null;
|
|
10867
11109
|
function stopHeartbeat() {
|
|
10868
11110
|
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
@@ -10885,6 +11127,7 @@ async function startLocal(opts = {}) {
|
|
|
10885
11127
|
ws = sock;
|
|
10886
11128
|
sock.on("open", () => {
|
|
10887
11129
|
console.log("[local] connected");
|
|
11130
|
+
if (phase !== "needs-pairing") setPhase("connecting");
|
|
10888
11131
|
sendAuth();
|
|
10889
11132
|
stopHeartbeat();
|
|
10890
11133
|
heartbeatTimer = setInterval(() => {
|
|
@@ -10901,6 +11144,7 @@ async function startLocal(opts = {}) {
|
|
|
10901
11144
|
process.exit(userExitCode);
|
|
10902
11145
|
}
|
|
10903
11146
|
console.log("[local] disconnected; PTY sessions kept alive, reconnecting\u2026");
|
|
11147
|
+
if (phase !== "needs-pairing") setPhase("reconnecting");
|
|
10904
11148
|
scheduleReconnect();
|
|
10905
11149
|
});
|
|
10906
11150
|
sock.on("error", (err) => {
|
|
@@ -10909,6 +11153,7 @@ async function startLocal(opts = {}) {
|
|
|
10909
11153
|
}
|
|
10910
11154
|
async function scheduleReconnect() {
|
|
10911
11155
|
if (reconnectTimer || userExitCode !== null) return;
|
|
11156
|
+
if (phase === "needs-pairing") return;
|
|
10912
11157
|
const idx = Math.min(reconnectAttempts, RECONNECT_DELAYS_MS.length - 1);
|
|
10913
11158
|
const delay = RECONNECT_DELAYS_MS[idx];
|
|
10914
11159
|
reconnectAttempts++;
|
|
@@ -10918,11 +11163,13 @@ async function startLocal(opts = {}) {
|
|
|
10918
11163
|
ensureFreshAccessToken(baseUrl, credentials).then((creds) => {
|
|
10919
11164
|
credentials = creds;
|
|
10920
11165
|
connect();
|
|
10921
|
-
}).catch(
|
|
10922
|
-
|
|
10923
|
-
|
|
10924
|
-
|
|
10925
|
-
|
|
11166
|
+
}).catch((e) => {
|
|
11167
|
+
if (isPermanentAuthFailure(e)) {
|
|
11168
|
+
enterNeedsPairing(e.message);
|
|
11169
|
+
return;
|
|
11170
|
+
}
|
|
11171
|
+
console.error(`[local] token \u5237\u65B0\u6682\u65F6\u5931\u8D25: ${e.message}`);
|
|
11172
|
+
scheduleReconnect();
|
|
10926
11173
|
});
|
|
10927
11174
|
}, delay);
|
|
10928
11175
|
}
|
|
@@ -10930,6 +11177,9 @@ async function startLocal(opts = {}) {
|
|
|
10930
11177
|
userExitCode = code;
|
|
10931
11178
|
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
10932
11179
|
reconnectTimer = null;
|
|
11180
|
+
if (pairingTimer) clearInterval(pairingTimer);
|
|
11181
|
+
pairingTimer = null;
|
|
11182
|
+
controlApi?.close();
|
|
10933
11183
|
if (opts.daemon) removeGatewayPid();
|
|
10934
11184
|
transfers.abortAll();
|
|
10935
11185
|
pty.killAll();
|
|
@@ -10959,6 +11209,7 @@ async function startLocal(opts = {}) {
|
|
|
10959
11209
|
console.log(`[local] reconnected after ${reconnectAttempts} attempt(s); ${pty.list().length} PTY session(s) alive`);
|
|
10960
11210
|
}
|
|
10961
11211
|
reconnectAttempts = 0;
|
|
11212
|
+
setPhase("connected");
|
|
10962
11213
|
console.log(`[local] auth ok, deviceId=${frame.channelId}`);
|
|
10963
11214
|
break;
|
|
10964
11215
|
case "auth-err":
|
|
@@ -11001,25 +11252,31 @@ async function startLocal(opts = {}) {
|
|
|
11001
11252
|
}
|
|
11002
11253
|
}
|
|
11003
11254
|
async function handleAuthErr(reason) {
|
|
11004
|
-
if (reason === "access token expired") {
|
|
11255
|
+
if (reason === "access token expired" || reason === "invalid access token") {
|
|
11005
11256
|
try {
|
|
11006
11257
|
credentials = await ensureFreshAccessToken(baseUrl, credentials);
|
|
11007
11258
|
console.log("[local] token refreshed, retrying auth");
|
|
11008
11259
|
sendAuth();
|
|
11009
11260
|
return;
|
|
11010
11261
|
} catch (e) {
|
|
11011
|
-
|
|
11262
|
+
if (isPermanentAuthFailure(e)) {
|
|
11263
|
+
enterNeedsPairing(e.message);
|
|
11264
|
+
return;
|
|
11265
|
+
}
|
|
11266
|
+
console.error(`[local] token \u5237\u65B0\u6682\u65F6\u5931\u8D25\uFF08${e.message}\uFF09\uFF0C\u8F6C\u5165\u91CD\u8FDE`);
|
|
11267
|
+
try {
|
|
11268
|
+
ws?.close();
|
|
11269
|
+
} catch {
|
|
11270
|
+
}
|
|
11271
|
+
return;
|
|
11012
11272
|
}
|
|
11013
11273
|
}
|
|
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);
|
|
11274
|
+
enterNeedsPairing(reason);
|
|
11018
11275
|
}
|
|
11019
11276
|
function handleSpawn(frame) {
|
|
11020
11277
|
const sessionId = frame.sessionId;
|
|
11021
11278
|
try {
|
|
11022
|
-
const logger = new SessionLogger(sessionId, frame.cmd);
|
|
11279
|
+
const logger = new SessionLogger(sessionId, frame.cmd, auditMode);
|
|
11023
11280
|
pty.spawn(
|
|
11024
11281
|
sessionId,
|
|
11025
11282
|
{ cmd: frame.cmd, cols: frame.cols, rows: frame.rows, cwd: frame.cwd },
|
|
@@ -11097,7 +11354,7 @@ async function startLocal(opts = {}) {
|
|
|
11097
11354
|
if (char === "\r" || char === "\n") {
|
|
11098
11355
|
const line = buf;
|
|
11099
11356
|
buf = "";
|
|
11100
|
-
const result = checkCommand(line,
|
|
11357
|
+
const result = checkCommand(line, dangerConfirm);
|
|
11101
11358
|
if (result.allowed) {
|
|
11102
11359
|
ptyWriteChar(sessionId, "\r");
|
|
11103
11360
|
logger.logInput(new TextEncoder().encode(line + "\r"));
|
|
@@ -11160,7 +11417,57 @@ async function startLocal(opts = {}) {
|
|
|
11160
11417
|
}
|
|
11161
11418
|
process.on("SIGINT", () => cleanup(0));
|
|
11162
11419
|
process.on("SIGTERM", () => cleanup(0));
|
|
11163
|
-
|
|
11420
|
+
if (credentials.deviceId) {
|
|
11421
|
+
setPhase("connecting");
|
|
11422
|
+
connect();
|
|
11423
|
+
} else {
|
|
11424
|
+
enterNeedsPairing("\u65E0\u51ED\u8BC1\uFF08credentials.json \u7F3A\u5931\u6216\u4E3A\u7A7A\uFF09");
|
|
11425
|
+
}
|
|
11426
|
+
}
|
|
11427
|
+
function credentialsPathOf() {
|
|
11428
|
+
return (0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR, "credentials.json");
|
|
11429
|
+
}
|
|
11430
|
+
function instanceLockPath() {
|
|
11431
|
+
return (0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR, "instance.lock");
|
|
11432
|
+
}
|
|
11433
|
+
function processAlive(pid) {
|
|
11434
|
+
try {
|
|
11435
|
+
process.kill(pid, 0);
|
|
11436
|
+
return true;
|
|
11437
|
+
} catch {
|
|
11438
|
+
return false;
|
|
11439
|
+
}
|
|
11440
|
+
}
|
|
11441
|
+
function acquireSingleInstanceLock() {
|
|
11442
|
+
const path = instanceLockPath();
|
|
11443
|
+
try {
|
|
11444
|
+
const pid = Number((0, import_node_fs4.readFileSync)(path, "utf-8").trim());
|
|
11445
|
+
if (Number.isFinite(pid) && pid !== process.pid && processAlive(pid)) {
|
|
11446
|
+
return pid;
|
|
11447
|
+
}
|
|
11448
|
+
} catch {
|
|
11449
|
+
}
|
|
11450
|
+
try {
|
|
11451
|
+
(0, import_node_fs4.mkdirSync)((0, import_node_path6.join)(path, ".."), { recursive: true });
|
|
11452
|
+
(0, import_node_fs4.writeFileSync)(path, String(process.pid));
|
|
11453
|
+
} catch {
|
|
11454
|
+
}
|
|
11455
|
+
return null;
|
|
11456
|
+
}
|
|
11457
|
+
function patchConsoleIntoRing(ring) {
|
|
11458
|
+
const CAP = 500;
|
|
11459
|
+
const push = (line) => {
|
|
11460
|
+
ring.push(line);
|
|
11461
|
+
if (ring.length > CAP) ring.splice(0, ring.length - CAP);
|
|
11462
|
+
};
|
|
11463
|
+
const wrap = (orig) => {
|
|
11464
|
+
return (...args) => {
|
|
11465
|
+
push(args.map((a) => typeof a === "string" ? a : String(a)).join(" "));
|
|
11466
|
+
orig(...args);
|
|
11467
|
+
};
|
|
11468
|
+
};
|
|
11469
|
+
console.log = wrap(console.log.bind(console));
|
|
11470
|
+
console.error = wrap(console.error.bind(console));
|
|
11164
11471
|
}
|
|
11165
11472
|
function toUint8(raw) {
|
|
11166
11473
|
if (raw instanceof Buffer) return new Uint8Array(raw.buffer, raw.byteOffset, raw.byteLength);
|
|
@@ -11171,21 +11478,21 @@ function toUint8(raw) {
|
|
|
11171
11478
|
throw new Error("unexpected raw type");
|
|
11172
11479
|
}
|
|
11173
11480
|
function gatewayPidFile() {
|
|
11174
|
-
return (0,
|
|
11481
|
+
return (0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR, "gateway.pid");
|
|
11175
11482
|
}
|
|
11176
11483
|
function writeGatewayPid() {
|
|
11177
|
-
(0,
|
|
11178
|
-
(0,
|
|
11484
|
+
(0, import_node_fs4.mkdirSync)((0, import_node_path6.join)((0, import_node_os6.homedir)(), DEFAULT_CONFIG_DIR), { recursive: true });
|
|
11485
|
+
(0, import_node_fs4.writeFileSync)(gatewayPidFile(), String(process.pid));
|
|
11179
11486
|
}
|
|
11180
11487
|
function removeGatewayPid() {
|
|
11181
11488
|
try {
|
|
11182
|
-
(0,
|
|
11489
|
+
(0, import_node_fs4.rmSync)(gatewayPidFile());
|
|
11183
11490
|
} catch {
|
|
11184
11491
|
}
|
|
11185
11492
|
}
|
|
11186
11493
|
function gatewayPid() {
|
|
11187
11494
|
try {
|
|
11188
|
-
const pid = Number((0,
|
|
11495
|
+
const pid = Number((0, import_node_fs4.readFileSync)(gatewayPidFile(), "utf-8").trim());
|
|
11189
11496
|
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
11190
11497
|
} catch {
|
|
11191
11498
|
return null;
|
|
@@ -11219,15 +11526,15 @@ function resolveHere() {
|
|
|
11219
11526
|
if (DEBUG) console.error("[debug] cannot resolve script path");
|
|
11220
11527
|
return;
|
|
11221
11528
|
}
|
|
11222
|
-
const hereDir = (0,
|
|
11529
|
+
const hereDir = (0, import_node_path7.dirname)(here);
|
|
11223
11530
|
const candidates = [
|
|
11224
|
-
(0,
|
|
11225
|
-
(0,
|
|
11226
|
-
(0,
|
|
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")
|
|
11227
11534
|
];
|
|
11228
11535
|
let prebuildsDir = null;
|
|
11229
11536
|
for (const c of candidates) {
|
|
11230
|
-
if ((0,
|
|
11537
|
+
if ((0, import_node_fs5.existsSync)(c)) {
|
|
11231
11538
|
prebuildsDir = c;
|
|
11232
11539
|
break;
|
|
11233
11540
|
}
|
|
@@ -11237,11 +11544,11 @@ function resolveHere() {
|
|
|
11237
11544
|
return;
|
|
11238
11545
|
}
|
|
11239
11546
|
let n = 0;
|
|
11240
|
-
for (const sub of (0,
|
|
11241
|
-
const helper = (0,
|
|
11242
|
-
if ((0,
|
|
11547
|
+
for (const sub of (0, import_node_fs5.readdirSync)(prebuildsDir)) {
|
|
11548
|
+
const helper = (0, import_node_path7.join)(prebuildsDir, sub, "spawn-helper");
|
|
11549
|
+
if ((0, import_node_fs5.existsSync)(helper)) {
|
|
11243
11550
|
try {
|
|
11244
|
-
(0,
|
|
11551
|
+
(0, import_node_fs5.chmodSync)(helper, 493);
|
|
11245
11552
|
n++;
|
|
11246
11553
|
} catch {
|
|
11247
11554
|
}
|
|
@@ -11252,49 +11559,56 @@ function resolveHere() {
|
|
|
11252
11559
|
var argUrl = process.argv[2];
|
|
11253
11560
|
var argMatch = argUrl && (argUrl.startsWith("ws://") || argUrl.startsWith("wss://"));
|
|
11254
11561
|
if (argUrl && !argMatch && (argUrl === "-h" || argUrl === "--help" || argUrl === "help")) {
|
|
11255
|
-
console.log(`cli-local \u2014 local PTY agent for cli-remote
|
|
11562
|
+
console.log(`cli-local \u2014 local PTY agent for cli-remote (gateway \u5E38\u9A7B\u670D\u52A1)
|
|
11256
11563
|
|
|
11257
11564
|
usage:
|
|
11258
|
-
cli-local
|
|
11259
|
-
cli-local
|
|
11260
|
-
cli-local
|
|
11565
|
+
cli-local \u65E0 gateway \u4E14\u5DF2\u914D\u7F6E\u65F6\u524D\u53F0\u8FD0\u884C\uFF1B\u5426\u5219\u663E\u793A\u72B6\u6001
|
|
11566
|
+
cli-local pair \u91CD\u65B0\u6388\u6743\uFF08\u540A\u9500/\u8FC7\u671F\u540E\u91CD\u914D\u5BF9\uFF1Bgateway \u70ED\u52A0\u8F7D\uFF09
|
|
11567
|
+
cli-local gateway start \u540E\u53F0\u542F\u52A8\u5E38\u9A7B gateway\uFF08\u63A8\u8350\uFF09
|
|
11568
|
+
cli-local gateway stop|restart|status|logs [-f] [-n N]
|
|
11569
|
+
cli-local install [HUB_URL] \u5B89\u88C5 macOS launchd \u5E38\u9A7B\u670D\u52A1
|
|
11570
|
+
cli-local uninstall \u5378\u8F7D launchd \u5E38\u9A7B\u670D\u52A1
|
|
11571
|
+
|
|
11572
|
+
\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
|
|
11573
|
+
\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
11574
|
|
|
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
|
|
11575
|
+
\u88AB\u540A\u9500/\u51ED\u8BC1\u8FC7\u671F\u65F6 gateway \u4E0D\u4F1A\u9000\u51FA\uFF0C\u800C\u662F\u8FDB\u5165 needs-pairing \u7B49\u5F85\uFF1B
|
|
11576
|
+
\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
11577
|
|
|
11269
|
-
\
|
|
11578
|
+
\u672C\u5730\u63A7\u5236 API\uFF08\u4EC5 127.0.0.1\uFF0Cgateway \u8FD0\u884C\u65F6\uFF09\uFF1A
|
|
11579
|
+
GET /status /healthz /logs /sessions POST /stop
|
|
11580
|
+
\u7AEF\u53E3\u9ED8\u8BA4 18790\uFF0Cconfig.json gateway.apiPort \u6216 CLI_LOCAL_API_PORT \u53EF\u6539
|
|
11270
11581
|
|
|
11271
11582
|
environment:
|
|
11272
11583
|
RELAY_URL hub ws url (overrides argv and config)
|
|
11273
11584
|
REGISTRATION_KEY registration key (fallback of config.json)
|
|
11274
11585
|
CLI_LOCAL_NO_BROWSER=1 \u4E0D\u81EA\u52A8\u6253\u5F00\u6D4F\u89C8\u5668\uFF08\u53EA\u6253\u5370\u94FE\u63A5\uFF09
|
|
11586
|
+
CLI_LOCAL_API_PORT gateway \u63A7\u5236API\u7AEF\u53E3
|
|
11275
11587
|
HOME where ~/.cli-remote/ lives
|
|
11276
11588
|
|
|
11277
11589
|
config (~/.cli-remote/config.json):
|
|
11278
11590
|
{
|
|
11279
11591
|
"relayUrl": "wss://your-hub/ws",
|
|
11280
11592
|
"registrationKey": "from-the-web-devices-page",
|
|
11593
|
+
"gateway": { "apiPort": 18790 },
|
|
11281
11594
|
"whitelist": { "add": [], "remove": [] }
|
|
11282
11595
|
}
|
|
11283
11596
|
|
|
11284
11597
|
example:
|
|
11285
|
-
cli-local
|
|
11286
|
-
cli-local
|
|
11598
|
+
cli-local pair # \u9996\u6B21/\u91CD\u65B0\u6388\u6743
|
|
11599
|
+
cli-local gateway start # \u540E\u53F0\u5E38\u9A7B
|
|
11600
|
+
cli-local gateway status # \u67E5\u770B\u8FDE\u63A5\u72B6\u6001`);
|
|
11287
11601
|
process.exit(0);
|
|
11288
11602
|
}
|
|
11289
11603
|
var LAUNCHD_LABEL = "net.cli-remote.local";
|
|
11290
11604
|
function launchdPlistPath() {
|
|
11291
|
-
return (0,
|
|
11605
|
+
return (0, import_node_path7.join)((0, import_node_os7.homedir)(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
|
|
11292
11606
|
}
|
|
11293
11607
|
function entryScript() {
|
|
11294
11608
|
const a1 = process.argv[1];
|
|
11295
11609
|
if (!a1 || a1.endsWith(".ts")) return null;
|
|
11296
11610
|
try {
|
|
11297
|
-
return (0,
|
|
11611
|
+
return (0, import_node_fs5.realpathSync)(a1);
|
|
11298
11612
|
} catch {
|
|
11299
11613
|
return a1;
|
|
11300
11614
|
}
|
|
@@ -11312,7 +11626,7 @@ function launchdInstall(urlArg) {
|
|
|
11312
11626
|
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
11627
|
process.exit(1);
|
|
11314
11628
|
}
|
|
11315
|
-
const home = (0,
|
|
11629
|
+
const home = (0, import_node_os7.homedir)();
|
|
11316
11630
|
const entry = entryScript();
|
|
11317
11631
|
if (!entry) {
|
|
11318
11632
|
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 +11634,28 @@ function launchdInstall(urlArg) {
|
|
|
11320
11634
|
process.exit(1);
|
|
11321
11635
|
}
|
|
11322
11636
|
if (urlArg) {
|
|
11323
|
-
const cfgDir = (0,
|
|
11324
|
-
(0,
|
|
11325
|
-
const cfgPath = (0,
|
|
11637
|
+
const cfgDir = (0, import_node_path7.join)(home, DEFAULT_CONFIG_DIR);
|
|
11638
|
+
(0, import_node_fs5.mkdirSync)(cfgDir, { recursive: true });
|
|
11639
|
+
const cfgPath = (0, import_node_path7.join)(cfgDir, "config.json");
|
|
11326
11640
|
let cfg = {};
|
|
11327
11641
|
try {
|
|
11328
|
-
cfg = JSON.parse((0,
|
|
11642
|
+
cfg = JSON.parse((0, import_node_fs5.readFileSync)(cfgPath, "utf-8"));
|
|
11329
11643
|
} catch {
|
|
11330
11644
|
}
|
|
11331
11645
|
cfg.relayUrl = urlArg;
|
|
11332
|
-
(0,
|
|
11646
|
+
(0, import_node_fs5.writeFileSync)(cfgPath, JSON.stringify(cfg, null, 2) + "\n");
|
|
11333
11647
|
console.log(`[local] relayUrl \u5DF2\u5199\u5165 ${cfgPath}`);
|
|
11334
11648
|
}
|
|
11335
|
-
const credPath = (0,
|
|
11336
|
-
if (!(0,
|
|
11649
|
+
const credPath = (0, import_node_path7.join)(home, DEFAULT_CONFIG_DIR, CREDENTIALS_FILE);
|
|
11650
|
+
if (!(0, import_node_fs5.existsSync)(credPath)) {
|
|
11337
11651
|
console.error("[local] \u5C1A\u672A\u6388\u6743\uFF08\u627E\u4E0D\u5230 credentials.json\uFF09\u3002");
|
|
11338
11652
|
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
11653
|
process.exit(1);
|
|
11340
11654
|
}
|
|
11341
11655
|
const plist = launchdPlistPath();
|
|
11342
|
-
const logDir = (0,
|
|
11343
|
-
(0,
|
|
11344
|
-
(0,
|
|
11656
|
+
const logDir = (0, import_node_path7.join)(home, DEFAULT_CONFIG_DIR, "logs");
|
|
11657
|
+
(0, import_node_fs5.mkdirSync)((0, import_node_path7.dirname)(plist), { recursive: true });
|
|
11658
|
+
(0, import_node_fs5.mkdirSync)(logDir, { recursive: true });
|
|
11345
11659
|
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
11346
11660
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
11347
11661
|
<plist version="1.0">
|
|
@@ -11355,12 +11669,12 @@ function launchdInstall(urlArg) {
|
|
|
11355
11669
|
<key>RunAtLoad</key><true/>
|
|
11356
11670
|
<key>KeepAlive</key><true/>
|
|
11357
11671
|
<key>ThrottleInterval</key><integer>30</integer>
|
|
11358
|
-
<key>StandardOutPath</key><string>${(0,
|
|
11359
|
-
<key>StandardErrorPath</key><string>${(0,
|
|
11672
|
+
<key>StandardOutPath</key><string>${(0, import_node_path7.join)(logDir, "daemon.out.log")}</string>
|
|
11673
|
+
<key>StandardErrorPath</key><string>${(0, import_node_path7.join)(logDir, "daemon.err.log")}</string>
|
|
11360
11674
|
</dict>
|
|
11361
11675
|
</plist>
|
|
11362
11676
|
`;
|
|
11363
|
-
(0,
|
|
11677
|
+
(0, import_node_fs5.writeFileSync)(plist, xml);
|
|
11364
11678
|
const uid = process.getuid?.();
|
|
11365
11679
|
if (uid === void 0) {
|
|
11366
11680
|
console.error("[local] \u65E0\u6CD5\u786E\u5B9A uid\uFF0Claunchd \u5B89\u88C5\u5931\u8D25");
|
|
@@ -11378,7 +11692,7 @@ function launchdInstall(urlArg) {
|
|
|
11378
11692
|
}
|
|
11379
11693
|
function launchdUninstall() {
|
|
11380
11694
|
const plist = launchdPlistPath();
|
|
11381
|
-
if (!(0,
|
|
11695
|
+
if (!(0, import_node_fs5.existsSync)(plist)) {
|
|
11382
11696
|
console.log("[local] \u672A\u627E\u5230\u5DF2\u5B89\u88C5\u7684\u5E38\u9A7B\u670D\u52A1\uFF08nothing to uninstall\uFF09");
|
|
11383
11697
|
process.exit(0);
|
|
11384
11698
|
}
|
|
@@ -11386,7 +11700,7 @@ function launchdUninstall() {
|
|
|
11386
11700
|
const guiDomain = `gui/${uid ?? 0}`;
|
|
11387
11701
|
run("launchctl", ["bootout", guiDomain, plist], true);
|
|
11388
11702
|
run("launchctl", ["unload", "-w", plist], true);
|
|
11389
|
-
(0,
|
|
11703
|
+
(0, import_node_fs5.rmSync)(plist);
|
|
11390
11704
|
console.log(`[local] launchd \u670D\u52A1\u5DF2\u505C\u6B62\u5E76\u5378\u8F7D\uFF1A${LAUNCHD_LABEL}`);
|
|
11391
11705
|
}
|
|
11392
11706
|
if (argUrl && !argMatch && argUrl === "install") {
|
|
@@ -11397,70 +11711,70 @@ if (argUrl && !argMatch && argUrl === "uninstall") {
|
|
|
11397
11711
|
launchdUninstall();
|
|
11398
11712
|
process.exit(0);
|
|
11399
11713
|
}
|
|
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
|
-
}
|
|
11714
|
+
if (argUrl === "pair") {
|
|
11715
|
+
void (async () => {
|
|
11716
|
+
const home = (0, import_node_os7.homedir)();
|
|
11717
|
+
const cfgPath = (0, import_node_path7.join)(home, DEFAULT_CONFIG_DIR, "config.json");
|
|
11718
|
+
let cfg = {};
|
|
11409
11719
|
try {
|
|
11410
|
-
|
|
11720
|
+
cfg = JSON.parse((0, import_node_fs5.readFileSync)(cfgPath, "utf-8"));
|
|
11411
11721
|
} catch {
|
|
11412
11722
|
}
|
|
11413
|
-
const
|
|
11414
|
-
|
|
11415
|
-
|
|
11416
|
-
|
|
11417
|
-
}
|
|
11723
|
+
const url = process.argv[4] ?? process.env.RELAY_URL ?? cfg.relayUrl;
|
|
11724
|
+
if (!url) {
|
|
11725
|
+
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");
|
|
11726
|
+
process.exit(1);
|
|
11418
11727
|
}
|
|
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");
|
|
11728
|
+
if (!cfg.registrationKey && !process.env.REGISTRATION_KEY) {
|
|
11729
|
+
console.error("[pair] \u7F3A\u5C11 registrationKey\uFF1A\u5199\u5165 ~/.cli-remote/config.json \u6216\u8BBE\u7F6E REGISTRATION_KEY");
|
|
11429
11730
|
process.exit(1);
|
|
11430
11731
|
}
|
|
11431
|
-
const
|
|
11432
|
-
|
|
11433
|
-
|
|
11434
|
-
|
|
11435
|
-
|
|
11436
|
-
|
|
11732
|
+
const prev = await loadCredentials();
|
|
11733
|
+
const deviceId = prev?.deviceId || await stableDeviceId();
|
|
11734
|
+
console.log(`[pair] hub: ${url}\uFF08\u8BBE\u5907 ${deviceId}${prev ? "\uFF0C\u590D\u7528" : "\uFF0C\u7531\u673A\u5668\u7801\u6D3E\u751F"}\uFF09`);
|
|
11735
|
+
const creds = await deviceFlowAuthorize(
|
|
11736
|
+
baseUrlFromWsUrl(url),
|
|
11737
|
+
cfg.registrationKey ?? process.env.REGISTRATION_KEY,
|
|
11738
|
+
deviceId
|
|
11739
|
+
);
|
|
11740
|
+
console.log(`[pair] \u6388\u6743\u5B8C\u6210 deviceId=${creds.deviceId}`);
|
|
11741
|
+
const port = readConfiguredPort();
|
|
11742
|
+
const health = await apiGet(port, "/healthz");
|
|
11743
|
+
if (health?.ok) {
|
|
11744
|
+
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");
|
|
11745
|
+
} else {
|
|
11746
|
+
console.log("[pair] gateway \u672A\u8FD0\u884C\uFF1Acli-local gateway start \u542F\u52A8\u5E38\u9A7B\u670D\u52A1");
|
|
11437
11747
|
}
|
|
11438
|
-
|
|
11439
|
-
console.
|
|
11440
|
-
|
|
11441
|
-
|
|
11748
|
+
})().catch((e) => {
|
|
11749
|
+
console.error(`[pair] ${e.message}`);
|
|
11750
|
+
process.exit(1);
|
|
11751
|
+
});
|
|
11752
|
+
}
|
|
11753
|
+
if (argUrl === "gateway") {
|
|
11754
|
+
let gatewayLogFile = function() {
|
|
11755
|
+
return (0, import_node_path7.join)((0, import_node_os7.homedir)(), DEFAULT_CONFIG_DIR, "logs", "gateway.log");
|
|
11442
11756
|
}, gatewayLogs = function() {
|
|
11443
11757
|
const follow = rest.includes("-f") || rest.includes("--follow");
|
|
11444
11758
|
const li = rest.findIndex((a) => a === "-n" || a === "--lines");
|
|
11445
11759
|
const lines = li >= 0 ? Number(rest[li + 1]) || 50 : 50;
|
|
11446
11760
|
const file = gatewayLogFile();
|
|
11447
|
-
if (!(0,
|
|
11761
|
+
if (!(0, import_node_fs5.existsSync)(file)) {
|
|
11448
11762
|
console.log(`[gateway] \u6682\u65E0\u65E5\u5FD7\uFF08${file} \u4E0D\u5B58\u5728\uFF09`);
|
|
11449
11763
|
process.exit(0);
|
|
11450
11764
|
}
|
|
11451
|
-
const tail = (0,
|
|
11765
|
+
const tail = (0, import_node_fs5.readFileSync)(file, "utf-8").trimEnd().split("\n").slice(-lines);
|
|
11452
11766
|
console.log(tail.join("\n"));
|
|
11453
11767
|
if (!follow) process.exit(0);
|
|
11454
|
-
let size = (0,
|
|
11768
|
+
let size = (0, import_node_fs5.statSync)(file).size;
|
|
11455
11769
|
console.log("--- following (Ctrl+C \u9000\u51FA) ---");
|
|
11456
11770
|
setInterval(() => {
|
|
11457
11771
|
try {
|
|
11458
|
-
const st = (0,
|
|
11772
|
+
const st = (0, import_node_fs5.statSync)(file);
|
|
11459
11773
|
if (st.size > size) {
|
|
11460
|
-
const fd = (0,
|
|
11774
|
+
const fd = (0, import_node_fs5.openSync)(file, "r");
|
|
11461
11775
|
const buf = Buffer.alloc(st.size - size);
|
|
11462
|
-
(0,
|
|
11463
|
-
(0,
|
|
11776
|
+
(0, import_node_fs5.readSync)(fd, buf, 0, buf.length, size);
|
|
11777
|
+
(0, import_node_fs5.closeSync)(fd);
|
|
11464
11778
|
size = st.size;
|
|
11465
11779
|
process.stdout.write(buf.toString("utf-8"));
|
|
11466
11780
|
} else if (st.size < size) {
|
|
@@ -11470,7 +11784,7 @@ if (argUrl === "gateway") {
|
|
|
11470
11784
|
}
|
|
11471
11785
|
}, 1e3);
|
|
11472
11786
|
};
|
|
11473
|
-
gatewayLogFile2 = gatewayLogFile,
|
|
11787
|
+
gatewayLogFile2 = gatewayLogFile, gatewayLogs2 = gatewayLogs;
|
|
11474
11788
|
const action = process.argv[3] ?? "";
|
|
11475
11789
|
const rest = process.argv.slice(4);
|
|
11476
11790
|
const hubArg = (() => {
|
|
@@ -11482,22 +11796,22 @@ if (argUrl === "gateway") {
|
|
|
11482
11796
|
console.log(`[gateway] already running (pid ${gatewayPid()})`);
|
|
11483
11797
|
process.exit(0);
|
|
11484
11798
|
}
|
|
11485
|
-
const home = (0,
|
|
11799
|
+
const home = (0, import_node_os7.homedir)();
|
|
11486
11800
|
const entry = entryScript();
|
|
11487
|
-
const cfgDir = (0,
|
|
11488
|
-
const
|
|
11489
|
-
const
|
|
11490
|
-
const hasCfg = (0, import_node_fs4.existsSync)(cfgPath);
|
|
11801
|
+
const cfgDir = (0, import_node_path7.join)(home, DEFAULT_CONFIG_DIR);
|
|
11802
|
+
const cfgPath = (0, import_node_path7.join)(cfgDir, "config.json");
|
|
11803
|
+
const hasCfg = (0, import_node_fs5.existsSync)(cfgPath);
|
|
11491
11804
|
if (!entry) {
|
|
11492
11805
|
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
11806
|
process.exit(1);
|
|
11494
11807
|
}
|
|
11495
|
-
if (!hasCfg
|
|
11496
|
-
console.error("[gateway] \u5C1A\u672A\u914D\u7F6E
|
|
11808
|
+
if (!hasCfg) {
|
|
11809
|
+
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");
|
|
11810
|
+
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
11811
|
process.exit(1);
|
|
11498
11812
|
}
|
|
11499
|
-
(0,
|
|
11500
|
-
const out = (0,
|
|
11813
|
+
(0, import_node_fs5.mkdirSync)((0, import_node_path7.dirname)(gatewayLogFile()), { recursive: true });
|
|
11814
|
+
const out = (0, import_node_fs5.openSync)(gatewayLogFile(), "a");
|
|
11501
11815
|
const env = {};
|
|
11502
11816
|
for (const [k, v] of Object.entries(process.env)) if (v !== void 0) env[k] = v;
|
|
11503
11817
|
const args = [entry, "gateway", "run"];
|
|
@@ -11509,41 +11823,124 @@ if (argUrl === "gateway") {
|
|
|
11509
11823
|
});
|
|
11510
11824
|
child.unref();
|
|
11511
11825
|
console.log(`[gateway] started (pid ${child.pid})`);
|
|
11826
|
+
const port = readConfiguredPort();
|
|
11827
|
+
for (let i = 0; i < 20; i++) {
|
|
11828
|
+
await new Promise((res) => setTimeout(res, 250));
|
|
11829
|
+
const h = await apiGet(port, "/healthz", 400);
|
|
11830
|
+
if (h?.ok) {
|
|
11831
|
+
console.log(`[gateway] \u63A7\u5236API\u5C31\u7EEA: http://127.0.0.1:${port}\uFF08\u4EC5\u672C\u673A\uFF09`);
|
|
11832
|
+
break;
|
|
11833
|
+
}
|
|
11834
|
+
}
|
|
11512
11835
|
console.log(`[gateway] \u65E5\u5FD7: ${gatewayLogFile()}`);
|
|
11513
11836
|
console.log("[gateway] \u72B6\u6001: cli-local gateway status");
|
|
11514
11837
|
}
|
|
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
|
-
|
|
11838
|
+
async function stopWorker() {
|
|
11839
|
+
const port = readConfiguredPort();
|
|
11840
|
+
if (await apiPost(port, "/stop", 1500)) {
|
|
11841
|
+
const t02 = Date.now();
|
|
11842
|
+
while (Date.now() - t02 < 5e3 && gatewayAlive()) {
|
|
11843
|
+
await new Promise((res) => setTimeout(res, 150));
|
|
11844
|
+
}
|
|
11845
|
+
(0, import_node_fs5.rmSync)((0, import_node_path7.join)((0, import_node_os7.homedir)(), DEFAULT_CONFIG_DIR, "gateway.pid"), { force: true });
|
|
11846
|
+
return;
|
|
11847
|
+
}
|
|
11848
|
+
const pid = gatewayPid();
|
|
11849
|
+
if (pid === null || !gatewayAlive()) {
|
|
11850
|
+
(0, import_node_fs5.rmSync)((0, import_node_path7.join)((0, import_node_os7.homedir)(), DEFAULT_CONFIG_DIR, "gateway.pid"), { force: true });
|
|
11851
|
+
return;
|
|
11852
|
+
}
|
|
11853
|
+
try {
|
|
11854
|
+
process.kill(pid, "SIGTERM");
|
|
11855
|
+
} catch {
|
|
11856
|
+
}
|
|
11857
|
+
const t0 = Date.now();
|
|
11858
|
+
while (Date.now() - t0 < 5e3 && gatewayAlive()) {
|
|
11859
|
+
await new Promise((res) => setTimeout(res, 150));
|
|
11860
|
+
}
|
|
11861
|
+
(0, import_node_fs5.rmSync)((0, import_node_path7.join)((0, import_node_os7.homedir)(), DEFAULT_CONFIG_DIR, "gateway.pid"), { force: true });
|
|
11862
|
+
}
|
|
11863
|
+
async function gatewayStop() {
|
|
11864
|
+
const pid = gatewayPid();
|
|
11865
|
+
const wasRunning = gatewayAlive();
|
|
11866
|
+
await stopWorker();
|
|
11867
|
+
console.log(wasRunning ? `[gateway] stopped (pid ${pid ?? "?"})` : "[gateway] not running");
|
|
11868
|
+
process.exit(0);
|
|
11869
|
+
}
|
|
11870
|
+
async function gatewayStatus() {
|
|
11871
|
+
const port = readConfiguredPort();
|
|
11872
|
+
const st = await apiGet(port, "/status", 600);
|
|
11873
|
+
if (st) {
|
|
11874
|
+
const phaseLabel = {
|
|
11875
|
+
starting: "\u542F\u52A8\u4E2D",
|
|
11876
|
+
connecting: "\u8FDE\u63A5\u4E2D",
|
|
11877
|
+
connected: "\u5DF2\u8FDE\u63A5",
|
|
11878
|
+
reconnecting: "\u91CD\u8FDE\u4E2D",
|
|
11879
|
+
"needs-pairing": "\u9700\u8981\u91CD\u65B0\u6388\u6743"
|
|
11880
|
+
};
|
|
11881
|
+
const up = Math.floor(st.uptimeMs / 1e3);
|
|
11882
|
+
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`;
|
|
11883
|
+
const exp = st.accessTokenExpiresAt ? new Date(st.accessTokenExpiresAt).toLocaleTimeString() : "\u2014";
|
|
11884
|
+
console.log(`[gateway] running (pid ${gatewayPid() ?? "?"}, api :${port})`);
|
|
11885
|
+
console.log(`[gateway] \u72B6\u6001: ${phaseLabel[st.phase] ?? st.phase}${st.reason ? ` (${st.reason})` : ""}`);
|
|
11886
|
+
console.log(`[gateway] hub: ${st.hubUrl}`);
|
|
11887
|
+
console.log(`[gateway] deviceId: ${st.deviceId ?? "\u2014"}`);
|
|
11888
|
+
console.log(`[gateway] uptime: ${upStr}, token \u5230\u671F: ${exp}`);
|
|
11889
|
+
console.log(`[gateway] PTY \u4F1A\u8BDD: ${st.sessions.length} \u4E2A${st.sessions.length ? ` (${st.sessions.map((s) => s.cmd).join(", ")})` : ""}`);
|
|
11890
|
+
if (st.phase === "needs-pairing") {
|
|
11891
|
+
console.log("[gateway] \u26A0 \u8FD0\u884C cli-local pair \u91CD\u65B0\u6388\u6743\uFF08\u5B8C\u6210\u540E\u81EA\u52A8\u91CD\u8FDE\uFF09");
|
|
11892
|
+
}
|
|
11893
|
+
process.exit(0);
|
|
11894
|
+
}
|
|
11895
|
+
if (!gatewayAlive()) {
|
|
11896
|
+
console.log("[gateway] stopped");
|
|
11897
|
+
process.exit(1);
|
|
11898
|
+
}
|
|
11899
|
+
const pid = gatewayPid();
|
|
11900
|
+
let hub = "(\u672A\u914D\u7F6E)";
|
|
11901
|
+
try {
|
|
11902
|
+
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"));
|
|
11903
|
+
hub = cfg.relayUrl ?? hub;
|
|
11904
|
+
} catch {
|
|
11905
|
+
}
|
|
11906
|
+
console.log(`[gateway] running (pid ${pid}) \u2014 \u63A7\u5236API\u672A\u54CD\u5E94\uFF08\u65E7\u7248\u672C\uFF1F\uFF09`);
|
|
11907
|
+
console.log(`[gateway] hub: ${hub}`);
|
|
11908
|
+
console.log(`[gateway] \u65E5\u5FD7: ${gatewayLogFile()}`);
|
|
11909
|
+
process.exit(0);
|
|
11910
|
+
}
|
|
11911
|
+
void (async () => {
|
|
11912
|
+
switch (action) {
|
|
11913
|
+
case "start":
|
|
11914
|
+
gatewayStart().then(() => process.exit(0)).catch((e) => {
|
|
11915
|
+
console.error(`[gateway] ${e.message}`);
|
|
11916
|
+
process.exit(1);
|
|
11917
|
+
});
|
|
11918
|
+
break;
|
|
11919
|
+
case "stop":
|
|
11920
|
+
gatewayStop();
|
|
11921
|
+
break;
|
|
11922
|
+
case "restart":
|
|
11923
|
+
await stopWorker();
|
|
11924
|
+
gatewayStart().then(() => process.exit(0)).catch((e) => {
|
|
11925
|
+
console.error(`[gateway] ${e.message}`);
|
|
11926
|
+
process.exit(1);
|
|
11927
|
+
});
|
|
11928
|
+
break;
|
|
11929
|
+
case "status":
|
|
11930
|
+
void gatewayStatus();
|
|
11931
|
+
break;
|
|
11932
|
+
case "logs":
|
|
11933
|
+
gatewayLogs();
|
|
11934
|
+
break;
|
|
11935
|
+
case "run":
|
|
11936
|
+
startLocal({ url: hubArg, daemon: true }).catch((err) => {
|
|
11937
|
+
if (err instanceof AuthError) console.error(`[local] ${err.message}`);
|
|
11938
|
+
else console.error("[local] fatal:", err);
|
|
11939
|
+
process.exit(1);
|
|
11940
|
+
});
|
|
11941
|
+
break;
|
|
11942
|
+
default:
|
|
11943
|
+
console.log(`cli-local gateway \u2014 \u540E\u53F0\u8FD0\u884C\u7BA1\u7406
|
|
11547
11944
|
|
|
11548
11945
|
usage:
|
|
11549
11946
|
cli-local gateway start [--hub URL] \u540E\u53F0\u542F\u52A8\uFF08\u81EA\u52A8\u91CD\u8FDE\uFF09
|
|
@@ -11553,24 +11950,55 @@ usage:
|
|
|
11553
11950
|
cli-local gateway logs [-f] [-n N] \u67E5\u770B\u65E5\u5FD7\uFF08-f \u8DDF\u968F\uFF09
|
|
11554
11951
|
cli-local gateway run [--hub URL] \u524D\u53F0\u8FD0\u884C\uFF08\u4F9B start \u62C9\u8D77\uFF09
|
|
11555
11952
|
|
|
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
|
-
|
|
11953
|
+
\u8BF4\u660E\uFF1A\u540E\u53F0\u8FDB\u7A0B\u7531 pid \u6587\u4EF6\u7BA1\u7406\uFF08~/.cli-remote/gateway.pid\uFF09\uFF0C\u672C\u5730\u63A7\u5236API
|
|
11954
|
+
\u4EC5 127.0.0.1\uFF08\u9ED8\u8BA4 18790\uFF0Cconfig.json gateway.apiPort \u53EF\u6539\uFF09\uFF0C\u65E5\u5FD7\u5728
|
|
11955
|
+
~/.cli-remote/logs/gateway.log\u3002\u65E0\u51ED\u8BC1\u4E5F\u80FD\u542F\u52A8\uFF08\u8FDB\u5165 needs-pairing \u7B49
|
|
11956
|
+
cli-local pair\uFF09\u3002macOS \u60F3\u5F00\u673A\u81EA\u542F\u7528 launchd\uFF1Acli-local install\u3002`);
|
|
11957
|
+
process.exit(action ? 1 : 0);
|
|
11958
|
+
}
|
|
11959
|
+
})().catch((e) => {
|
|
11960
|
+
console.error(`[gateway] ${e.message}`);
|
|
11961
|
+
process.exit(1);
|
|
11962
|
+
});
|
|
11561
11963
|
}
|
|
11562
11964
|
var gatewayLogFile2;
|
|
11563
|
-
var stopWorker2;
|
|
11564
|
-
var gatewayStop2;
|
|
11565
|
-
var gatewayStatus2;
|
|
11566
11965
|
var gatewayLogs2;
|
|
11567
|
-
|
|
11568
|
-
if (
|
|
11569
|
-
|
|
11966
|
+
if (!argUrl || argMatch) {
|
|
11967
|
+
if (!argUrl) {
|
|
11968
|
+
void (async () => {
|
|
11969
|
+
const port = readConfiguredPort();
|
|
11970
|
+
const health = await apiGet(port, "/healthz", 400);
|
|
11971
|
+
if (health?.ok) {
|
|
11972
|
+
console.log("[local] gateway \u5E38\u9A7B\u670D\u52A1\u8FD0\u884C\u4E2D\u3002\u5B9E\u65F6\u72B6\u6001:");
|
|
11973
|
+
const entry = process.argv[1];
|
|
11974
|
+
if (entry && !entry.endsWith(".ts")) {
|
|
11975
|
+
const { execFileSync: ex } = await import("node:child_process");
|
|
11976
|
+
try {
|
|
11977
|
+
ex(process.execPath, [entry, "gateway", "status"], { stdio: "inherit" });
|
|
11978
|
+
} catch {
|
|
11979
|
+
console.log(`[local] \u63A7\u5236API: http://127.0.0.1:${port}/status`);
|
|
11980
|
+
}
|
|
11981
|
+
} else {
|
|
11982
|
+
console.log(`[local] \u63A7\u5236API: http://127.0.0.1:${port}/status`);
|
|
11983
|
+
}
|
|
11984
|
+
process.exit(0);
|
|
11985
|
+
}
|
|
11986
|
+
await startLocal({});
|
|
11987
|
+
})().catch((err) => {
|
|
11988
|
+
if (err instanceof AuthError) console.error(`[local] ${err.message}`);
|
|
11989
|
+
else console.error("[local] fatal:", err);
|
|
11990
|
+
process.exit(1);
|
|
11991
|
+
});
|
|
11570
11992
|
} else {
|
|
11571
|
-
|
|
11993
|
+
startLocal({ url: argUrl }).catch((err) => {
|
|
11994
|
+
if (err instanceof AuthError) {
|
|
11995
|
+
console.error(`[local] ${err.message}`);
|
|
11996
|
+
} else {
|
|
11997
|
+
console.error("[local] fatal:", err);
|
|
11998
|
+
}
|
|
11999
|
+
process.exit(1);
|
|
12000
|
+
});
|
|
11572
12001
|
}
|
|
11573
|
-
|
|
11574
|
-
});
|
|
12002
|
+
}
|
|
11575
12003
|
process.on("SIGINT", () => process.exit(0));
|
|
11576
12004
|
process.on("SIGTERM", () => process.exit(0));
|