iframer-cli 2.1.1 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +212 -12
- package/mcp-server.cjs +56 -192
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -7,10 +7,182 @@ const { execSync } = require("child_process");
|
|
|
7
7
|
|
|
8
8
|
const readline = require("readline");
|
|
9
9
|
|
|
10
|
+
const net = require("net");
|
|
11
|
+
const { spawn } = require("child_process");
|
|
12
|
+
|
|
10
13
|
const CONFIG_DIR = path.join(require("os").homedir(), ".iframer");
|
|
11
14
|
const CREDENTIALS_FILE = path.join(CONFIG_DIR, "credentials.json");
|
|
15
|
+
const PROXY_FILE = path.join(CONFIG_DIR, "proxy.json");
|
|
12
16
|
const DEFAULT_SERVER = "https://api.iframer.sh";
|
|
13
17
|
|
|
18
|
+
// ─── SOCKS5 server ───────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
function createSocks5Server() {
|
|
21
|
+
function handleClient(socket) {
|
|
22
|
+
socket.on("error", () => socket.destroy());
|
|
23
|
+
socket.once("data", (greeting) => {
|
|
24
|
+
if (greeting[0] !== 5) { socket.destroy(); return; }
|
|
25
|
+
socket.write(Buffer.from([5, 0]));
|
|
26
|
+
socket.once("data", (req) => {
|
|
27
|
+
if (req[0] !== 5 || req[1] !== 1) { socket.destroy(); return; }
|
|
28
|
+
let host, port;
|
|
29
|
+
const atype = req[3];
|
|
30
|
+
if (atype === 1) {
|
|
31
|
+
host = `${req[4]}.${req[5]}.${req[6]}.${req[7]}`;
|
|
32
|
+
port = req.readUInt16BE(8);
|
|
33
|
+
} else if (atype === 3) {
|
|
34
|
+
const len = req[4];
|
|
35
|
+
host = req.slice(5, 5 + len).toString();
|
|
36
|
+
port = req.readUInt16BE(5 + len);
|
|
37
|
+
} else if (atype === 4) {
|
|
38
|
+
const parts = [];
|
|
39
|
+
for (let i = 0; i < 16; i += 2) parts.push(req.readUInt16BE(4 + i).toString(16));
|
|
40
|
+
host = parts.join(":");
|
|
41
|
+
port = req.readUInt16BE(20);
|
|
42
|
+
} else { socket.destroy(); return; }
|
|
43
|
+
const target = net.connect(port, host, () => {
|
|
44
|
+
const reply = Buffer.alloc(10);
|
|
45
|
+
reply[0] = 5; reply[1] = 0; reply[2] = 0; reply[3] = 1;
|
|
46
|
+
socket.write(reply);
|
|
47
|
+
socket.pipe(target);
|
|
48
|
+
target.pipe(socket);
|
|
49
|
+
});
|
|
50
|
+
target.on("error", () => socket.destroy());
|
|
51
|
+
socket.on("close", () => target.destroy());
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return net.createServer(handleClient);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function startSocks5(listenAll) {
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
const server = createSocks5Server();
|
|
61
|
+
server.listen(0, listenAll ? "0.0.0.0" : "127.0.0.1", () => {
|
|
62
|
+
resolve({ server, port: server.address().port });
|
|
63
|
+
});
|
|
64
|
+
server.on("error", reject);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ─── WebSocket tunnel client ─────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
function startWsTunnel(baseUrl, authToken, localSocksPort) {
|
|
71
|
+
return new Promise((resolve, reject) => {
|
|
72
|
+
if (!globalThis.WebSocket) {
|
|
73
|
+
reject(new Error("Node.js 22+ is required for the home proxy tunnel. Please upgrade Node."));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const wsUrl = baseUrl.replace(/^http/, "ws") + "/proxy/tunnel";
|
|
77
|
+
const ws = new globalThis.WebSocket(wsUrl);
|
|
78
|
+
const sockets = new Map();
|
|
79
|
+
let done = false;
|
|
80
|
+
|
|
81
|
+
const fail = (reason) => {
|
|
82
|
+
if (!done) { done = true; reject(new Error(reason)); }
|
|
83
|
+
try { ws.close(); } catch {}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
ws.onopen = () => {
|
|
87
|
+
try { ws.send(JSON.stringify({ type: "auth", token: authToken })); }
|
|
88
|
+
catch (e) { fail(`Auth failed: ${e.message}`); }
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
ws.onmessage = (event) => {
|
|
92
|
+
try {
|
|
93
|
+
const msg = JSON.parse(event.data);
|
|
94
|
+
if (msg.type === "ready") {
|
|
95
|
+
if (!done) { done = true; resolve({ remotePort: msg.port, ws, sockets }); }
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (msg.type === "open") {
|
|
99
|
+
const sock = net.connect(localSocksPort, "127.0.0.1");
|
|
100
|
+
sockets.set(msg.id, sock);
|
|
101
|
+
sock.on("data", (d) => {
|
|
102
|
+
try { if (ws.readyState === 1) ws.send(JSON.stringify({ type: "data", id: msg.id, data: d.toString("base64") })); }
|
|
103
|
+
catch {}
|
|
104
|
+
});
|
|
105
|
+
sock.on("close", () => {
|
|
106
|
+
sockets.delete(msg.id);
|
|
107
|
+
try { if (ws.readyState === 1) ws.send(JSON.stringify({ type: "close", id: msg.id })); } catch {}
|
|
108
|
+
});
|
|
109
|
+
sock.on("error", () => sock.destroy());
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (msg.type === "data") { sockets.get(msg.id)?.write(Buffer.from(msg.data, "base64")); return; }
|
|
113
|
+
if (msg.type === "close") { sockets.get(msg.id)?.destroy(); sockets.delete(msg.id); }
|
|
114
|
+
} catch {}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
ws.onerror = (e) => fail(`WebSocket error: ${e?.message ?? "unknown"}`);
|
|
118
|
+
ws.onclose = (e) => {
|
|
119
|
+
sockets.forEach(s => s.destroy()); sockets.clear();
|
|
120
|
+
fail(`Tunnel closed (code=${e?.code ?? "?"}, reason=${e?.reason || "unknown"})`);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
setTimeout(() => fail("Tunnel setup timed out (15s)"), 15000);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ─── Proxy daemon ────────────────────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
function readProxyState() {
|
|
130
|
+
try {
|
|
131
|
+
const data = JSON.parse(fs.readFileSync(PROXY_FILE, "utf8"));
|
|
132
|
+
try { process.kill(data.pid, 0); return { active: true, ...data }; }
|
|
133
|
+
catch { try { fs.unlinkSync(PROXY_FILE); } catch {} return { active: false }; }
|
|
134
|
+
} catch { return { active: false }; }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function runProxyDaemon() {
|
|
138
|
+
const server = getServer();
|
|
139
|
+
const isLocal = server.includes("localhost") || server.includes("127.0.0.1");
|
|
140
|
+
|
|
141
|
+
let socks, tunnel;
|
|
142
|
+
const cleanup = async () => {
|
|
143
|
+
try { socks?.server.close(); } catch {}
|
|
144
|
+
try { tunnel?.ws.close(); } catch {}
|
|
145
|
+
try {
|
|
146
|
+
const ac = new AbortController();
|
|
147
|
+
setTimeout(() => ac.abort(), 3000);
|
|
148
|
+
await fetch(`${server}/proxy`, {
|
|
149
|
+
method: "POST", headers: { "Content-Type": "application/json" },
|
|
150
|
+
body: JSON.stringify({ server: null }),
|
|
151
|
+
signal: ac.signal,
|
|
152
|
+
});
|
|
153
|
+
} catch {}
|
|
154
|
+
try { fs.unlinkSync(PROXY_FILE); } catch {}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
socks = await startSocks5(true); // listen on 0.0.0.0 so Docker can reach it
|
|
159
|
+
|
|
160
|
+
let proxyUrl;
|
|
161
|
+
if (isLocal) {
|
|
162
|
+
proxyUrl = `socks5://host.docker.internal:${socks.port}`;
|
|
163
|
+
} else {
|
|
164
|
+
const token = loadToken();
|
|
165
|
+
tunnel = await startWsTunnel(server, token, socks.port);
|
|
166
|
+
proxyUrl = `socks5://127.0.0.1:${tunnel.remotePort}`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
await fetch(`${server}/proxy`, {
|
|
170
|
+
method: "POST", headers: { "Content-Type": "application/json" },
|
|
171
|
+
body: JSON.stringify({ server: proxyUrl }),
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
175
|
+
fs.writeFileSync(PROXY_FILE, JSON.stringify({ pid: process.pid, proxyUrl, startedAt: new Date().toISOString() }, null, 2));
|
|
176
|
+
|
|
177
|
+
await new Promise((resolve) => {
|
|
178
|
+
process.once("SIGTERM", resolve);
|
|
179
|
+
process.once("SIGINT", resolve);
|
|
180
|
+
});
|
|
181
|
+
} finally {
|
|
182
|
+
await cleanup();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
14
186
|
function getServer() {
|
|
15
187
|
return process.env.IFRAMER_URL || DEFAULT_SERVER;
|
|
16
188
|
}
|
|
@@ -518,23 +690,51 @@ async function main() {
|
|
|
518
690
|
|
|
519
691
|
case "proxy": {
|
|
520
692
|
const action = args[0];
|
|
693
|
+
const isDaemon = args.includes("--daemon");
|
|
694
|
+
const isForeground = args.includes("--foreground");
|
|
695
|
+
|
|
521
696
|
if (!action || !["start", "stop", "status"].includes(action)) {
|
|
522
697
|
console.log(" Usage: iframer proxy <start|stop|status>");
|
|
698
|
+
console.log(" start Route browser traffic through your home IP (runs in foreground)");
|
|
699
|
+
console.log(" stop Stop the home proxy");
|
|
700
|
+
console.log(" status Check proxy status");
|
|
523
701
|
break;
|
|
524
702
|
}
|
|
703
|
+
|
|
525
704
|
if (action === "status") {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
|
|
705
|
+
console.log(JSON.stringify(readProxyState(), null, 2));
|
|
706
|
+
break;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
if (action === "stop") {
|
|
710
|
+
const state = readProxyState();
|
|
711
|
+
if (!state.active) { console.log(JSON.stringify({ ok: true, message: "No proxy running" })); break; }
|
|
712
|
+
try { process.kill(state.pid, "SIGTERM"); } catch {}
|
|
713
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
714
|
+
console.log(JSON.stringify({ ok: true, message: "Proxy stopped" }));
|
|
715
|
+
break;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
if (action === "start") {
|
|
719
|
+
if (isDaemon) {
|
|
720
|
+
// Fork self with --foreground, detach, wait for proxy.json
|
|
721
|
+
const child = spawn(process.execPath, [__filename, "proxy", "start", "--foreground"], {
|
|
722
|
+
detached: true, stdio: "ignore", env: process.env,
|
|
723
|
+
});
|
|
724
|
+
child.unref();
|
|
725
|
+
const deadline = Date.now() + 20000;
|
|
726
|
+
while (Date.now() < deadline) {
|
|
727
|
+
await new Promise(r => setTimeout(r, 500));
|
|
728
|
+
const state = readProxyState();
|
|
729
|
+
if (state.active) { console.log(JSON.stringify({ ok: true, ...state })); process.exit(0); }
|
|
730
|
+
}
|
|
731
|
+
console.error(JSON.stringify({ ok: false, error: "Proxy daemon failed to start within 20s" }));
|
|
732
|
+
process.exit(1);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// Foreground mode (both --foreground and plain start)
|
|
736
|
+
await runProxyDaemon();
|
|
737
|
+
break;
|
|
538
738
|
}
|
|
539
739
|
break;
|
|
540
740
|
}
|
package/mcp-server.cjs
CHANGED
|
@@ -28388,171 +28388,43 @@ class StdioServerTransport {
|
|
|
28388
28388
|
|
|
28389
28389
|
// src/mcp/server.ts
|
|
28390
28390
|
import { readFileSync } from "fs";
|
|
28391
|
-
import { join } from "path";
|
|
28391
|
+
import { join, dirname } from "path";
|
|
28392
|
+
import { fileURLToPath } from "url";
|
|
28392
28393
|
import { homedir } from "os";
|
|
28393
|
-
|
|
28394
|
-
|
|
28395
|
-
|
|
28396
|
-
var
|
|
28397
|
-
|
|
28398
|
-
|
|
28399
|
-
|
|
28400
|
-
|
|
28401
|
-
|
|
28402
|
-
|
|
28394
|
+
import { execFile } from "child_process";
|
|
28395
|
+
var BASE_URL = process.env.IFRAMER_URL || "https://api.iframer.sh";
|
|
28396
|
+
var CREDENTIALS_PATH = join(homedir(), ".iframer", "credentials.json");
|
|
28397
|
+
var PROXY_FILE = join(homedir(), ".iframer", "proxy.json");
|
|
28398
|
+
var _thisFile = fileURLToPath(import.meta.url);
|
|
28399
|
+
var CLI_PATH = _thisFile.endsWith(".cjs") ? join(dirname(_thisFile), "cli.js") : join(dirname(_thisFile), "../../bin/cli.js");
|
|
28400
|
+
function readProxyState() {
|
|
28401
|
+
try {
|
|
28402
|
+
const data = JSON.parse(readFileSync(PROXY_FILE, "utf8"));
|
|
28403
|
+
try {
|
|
28404
|
+
process.kill(data.pid, 0);
|
|
28405
|
+
return { active: true, ...data };
|
|
28406
|
+
} catch {
|
|
28407
|
+
return { active: false };
|
|
28403
28408
|
}
|
|
28404
|
-
|
|
28405
|
-
|
|
28406
|
-
|
|
28407
|
-
|
|
28409
|
+
} catch {
|
|
28410
|
+
return { active: false };
|
|
28411
|
+
}
|
|
28412
|
+
}
|
|
28413
|
+
function spawnCliProxy(cliArgs) {
|
|
28414
|
+
return new Promise((resolve) => {
|
|
28415
|
+
execFile(process.execPath, [CLI_PATH, ...cliArgs], { timeout: 25000 }, (err, stdout, stderr) => {
|
|
28416
|
+
if (err) {
|
|
28417
|
+
resolve({ ok: false, error: stderr || err.message });
|
|
28408
28418
|
return;
|
|
28409
28419
|
}
|
|
28410
|
-
|
|
28411
|
-
|
|
28412
|
-
|
|
28413
|
-
|
|
28414
|
-
host = `${req[4]}.${req[5]}.${req[6]}.${req[7]}`;
|
|
28415
|
-
port = req.readUInt16BE(8);
|
|
28416
|
-
} else if (addrType === 3) {
|
|
28417
|
-
const len = req[4];
|
|
28418
|
-
host = req.slice(5, 5 + len).toString();
|
|
28419
|
-
port = req.readUInt16BE(5 + len);
|
|
28420
|
-
} else if (addrType === 4) {
|
|
28421
|
-
const parts = [];
|
|
28422
|
-
for (let i = 0;i < 16; i += 2)
|
|
28423
|
-
parts.push(req.readUInt16BE(4 + i).toString(16));
|
|
28424
|
-
host = parts.join(":");
|
|
28425
|
-
port = req.readUInt16BE(20);
|
|
28426
|
-
} else {
|
|
28427
|
-
socket.destroy();
|
|
28428
|
-
return;
|
|
28420
|
+
try {
|
|
28421
|
+
resolve({ ok: true, ...JSON.parse(stdout) });
|
|
28422
|
+
} catch {
|
|
28423
|
+
resolve({ ok: false, error: stdout || "no output from proxy daemon" });
|
|
28429
28424
|
}
|
|
28430
|
-
const target = net.connect(port, host, () => {
|
|
28431
|
-
const reply = Buffer.alloc(10);
|
|
28432
|
-
reply[0] = 5;
|
|
28433
|
-
reply[1] = 0;
|
|
28434
|
-
reply[2] = 0;
|
|
28435
|
-
reply[3] = 1;
|
|
28436
|
-
socket.write(reply);
|
|
28437
|
-
socket.pipe(target);
|
|
28438
|
-
target.pipe(socket);
|
|
28439
|
-
});
|
|
28440
|
-
target.on("error", () => socket.destroy());
|
|
28441
|
-
socket.on("close", () => target.destroy());
|
|
28442
|
-
});
|
|
28443
|
-
});
|
|
28444
|
-
}
|
|
28445
|
-
function startHomeProxy(preferredPort = 0) {
|
|
28446
|
-
return new Promise((resolve, reject) => {
|
|
28447
|
-
if (state.active && state.server) {
|
|
28448
|
-
resolve(state.port);
|
|
28449
|
-
return;
|
|
28450
|
-
}
|
|
28451
|
-
const server = net.createServer(handleSocks5);
|
|
28452
|
-
server.listen(preferredPort, "0.0.0.0", () => {
|
|
28453
|
-
state.server = server;
|
|
28454
|
-
state.port = server.address().port;
|
|
28455
|
-
state.active = true;
|
|
28456
|
-
resolve(state.port);
|
|
28457
|
-
});
|
|
28458
|
-
server.on("error", reject);
|
|
28459
|
-
});
|
|
28460
|
-
}
|
|
28461
|
-
function stopHomeProxy() {
|
|
28462
|
-
return new Promise((resolve) => {
|
|
28463
|
-
if (!state.server) {
|
|
28464
|
-
resolve();
|
|
28465
|
-
return;
|
|
28466
|
-
}
|
|
28467
|
-
state.server.close(() => {
|
|
28468
|
-
state.server = null;
|
|
28469
|
-
state.active = false;
|
|
28470
|
-
state.port = 0;
|
|
28471
|
-
resolve();
|
|
28472
28425
|
});
|
|
28473
28426
|
});
|
|
28474
28427
|
}
|
|
28475
|
-
function getHomeProxyInfo() {
|
|
28476
|
-
return {
|
|
28477
|
-
active: state.active,
|
|
28478
|
-
port: state.port,
|
|
28479
|
-
proxyUrl: state.active ? `socks5://host.docker.internal:${state.port}` : null
|
|
28480
|
-
};
|
|
28481
|
-
}
|
|
28482
|
-
|
|
28483
|
-
// src/proxy/tunnel-client.ts
|
|
28484
|
-
import net2 from "net";
|
|
28485
|
-
async function startTunnel(apiBaseUrl, authToken) {
|
|
28486
|
-
const localSocksPort = await startHomeProxy();
|
|
28487
|
-
const wsUrl = apiBaseUrl.replace(/^http/, "ws") + "/proxy/tunnel";
|
|
28488
|
-
const ws = new globalThis.WebSocket(wsUrl);
|
|
28489
|
-
return new Promise((resolve, reject) => {
|
|
28490
|
-
const sockets = new Map;
|
|
28491
|
-
let done = false;
|
|
28492
|
-
const fail = (reason) => {
|
|
28493
|
-
if (!done) {
|
|
28494
|
-
done = true;
|
|
28495
|
-
reject(new Error(reason));
|
|
28496
|
-
}
|
|
28497
|
-
ws.close();
|
|
28498
|
-
};
|
|
28499
|
-
const stop = () => {
|
|
28500
|
-
ws.close();
|
|
28501
|
-
sockets.forEach((s) => s.destroy());
|
|
28502
|
-
sockets.clear();
|
|
28503
|
-
};
|
|
28504
|
-
ws.onopen = () => {
|
|
28505
|
-
ws.send(JSON.stringify({ type: "auth", token: authToken }));
|
|
28506
|
-
};
|
|
28507
|
-
ws.onmessage = (event) => {
|
|
28508
|
-
try {
|
|
28509
|
-
const msg = JSON.parse(typeof event.data === "string" ? event.data : event.data.toString());
|
|
28510
|
-
if (msg.type === "ready") {
|
|
28511
|
-
if (!done) {
|
|
28512
|
-
done = true;
|
|
28513
|
-
resolve({ remotePort: msg.port, stop });
|
|
28514
|
-
}
|
|
28515
|
-
return;
|
|
28516
|
-
}
|
|
28517
|
-
if (msg.type === "open") {
|
|
28518
|
-
const socket = net2.connect(localSocksPort, "127.0.0.1");
|
|
28519
|
-
sockets.set(msg.id, socket);
|
|
28520
|
-
socket.on("data", (data) => {
|
|
28521
|
-
if (ws.readyState === 1) {
|
|
28522
|
-
ws.send(JSON.stringify({ type: "data", id: msg.id, data: data.toString("base64") }));
|
|
28523
|
-
}
|
|
28524
|
-
});
|
|
28525
|
-
socket.on("close", () => {
|
|
28526
|
-
sockets.delete(msg.id);
|
|
28527
|
-
if (ws.readyState === 1)
|
|
28528
|
-
ws.send(JSON.stringify({ type: "close", id: msg.id }));
|
|
28529
|
-
});
|
|
28530
|
-
socket.on("error", () => socket.destroy());
|
|
28531
|
-
return;
|
|
28532
|
-
}
|
|
28533
|
-
if (msg.type === "data") {
|
|
28534
|
-
sockets.get(msg.id)?.write(Buffer.from(msg.data, "base64"));
|
|
28535
|
-
return;
|
|
28536
|
-
}
|
|
28537
|
-
if (msg.type === "close") {
|
|
28538
|
-
sockets.get(msg.id)?.destroy();
|
|
28539
|
-
sockets.delete(msg.id);
|
|
28540
|
-
}
|
|
28541
|
-
} catch {}
|
|
28542
|
-
};
|
|
28543
|
-
ws.onerror = (err) => fail(`WebSocket error: ${err?.message ?? "unknown"}`);
|
|
28544
|
-
ws.onclose = () => {
|
|
28545
|
-
sockets.forEach((s) => s.destroy());
|
|
28546
|
-
sockets.clear();
|
|
28547
|
-
};
|
|
28548
|
-
setTimeout(() => fail("Tunnel setup timed out (10s)"), 1e4);
|
|
28549
|
-
});
|
|
28550
|
-
}
|
|
28551
|
-
|
|
28552
|
-
// src/mcp/server.ts
|
|
28553
|
-
var activeTunnel = null;
|
|
28554
|
-
var BASE_URL = process.env.IFRAMER_URL || "https://api.iframer.sh";
|
|
28555
|
-
var CREDENTIALS_PATH = join(homedir(), ".iframer", "credentials.json");
|
|
28556
28428
|
var cachedToken = null;
|
|
28557
28429
|
function loadToken() {
|
|
28558
28430
|
if (cachedToken)
|
|
@@ -28883,49 +28755,41 @@ WHEN TO USE: Try this whenever a site shows a captcha or bot detection wall. The
|
|
|
28883
28755
|
}, async ({ action }) => {
|
|
28884
28756
|
try {
|
|
28885
28757
|
if (action === "status") {
|
|
28886
|
-
const
|
|
28758
|
+
const state = readProxyState();
|
|
28887
28759
|
const apiProxy = await fetch(`${BASE_URL}/proxy`).then((r) => r.json()).catch(() => null);
|
|
28888
28760
|
return { content: [{ type: "text", text: JSON.stringify({
|
|
28889
|
-
|
|
28890
|
-
apiProxy: apiProxy?.proxy ??
|
|
28891
|
-
apiProxySource: apiProxy?.source ??
|
|
28761
|
+
daemon: state,
|
|
28762
|
+
apiProxy: apiProxy?.proxy ?? null,
|
|
28763
|
+
apiProxySource: apiProxy?.source ?? null
|
|
28892
28764
|
}, null, 2) }] };
|
|
28893
28765
|
}
|
|
28894
28766
|
if (action === "start") {
|
|
28895
|
-
const
|
|
28896
|
-
|
|
28897
|
-
|
|
28898
|
-
|
|
28899
|
-
|
|
28900
|
-
}
|
|
28901
|
-
|
|
28902
|
-
|
|
28903
|
-
|
|
28904
|
-
|
|
28905
|
-
|
|
28906
|
-
|
|
28907
|
-
}
|
|
28908
|
-
const res = await fetch(`${BASE_URL}/proxy`, {
|
|
28909
|
-
method: "POST",
|
|
28910
|
-
headers: { "Content-Type": "application/json" },
|
|
28911
|
-
body: JSON.stringify({ server: proxyUrl })
|
|
28912
|
-
}).then((r) => r.json()).catch((e) => ({ ok: false, error: e.message }));
|
|
28913
|
-
if (!res.ok)
|
|
28914
|
-
return err(`Home proxy started but failed to configure API: ${res.error}`);
|
|
28767
|
+
const existing = readProxyState();
|
|
28768
|
+
if (existing.active && existing.proxyUrl) {
|
|
28769
|
+
await apiPost("/proxy", { server: existing.proxyUrl }).catch(() => {});
|
|
28770
|
+
await apiPost("/interactive/stop").catch(() => {});
|
|
28771
|
+
return { content: [{ type: "text", text: "Home proxy already running. Session restarted." }] };
|
|
28772
|
+
}
|
|
28773
|
+
const result = await spawnCliProxy(["proxy", "start", "--daemon"]);
|
|
28774
|
+
if (!result.ok)
|
|
28775
|
+
return err(`Failed to start proxy daemon: ${result.error}`);
|
|
28776
|
+
const state = readProxyState();
|
|
28777
|
+
if (!state.active || !state.proxyUrl)
|
|
28778
|
+
return err("Proxy daemon started but proxy.json not found");
|
|
28779
|
+
await apiPost("/proxy", { server: state.proxyUrl }).catch(() => {});
|
|
28915
28780
|
await apiPost("/interactive/stop").catch(() => {});
|
|
28916
|
-
return { content: [{ type: "text", text: `Home proxy active. Session restarted. Browser traffic now exits through your
|
|
28781
|
+
return { content: [{ type: "text", text: `Home proxy active. Session restarted. Browser traffic now exits through your home IP.` }] };
|
|
28917
28782
|
}
|
|
28918
28783
|
if (action === "stop") {
|
|
28919
|
-
|
|
28920
|
-
|
|
28921
|
-
|
|
28922
|
-
|
|
28923
|
-
|
|
28924
|
-
|
|
28925
|
-
|
|
28926
|
-
|
|
28927
|
-
|
|
28928
|
-
return { content: [{ type: "text", text: "Home proxy stopped. Session restarted. Browser traffic will use the default proxy (IPRoyal) or no proxy." }] };
|
|
28784
|
+
const state = readProxyState();
|
|
28785
|
+
if (state.active && state.pid) {
|
|
28786
|
+
try {
|
|
28787
|
+
process.kill(state.pid, "SIGTERM");
|
|
28788
|
+
} catch {}
|
|
28789
|
+
}
|
|
28790
|
+
await apiPost("/proxy", { server: null }).catch(() => {});
|
|
28791
|
+
apiPost("/interactive/stop").catch(() => {});
|
|
28792
|
+
return { content: [{ type: "text", text: "Home proxy stopped. Session will restart on next use." }] };
|
|
28929
28793
|
}
|
|
28930
28794
|
return err("Unknown action");
|
|
28931
28795
|
} catch (e) {
|