@fengye404/termpilot 0.1.8 → 0.2.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/cli.js +33 -28
- package/docs/getting-started.md +1 -1
- package/docs/tech-selection-2026.md +2 -2
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -849,11 +849,7 @@ function isLocalRelayHost(hostname) {
|
|
|
849
849
|
}
|
|
850
850
|
function normalizeRelayUrl(rawHost, rawPort) {
|
|
851
851
|
const hostInput = rawHost.trim();
|
|
852
|
-
const portInput = rawPort.trim()
|
|
853
|
-
const normalizedPort = Number(portInput);
|
|
854
|
-
if (!Number.isFinite(normalizedPort) || normalizedPort <= 0 || normalizedPort > 65535) {
|
|
855
|
-
throw new Error("\u7AEF\u53E3\u65E0\u6548\uFF0C\u8BF7\u8F93\u5165 1 \u5230 65535 \u4E4B\u95F4\u7684\u6570\u5B57\u3002");
|
|
856
|
-
}
|
|
852
|
+
const portInput = rawPort.trim();
|
|
857
853
|
if (hostInput.includes("://")) {
|
|
858
854
|
const parsed = new URL(hostInput);
|
|
859
855
|
if (parsed.protocol === "http:") {
|
|
@@ -861,8 +857,14 @@ function normalizeRelayUrl(rawHost, rawPort) {
|
|
|
861
857
|
} else if (parsed.protocol === "https:") {
|
|
862
858
|
parsed.protocol = "wss:";
|
|
863
859
|
}
|
|
864
|
-
if (
|
|
865
|
-
|
|
860
|
+
if (portInput) {
|
|
861
|
+
const normalizedPort2 = Number(portInput);
|
|
862
|
+
if (!Number.isFinite(normalizedPort2) || normalizedPort2 <= 0 || normalizedPort2 > 65535) {
|
|
863
|
+
throw new Error("\u7AEF\u53E3\u65E0\u6548\uFF0C\u8BF7\u8F93\u5165 1 \u5230 65535 \u4E4B\u95F4\u7684\u6570\u5B57\u3002");
|
|
864
|
+
}
|
|
865
|
+
parsed.port = String(normalizedPort2);
|
|
866
|
+
} else if (!parsed.port && parsed.protocol === "ws:" && isLocalRelayHost(parsed.hostname)) {
|
|
867
|
+
parsed.port = "8787";
|
|
866
868
|
}
|
|
867
869
|
if (!parsed.pathname || parsed.pathname === "/") {
|
|
868
870
|
parsed.pathname = "/ws";
|
|
@@ -872,6 +874,16 @@ function normalizeRelayUrl(rawHost, rawPort) {
|
|
|
872
874
|
return parsed.toString();
|
|
873
875
|
}
|
|
874
876
|
const protocol = isLocalRelayHost(hostInput) ? "ws:" : "wss:";
|
|
877
|
+
if (!portInput) {
|
|
878
|
+
if (protocol === "ws:") {
|
|
879
|
+
return `${protocol}//${hostInput}:8787/ws`;
|
|
880
|
+
}
|
|
881
|
+
return `${protocol}//${hostInput}/ws`;
|
|
882
|
+
}
|
|
883
|
+
const normalizedPort = Number(portInput);
|
|
884
|
+
if (!Number.isFinite(normalizedPort) || normalizedPort <= 0 || normalizedPort > 65535) {
|
|
885
|
+
throw new Error("\u7AEF\u53E3\u65E0\u6548\uFF0C\u8BF7\u8F93\u5165 1 \u5230 65535 \u4E4B\u95F4\u7684\u6570\u5B57\u3002");
|
|
886
|
+
}
|
|
875
887
|
return `${protocol}//${hostInput}:${normalizedPort}/ws`;
|
|
876
888
|
}
|
|
877
889
|
async function promptForAgentConfig(deviceId) {
|
|
@@ -885,7 +897,7 @@ async function promptForAgentConfig(deviceId) {
|
|
|
885
897
|
if (!host) {
|
|
886
898
|
throw new Error("\u672A\u8F93\u5165 relay \u57DF\u540D\u6216 IP\uFF0C\u5DF2\u53D6\u6D88\u3002");
|
|
887
899
|
}
|
|
888
|
-
const port = await rl.question("\u8BF7\u8F93\u5165 relay \u7AEF\u53E3\uFF08\u76F4\u63A5\u56DE\u8F66\u9ED8\u8BA4 8787\uFF09: ");
|
|
900
|
+
const port = await rl.question("\u8BF7\u8F93\u5165 relay \u7AEF\u53E3\uFF08\u53CD\u4EE3\u57DF\u540D\u53EF\u76F4\u63A5\u56DE\u8F66\uFF1B\u672C\u5730 IP \u9ED8\u8BA4 8787\uFF09: ");
|
|
889
901
|
const relayUrl = normalizeRelayUrl(host, port);
|
|
890
902
|
console.log(`\u5C06\u4F7F\u7528 relay: ${relayUrl}`);
|
|
891
903
|
return { relayUrl, deviceId };
|
|
@@ -1324,7 +1336,8 @@ function loadRelayRuntime() {
|
|
|
1324
1336
|
pid: parsed.pid,
|
|
1325
1337
|
host: parsed.host,
|
|
1326
1338
|
port: parsed.port,
|
|
1327
|
-
startedAt: parsed.startedAt
|
|
1339
|
+
startedAt: parsed.startedAt,
|
|
1340
|
+
cliPath: typeof parsed.cliPath === "string" ? parsed.cliPath : void 0
|
|
1328
1341
|
};
|
|
1329
1342
|
} catch {
|
|
1330
1343
|
return null;
|
|
@@ -2344,26 +2357,14 @@ function readRuntimeStatus2() {
|
|
|
2344
2357
|
}
|
|
2345
2358
|
return { runtime, alive };
|
|
2346
2359
|
}
|
|
2347
|
-
function printRuntime(runtime = readRuntimeStatus2().runtime) {
|
|
2348
|
-
if (!runtime) {
|
|
2349
|
-
console.log("\u540E\u53F0 relay \u5F53\u524D\u672A\u8FD0\u884C\u3002");
|
|
2350
|
-
console.log(`\u8FD0\u884C\u65F6\u6587\u4EF6: ${getRelayRuntimeFilePath()}`);
|
|
2351
|
-
console.log(`\u65E5\u5FD7: ${getRelayLogFilePath()}`);
|
|
2352
|
-
return;
|
|
2353
|
-
}
|
|
2354
|
-
console.log("\u540E\u53F0 relay \u6B63\u5728\u8FD0\u884C\u3002");
|
|
2355
|
-
console.log(`PID: ${runtime.pid}`);
|
|
2356
|
-
console.log(`\u76D1\u542C: http://${runtime.host}:${runtime.port}`);
|
|
2357
|
-
console.log(`\u542F\u52A8\u65F6\u95F4: ${runtime.startedAt}`);
|
|
2358
|
-
console.log(`\u65E5\u5FD7: ${getRelayLogFilePath()}`);
|
|
2359
|
-
}
|
|
2360
2360
|
async function runForeground() {
|
|
2361
2361
|
const config = loadConfig();
|
|
2362
2362
|
saveRelayRuntime({
|
|
2363
2363
|
pid: process.pid,
|
|
2364
2364
|
host: config.host,
|
|
2365
2365
|
port: config.port,
|
|
2366
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2366
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2367
|
+
cliPath: process.argv[1]
|
|
2367
2368
|
});
|
|
2368
2369
|
process.on("exit", () => {
|
|
2369
2370
|
clearRelayRuntime(process.pid);
|
|
@@ -2377,11 +2378,14 @@ async function runStart2() {
|
|
|
2377
2378
|
const existing = readRuntimeStatus2();
|
|
2378
2379
|
if (existing.runtime && existing.alive) {
|
|
2379
2380
|
const sameConfig = existing.runtime.host === config.host && existing.runtime.port === config.port;
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2381
|
+
const sameCliPath = existing.runtime.cliPath === process.argv[1];
|
|
2382
|
+
if (sameConfig && sameCliPath) {
|
|
2383
|
+
console.log("\u68C0\u6D4B\u5230\u540E\u53F0 relay \u5DF2\u5728\u8FD0\u884C\uFF0C\u6B63\u5728\u91CD\u542F\u5230\u5F53\u524D\u914D\u7F6E\u3002");
|
|
2384
|
+
} else if (!sameCliPath) {
|
|
2385
|
+
console.log("\u68C0\u6D4B\u5230\u540E\u53F0 relay \u6B63\u5728\u8FD0\u884C\uFF0C\u4F46\u5B89\u88C5\u7248\u672C\u6216\u5165\u53E3\u5DF2\u53D8\u5316\uFF0C\u6B63\u5728\u91CD\u542F\u5230\u5F53\u524D\u7248\u672C\u3002");
|
|
2386
|
+
} else {
|
|
2387
|
+
console.log("\u68C0\u6D4B\u5230\u540E\u53F0 relay \u5DF2\u5728\u8FD0\u884C\uFF0C\u4F46\u76D1\u542C\u914D\u7F6E\u548C\u5F53\u524D\u547D\u4EE4\u4E0D\u4E00\u81F4\uFF0C\u6B63\u5728\u91CD\u542F\u3002");
|
|
2383
2388
|
}
|
|
2384
|
-
console.log("\u68C0\u6D4B\u5230\u540E\u53F0 relay \u5DF2\u5728\u8FD0\u884C\uFF0C\u4F46\u76D1\u542C\u914D\u7F6E\u548C\u5F53\u524D\u547D\u4EE4\u4E0D\u4E00\u81F4\uFF0C\u6B63\u5728\u91CD\u542F\u3002");
|
|
2385
2389
|
await runStop2();
|
|
2386
2390
|
}
|
|
2387
2391
|
clearRelayRuntime();
|
|
@@ -2400,7 +2404,8 @@ async function runStart2() {
|
|
|
2400
2404
|
pid: child.pid,
|
|
2401
2405
|
host: config.host,
|
|
2402
2406
|
port: config.port,
|
|
2403
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2407
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2408
|
+
cliPath: process.argv[1]
|
|
2404
2409
|
});
|
|
2405
2410
|
console.log(`\u540E\u53F0 relay \u5DF2\u542F\u52A8\uFF0CPID: ${child.pid}`);
|
|
2406
2411
|
console.log(`\u76D1\u542C: http://${config.host}:${config.port}`);
|
package/docs/getting-started.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
这次技术选型只保留已经拍板的方案,不再保留多余备选。
|
|
6
6
|
|
|
7
7
|
- 语言:TypeScript
|
|
8
|
-
- 运行时:Node.js 24 LTS
|
|
8
|
+
- 运行时:Node.js 22+(推荐 24 LTS)
|
|
9
9
|
- 包管理:pnpm workspace
|
|
10
10
|
|
|
11
11
|
手机端 `app/`:
|
|
@@ -74,4 +74,4 @@ TermPilot 现在要的不是“技术看起来完整”,而是:
|
|
|
74
74
|
|
|
75
75
|
## 一句话总结
|
|
76
76
|
|
|
77
|
-
**TermPilot 当前最合理、最 AI 友好的全栈方案,就是 `TypeScript + Node.js
|
|
77
|
+
**TermPilot 当前最合理、最 AI 友好的全栈方案,就是 `TypeScript + Node.js 22+ + pnpm workspace`,在此基础上,手机端用 `React + Vite + Tailwind CSS + xterm.js`,中继服务用 `Fastify + WebSocket + PostgreSQL`,PC 端围绕 `tmux`。推荐运行在 Node.js 24 LTS 上,但不强制要求。**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fengye404/termpilot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"packageManager": "pnpm@10.31.0",
|
|
6
6
|
"description": "一个基于 tmux 的终端会话跨端查看与控制原型。",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
".env.example"
|
|
16
16
|
],
|
|
17
17
|
"engines": {
|
|
18
|
-
"node": ">=
|
|
18
|
+
"node": ">=22"
|
|
19
19
|
},
|
|
20
20
|
"publishConfig": {
|
|
21
21
|
"access": "public"
|