tmex-cli 0.3.1 → 0.4.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/runtime/server.js +445 -219
- package/package.json +1 -1
- package/resources/fe-dist/assets/DevicePage-iSkEDEpS.js +4570 -0
- package/resources/fe-dist/assets/DevicePage-iSkEDEpS.js.map +1 -0
- package/resources/fe-dist/assets/DevicesPage-CtNzaW_c.js +2143 -0
- package/resources/fe-dist/assets/{DevicesPage-C76Xejy5.js.map → DevicesPage-CtNzaW_c.js.map} +1 -1
- package/resources/fe-dist/assets/SettingsPage-D25_d6j9.js +1144 -0
- package/resources/fe-dist/assets/{SettingsPage-DQ9W4fOo.js.map → SettingsPage-D25_d6j9.js.map} +1 -1
- package/resources/fe-dist/assets/index-CJaX5rlK.css +4527 -0
- package/resources/fe-dist/assets/index-dsVN7rgz.js +200 -0
- package/resources/fe-dist/assets/index-dsVN7rgz.js.map +1 -0
- package/resources/fe-dist/assets/select-BNsiC9zT.js +2805 -0
- package/resources/fe-dist/assets/{select-Wn7lKWHQ.js.map → select-BNsiC9zT.js.map} +1 -1
- package/resources/fe-dist/assets/switch-CIU4AisU.js +234 -0
- package/resources/fe-dist/assets/{switch-JVIhfemP.js.map → switch-CIU4AisU.js.map} +1 -1
- package/resources/fe-dist/assets/useValueChanged-V23H0VpC.js +351 -0
- package/resources/fe-dist/assets/{useValueChanged-DU---PIl.js.map → useValueChanged-V23H0VpC.js.map} +1 -1
- package/resources/fe-dist/index.html +2 -2
- package/resources/gateway-drizzle/meta/0000_snapshot.json +6 -17
- package/resources/gateway-drizzle/meta/0001_snapshot.json +6 -17
- package/resources/gateway-drizzle/meta/_journal.json +1 -1
- package/resources/fe-dist/assets/DevicePage-BTbDSWYN.js +0 -26
- package/resources/fe-dist/assets/DevicePage-BTbDSWYN.js.map +0 -1
- package/resources/fe-dist/assets/DevicesPage-C76Xejy5.js +0 -17
- package/resources/fe-dist/assets/SettingsPage-DQ9W4fOo.js +0 -17
- package/resources/fe-dist/assets/index-Bmahx5fj.js +0 -448
- package/resources/fe-dist/assets/index-Bmahx5fj.js.map +0 -1
- package/resources/fe-dist/assets/index-CyKyNcdz.css +0 -1
- package/resources/fe-dist/assets/select-Wn7lKWHQ.js +0 -17
- package/resources/fe-dist/assets/switch-JVIhfemP.js +0 -12
- package/resources/fe-dist/assets/useValueChanged-DU---PIl.js +0 -7
package/dist/runtime/server.js
CHANGED
|
@@ -20281,8 +20281,8 @@ var require_lib3 = __commonJS((exports, module) => {
|
|
|
20281
20281
|
});
|
|
20282
20282
|
|
|
20283
20283
|
// src/runtime/server.ts
|
|
20284
|
-
import { existsSync as
|
|
20285
|
-
import { extname, join as
|
|
20284
|
+
import { existsSync as existsSync4 } from "fs";
|
|
20285
|
+
import { extname, join as join5, normalize, resolve as resolve2, sep } from "path";
|
|
20286
20286
|
|
|
20287
20287
|
// ../../apps/gateway/src/crypto/errors.ts
|
|
20288
20288
|
function contextLabel(context) {
|
|
@@ -52310,7 +52310,7 @@ class LocalExternalTmuxConnection {
|
|
|
52310
52310
|
if (!this.connected) {
|
|
52311
52311
|
return;
|
|
52312
52312
|
}
|
|
52313
|
-
const argv = name ? ["new-window", "-n", name] : ["new-window"];
|
|
52313
|
+
const argv = name ? ["new-window", "-t", this.sessionName, "-n", name] : ["new-window", "-t", this.sessionName];
|
|
52314
52314
|
this.runAndRefresh(argv).catch((error) => {
|
|
52315
52315
|
this.callbacks.onError(error);
|
|
52316
52316
|
});
|
|
@@ -52479,8 +52479,8 @@ class LocalExternalTmuxConnection {
|
|
|
52479
52479
|
async capturePaneHistory(paneId) {
|
|
52480
52480
|
const mode = (await this.runTmux(["display-message", "-p", "-t", paneId, "#{alternate_on}"], true)).stdout.trim();
|
|
52481
52481
|
const alternateScreen = mode === "1";
|
|
52482
|
-
const normal = (await this.runTmux(["capture-pane", "-t", paneId, "-S", "-", "-E", "-", "-e", "-p"], true)).stdout;
|
|
52483
|
-
const alternate = (await this.runTmux(["capture-pane", "-t", paneId, "-a", "-S", "-", "-E", "-", "-e", "-p", "-q"], true)).stdout;
|
|
52482
|
+
const normal = (await this.runTmux(["capture-pane", "-t", paneId, "-S", "-", "-E", "-", "-e", "-N", "-p"], true)).stdout;
|
|
52483
|
+
const alternate = (await this.runTmux(["capture-pane", "-t", paneId, "-a", "-S", "-", "-E", "-", "-e", "-N", "-p", "-q"], true)).stdout;
|
|
52484
52484
|
const history = alternateScreen ? hasRenderableTerminalContent(normal) ? normal : alternate : normal || alternate;
|
|
52485
52485
|
if (history) {
|
|
52486
52486
|
this.callbacks.onTerminalHistory(paneId, history, alternateScreen);
|
|
@@ -52733,6 +52733,10 @@ class LocalExternalTmuxConnection {
|
|
|
52733
52733
|
// ../../apps/gateway/src/tmux-client/ssh-external-connection.ts
|
|
52734
52734
|
var import_ssh2 = __toESM(require_lib3(), 1);
|
|
52735
52735
|
|
|
52736
|
+
// ../../apps/gateway/src/tmux-client/ssh-connect-config.ts
|
|
52737
|
+
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
52738
|
+
import { join as join4 } from "path";
|
|
52739
|
+
|
|
52736
52740
|
// ../../apps/gateway/src/tmux/ssh-auth.ts
|
|
52737
52741
|
function normalizeEnvValue(value) {
|
|
52738
52742
|
const trimmed = value?.trim();
|
|
@@ -52765,6 +52769,219 @@ function resolveSshAgentSocket(authMode, env = process.env) {
|
|
|
52765
52769
|
return;
|
|
52766
52770
|
}
|
|
52767
52771
|
|
|
52772
|
+
// ../../apps/gateway/src/tmux-client/ssh-connect-config.ts
|
|
52773
|
+
function defaultRunSync2(cmd) {
|
|
52774
|
+
const result = Bun.spawnSync(cmd, {
|
|
52775
|
+
env: process.env,
|
|
52776
|
+
stdout: "pipe",
|
|
52777
|
+
stderr: "pipe"
|
|
52778
|
+
});
|
|
52779
|
+
return {
|
|
52780
|
+
exitCode: result.exitCode,
|
|
52781
|
+
stdout: Buffer.from(result.stdout).toString("utf8"),
|
|
52782
|
+
stderr: Buffer.from(result.stderr).toString("utf8")
|
|
52783
|
+
};
|
|
52784
|
+
}
|
|
52785
|
+
function expandHomePath(value, env) {
|
|
52786
|
+
const trimmed = value.trim();
|
|
52787
|
+
if (trimmed === "~") {
|
|
52788
|
+
return env.HOME?.trim() || trimmed;
|
|
52789
|
+
}
|
|
52790
|
+
if (trimmed.startsWith("~/") && env.HOME?.trim()) {
|
|
52791
|
+
return join4(env.HOME.trim(), trimmed.slice(2));
|
|
52792
|
+
}
|
|
52793
|
+
return trimmed;
|
|
52794
|
+
}
|
|
52795
|
+
function parseSshConfigOutput(stdout, env) {
|
|
52796
|
+
let host = "";
|
|
52797
|
+
let port;
|
|
52798
|
+
let username;
|
|
52799
|
+
let identityAgent;
|
|
52800
|
+
const identityFiles = [];
|
|
52801
|
+
for (const rawLine of stdout.split(/\r?\n/)) {
|
|
52802
|
+
const line = rawLine.trim();
|
|
52803
|
+
if (!line) {
|
|
52804
|
+
continue;
|
|
52805
|
+
}
|
|
52806
|
+
const firstSpace = line.indexOf(" ");
|
|
52807
|
+
if (firstSpace <= 0) {
|
|
52808
|
+
continue;
|
|
52809
|
+
}
|
|
52810
|
+
const key = line.slice(0, firstSpace).trim().toLowerCase();
|
|
52811
|
+
const value = line.slice(firstSpace + 1).trim();
|
|
52812
|
+
if (!value) {
|
|
52813
|
+
continue;
|
|
52814
|
+
}
|
|
52815
|
+
switch (key) {
|
|
52816
|
+
case "hostname":
|
|
52817
|
+
host = value;
|
|
52818
|
+
break;
|
|
52819
|
+
case "port": {
|
|
52820
|
+
const parsedPort = Number.parseInt(value, 10);
|
|
52821
|
+
port = Number.isNaN(parsedPort) ? undefined : parsedPort;
|
|
52822
|
+
break;
|
|
52823
|
+
}
|
|
52824
|
+
case "user":
|
|
52825
|
+
username = value;
|
|
52826
|
+
break;
|
|
52827
|
+
case "identityagent":
|
|
52828
|
+
identityAgent = value;
|
|
52829
|
+
break;
|
|
52830
|
+
case "identityfile":
|
|
52831
|
+
identityFiles.push(expandHomePath(value, env));
|
|
52832
|
+
break;
|
|
52833
|
+
}
|
|
52834
|
+
}
|
|
52835
|
+
if (!host) {
|
|
52836
|
+
throw new Error("ssh_config_ref_invalid: SSH Config \u5F15\u7528\u672A\u89E3\u6790\u5230 hostname");
|
|
52837
|
+
}
|
|
52838
|
+
return {
|
|
52839
|
+
host,
|
|
52840
|
+
port,
|
|
52841
|
+
username,
|
|
52842
|
+
identityAgent,
|
|
52843
|
+
identityFiles
|
|
52844
|
+
};
|
|
52845
|
+
}
|
|
52846
|
+
function toSshAuthEnv(env) {
|
|
52847
|
+
return {
|
|
52848
|
+
SSH_AUTH_SOCK: env.SSH_AUTH_SOCK,
|
|
52849
|
+
USER: env.USER,
|
|
52850
|
+
LOGNAME: env.LOGNAME
|
|
52851
|
+
};
|
|
52852
|
+
}
|
|
52853
|
+
function resolveAgentFromConfig(identityAgent, deps) {
|
|
52854
|
+
const trimmed = identityAgent?.trim();
|
|
52855
|
+
if (!trimmed || trimmed.toLowerCase() === "none") {
|
|
52856
|
+
return;
|
|
52857
|
+
}
|
|
52858
|
+
if (trimmed === "SSH_AUTH_SOCK" || trimmed === "$SSH_AUTH_SOCK") {
|
|
52859
|
+
return resolveSshAgentSocket("auto", toSshAuthEnv(deps.env));
|
|
52860
|
+
}
|
|
52861
|
+
const expanded = expandHomePath(trimmed, deps.env);
|
|
52862
|
+
return deps.fileExists(expanded) ? expanded : undefined;
|
|
52863
|
+
}
|
|
52864
|
+
function resolvePrivateKeyFromConfig(identityFiles, deps) {
|
|
52865
|
+
for (const identityFile of identityFiles) {
|
|
52866
|
+
if (!deps.fileExists(identityFile)) {
|
|
52867
|
+
continue;
|
|
52868
|
+
}
|
|
52869
|
+
return deps.readTextFile(identityFile);
|
|
52870
|
+
}
|
|
52871
|
+
return;
|
|
52872
|
+
}
|
|
52873
|
+
function resolveSshConfigRef(device, deps) {
|
|
52874
|
+
const ref = device.sshConfigRef?.trim();
|
|
52875
|
+
if (!ref) {
|
|
52876
|
+
return null;
|
|
52877
|
+
}
|
|
52878
|
+
const result = deps.runSync(["ssh", "-G", ref]);
|
|
52879
|
+
if (result.exitCode !== 0) {
|
|
52880
|
+
const detail = result.stderr.trim() || result.stdout.trim() || ref;
|
|
52881
|
+
throw new Error(`ssh_config_ref_resolve_failed: ${detail}`);
|
|
52882
|
+
}
|
|
52883
|
+
return parseSshConfigOutput(result.stdout, deps.env);
|
|
52884
|
+
}
|
|
52885
|
+
async function resolveSshConnectConfig(device, decrypt2, inputDeps = {}) {
|
|
52886
|
+
const deps = {
|
|
52887
|
+
env: inputDeps.env ?? process.env,
|
|
52888
|
+
runSync: inputDeps.runSync ?? defaultRunSync2,
|
|
52889
|
+
fileExists: inputDeps.fileExists ?? existsSync2,
|
|
52890
|
+
readTextFile: inputDeps.readTextFile ?? ((path) => readFileSync(path, "utf8"))
|
|
52891
|
+
};
|
|
52892
|
+
const sshEnv = toSshAuthEnv(deps.env);
|
|
52893
|
+
const resolvedConfig = resolveSshConfigRef(device, deps);
|
|
52894
|
+
const host = resolvedConfig?.host ?? device.host;
|
|
52895
|
+
const port = resolvedConfig?.port ?? device.port ?? 22;
|
|
52896
|
+
const username = resolvedConfig?.username ?? resolveSshUsername(device.username, device.authMode, sshEnv);
|
|
52897
|
+
if (!host) {
|
|
52898
|
+
throw new Error("SSH device missing host");
|
|
52899
|
+
}
|
|
52900
|
+
const authConfig = {
|
|
52901
|
+
host,
|
|
52902
|
+
port,
|
|
52903
|
+
username
|
|
52904
|
+
};
|
|
52905
|
+
const configAgent = resolveAgentFromConfig(resolvedConfig?.identityAgent, deps);
|
|
52906
|
+
const envAgent = resolveSshAgentSocket("auto", sshEnv);
|
|
52907
|
+
const configPrivateKey = resolvePrivateKeyFromConfig(resolvedConfig?.identityFiles ?? [], deps);
|
|
52908
|
+
switch (device.authMode) {
|
|
52909
|
+
case "password": {
|
|
52910
|
+
if (!device.passwordEnc) {
|
|
52911
|
+
throw new Error("auth_password_missing: \u5BC6\u7801\u8BA4\u8BC1\u672A\u63D0\u4F9B\u5BC6\u7801");
|
|
52912
|
+
}
|
|
52913
|
+
authConfig.password = await decrypt2(device.passwordEnc, {
|
|
52914
|
+
scope: "device",
|
|
52915
|
+
entityId: device.id,
|
|
52916
|
+
field: "password_enc"
|
|
52917
|
+
});
|
|
52918
|
+
break;
|
|
52919
|
+
}
|
|
52920
|
+
case "key": {
|
|
52921
|
+
if (!device.privateKeyEnc) {
|
|
52922
|
+
throw new Error("auth_key_missing: \u79C1\u94A5\u8BA4\u8BC1\u672A\u63D0\u4F9B\u79C1\u94A5");
|
|
52923
|
+
}
|
|
52924
|
+
authConfig.privateKey = await decrypt2(device.privateKeyEnc, {
|
|
52925
|
+
scope: "device",
|
|
52926
|
+
entityId: device.id,
|
|
52927
|
+
field: "private_key_enc"
|
|
52928
|
+
});
|
|
52929
|
+
if (device.privateKeyPassphraseEnc) {
|
|
52930
|
+
authConfig.passphrase = await decrypt2(device.privateKeyPassphraseEnc, {
|
|
52931
|
+
scope: "device",
|
|
52932
|
+
entityId: device.id,
|
|
52933
|
+
field: "private_key_passphrase_enc"
|
|
52934
|
+
});
|
|
52935
|
+
}
|
|
52936
|
+
break;
|
|
52937
|
+
}
|
|
52938
|
+
case "agent": {
|
|
52939
|
+
authConfig.agent = configAgent ?? resolveSshAgentSocket("agent", sshEnv);
|
|
52940
|
+
break;
|
|
52941
|
+
}
|
|
52942
|
+
case "configRef": {
|
|
52943
|
+
if (!resolvedConfig) {
|
|
52944
|
+
throw new Error("ssh_config_ref_missing: SSH Config \u5F15\u7528\u4E0D\u80FD\u4E3A\u7A7A");
|
|
52945
|
+
}
|
|
52946
|
+
if (configAgent ?? envAgent) {
|
|
52947
|
+
authConfig.agent = configAgent ?? envAgent;
|
|
52948
|
+
}
|
|
52949
|
+
if (configPrivateKey) {
|
|
52950
|
+
authConfig.privateKey = configPrivateKey;
|
|
52951
|
+
}
|
|
52952
|
+
if (!authConfig.agent && !authConfig.privateKey) {
|
|
52953
|
+
throw new Error("ssh_config_ref_auth_missing: SSH Config \u5F15\u7528\u672A\u89E3\u6790\u5230\u53EF\u7528\u8BA4\u8BC1\u65B9\u5F0F\uFF08IdentityAgent / IdentityFile / SSH_AUTH_SOCK\uFF09");
|
|
52954
|
+
}
|
|
52955
|
+
break;
|
|
52956
|
+
}
|
|
52957
|
+
case "auto": {
|
|
52958
|
+
if (configAgent ?? envAgent) {
|
|
52959
|
+
authConfig.agent = configAgent ?? envAgent;
|
|
52960
|
+
}
|
|
52961
|
+
if (device.privateKeyEnc) {
|
|
52962
|
+
authConfig.privateKey = await decrypt2(device.privateKeyEnc, {
|
|
52963
|
+
scope: "device",
|
|
52964
|
+
entityId: device.id,
|
|
52965
|
+
field: "private_key_enc"
|
|
52966
|
+
});
|
|
52967
|
+
} else if (configPrivateKey) {
|
|
52968
|
+
authConfig.privateKey = configPrivateKey;
|
|
52969
|
+
} else if (device.passwordEnc) {
|
|
52970
|
+
authConfig.password = await decrypt2(device.passwordEnc, {
|
|
52971
|
+
scope: "device",
|
|
52972
|
+
entityId: device.id,
|
|
52973
|
+
field: "password_enc"
|
|
52974
|
+
});
|
|
52975
|
+
}
|
|
52976
|
+
break;
|
|
52977
|
+
}
|
|
52978
|
+
}
|
|
52979
|
+
if (device.authMode === "auto" && !authConfig.agent && !authConfig.privateKey && !authConfig.password) {
|
|
52980
|
+
throw new Error("auth_auto_missing: auto \u6A21\u5F0F\u4E0B\u672A\u627E\u5230\u53EF\u7528\u8BA4\u8BC1\u65B9\u5F0F\uFF08SSH_AUTH_SOCK / \u79C1\u94A5 / \u5BC6\u7801\uFF09");
|
|
52981
|
+
}
|
|
52982
|
+
return authConfig;
|
|
52983
|
+
}
|
|
52984
|
+
|
|
52768
52985
|
// ../../apps/gateway/src/tmux-client/ssh-bootstrap.ts
|
|
52769
52986
|
function buildSshBootstrapScript() {
|
|
52770
52987
|
return [
|
|
@@ -52810,6 +53027,31 @@ function hasRenderableTerminalContent2(value) {
|
|
|
52810
53027
|
}
|
|
52811
53028
|
var BELL_DEDUP_WINDOW_MS2 = 200;
|
|
52812
53029
|
var COMMAND_SENTINEL = "\x1ETMEX_END ";
|
|
53030
|
+
var SNAPSHOT_FIELD_SEPARATOR = "|";
|
|
53031
|
+
function splitSnapshotFields(line, fieldCount) {
|
|
53032
|
+
const parts = line.split(SNAPSHOT_FIELD_SEPARATOR);
|
|
53033
|
+
if (parts.length <= fieldCount) {
|
|
53034
|
+
return parts;
|
|
53035
|
+
}
|
|
53036
|
+
if (fieldCount === 2) {
|
|
53037
|
+
return [parts[0] ?? "", parts.slice(1).join(SNAPSHOT_FIELD_SEPARATOR)];
|
|
53038
|
+
}
|
|
53039
|
+
if (fieldCount === 4) {
|
|
53040
|
+
return [parts[0] ?? "", parts[1] ?? "", parts.slice(2, -1).join(SNAPSHOT_FIELD_SEPARATOR), parts.at(-1) ?? ""];
|
|
53041
|
+
}
|
|
53042
|
+
if (fieldCount === 7) {
|
|
53043
|
+
return [
|
|
53044
|
+
parts[0] ?? "",
|
|
53045
|
+
parts[1] ?? "",
|
|
53046
|
+
parts[2] ?? "",
|
|
53047
|
+
parts.slice(3, -3).join(SNAPSHOT_FIELD_SEPARATOR),
|
|
53048
|
+
parts.at(-3) ?? "",
|
|
53049
|
+
parts.at(-2) ?? "",
|
|
53050
|
+
parts.at(-1) ?? ""
|
|
53051
|
+
];
|
|
53052
|
+
}
|
|
53053
|
+
return parts;
|
|
53054
|
+
}
|
|
52813
53055
|
|
|
52814
53056
|
class SshExternalTmuxConnection {
|
|
52815
53057
|
deviceId;
|
|
@@ -52938,7 +53180,7 @@ class SshExternalTmuxConnection {
|
|
|
52938
53180
|
if (!this.connected) {
|
|
52939
53181
|
return;
|
|
52940
53182
|
}
|
|
52941
|
-
const argv = name ? ["new-window", "-n", name] : ["new-window"];
|
|
53183
|
+
const argv = name ? ["new-window", "-t", this.sessionName, "-n", name] : ["new-window", "-t", this.sessionName];
|
|
52942
53184
|
this.runAndRefresh(argv).catch((error) => {
|
|
52943
53185
|
this.callbacks.onError(error);
|
|
52944
53186
|
});
|
|
@@ -52971,80 +53213,7 @@ class SshExternalTmuxConnection {
|
|
|
52971
53213
|
if (!this.device) {
|
|
52972
53214
|
throw new Error("SSH device not loaded");
|
|
52973
53215
|
}
|
|
52974
|
-
const
|
|
52975
|
-
const port = this.device.port ?? 22;
|
|
52976
|
-
const username = resolveSshUsername(this.device.username, this.device.authMode);
|
|
52977
|
-
if (this.device.authMode === "configRef" || !host && this.device.sshConfigRef) {
|
|
52978
|
-
throw new Error("ssh_config_ref_not_supported: \u5F53\u524D\u7248\u672C\u6682\u4E0D\u652F\u6301 SSH Config \u5F15\u7528\uFF0C\u8BF7\u6539\u4E3A\u586B\u5199 host + username\uFF0C\u5E76\u9009\u62E9 Agent/\u79C1\u94A5/\u5BC6\u7801\u8BA4\u8BC1");
|
|
52979
|
-
}
|
|
52980
|
-
if (!host) {
|
|
52981
|
-
throw new Error("SSH device missing host");
|
|
52982
|
-
}
|
|
52983
|
-
const authConfig = {
|
|
52984
|
-
host,
|
|
52985
|
-
port,
|
|
52986
|
-
username
|
|
52987
|
-
};
|
|
52988
|
-
switch (this.device.authMode) {
|
|
52989
|
-
case "password": {
|
|
52990
|
-
if (!this.device.passwordEnc) {
|
|
52991
|
-
throw new Error("auth_password_missing: \u5BC6\u7801\u8BA4\u8BC1\u672A\u63D0\u4F9B\u5BC6\u7801");
|
|
52992
|
-
}
|
|
52993
|
-
authConfig.password = await this.deps.decrypt(this.device.passwordEnc, {
|
|
52994
|
-
scope: "device",
|
|
52995
|
-
entityId: this.device.id,
|
|
52996
|
-
field: "password_enc"
|
|
52997
|
-
});
|
|
52998
|
-
break;
|
|
52999
|
-
}
|
|
53000
|
-
case "key": {
|
|
53001
|
-
if (!this.device.privateKeyEnc) {
|
|
53002
|
-
throw new Error("auth_key_missing: \u79C1\u94A5\u8BA4\u8BC1\u672A\u63D0\u4F9B\u79C1\u94A5");
|
|
53003
|
-
}
|
|
53004
|
-
authConfig.privateKey = await this.deps.decrypt(this.device.privateKeyEnc, {
|
|
53005
|
-
scope: "device",
|
|
53006
|
-
entityId: this.device.id,
|
|
53007
|
-
field: "private_key_enc"
|
|
53008
|
-
});
|
|
53009
|
-
if (this.device.privateKeyPassphraseEnc) {
|
|
53010
|
-
authConfig.passphrase = await this.deps.decrypt(this.device.privateKeyPassphraseEnc, {
|
|
53011
|
-
scope: "device",
|
|
53012
|
-
entityId: this.device.id,
|
|
53013
|
-
field: "private_key_passphrase_enc"
|
|
53014
|
-
});
|
|
53015
|
-
}
|
|
53016
|
-
break;
|
|
53017
|
-
}
|
|
53018
|
-
case "agent": {
|
|
53019
|
-
authConfig.agent = resolveSshAgentSocket("agent");
|
|
53020
|
-
break;
|
|
53021
|
-
}
|
|
53022
|
-
case "auto": {
|
|
53023
|
-
const agentSocket = resolveSshAgentSocket("auto");
|
|
53024
|
-
if (agentSocket) {
|
|
53025
|
-
authConfig.agent = agentSocket;
|
|
53026
|
-
}
|
|
53027
|
-
if (this.device.privateKeyEnc) {
|
|
53028
|
-
authConfig.privateKey = await this.deps.decrypt(this.device.privateKeyEnc, {
|
|
53029
|
-
scope: "device",
|
|
53030
|
-
entityId: this.device.id,
|
|
53031
|
-
field: "private_key_enc"
|
|
53032
|
-
});
|
|
53033
|
-
} else if (this.device.passwordEnc) {
|
|
53034
|
-
authConfig.password = await this.deps.decrypt(this.device.passwordEnc, {
|
|
53035
|
-
scope: "device",
|
|
53036
|
-
entityId: this.device.id,
|
|
53037
|
-
field: "password_enc"
|
|
53038
|
-
});
|
|
53039
|
-
}
|
|
53040
|
-
break;
|
|
53041
|
-
}
|
|
53042
|
-
case "configRef":
|
|
53043
|
-
break;
|
|
53044
|
-
}
|
|
53045
|
-
if (this.device.authMode === "auto" && !authConfig.agent && !authConfig.privateKey && !authConfig.password) {
|
|
53046
|
-
throw new Error("auth_auto_missing: auto \u6A21\u5F0F\u4E0B\u672A\u627E\u5230\u53EF\u7528\u8BA4\u8BC1\u65B9\u5F0F\uFF08SSH_AUTH_SOCK / \u79C1\u94A5 / \u5BC6\u7801\uFF09");
|
|
53047
|
-
}
|
|
53216
|
+
const authConfig = await resolveSshConnectConfig(this.device, this.deps.decrypt);
|
|
53048
53217
|
const client = this.deps.createClient();
|
|
53049
53218
|
this.sshClient = client;
|
|
53050
53219
|
await new Promise((resolve, reject) => {
|
|
@@ -53270,8 +53439,8 @@ class SshExternalTmuxConnection {
|
|
|
53270
53439
|
async capturePaneHistory(paneId) {
|
|
53271
53440
|
const mode = (await this.runTmux(["display-message", "-p", "-t", paneId, "#{alternate_on}"], true)).stdout.trim();
|
|
53272
53441
|
const alternateScreen = mode === "1";
|
|
53273
|
-
const normal = (await this.runTmux(["capture-pane", "-t", paneId, "-S", "-", "-E", "-", "-e", "-p"], true, 30000)).stdout;
|
|
53274
|
-
const alternate = (await this.runTmux(["capture-pane", "-t", paneId, "-a", "-S", "-", "-E", "-", "-e", "-p", "-q"], true, 30000)).stdout;
|
|
53442
|
+
const normal = (await this.runTmux(["capture-pane", "-t", paneId, "-S", "-", "-E", "-", "-e", "-N", "-p"], true, 30000)).stdout;
|
|
53443
|
+
const alternate = (await this.runTmux(["capture-pane", "-t", paneId, "-a", "-S", "-", "-E", "-", "-e", "-N", "-p", "-q"], true, 30000)).stdout;
|
|
53275
53444
|
const history = alternateScreen ? hasRenderableTerminalContent2(normal) ? normal : alternate : normal || alternate;
|
|
53276
53445
|
if (history) {
|
|
53277
53446
|
this.callbacks.onTerminalHistory(paneId, history, alternateScreen);
|
|
@@ -53287,21 +53456,21 @@ class SshExternalTmuxConnection {
|
|
|
53287
53456
|
"-p",
|
|
53288
53457
|
"-t",
|
|
53289
53458
|
this.sessionName,
|
|
53290
|
-
"#{session_id}
|
|
53459
|
+
"#{session_id}|#{session_name}"
|
|
53291
53460
|
]),
|
|
53292
53461
|
this.runTmuxAllowFailure([
|
|
53293
53462
|
"list-windows",
|
|
53294
53463
|
"-t",
|
|
53295
53464
|
this.sessionName,
|
|
53296
53465
|
"-F",
|
|
53297
|
-
"#{window_id}
|
|
53466
|
+
"#{window_id}|#{window_index}|#{window_name}|#{window_active}"
|
|
53298
53467
|
]),
|
|
53299
53468
|
this.runTmuxAllowFailure([
|
|
53300
53469
|
"list-panes",
|
|
53301
53470
|
"-t",
|
|
53302
53471
|
this.sessionName,
|
|
53303
53472
|
"-F",
|
|
53304
|
-
"#{pane_id}
|
|
53473
|
+
"#{pane_id}|#{window_id}|#{pane_index}|#{pane_title}|#{pane_active}|#{pane_width}|#{pane_height}"
|
|
53305
53474
|
])
|
|
53306
53475
|
]);
|
|
53307
53476
|
if (sessionRes.exitCode !== 0 || windowsRes.exitCode !== 0 || panesRes.exitCode !== 0) {
|
|
@@ -53319,7 +53488,7 @@ class SshExternalTmuxConnection {
|
|
|
53319
53488
|
if (!line.trim()) {
|
|
53320
53489
|
continue;
|
|
53321
53490
|
}
|
|
53322
|
-
const [id, name] = line
|
|
53491
|
+
const [id, name] = splitSnapshotFields(line, 2);
|
|
53323
53492
|
if (id) {
|
|
53324
53493
|
this.snapshotSession = { id, name: name ?? "" };
|
|
53325
53494
|
}
|
|
@@ -53332,7 +53501,7 @@ class SshExternalTmuxConnection {
|
|
|
53332
53501
|
if (!line.trim()) {
|
|
53333
53502
|
continue;
|
|
53334
53503
|
}
|
|
53335
|
-
const [id, indexRaw, name, activeRaw] = line
|
|
53504
|
+
const [id, indexRaw, name, activeRaw] = splitSnapshotFields(line, 4);
|
|
53336
53505
|
if (!id) {
|
|
53337
53506
|
continue;
|
|
53338
53507
|
}
|
|
@@ -53358,7 +53527,7 @@ class SshExternalTmuxConnection {
|
|
|
53358
53527
|
if (!line.trim()) {
|
|
53359
53528
|
continue;
|
|
53360
53529
|
}
|
|
53361
|
-
const [paneId, windowId, indexRaw, titleRaw, activeRaw, widthRaw, heightRaw] = line
|
|
53530
|
+
const [paneId, windowId, indexRaw, titleRaw, activeRaw, widthRaw, heightRaw] = splitSnapshotFields(line, 7);
|
|
53362
53531
|
if (!paneId || !windowId) {
|
|
53363
53532
|
continue;
|
|
53364
53533
|
}
|
|
@@ -53702,6 +53871,7 @@ class DeviceSessionRuntime {
|
|
|
53702
53871
|
}
|
|
53703
53872
|
this.closeEmitted = true;
|
|
53704
53873
|
this.terminated = true;
|
|
53874
|
+
this.connectPromise = null;
|
|
53705
53875
|
this.broadcast((listener) => listener.onClose?.());
|
|
53706
53876
|
}
|
|
53707
53877
|
});
|
|
@@ -53713,7 +53883,7 @@ class DeviceSessionRuntime {
|
|
|
53713
53883
|
};
|
|
53714
53884
|
}
|
|
53715
53885
|
async connect() {
|
|
53716
|
-
if (this.terminated
|
|
53886
|
+
if (this.terminated) {
|
|
53717
53887
|
return Promise.reject(new Error(`Device session runtime already terminated: ${this.deviceId}`));
|
|
53718
53888
|
}
|
|
53719
53889
|
if (this.connectPromise) {
|
|
@@ -53721,6 +53891,7 @@ class DeviceSessionRuntime {
|
|
|
53721
53891
|
}
|
|
53722
53892
|
this.connectPromise = this.connection.connect().catch((error) => {
|
|
53723
53893
|
this.terminated = true;
|
|
53894
|
+
this.connectPromise = null;
|
|
53724
53895
|
throw error;
|
|
53725
53896
|
});
|
|
53726
53897
|
return this.connectPromise;
|
|
@@ -53731,6 +53902,7 @@ class DeviceSessionRuntime {
|
|
|
53731
53902
|
}
|
|
53732
53903
|
this.terminated = true;
|
|
53733
53904
|
this.manualDisconnect = true;
|
|
53905
|
+
this.connectPromise = null;
|
|
53734
53906
|
this.connection.disconnect();
|
|
53735
53907
|
}
|
|
53736
53908
|
async shutdown() {
|
|
@@ -54145,6 +54317,144 @@ class PushSupervisor {
|
|
|
54145
54317
|
}
|
|
54146
54318
|
var pushSupervisor = new PushSupervisor;
|
|
54147
54319
|
|
|
54320
|
+
// ../../apps/gateway/src/ws/error-classify.ts
|
|
54321
|
+
function classifySshError(error) {
|
|
54322
|
+
const msg = error.message.toLowerCase();
|
|
54323
|
+
if (msg.includes("ssh_config_ref_not_supported")) {
|
|
54324
|
+
return {
|
|
54325
|
+
type: "ssh_config_ref_not_supported",
|
|
54326
|
+
messageKey: "sshError.configRefNotSupported"
|
|
54327
|
+
};
|
|
54328
|
+
}
|
|
54329
|
+
if (msg.includes("ssh_auth_sock") || msg.includes("auth_sock")) {
|
|
54330
|
+
return {
|
|
54331
|
+
type: "agent_unavailable",
|
|
54332
|
+
messageKey: "sshError.agentUnavailable"
|
|
54333
|
+
};
|
|
54334
|
+
}
|
|
54335
|
+
if (msg.includes("agent") && (msg.includes("no identities") || msg.includes("failure"))) {
|
|
54336
|
+
return {
|
|
54337
|
+
type: "agent_no_identity",
|
|
54338
|
+
messageKey: "sshError.agentNoIdentities"
|
|
54339
|
+
};
|
|
54340
|
+
}
|
|
54341
|
+
if (msg.includes("permission denied")) {
|
|
54342
|
+
return {
|
|
54343
|
+
type: "auth_failed",
|
|
54344
|
+
messageKey: "sshError.authFailed"
|
|
54345
|
+
};
|
|
54346
|
+
}
|
|
54347
|
+
if (msg.includes("all configured authentication methods failed")) {
|
|
54348
|
+
return {
|
|
54349
|
+
type: "auth_failed",
|
|
54350
|
+
messageKey: "sshError.authFailedGeneric"
|
|
54351
|
+
};
|
|
54352
|
+
}
|
|
54353
|
+
if (msg.includes("enetunreach") || msg.includes("ehostunreach")) {
|
|
54354
|
+
return {
|
|
54355
|
+
type: "network_unreachable",
|
|
54356
|
+
messageKey: "sshError.networkUnreachable"
|
|
54357
|
+
};
|
|
54358
|
+
}
|
|
54359
|
+
if (msg.includes("connect refused") || msg.includes("connection refused") || msg.includes("econnrefused")) {
|
|
54360
|
+
return {
|
|
54361
|
+
type: "connection_refused",
|
|
54362
|
+
messageKey: "sshError.connectionRefused"
|
|
54363
|
+
};
|
|
54364
|
+
}
|
|
54365
|
+
if (msg.includes("timeout") || msg.includes("etimedout")) {
|
|
54366
|
+
return {
|
|
54367
|
+
type: "timeout",
|
|
54368
|
+
messageKey: "sshError.connectionTimeout"
|
|
54369
|
+
};
|
|
54370
|
+
}
|
|
54371
|
+
if (msg.includes("host not found") || msg.includes("getaddrinfo") || msg.includes("enotfound")) {
|
|
54372
|
+
return {
|
|
54373
|
+
type: "host_not_found",
|
|
54374
|
+
messageKey: "sshError.hostNotFound"
|
|
54375
|
+
};
|
|
54376
|
+
}
|
|
54377
|
+
if (msg.includes("handshake failed") || msg.includes("unable to verify")) {
|
|
54378
|
+
return {
|
|
54379
|
+
type: "handshake_failed",
|
|
54380
|
+
messageKey: "sshError.handshakeFailed"
|
|
54381
|
+
};
|
|
54382
|
+
}
|
|
54383
|
+
if (msg.includes("remote tmux unavailable") || msg.includes("tmux_not_found") || msg.includes("tmux: command not found") || msg.includes("tmux control mode not ready") || msg.includes("tmux exited") || msg.includes("tmux_exec_failed")) {
|
|
54384
|
+
return {
|
|
54385
|
+
type: "tmux_unavailable",
|
|
54386
|
+
messageKey: "sshError.tmuxUnavailable"
|
|
54387
|
+
};
|
|
54388
|
+
}
|
|
54389
|
+
return {
|
|
54390
|
+
type: "unknown",
|
|
54391
|
+
messageKey: "sshError.unknown",
|
|
54392
|
+
messageParams: { message: error.message }
|
|
54393
|
+
};
|
|
54394
|
+
}
|
|
54395
|
+
|
|
54396
|
+
// ../../apps/gateway/src/api/test-connection.ts
|
|
54397
|
+
function inferFailurePhase(errorType) {
|
|
54398
|
+
if (errorType === "tmux_unavailable") {
|
|
54399
|
+
return "bootstrap";
|
|
54400
|
+
}
|
|
54401
|
+
return "connect";
|
|
54402
|
+
}
|
|
54403
|
+
function json(data, status = 200) {
|
|
54404
|
+
return new Response(JSON.stringify(data), {
|
|
54405
|
+
status,
|
|
54406
|
+
headers: {
|
|
54407
|
+
"Content-Type": "application/json"
|
|
54408
|
+
}
|
|
54409
|
+
});
|
|
54410
|
+
}
|
|
54411
|
+
async function handleDeviceTestConnection(deviceId, inputDeps = {}) {
|
|
54412
|
+
const deps = {
|
|
54413
|
+
getDevice: inputDeps.getDevice ?? ((currentDeviceId) => getDeviceById(currentDeviceId)),
|
|
54414
|
+
acquireRuntime: inputDeps.acquireRuntime ?? ((currentDeviceId) => tmuxRuntimeRegistry.acquire(currentDeviceId)),
|
|
54415
|
+
releaseRuntime: inputDeps.releaseRuntime ?? (async (currentDeviceId) => {
|
|
54416
|
+
await tmuxRuntimeRegistry.release(currentDeviceId);
|
|
54417
|
+
}),
|
|
54418
|
+
translate: inputDeps.translate ?? t2
|
|
54419
|
+
};
|
|
54420
|
+
const device = deps.getDevice(deviceId);
|
|
54421
|
+
if (!device) {
|
|
54422
|
+
return json({ error: deps.translate("apiError.deviceNotFound") }, 404);
|
|
54423
|
+
}
|
|
54424
|
+
const classifyErrorResponse = (error) => {
|
|
54425
|
+
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
54426
|
+
const classified = classifySshError(new Error(rawMessage));
|
|
54427
|
+
const payload = {
|
|
54428
|
+
success: false,
|
|
54429
|
+
tmuxAvailable: false,
|
|
54430
|
+
phase: inferFailurePhase(classified.type),
|
|
54431
|
+
errorType: classified.type,
|
|
54432
|
+
message: deps.translate(classified.messageKey, classified.messageParams),
|
|
54433
|
+
rawMessage
|
|
54434
|
+
};
|
|
54435
|
+
return json(payload);
|
|
54436
|
+
};
|
|
54437
|
+
let runtime = null;
|
|
54438
|
+
try {
|
|
54439
|
+
runtime = await deps.acquireRuntime(deviceId);
|
|
54440
|
+
await runtime.connect();
|
|
54441
|
+
runtime.requestSnapshot();
|
|
54442
|
+
const payload = {
|
|
54443
|
+
success: true,
|
|
54444
|
+
tmuxAvailable: true,
|
|
54445
|
+
phase: "ready",
|
|
54446
|
+
message: deps.translate("common.success")
|
|
54447
|
+
};
|
|
54448
|
+
return json(payload);
|
|
54449
|
+
} catch (error) {
|
|
54450
|
+
return classifyErrorResponse(error);
|
|
54451
|
+
} finally {
|
|
54452
|
+
if (runtime) {
|
|
54453
|
+
await deps.releaseRuntime(deviceId, runtime);
|
|
54454
|
+
}
|
|
54455
|
+
}
|
|
54456
|
+
}
|
|
54457
|
+
|
|
54148
54458
|
// ../../apps/gateway/src/api/index.ts
|
|
54149
54459
|
function shouldReconnectPushSupervisor(existing, updates) {
|
|
54150
54460
|
if (updates.type !== undefined && updates.type !== existing.type)
|
|
@@ -54293,28 +54603,28 @@ function handleApiRequest(req, _server) {
|
|
|
54293
54603
|
return handleGetManifest(req.method);
|
|
54294
54604
|
}
|
|
54295
54605
|
if (path === "/healthz" && req.method === "GET") {
|
|
54296
|
-
return
|
|
54606
|
+
return json2({ status: "ok", restarting: runtimeController.isRestarting() });
|
|
54297
54607
|
}
|
|
54298
|
-
return
|
|
54608
|
+
return json2({ error: t2("apiError.notFound") }, 404);
|
|
54299
54609
|
}
|
|
54300
54610
|
async function handleGetDevices() {
|
|
54301
54611
|
const devices2 = getAllDevices();
|
|
54302
|
-
return
|
|
54612
|
+
return json2({ devices: devices2 });
|
|
54303
54613
|
}
|
|
54304
54614
|
async function handleGetDevice(id) {
|
|
54305
54615
|
const device = getDeviceById(id);
|
|
54306
54616
|
if (!device) {
|
|
54307
|
-
return
|
|
54617
|
+
return json2({ error: t2("apiError.deviceNotFound") }, 404);
|
|
54308
54618
|
}
|
|
54309
|
-
return
|
|
54619
|
+
return json2({ device });
|
|
54310
54620
|
}
|
|
54311
54621
|
async function handleCreateDevice(req) {
|
|
54312
54622
|
const body = await req.json();
|
|
54313
54623
|
if (!body.name || !body.type || !body.authMode) {
|
|
54314
|
-
return
|
|
54624
|
+
return json2({ error: t2("apiError.missingFields") }, 400);
|
|
54315
54625
|
}
|
|
54316
54626
|
if (body.type === "ssh" && !body.host && !body.sshConfigRef) {
|
|
54317
|
-
return
|
|
54627
|
+
return json2({ error: t2("apiError.sshRequiresHost") }, 400);
|
|
54318
54628
|
}
|
|
54319
54629
|
const now = new Date().toISOString();
|
|
54320
54630
|
const device = {
|
|
@@ -54335,12 +54645,12 @@ async function handleCreateDevice(req) {
|
|
|
54335
54645
|
};
|
|
54336
54646
|
createDevice(device);
|
|
54337
54647
|
await pushSupervisor.upsert(device.id);
|
|
54338
|
-
return
|
|
54648
|
+
return json2({ device }, 201);
|
|
54339
54649
|
}
|
|
54340
54650
|
async function handleUpdateDevice(req, id) {
|
|
54341
54651
|
const existing = getDeviceById(id);
|
|
54342
54652
|
if (!existing) {
|
|
54343
|
-
return
|
|
54653
|
+
return json2({ error: t2("apiError.deviceNotFound") }, 404);
|
|
54344
54654
|
}
|
|
54345
54655
|
const body = await req.json();
|
|
54346
54656
|
const updates = {};
|
|
@@ -54370,61 +54680,53 @@ async function handleUpdateDevice(req, id) {
|
|
|
54370
54680
|
await pushSupervisor.reconnect(id);
|
|
54371
54681
|
}
|
|
54372
54682
|
const device = getDeviceById(id);
|
|
54373
|
-
return
|
|
54683
|
+
return json2({ device });
|
|
54374
54684
|
}
|
|
54375
54685
|
async function handleDeleteDevice(id) {
|
|
54376
54686
|
const existing = getDeviceById(id);
|
|
54377
54687
|
if (!existing) {
|
|
54378
|
-
return
|
|
54688
|
+
return json2({ error: t2("apiError.deviceNotFound") }, 404);
|
|
54379
54689
|
}
|
|
54380
54690
|
deleteDevice(id);
|
|
54381
54691
|
pushSupervisor.remove(id);
|
|
54382
|
-
return
|
|
54692
|
+
return json2({ success: true });
|
|
54383
54693
|
}
|
|
54384
54694
|
async function handleTestConnection(id) {
|
|
54385
|
-
|
|
54386
|
-
if (!device) {
|
|
54387
|
-
return json({ error: t2("apiError.deviceNotFound") }, 404);
|
|
54388
|
-
}
|
|
54389
|
-
return json({
|
|
54390
|
-
success: true,
|
|
54391
|
-
tmuxAvailable: false,
|
|
54392
|
-
message: "Connection test not fully implemented yet"
|
|
54393
|
-
});
|
|
54695
|
+
return handleDeviceTestConnection(id);
|
|
54394
54696
|
}
|
|
54395
54697
|
async function handleGetSiteSettings() {
|
|
54396
|
-
return
|
|
54698
|
+
return json2({ settings: getSiteSettings() });
|
|
54397
54699
|
}
|
|
54398
54700
|
async function handleUpdateSiteSettings(req) {
|
|
54399
54701
|
try {
|
|
54400
54702
|
const body = await req.json();
|
|
54401
54703
|
const updates = normalizeSiteSettingsInput(body);
|
|
54402
54704
|
const settings = updateSiteSettings(updates);
|
|
54403
|
-
return
|
|
54705
|
+
return json2({ settings });
|
|
54404
54706
|
} catch (err) {
|
|
54405
|
-
return
|
|
54707
|
+
return json2({ error: err instanceof Error ? err.message : t2("apiError.invalidRequest") }, 400);
|
|
54406
54708
|
}
|
|
54407
54709
|
}
|
|
54408
54710
|
async function handleRestartGateway() {
|
|
54409
54711
|
setTimeout(() => {
|
|
54410
54712
|
runtimeController.requestRestart();
|
|
54411
54713
|
}, 50);
|
|
54412
|
-
return
|
|
54714
|
+
return json2({
|
|
54413
54715
|
success: true,
|
|
54414
54716
|
message: t2("settings.restartScheduled")
|
|
54415
54717
|
});
|
|
54416
54718
|
}
|
|
54417
54719
|
async function handleGetTelegramBots() {
|
|
54418
54720
|
const bots = getTelegramBotsWithStats();
|
|
54419
|
-
return
|
|
54721
|
+
return json2({ bots });
|
|
54420
54722
|
}
|
|
54421
54723
|
async function handleCreateTelegramBot(req) {
|
|
54422
54724
|
const body = await req.json();
|
|
54423
54725
|
if (!body.name?.trim()) {
|
|
54424
|
-
return
|
|
54726
|
+
return json2({ error: t2("apiError.botNameRequired") }, 400);
|
|
54425
54727
|
}
|
|
54426
54728
|
if (!body.token?.trim()) {
|
|
54427
|
-
return
|
|
54729
|
+
return json2({ error: t2("apiError.botTokenRequired") }, 400);
|
|
54428
54730
|
}
|
|
54429
54731
|
const now = new Date().toISOString();
|
|
54430
54732
|
createTelegramBot({
|
|
@@ -54438,26 +54740,26 @@ async function handleCreateTelegramBot(req) {
|
|
|
54438
54740
|
updatedAt: now
|
|
54439
54741
|
});
|
|
54440
54742
|
await telegramService.refresh();
|
|
54441
|
-
return
|
|
54743
|
+
return json2({ success: true }, 201);
|
|
54442
54744
|
}
|
|
54443
54745
|
async function handleUpdateTelegramBot(req, botId) {
|
|
54444
54746
|
const existing = getTelegramBotById(botId);
|
|
54445
54747
|
if (!existing) {
|
|
54446
|
-
return
|
|
54748
|
+
return json2({ error: t2("apiError.botNotFound") }, 404);
|
|
54447
54749
|
}
|
|
54448
54750
|
const body = await req.json();
|
|
54449
54751
|
const updates = {};
|
|
54450
54752
|
if (body.name !== undefined) {
|
|
54451
54753
|
const value = body.name.trim();
|
|
54452
54754
|
if (!value) {
|
|
54453
|
-
return
|
|
54755
|
+
return json2({ error: t2("apiError.botNameRequired") }, 400);
|
|
54454
54756
|
}
|
|
54455
54757
|
updates.name = value;
|
|
54456
54758
|
}
|
|
54457
54759
|
if (body.token !== undefined) {
|
|
54458
54760
|
const token = body.token.trim();
|
|
54459
54761
|
if (!token) {
|
|
54460
|
-
return
|
|
54762
|
+
return json2({ error: t2("apiError.botTokenRequired") }, 400);
|
|
54461
54763
|
}
|
|
54462
54764
|
updates.tokenEnc = await encrypt(token);
|
|
54463
54765
|
}
|
|
@@ -54469,69 +54771,69 @@ async function handleUpdateTelegramBot(req, botId) {
|
|
|
54469
54771
|
}
|
|
54470
54772
|
updateTelegramBot(botId, updates);
|
|
54471
54773
|
await telegramService.refresh();
|
|
54472
|
-
return
|
|
54774
|
+
return json2({ success: true });
|
|
54473
54775
|
}
|
|
54474
54776
|
async function handleDeleteTelegramBot(botId) {
|
|
54475
54777
|
const existing = getTelegramBotById(botId);
|
|
54476
54778
|
if (!existing) {
|
|
54477
|
-
return
|
|
54779
|
+
return json2({ error: t2("apiError.botNotFound") }, 404);
|
|
54478
54780
|
}
|
|
54479
54781
|
deleteTelegramBot(botId);
|
|
54480
54782
|
await telegramService.refresh();
|
|
54481
|
-
return
|
|
54783
|
+
return json2({ success: true });
|
|
54482
54784
|
}
|
|
54483
54785
|
async function handleListTelegramChats(botId) {
|
|
54484
54786
|
const existing = getTelegramBotById(botId);
|
|
54485
54787
|
if (!existing) {
|
|
54486
|
-
return
|
|
54788
|
+
return json2({ error: t2("apiError.botNotFound") }, 404);
|
|
54487
54789
|
}
|
|
54488
54790
|
const chats = listTelegramChatsByBot(botId);
|
|
54489
|
-
return
|
|
54791
|
+
return json2({ chats });
|
|
54490
54792
|
}
|
|
54491
54793
|
async function handleApproveTelegramChat(botId, chatId) {
|
|
54492
54794
|
const existing = getTelegramBotById(botId);
|
|
54493
54795
|
if (!existing) {
|
|
54494
|
-
return
|
|
54796
|
+
return json2({ error: t2("apiError.botNotFound") }, 404);
|
|
54495
54797
|
}
|
|
54496
54798
|
const chat = approveTelegramChat(botId, chatId);
|
|
54497
54799
|
if (!chat) {
|
|
54498
|
-
return
|
|
54800
|
+
return json2({ error: t2("apiError.chatNotFound") }, 404);
|
|
54499
54801
|
}
|
|
54500
54802
|
const settings = getSiteSettings();
|
|
54501
54803
|
await telegramService.sendTestMessage(botId, chatId, t2("telegram.approveMessageTemplate", {
|
|
54502
54804
|
botName: existing.name,
|
|
54503
54805
|
time: new Date().toLocaleString(toBCP47(settings.language))
|
|
54504
54806
|
}));
|
|
54505
|
-
return
|
|
54807
|
+
return json2({ chat });
|
|
54506
54808
|
}
|
|
54507
54809
|
async function handleDeleteTelegramChat(botId, chatId) {
|
|
54508
54810
|
const existing = getTelegramBotById(botId);
|
|
54509
54811
|
if (!existing) {
|
|
54510
|
-
return
|
|
54812
|
+
return json2({ error: t2("apiError.botNotFound") }, 404);
|
|
54511
54813
|
}
|
|
54512
54814
|
deleteTelegramChat(botId, chatId);
|
|
54513
|
-
return
|
|
54815
|
+
return json2({ success: true });
|
|
54514
54816
|
}
|
|
54515
54817
|
async function handleTestTelegramChat(botId, chatId) {
|
|
54516
54818
|
const bot = getTelegramBotById(botId);
|
|
54517
54819
|
if (!bot) {
|
|
54518
|
-
return
|
|
54820
|
+
return json2({ error: t2("apiError.botNotFound") }, 404);
|
|
54519
54821
|
}
|
|
54520
54822
|
const settings = getSiteSettings();
|
|
54521
54823
|
await telegramService.sendTestMessage(botId, chatId, t2("telegram.testMessageTemplate", {
|
|
54522
54824
|
siteName: settings.siteName,
|
|
54523
54825
|
time: new Date().toLocaleString(toBCP47(settings.language))
|
|
54524
54826
|
}));
|
|
54525
|
-
return
|
|
54827
|
+
return json2({ success: true });
|
|
54526
54828
|
}
|
|
54527
54829
|
async function handleGetWebhooks() {
|
|
54528
54830
|
const webhooks = getAllWebhookEndpoints();
|
|
54529
|
-
return
|
|
54831
|
+
return json2({ webhooks });
|
|
54530
54832
|
}
|
|
54531
54833
|
async function handleCreateWebhook(req) {
|
|
54532
54834
|
const body = await req.json();
|
|
54533
54835
|
if (!body.url || !body.secret) {
|
|
54534
|
-
return
|
|
54836
|
+
return json2({ error: t2("apiError.urlAndSecretRequired") }, 400);
|
|
54535
54837
|
}
|
|
54536
54838
|
const now = new Date().toISOString();
|
|
54537
54839
|
const endpoint = {
|
|
@@ -54544,11 +54846,11 @@ async function handleCreateWebhook(req) {
|
|
|
54544
54846
|
updatedAt: now
|
|
54545
54847
|
};
|
|
54546
54848
|
createWebhookEndpoint(endpoint);
|
|
54547
|
-
return
|
|
54849
|
+
return json2({ webhook: endpoint }, 201);
|
|
54548
54850
|
}
|
|
54549
54851
|
async function handleDeleteWebhook(id) {
|
|
54550
54852
|
deleteWebhookEndpoint(id);
|
|
54551
|
-
return
|
|
54853
|
+
return json2({ success: true });
|
|
54552
54854
|
}
|
|
54553
54855
|
async function handleGetManifest(method) {
|
|
54554
54856
|
const settings = getSiteSettings();
|
|
@@ -54581,7 +54883,7 @@ function manifestJson(data, method) {
|
|
|
54581
54883
|
}
|
|
54582
54884
|
});
|
|
54583
54885
|
}
|
|
54584
|
-
function
|
|
54886
|
+
function json2(data, status = 200, headers = {}) {
|
|
54585
54887
|
return new Response(JSON.stringify(data), {
|
|
54586
54888
|
status,
|
|
54587
54889
|
headers: {
|
|
@@ -54592,7 +54894,7 @@ function json(data, status = 200, headers = {}) {
|
|
|
54592
54894
|
}
|
|
54593
54895
|
|
|
54594
54896
|
// ../../apps/gateway/src/db/migrate.ts
|
|
54595
|
-
import { existsSync as
|
|
54897
|
+
import { existsSync as existsSync3 } from "fs";
|
|
54596
54898
|
import { resolve } from "path";
|
|
54597
54899
|
|
|
54598
54900
|
// ../../node_modules/.bun/drizzle-orm@0.45.1+1608dc8003c5413e/node_modules/drizzle-orm/migrator.js
|
|
@@ -54639,7 +54941,7 @@ function resolveMigrationsFolder() {
|
|
|
54639
54941
|
if (fromEnv)
|
|
54640
54942
|
return fromEnv;
|
|
54641
54943
|
const byCwd = resolve(process.cwd(), "drizzle");
|
|
54642
|
-
if (
|
|
54944
|
+
if (existsSync3(byCwd))
|
|
54643
54945
|
return byCwd;
|
|
54644
54946
|
return resolve(import.meta.dir, "../../drizzle");
|
|
54645
54947
|
}
|
|
@@ -55208,82 +55510,6 @@ class SwitchBarrier {
|
|
|
55208
55510
|
}
|
|
55209
55511
|
var switchBarrier = new SwitchBarrier;
|
|
55210
55512
|
|
|
55211
|
-
// ../../apps/gateway/src/ws/error-classify.ts
|
|
55212
|
-
function classifySshError(error) {
|
|
55213
|
-
const msg = error.message.toLowerCase();
|
|
55214
|
-
if (msg.includes("ssh_config_ref_not_supported")) {
|
|
55215
|
-
return {
|
|
55216
|
-
type: "ssh_config_ref_not_supported",
|
|
55217
|
-
messageKey: "sshError.configRefNotSupported"
|
|
55218
|
-
};
|
|
55219
|
-
}
|
|
55220
|
-
if (msg.includes("ssh_auth_sock") || msg.includes("auth_sock")) {
|
|
55221
|
-
return {
|
|
55222
|
-
type: "agent_unavailable",
|
|
55223
|
-
messageKey: "sshError.agentUnavailable"
|
|
55224
|
-
};
|
|
55225
|
-
}
|
|
55226
|
-
if (msg.includes("agent") && (msg.includes("no identities") || msg.includes("failure"))) {
|
|
55227
|
-
return {
|
|
55228
|
-
type: "agent_no_identity",
|
|
55229
|
-
messageKey: "sshError.agentNoIdentities"
|
|
55230
|
-
};
|
|
55231
|
-
}
|
|
55232
|
-
if (msg.includes("permission denied")) {
|
|
55233
|
-
return {
|
|
55234
|
-
type: "auth_failed",
|
|
55235
|
-
messageKey: "sshError.authFailed"
|
|
55236
|
-
};
|
|
55237
|
-
}
|
|
55238
|
-
if (msg.includes("all configured authentication methods failed")) {
|
|
55239
|
-
return {
|
|
55240
|
-
type: "auth_failed",
|
|
55241
|
-
messageKey: "sshError.authFailedGeneric"
|
|
55242
|
-
};
|
|
55243
|
-
}
|
|
55244
|
-
if (msg.includes("enetunreach") || msg.includes("ehostunreach")) {
|
|
55245
|
-
return {
|
|
55246
|
-
type: "network_unreachable",
|
|
55247
|
-
messageKey: "sshError.networkUnreachable"
|
|
55248
|
-
};
|
|
55249
|
-
}
|
|
55250
|
-
if (msg.includes("connect refused") || msg.includes("connection refused") || msg.includes("econnrefused")) {
|
|
55251
|
-
return {
|
|
55252
|
-
type: "connection_refused",
|
|
55253
|
-
messageKey: "sshError.connectionRefused"
|
|
55254
|
-
};
|
|
55255
|
-
}
|
|
55256
|
-
if (msg.includes("timeout") || msg.includes("etimedout")) {
|
|
55257
|
-
return {
|
|
55258
|
-
type: "timeout",
|
|
55259
|
-
messageKey: "sshError.connectionTimeout"
|
|
55260
|
-
};
|
|
55261
|
-
}
|
|
55262
|
-
if (msg.includes("host not found") || msg.includes("getaddrinfo") || msg.includes("enotfound")) {
|
|
55263
|
-
return {
|
|
55264
|
-
type: "host_not_found",
|
|
55265
|
-
messageKey: "sshError.hostNotFound"
|
|
55266
|
-
};
|
|
55267
|
-
}
|
|
55268
|
-
if (msg.includes("handshake failed") || msg.includes("unable to verify")) {
|
|
55269
|
-
return {
|
|
55270
|
-
type: "handshake_failed",
|
|
55271
|
-
messageKey: "sshError.handshakeFailed"
|
|
55272
|
-
};
|
|
55273
|
-
}
|
|
55274
|
-
if (msg.includes("remote tmux unavailable") || msg.includes("tmux_not_found") || msg.includes("tmux: command not found") || msg.includes("tmux control mode not ready") || msg.includes("tmux exited") || msg.includes("tmux_exec_failed")) {
|
|
55275
|
-
return {
|
|
55276
|
-
type: "tmux_unavailable",
|
|
55277
|
-
messageKey: "sshError.tmuxUnavailable"
|
|
55278
|
-
};
|
|
55279
|
-
}
|
|
55280
|
-
return {
|
|
55281
|
-
type: "unknown",
|
|
55282
|
-
messageKey: "sshError.unknown",
|
|
55283
|
-
messageParams: { message: error.message }
|
|
55284
|
-
};
|
|
55285
|
-
}
|
|
55286
|
-
|
|
55287
55513
|
// ../../apps/gateway/src/ws/index.ts
|
|
55288
55514
|
var defaultDeps2 = {
|
|
55289
55515
|
acquireRuntime: async (deviceId) => tmuxRuntimeRegistry.acquire(deviceId),
|
|
@@ -56302,9 +56528,9 @@ async function serveFrontend(req, staticRoot) {
|
|
|
56302
56528
|
if (!requestedPath) {
|
|
56303
56529
|
return new Response(t3("runtime.forbidden"), { status: 403 });
|
|
56304
56530
|
}
|
|
56305
|
-
const indexPath =
|
|
56306
|
-
const targetPath =
|
|
56307
|
-
if (!
|
|
56531
|
+
const indexPath = join5(staticRoot, "index.html");
|
|
56532
|
+
const targetPath = existsSync4(requestedPath) ? requestedPath : indexPath;
|
|
56533
|
+
if (!existsSync4(targetPath)) {
|
|
56308
56534
|
return new Response(t3("runtime.frontendMissing"), { status: 500 });
|
|
56309
56535
|
}
|
|
56310
56536
|
const headers = new Headers;
|