@fengye404/termpilot 0.2.0 → 0.2.2
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 +93 -17
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -649,7 +649,10 @@ function createDaemonFromEnv() {
|
|
|
649
649
|
}
|
|
650
650
|
|
|
651
651
|
// agent/src/relay-admin.ts
|
|
652
|
-
function
|
|
652
|
+
function isLocalRelayHost(hostname) {
|
|
653
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || /^10\./.test(hostname) || /^192\.168\./.test(hostname) || /^172\.(1[6-9]|2\d|3[0-1])\./.test(hostname);
|
|
654
|
+
}
|
|
655
|
+
function getRelayBaseCandidates() {
|
|
653
656
|
const relayUrl = process.env.TERMPILOT_RELAY_URL ?? "ws://127.0.0.1:8787/ws";
|
|
654
657
|
let url;
|
|
655
658
|
try {
|
|
@@ -657,10 +660,31 @@ function getRelayBaseUrl() {
|
|
|
657
660
|
} catch {
|
|
658
661
|
throw new Error("TERMPILOT_RELAY_URL \u65E0\u6548\uFF0C\u8BF7\u63D0\u4F9B\u5B8C\u6574\u7684 ws:// \u6216 wss:// \u5730\u5740\u3002");
|
|
659
662
|
}
|
|
663
|
+
const wsUrl = new URL(url.toString());
|
|
664
|
+
wsUrl.search = "";
|
|
665
|
+
wsUrl.hash = "";
|
|
666
|
+
if (!wsUrl.pathname || wsUrl.pathname === "/") {
|
|
667
|
+
wsUrl.pathname = "/ws";
|
|
668
|
+
}
|
|
660
669
|
url.protocol = url.protocol === "wss:" ? "https:" : "http:";
|
|
661
670
|
url.pathname = "/";
|
|
662
671
|
url.search = "";
|
|
663
|
-
|
|
672
|
+
url.hash = "";
|
|
673
|
+
const candidates = [{
|
|
674
|
+
baseUrl: url.toString(),
|
|
675
|
+
relayUrl: wsUrl.toString()
|
|
676
|
+
}];
|
|
677
|
+
if (!isLocalRelayHost(url.hostname) && url.port === "8787") {
|
|
678
|
+
const fallbackBase = new URL(url.toString());
|
|
679
|
+
fallbackBase.port = "";
|
|
680
|
+
const fallbackRelay = new URL(wsUrl.toString());
|
|
681
|
+
fallbackRelay.port = "";
|
|
682
|
+
candidates.push({
|
|
683
|
+
baseUrl: fallbackBase.toString(),
|
|
684
|
+
relayUrl: fallbackRelay.toString()
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
return candidates;
|
|
664
688
|
}
|
|
665
689
|
function getAgentToken() {
|
|
666
690
|
return process.env.TERMPILOT_AGENT_TOKEN ?? DEFAULT_AGENT_TOKEN;
|
|
@@ -675,18 +699,30 @@ async function readJsonOrThrow(response, message) {
|
|
|
675
699
|
return response.json();
|
|
676
700
|
}
|
|
677
701
|
async function fetchJson(input, init, message) {
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
const
|
|
683
|
-
|
|
702
|
+
const candidates = getRelayBaseCandidates();
|
|
703
|
+
let lastError = null;
|
|
704
|
+
let lastOrigin = input.origin;
|
|
705
|
+
for (const candidate of candidates) {
|
|
706
|
+
const target = new URL(input.pathname + input.search, candidate.baseUrl);
|
|
707
|
+
lastOrigin = target.origin;
|
|
708
|
+
let response;
|
|
709
|
+
try {
|
|
710
|
+
response = await fetch(target, init);
|
|
711
|
+
} catch (error) {
|
|
712
|
+
lastError = error;
|
|
713
|
+
continue;
|
|
714
|
+
}
|
|
715
|
+
if (process.env.TERMPILOT_RELAY_URL !== candidate.relayUrl) {
|
|
716
|
+
process.env.TERMPILOT_RELAY_URL = candidate.relayUrl;
|
|
717
|
+
}
|
|
718
|
+
return readJsonOrThrow(response, message);
|
|
684
719
|
}
|
|
685
|
-
|
|
720
|
+
const detail = lastError instanceof Error ? lastError.message : "\u672A\u77E5\u7F51\u7EDC\u9519\u8BEF";
|
|
721
|
+
throw new Error(`${message}: \u65E0\u6CD5\u8FDE\u63A5 relay (${lastOrigin})\uFF0C${detail}`);
|
|
686
722
|
}
|
|
687
723
|
async function createPairingCode(deviceId) {
|
|
688
724
|
return fetchJson(
|
|
689
|
-
new URL("/api/pairing-codes",
|
|
725
|
+
new URL("/api/pairing-codes", "https://placeholder.invalid"),
|
|
690
726
|
{
|
|
691
727
|
method: "POST",
|
|
692
728
|
headers: {
|
|
@@ -700,7 +736,7 @@ async function createPairingCode(deviceId) {
|
|
|
700
736
|
}
|
|
701
737
|
async function listDeviceGrants(deviceId) {
|
|
702
738
|
return fetchJson(
|
|
703
|
-
new URL(`/api/devices/${deviceId}/grants`,
|
|
739
|
+
new URL(`/api/devices/${deviceId}/grants`, "https://placeholder.invalid"),
|
|
704
740
|
{
|
|
705
741
|
headers: {
|
|
706
742
|
authorization: `Bearer ${getAgentToken()}`
|
|
@@ -711,7 +747,7 @@ async function listDeviceGrants(deviceId) {
|
|
|
711
747
|
}
|
|
712
748
|
async function revokeDeviceGrant(deviceId, accessToken) {
|
|
713
749
|
await fetchJson(
|
|
714
|
-
new URL(`/api/devices/${deviceId}/grants/${accessToken}`,
|
|
750
|
+
new URL(`/api/devices/${deviceId}/grants/${accessToken}`, "https://placeholder.invalid"),
|
|
715
751
|
{
|
|
716
752
|
method: "DELETE",
|
|
717
753
|
headers: {
|
|
@@ -724,7 +760,7 @@ async function revokeDeviceGrant(deviceId, accessToken) {
|
|
|
724
760
|
async function listAuditEvents(deviceId, limit) {
|
|
725
761
|
const constrainedLimit = Math.max(1, Math.min(limit, 100));
|
|
726
762
|
return fetchJson(
|
|
727
|
-
new URL(`/api/devices/${deviceId}/audit-events?limit=${constrainedLimit}`,
|
|
763
|
+
new URL(`/api/devices/${deviceId}/audit-events?limit=${constrainedLimit}`, "https://placeholder.invalid"),
|
|
728
764
|
{
|
|
729
765
|
headers: {
|
|
730
766
|
authorization: `Bearer ${getAgentToken()}`
|
|
@@ -844,7 +880,7 @@ function getDeviceId(argv) {
|
|
|
844
880
|
const saved = loadAgentConfig();
|
|
845
881
|
return resolveDeviceId(saved?.deviceId);
|
|
846
882
|
}
|
|
847
|
-
function
|
|
883
|
+
function isLocalRelayHost2(hostname) {
|
|
848
884
|
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || /^10\./.test(hostname) || /^192\.168\./.test(hostname) || /^172\.(1[6-9]|2\d|3[0-1])\./.test(hostname);
|
|
849
885
|
}
|
|
850
886
|
function normalizeRelayUrl(rawHost, rawPort) {
|
|
@@ -863,7 +899,7 @@ function normalizeRelayUrl(rawHost, rawPort) {
|
|
|
863
899
|
throw new Error("\u7AEF\u53E3\u65E0\u6548\uFF0C\u8BF7\u8F93\u5165 1 \u5230 65535 \u4E4B\u95F4\u7684\u6570\u5B57\u3002");
|
|
864
900
|
}
|
|
865
901
|
parsed.port = String(normalizedPort2);
|
|
866
|
-
} else if (!parsed.port && parsed.protocol === "ws:" &&
|
|
902
|
+
} else if (!parsed.port && parsed.protocol === "ws:" && isLocalRelayHost2(parsed.hostname)) {
|
|
867
903
|
parsed.port = "8787";
|
|
868
904
|
}
|
|
869
905
|
if (!parsed.pathname || parsed.pathname === "/") {
|
|
@@ -873,7 +909,7 @@ function normalizeRelayUrl(rawHost, rawPort) {
|
|
|
873
909
|
parsed.hash = "";
|
|
874
910
|
return parsed.toString();
|
|
875
911
|
}
|
|
876
|
-
const protocol =
|
|
912
|
+
const protocol = isLocalRelayHost2(hostInput) ? "ws:" : "wss:";
|
|
877
913
|
if (!portInput) {
|
|
878
914
|
if (protocol === "ws:") {
|
|
879
915
|
return `${protocol}//${hostInput}:8787/ws`;
|
|
@@ -956,6 +992,31 @@ function applyAgentConfig(config) {
|
|
|
956
992
|
process.env.TERMPILOT_RELAY_URL = config.relayUrl;
|
|
957
993
|
process.env.TERMPILOT_DEVICE_ID = config.deviceId;
|
|
958
994
|
}
|
|
995
|
+
function persistMigratedRelayUrl(deviceId) {
|
|
996
|
+
const migratedRelayUrl = process.env.TERMPILOT_RELAY_URL?.trim();
|
|
997
|
+
if (!migratedRelayUrl) {
|
|
998
|
+
return false;
|
|
999
|
+
}
|
|
1000
|
+
let changed = false;
|
|
1001
|
+
const saved = loadAgentConfig();
|
|
1002
|
+
if (saved && saved.deviceId === deviceId && saved.relayUrl !== migratedRelayUrl) {
|
|
1003
|
+
saveAgentConfig({
|
|
1004
|
+
relayUrl: migratedRelayUrl,
|
|
1005
|
+
deviceId
|
|
1006
|
+
});
|
|
1007
|
+
console.log(`\u5DF2\u81EA\u52A8\u66F4\u65B0 relay \u5730\u5740\u4E3A: ${migratedRelayUrl}`);
|
|
1008
|
+
changed = true;
|
|
1009
|
+
}
|
|
1010
|
+
const runtime = loadAgentRuntime();
|
|
1011
|
+
if (runtime && runtime.deviceId === deviceId && runtime.relayUrl !== migratedRelayUrl) {
|
|
1012
|
+
saveAgentRuntime({
|
|
1013
|
+
...runtime,
|
|
1014
|
+
relayUrl: migratedRelayUrl
|
|
1015
|
+
});
|
|
1016
|
+
changed = true;
|
|
1017
|
+
}
|
|
1018
|
+
return changed;
|
|
1019
|
+
}
|
|
959
1020
|
function printRuntimeStatus(runtime = readRuntimeStatus().runtime) {
|
|
960
1021
|
if (!runtime) {
|
|
961
1022
|
console.log("\u540E\u53F0 agent \u5F53\u524D\u672A\u8FD0\u884C\u3002");
|
|
@@ -998,9 +1059,12 @@ async function waitForPairingCode(deviceId) {
|
|
|
998
1059
|
let lastError = null;
|
|
999
1060
|
for (let attempt = 0; attempt < 12; attempt += 1) {
|
|
1000
1061
|
try {
|
|
1001
|
-
|
|
1062
|
+
const payload = await createPairingCode(deviceId);
|
|
1063
|
+
persistMigratedRelayUrl(deviceId);
|
|
1064
|
+
return payload;
|
|
1002
1065
|
} catch (error) {
|
|
1003
1066
|
lastError = error;
|
|
1067
|
+
persistMigratedRelayUrl(deviceId);
|
|
1004
1068
|
await delay2(500);
|
|
1005
1069
|
}
|
|
1006
1070
|
}
|
|
@@ -1036,6 +1100,14 @@ async function runStart(argv) {
|
|
|
1036
1100
|
if (pairing) {
|
|
1037
1101
|
console.log(`\u914D\u5BF9\u7801: ${pairing.pairingCode}`);
|
|
1038
1102
|
console.log(`\u6709\u6548\u671F\u81F3: ${pairing.expiresAt}`);
|
|
1103
|
+
} else {
|
|
1104
|
+
const migrated = loadAgentConfig();
|
|
1105
|
+
if (migrated && migrated.deviceId === deviceId && migrated.relayUrl !== existing.runtime.relayUrl) {
|
|
1106
|
+
console.log("\u68C0\u6D4B\u5230\u540E\u53F0 agent \u4ECD\u5728\u4F7F\u7528\u65E7 relay \u5730\u5740\uFF0C\u6B63\u5728\u81EA\u52A8\u91CD\u542F\u540E\u91CD\u8BD5\u3002");
|
|
1107
|
+
await runStop();
|
|
1108
|
+
await runStart(argv);
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1039
1111
|
}
|
|
1040
1112
|
} else {
|
|
1041
1113
|
console.log("\u5982\u9700\u91CD\u65B0\u7ED9\u624B\u673A\u914D\u5BF9\uFF0C\u8BF7\u6267\u884C\uFF1Atermpilot agent --pair");
|
|
@@ -1171,6 +1243,7 @@ async function runPair(argv) {
|
|
|
1171
1243
|
applyAgentConfig(config.config);
|
|
1172
1244
|
const deviceId = getDeviceId(argv);
|
|
1173
1245
|
const payload = await createPairingCode(deviceId);
|
|
1246
|
+
persistMigratedRelayUrl(deviceId);
|
|
1174
1247
|
console.log(`\u8BBE\u5907: ${payload.deviceId}`);
|
|
1175
1248
|
console.log(`\u914D\u5BF9\u7801: ${payload.pairingCode}`);
|
|
1176
1249
|
console.log(`\u6709\u6548\u671F\u81F3: ${payload.expiresAt}`);
|
|
@@ -1181,6 +1254,7 @@ async function runGrants(argv) {
|
|
|
1181
1254
|
applyAgentConfig(config.config);
|
|
1182
1255
|
const deviceId = getDeviceId(argv);
|
|
1183
1256
|
const payload = await listDeviceGrants(deviceId);
|
|
1257
|
+
persistMigratedRelayUrl(deviceId);
|
|
1184
1258
|
if (payload.grants.length === 0) {
|
|
1185
1259
|
console.log(`\u8BBE\u5907 ${payload.deviceId} \u5F53\u524D\u6CA1\u6709\u4EFB\u4F55\u5DF2\u7ED1\u5B9A\u8BBF\u95EE\u4EE4\u724C\u3002`);
|
|
1186
1260
|
return;
|
|
@@ -1203,6 +1277,7 @@ async function runRevoke(argv) {
|
|
|
1203
1277
|
applyAgentConfig(config.config);
|
|
1204
1278
|
const deviceId = getDeviceId(argv);
|
|
1205
1279
|
await revokeDeviceGrant(deviceId, accessToken);
|
|
1280
|
+
persistMigratedRelayUrl(deviceId);
|
|
1206
1281
|
console.log(`\u5DF2\u64A4\u9500\u8BBE\u5907 ${deviceId} \u7684\u8BBF\u95EE\u4EE4\u724C ${accessToken}`);
|
|
1207
1282
|
}
|
|
1208
1283
|
async function runAudit(argv) {
|
|
@@ -1216,6 +1291,7 @@ async function runAudit(argv) {
|
|
|
1216
1291
|
}
|
|
1217
1292
|
const limit = Math.floor(parsedLimit);
|
|
1218
1293
|
const payload = await listAuditEvents(deviceId, limit);
|
|
1294
|
+
persistMigratedRelayUrl(deviceId);
|
|
1219
1295
|
if (payload.events.length === 0) {
|
|
1220
1296
|
console.log(`\u8BBE\u5907 ${payload.deviceId} \u5F53\u524D\u6CA1\u6709\u5BA1\u8BA1\u65E5\u5FD7\u3002`);
|
|
1221
1297
|
return;
|