@cli-remote/local 0.1.1 → 0.3.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 +229 -172
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
'#!/usr/bin/env node'
|
|
2
2
|
"use strict";
|
|
3
3
|
var __create = Object.create;
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -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:
|
|
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;
|
|
@@ -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
|
-
|
|
10230
|
+
if (!trimmed || !dangerConfirm) return { allowed: true };
|
|
10231
|
+
const pattern = findDangerousPattern(trimmed);
|
|
10232
|
+
if (pattern) {
|
|
10298
10233
|
return {
|
|
10299
10234
|
allowed: false,
|
|
10300
10235
|
rejectedCmd: trimmed,
|
|
10301
|
-
reason: "\
|
|
10236
|
+
reason: "\u5339\u914D\u5371\u9669\u547D\u4EE4\u6A21\u5F0F\uFF0C\u8BF7\u4E8C\u6B21\u786E\u8BA4",
|
|
10237
|
+
dangerous: true
|
|
10302
10238
|
};
|
|
10303
10239
|
}
|
|
10304
|
-
if (trimmed.startsWith("(")) {
|
|
10305
|
-
return {
|
|
10306
|
-
allowed: false,
|
|
10307
|
-
rejectedCmd: trimmed,
|
|
10308
|
-
reason: "\u5305\u542B\u5B50 shell ( )\uFF0C\u5DF2\u88AB\u7981\u6B62"
|
|
10309
|
-
};
|
|
10310
|
-
}
|
|
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
|
|
|
@@ -10595,9 +10502,108 @@ async function apiPost(port, path, timeoutMs = 800) {
|
|
|
10595
10502
|
}
|
|
10596
10503
|
}
|
|
10597
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
|
+
|
|
10598
10604
|
// src/auth.ts
|
|
10599
10605
|
var import_node_child_process = require("node:child_process");
|
|
10600
|
-
var
|
|
10606
|
+
var import_node_crypto2 = require("node:crypto");
|
|
10601
10607
|
var import_promises2 = require("node:fs/promises");
|
|
10602
10608
|
var import_node_os5 = require("node:os");
|
|
10603
10609
|
var import_node_path5 = require("node:path");
|
|
@@ -10785,7 +10791,7 @@ async function ensureMachineId() {
|
|
|
10785
10791
|
if (typeof cfg.machineId === "string" && cfg.machineId) return cfg.machineId;
|
|
10786
10792
|
} catch {
|
|
10787
10793
|
}
|
|
10788
|
-
const machineId = "m_" + (0,
|
|
10794
|
+
const machineId = "m_" + (0, import_node_crypto2.randomBytes)(16).toString("hex");
|
|
10789
10795
|
await mergeLocalConfig({ machineId });
|
|
10790
10796
|
return machineId;
|
|
10791
10797
|
}
|
|
@@ -10816,11 +10822,16 @@ async function loadConfig() {
|
|
|
10816
10822
|
return {};
|
|
10817
10823
|
}
|
|
10818
10824
|
}
|
|
10819
|
-
function
|
|
10820
|
-
|
|
10821
|
-
|
|
10822
|
-
|
|
10823
|
-
|
|
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
|
+
}
|
|
10824
10835
|
}
|
|
10825
10836
|
function isTTY() {
|
|
10826
10837
|
return process.stdin.isTTY === true;
|
|
@@ -10890,19 +10901,18 @@ async function setupWizard(config, opts) {
|
|
|
10890
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");
|
|
10891
10902
|
return null;
|
|
10892
10903
|
}
|
|
10893
|
-
console.log(" 1) \
|
|
10894
|
-
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");
|
|
10895
10907
|
const mode = (await readInput("\u9009\u62E9 [1]: ")).trim();
|
|
10896
10908
|
if (mode === "2") {
|
|
10897
|
-
openBrowser(base);
|
|
10898
|
-
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`);
|
|
10899
10909
|
const ans = (await readInput("\u7C98\u8D34 registrationKey: ")).trim();
|
|
10900
10910
|
if (!ans) {
|
|
10901
10911
|
console.log("[local] \u672A\u63D0\u4F9B registrationKey\uFF0C\u9000\u51FA\u3002");
|
|
10902
10912
|
return null;
|
|
10903
10913
|
}
|
|
10904
10914
|
registrationKey = ans;
|
|
10905
|
-
} else {
|
|
10915
|
+
} else if (mode === "3") {
|
|
10906
10916
|
for (; ; ) {
|
|
10907
10917
|
const email = (await readInput("\u90AE\u7BB1: ")).trim();
|
|
10908
10918
|
const password = (await readInput("\u5BC6\u7801: ")).trim();
|
|
@@ -10920,6 +10930,26 @@ async function setupWizard(config, opts) {
|
|
|
10920
10930
|
console.log("[local] \u91CD\u8BD5\uFF08\u6216 Ctrl+C \u9000\u51FA\uFF09\u3002");
|
|
10921
10931
|
}
|
|
10922
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();
|
|
10923
10953
|
}
|
|
10924
10954
|
changed = true;
|
|
10925
10955
|
}
|
|
@@ -10940,7 +10970,10 @@ async function startLocal(opts = {}) {
|
|
|
10940
10970
|
registrationKey = setup.registrationKey;
|
|
10941
10971
|
}
|
|
10942
10972
|
const baseUrl = baseUrlFromWsUrl(url);
|
|
10943
|
-
|
|
10973
|
+
applyConfigDeprecations(config);
|
|
10974
|
+
const dangerConfirm = config.dangerConfirm === true;
|
|
10975
|
+
const auditMode = config.auditMode ?? "full";
|
|
10976
|
+
const retentionDays = config.auditRetentionDays ?? AUDIT_RETENTION_DAYS;
|
|
10944
10977
|
const pty = new PtyManager();
|
|
10945
10978
|
const loggers = /* @__PURE__ */ new Map();
|
|
10946
10979
|
const lineBuffers = /* @__PURE__ */ new Map();
|
|
@@ -10984,8 +11017,15 @@ async function startLocal(opts = {}) {
|
|
|
10984
11017
|
const commands = detectCommands();
|
|
10985
11018
|
console.log(`[local] connecting to ${url}`);
|
|
10986
11019
|
console.log(`[local] deviceId=${credentials.deviceId || "(\u672A\u6388\u6743)"}`);
|
|
10987
|
-
console.log(`[local]
|
|
11020
|
+
console.log(`[local] bash: passthrough (audit: ${auditMode}, retention: ${retentionDays === 0 ? "forever" : retentionDays + "d"}${dangerConfirm ? ", dangerConfirm: on" : ""})`);
|
|
10988
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?.();
|
|
10989
11029
|
let phase = "starting";
|
|
10990
11030
|
let phaseReason = "";
|
|
10991
11031
|
let reconnectAttempts = 0;
|
|
@@ -11236,7 +11276,7 @@ async function startLocal(opts = {}) {
|
|
|
11236
11276
|
function handleSpawn(frame) {
|
|
11237
11277
|
const sessionId = frame.sessionId;
|
|
11238
11278
|
try {
|
|
11239
|
-
const logger = new SessionLogger(sessionId, frame.cmd);
|
|
11279
|
+
const logger = new SessionLogger(sessionId, frame.cmd, auditMode);
|
|
11240
11280
|
pty.spawn(
|
|
11241
11281
|
sessionId,
|
|
11242
11282
|
{ cmd: frame.cmd, cols: frame.cols, rows: frame.rows, cwd: frame.cwd },
|
|
@@ -11314,7 +11354,7 @@ async function startLocal(opts = {}) {
|
|
|
11314
11354
|
if (char === "\r" || char === "\n") {
|
|
11315
11355
|
const line = buf;
|
|
11316
11356
|
buf = "";
|
|
11317
|
-
const result = checkCommand(line,
|
|
11357
|
+
const result = checkCommand(line, dangerConfirm);
|
|
11318
11358
|
if (result.allowed) {
|
|
11319
11359
|
ptyWriteChar(sessionId, "\r");
|
|
11320
11360
|
logger.logInput(new TextEncoder().encode(line + "\r"));
|
|
@@ -11518,11 +11558,28 @@ function resolveHere() {
|
|
|
11518
11558
|
})();
|
|
11519
11559
|
var argUrl = process.argv[2];
|
|
11520
11560
|
var argMatch = argUrl && (argUrl.startsWith("ws://") || argUrl.startsWith("wss://"));
|
|
11561
|
+
function readVersion() {
|
|
11562
|
+
if (true) return "0.3.1";
|
|
11563
|
+
try {
|
|
11564
|
+
const pkg = JSON.parse(
|
|
11565
|
+
(0, import_node_fs5.readFileSync)(new URL("../package.json", import_meta.url), "utf-8")
|
|
11566
|
+
);
|
|
11567
|
+
return pkg.version ?? "unknown";
|
|
11568
|
+
} catch {
|
|
11569
|
+
return "unknown";
|
|
11570
|
+
}
|
|
11571
|
+
}
|
|
11572
|
+
if (argUrl === "-v" || argUrl === "--version" || argUrl === "version") {
|
|
11573
|
+
console.log(`cli-local ${readVersion()}`);
|
|
11574
|
+
console.log(`node ${process.version} (${process.platform} ${process.arch})`);
|
|
11575
|
+
process.exit(0);
|
|
11576
|
+
}
|
|
11521
11577
|
if (argUrl && !argMatch && (argUrl === "-h" || argUrl === "--help" || argUrl === "help")) {
|
|
11522
11578
|
console.log(`cli-local \u2014 local PTY agent for cli-remote (gateway \u5E38\u9A7B\u670D\u52A1)
|
|
11523
11579
|
|
|
11524
11580
|
usage:
|
|
11525
11581
|
cli-local \u65E0 gateway \u4E14\u5DF2\u914D\u7F6E\u65F6\u524D\u53F0\u8FD0\u884C\uFF1B\u5426\u5219\u663E\u793A\u72B6\u6001
|
|
11582
|
+
cli-local -v | --version \u67E5\u770B\u7248\u672C
|
|
11526
11583
|
cli-local pair \u91CD\u65B0\u6388\u6743\uFF08\u540A\u9500/\u8FC7\u671F\u540E\u91CD\u914D\u5BF9\uFF1Bgateway \u70ED\u52A0\u8F7D\uFF09
|
|
11527
11584
|
cli-local gateway start \u540E\u53F0\u542F\u52A8\u5E38\u9A7B gateway\uFF08\u63A8\u8350\uFF09
|
|
11528
11585
|
cli-local gateway stop|restart|status|logs [-f] [-n N]
|
|
@@ -11841,7 +11898,7 @@ if (argUrl === "gateway") {
|
|
|
11841
11898
|
const up = Math.floor(st.uptimeMs / 1e3);
|
|
11842
11899
|
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
11900
|
const exp = st.accessTokenExpiresAt ? new Date(st.accessTokenExpiresAt).toLocaleTimeString() : "\u2014";
|
|
11844
|
-
console.log(`[gateway] running (pid ${gatewayPid() ?? "?"}, api :${port})`);
|
|
11901
|
+
console.log(`[gateway] running (pid ${gatewayPid() ?? "?"}, api :${port}) \u2014 cli-local ${readVersion()}`);
|
|
11845
11902
|
console.log(`[gateway] \u72B6\u6001: ${phaseLabel[st.phase] ?? st.phase}${st.reason ? ` (${st.reason})` : ""}`);
|
|
11846
11903
|
console.log(`[gateway] hub: ${st.hubUrl}`);
|
|
11847
11904
|
console.log(`[gateway] deviceId: ${st.deviceId ?? "\u2014"}`);
|
|
@@ -11863,7 +11920,7 @@ if (argUrl === "gateway") {
|
|
|
11863
11920
|
hub = cfg.relayUrl ?? hub;
|
|
11864
11921
|
} catch {
|
|
11865
11922
|
}
|
|
11866
|
-
console.log(`[gateway] running (pid ${pid}) \u2014 \u63A7\u5236API\u672A\u54CD\u5E94\
|
|
11923
|
+
console.log(`[gateway] running (pid ${pid}) \u2014 cli-local ${readVersion()}\uFF08\u63A7\u5236API\u672A\u54CD\u5E94\uFF0C\u65E7\u7248\u672C\uFF1F\uFF09`);
|
|
11867
11924
|
console.log(`[gateway] hub: ${hub}`);
|
|
11868
11925
|
console.log(`[gateway] \u65E5\u5FD7: ${gatewayLogFile()}`);
|
|
11869
11926
|
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.3.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": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
],
|
|
27
27
|
"scripts": {
|
|
28
28
|
"dev": "tsx watch src/index.ts",
|
|
29
|
-
"build": "
|
|
29
|
+
"build": "node scripts/build.mjs",
|
|
30
30
|
"typecheck": "tsc --noEmit",
|
|
31
31
|
"prepublishOnly": "pnpm build"
|
|
32
32
|
},
|