@lzm04521/ssh-mcp-server 1.1.5

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.
Files changed (78) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +157 -0
  3. package/admin-web/dist/assets/index-DXbr5DVR.js +494 -0
  4. package/admin-web/dist/index.html +6 -0
  5. package/admin-web/dist/logo.png +0 -0
  6. package/build/cli/command-line-parser.js +715 -0
  7. package/build/cli/command-line-parser.js.map +1 -0
  8. package/build/cli/run-mode.js +44 -0
  9. package/build/cli/run-mode.js.map +1 -0
  10. package/build/cli/stdio-proxy.js +304 -0
  11. package/build/cli/stdio-proxy.js.map +1 -0
  12. package/build/config/index.js +5 -0
  13. package/build/config/index.js.map +1 -0
  14. package/build/config/server.js +16 -0
  15. package/build/config/server.js.map +1 -0
  16. package/build/core/mcp-http-server.js +14 -0
  17. package/build/core/mcp-http-server.js.map +1 -0
  18. package/build/core/mcp-server.js +95 -0
  19. package/build/core/mcp-server.js.map +1 -0
  20. package/build/index.js +98 -0
  21. package/build/index.js.map +1 -0
  22. package/build/models/admin-types.js +135 -0
  23. package/build/models/admin-types.js.map +1 -0
  24. package/build/models/types.js +2 -0
  25. package/build/models/types.js.map +1 -0
  26. package/build/server/index.js +128 -0
  27. package/build/server/index.js.map +1 -0
  28. package/build/server/routes/admin.js +929 -0
  29. package/build/server/routes/admin.js.map +1 -0
  30. package/build/server/routes/audit.js +9 -0
  31. package/build/server/routes/audit.js.map +1 -0
  32. package/build/server/routes/backups.js +11 -0
  33. package/build/server/routes/backups.js.map +1 -0
  34. package/build/server/routes/mcp.js +52 -0
  35. package/build/server/routes/mcp.js.map +1 -0
  36. package/build/server/routes/settings.js +49 -0
  37. package/build/server/routes/settings.js.map +1 -0
  38. package/build/server/routes/system.js +147 -0
  39. package/build/server/routes/system.js.map +1 -0
  40. package/build/services/audit-store.js +123 -0
  41. package/build/services/audit-store.js.map +1 -0
  42. package/build/services/autostart-service.js +55 -0
  43. package/build/services/autostart-service.js.map +1 -0
  44. package/build/services/backup-scheduler.js +90 -0
  45. package/build/services/backup-scheduler.js.map +1 -0
  46. package/build/services/backup-service.js +113 -0
  47. package/build/services/backup-service.js.map +1 -0
  48. package/build/services/config-store.js +163 -0
  49. package/build/services/config-store.js.map +1 -0
  50. package/build/services/defaults.js +35 -0
  51. package/build/services/defaults.js.map +1 -0
  52. package/build/services/restart-helper.js +43 -0
  53. package/build/services/restart-helper.js.map +1 -0
  54. package/build/services/ssh-connection-manager.js +1775 -0
  55. package/build/services/ssh-connection-manager.js.map +1 -0
  56. package/build/services/update-service.js +93 -0
  57. package/build/services/update-service.js.map +1 -0
  58. package/build/tools/download.js +41 -0
  59. package/build/tools/download.js.map +1 -0
  60. package/build/tools/execute-command.js +53 -0
  61. package/build/tools/execute-command.js.map +1 -0
  62. package/build/tools/index.js +17 -0
  63. package/build/tools/index.js.map +1 -0
  64. package/build/tools/list-directory.js +57 -0
  65. package/build/tools/list-directory.js.map +1 -0
  66. package/build/tools/list-servers.js +51 -0
  67. package/build/tools/list-servers.js.map +1 -0
  68. package/build/tools/upload.js +41 -0
  69. package/build/tools/upload.js.map +1 -0
  70. package/build/utils/logger.js +28 -0
  71. package/build/utils/logger.js.map +1 -0
  72. package/build/utils/ssh-config-parser.js +195 -0
  73. package/build/utils/ssh-config-parser.js.map +1 -0
  74. package/build/utils/status-collector.js +226 -0
  75. package/build/utils/status-collector.js.map +1 -0
  76. package/build/utils/tool-error.js +20 -0
  77. package/build/utils/tool-error.js.map +1 -0
  78. package/package.json +69 -0
@@ -0,0 +1,90 @@
1
+ let timer = null;
2
+ let stopped = false;
3
+ /** 上次启动调度器时的备份配置签名,用于去重避免重复重置 timer */
4
+ let lastSig = null;
5
+ /** 上次备份时间戳(ms),用于判断是否到达间隔 */
6
+ let lastBackupTs = 0;
7
+ /** 防止并发备份 */
8
+ let running = false;
9
+ /** 检查间隔:每 10 分钟检查一次是否该备份,避免 setTimeout 32-bit 溢出 */
10
+ const CHECK_INTERVAL_MS = 10 * 60 * 1000;
11
+ export function stopBackupScheduler() {
12
+ stopped = true;
13
+ if (timer) {
14
+ clearTimeout(timer);
15
+ timer = null;
16
+ }
17
+ lastSig = null;
18
+ }
19
+ export function startBackupScheduler(store, svc, cfg) {
20
+ const b = cfg?.backups;
21
+ if (!b?.autoEnabled) {
22
+ if (lastSig !== null)
23
+ stopBackupScheduler();
24
+ return;
25
+ }
26
+ const hours = b.intervalHours ?? 24;
27
+ if (!hours || hours < 1) {
28
+ if (lastSig !== null)
29
+ stopBackupScheduler();
30
+ return;
31
+ }
32
+ // 配置未变则跳过,避免 chokidar onChange 重复触发导致 timer 重置
33
+ const sig = `${b.autoEnabled}|${hours}|${b.retentionDays ?? ""}|${b.maxCount ?? ""}`;
34
+ if (sig === lastSig)
35
+ return;
36
+ stopBackupScheduler();
37
+ stopped = false;
38
+ lastSig = sig;
39
+ const intervalMs = hours * 3600 * 1000;
40
+ console.error("[backup-scheduler] starting auto backup every", hours, "hour(s)");
41
+ const doBackup = async () => {
42
+ if (stopped || running)
43
+ return;
44
+ running = true;
45
+ try {
46
+ console.error("[backup-scheduler] auto snapshot start");
47
+ await svc.snapshot();
48
+ const latest = await store.load();
49
+ await svc.prune(latest?.backups?.retentionDays, latest?.backups?.maxCount);
50
+ console.error("[backup-scheduler] auto snapshot done");
51
+ lastBackupTs = Date.now();
52
+ }
53
+ catch (e) {
54
+ console.error("[backup-scheduler] auto snapshot failed:", e);
55
+ }
56
+ finally {
57
+ running = false;
58
+ }
59
+ };
60
+ const tick = () => {
61
+ if (stopped)
62
+ return;
63
+ const now = Date.now();
64
+ if (now - lastBackupTs >= intervalMs) {
65
+ // 时间到了,执行备份(不 await,避免阻塞 tick 调度)
66
+ doBackup();
67
+ }
68
+ timer = setTimeout(tick, CHECK_INTERVAL_MS);
69
+ timer.unref?.();
70
+ };
71
+ // 首次启动时,如果从未备份过,立即执行一次
72
+ if (lastBackupTs === 0) {
73
+ lastBackupTs = Date.now();
74
+ // 延迟一个 tick 再执行,避免阻塞启动
75
+ timer = setTimeout(async () => {
76
+ if (stopped)
77
+ return;
78
+ await doBackup();
79
+ tick();
80
+ }, CHECK_INTERVAL_MS);
81
+ timer.unref?.();
82
+ }
83
+ else {
84
+ tick();
85
+ }
86
+ }
87
+ export function rescheduleBackupScheduler(store, svc, cfg) {
88
+ startBackupScheduler(store, svc, cfg);
89
+ }
90
+ //# sourceMappingURL=backup-scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup-scheduler.js","sourceRoot":"","sources":["../../src/services/backup-scheduler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ /** 从备份文件名解析创建时间(config-YYYY-MM-DDTHH-mm-ss-sssZ.json,UTC);解析失败返回 null。
4
+ * 不直接用 mtime:Windows 上 copyFile 保留源文件的 mtime,会让所有备份展示同一时间 */
5
+ export function tsFromName(name) {
6
+ const m = /^config-(\d{4})-(\d{2})-(\d{2})T(\d{2})-(\d{2})-(\d{2})-(\d{3})Z\.json$/.exec(name);
7
+ if (!m)
8
+ return null;
9
+ const [, y, mo, d, h, mi, s, ms] = m;
10
+ return Date.UTC(+y, +mo - 1, +d, +h, +mi, +s, +ms);
11
+ }
12
+ export class BackupService {
13
+ store;
14
+ constructor(store) {
15
+ this.store = store;
16
+ }
17
+ get dir() { return path.join(path.dirname(this.store.path), "backups"); }
18
+ async prune(retentionDays, maxCount) {
19
+ const items = await this.list();
20
+ const deleted = [];
21
+ const now = Date.now();
22
+ // 按 retentionDays 清理过期
23
+ if (retentionDays && retentionDays > 0) {
24
+ const cutoff = now - retentionDays * 24 * 3600 * 1000;
25
+ for (const it of items) {
26
+ if (it.ts && it.ts < cutoff) {
27
+ try {
28
+ await fs.unlink(it.path);
29
+ deleted.push(it.id);
30
+ }
31
+ catch { }
32
+ }
33
+ }
34
+ }
35
+ // 重新列出剩余,按 maxCount 截断
36
+ const remaining = (await this.list()).filter((i) => !deleted.includes(i.id));
37
+ if (maxCount && maxCount > 0 && remaining.length > maxCount) {
38
+ const toRemove = remaining.slice(maxCount);
39
+ for (const it of toRemove) {
40
+ try {
41
+ await fs.unlink(it.path);
42
+ deleted.push(it.id);
43
+ }
44
+ catch { }
45
+ }
46
+ }
47
+ return { deleted };
48
+ }
49
+ async snapshot() {
50
+ await fs.mkdir(this.dir, { recursive: true });
51
+ const ts = new Date().toISOString().replace(/[:.]/g, "-");
52
+ const name = `config-${ts}.json`;
53
+ const dst = path.join(this.dir, name);
54
+ try {
55
+ await fs.copyFile(this.store.path, dst);
56
+ }
57
+ catch (e) {
58
+ // 若源不存在,创建一个空快照
59
+ if (e.code === "ENOENT") {
60
+ const cfg = await this.store.load();
61
+ await fs.writeFile(dst, JSON.stringify(cfg, null, 2), "utf-8");
62
+ }
63
+ else
64
+ throw e;
65
+ }
66
+ // Windows copyFile 保留源 mtime,重置为当前时间,让文件系统时间与备份创建时间一致
67
+ const now = new Date();
68
+ try {
69
+ await fs.utimes(dst, now, now);
70
+ }
71
+ catch { }
72
+ // 自动按保留策略清理
73
+ try {
74
+ const cfg = await this.store.load();
75
+ await this.prune(cfg?.backups?.retentionDays, cfg?.backups?.maxCount);
76
+ }
77
+ catch { }
78
+ return { id: name, name, path: dst, ts: Date.now() };
79
+ }
80
+ async list() {
81
+ try {
82
+ const files = await fs.readdir(this.dir);
83
+ const items = [];
84
+ for (const f of files.filter(f => f.endsWith(".json")).sort().reverse()) {
85
+ const p = path.join(this.dir, f);
86
+ // 优先用文件名内嵌时间戳;非标准命名回退到 mtime
87
+ const fromName = tsFromName(f);
88
+ if (fromName !== null) {
89
+ items.push({ id: f, name: f, path: p, ts: fromName });
90
+ continue;
91
+ }
92
+ try {
93
+ const stat = await fs.stat(p);
94
+ items.push({ id: f, name: f, path: p, ts: stat.mtimeMs });
95
+ }
96
+ catch {
97
+ items.push({ id: f, name: f, path: p, ts: 0 });
98
+ }
99
+ }
100
+ return items;
101
+ }
102
+ catch {
103
+ return [];
104
+ }
105
+ }
106
+ async restore(id) {
107
+ // 先快照当前
108
+ await this.snapshot();
109
+ const src = path.join(this.dir, id);
110
+ await fs.copyFile(src, this.store.path);
111
+ }
112
+ }
113
+ //# sourceMappingURL=backup-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup-service.js","sourceRoot":"","sources":["../../src/services/backup-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,163 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import os from "node:os";
4
+ import chokidar from "chokidar";
5
+ import { DEFAULT_ADMIN_PORT } from "../models/admin-types.js";
6
+ import { GlobalConfigSchema } from "../models/admin-types.js";
7
+ import { DEFAULT_AUDIT_SETTINGS, DEFAULT_BACKUP_SETTINGS, DEFAULT_SECURITY, } from "./defaults.js";
8
+ export function getGlobalConfigPath() {
9
+ if (process.env.SSH_MCP_CONFIG)
10
+ return path.resolve(process.env.SSH_MCP_CONFIG);
11
+ if (process.platform === "win32") {
12
+ const base = process.env.ProgramData || "C:\\ProgramData";
13
+ return path.join(base, "SshMcpServer", "config.json");
14
+ }
15
+ const xdg = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
16
+ return path.join(xdg, "ssh-mcp-server", "config.json");
17
+ }
18
+ /**
19
+ * Rust 桌面版(serde Option)会把未设置的可选字段序列化为显式 null,
20
+ * 而 Zod 的 .optional() 不接受 null。本项目语义中两者均表示"未设置",
21
+ * 读取时统一归一为 undefined,保证两版配置文件互通(Rust 写 → Node 读)。
22
+ */
23
+ function nullToJsonReviver(_key, value) {
24
+ return value === null ? undefined : value;
25
+ }
26
+ /**
27
+ * 主机名读兜底:hosts map 的 key 是唯一标识,Rust 桌面版历史导入的数据
28
+ * 对象内 name 可能为空串(显示为空、编辑变新增)。必须在 Zod parse 之前
29
+ * 回填——ConnectionSchema.name 有 min(1) 约束,空 name 会让整个 load 抛错。
30
+ */
31
+ function normalizeHostNames(raw) {
32
+ for (const proj of Object.values(raw?.projects || {})) {
33
+ for (const env of Object.values(proj?.environments || {})) {
34
+ const hosts = env?.hosts;
35
+ if (!hosts)
36
+ continue;
37
+ for (const [hName, hCfg] of Object.entries(hosts)) {
38
+ if (hCfg && typeof hCfg === "object" &&
39
+ typeof hCfg.name === "string" && hCfg.name.trim() === "" &&
40
+ hName.trim() !== "") {
41
+ hCfg.name = hName;
42
+ }
43
+ }
44
+ }
45
+ }
46
+ return raw;
47
+ }
48
+ export function getFlatHosts(cfg) {
49
+ const m = new Map();
50
+ for (const [pName, proj] of Object.entries(cfg.projects || {})) {
51
+ for (const [eName, env] of Object.entries(proj.environments || {})) {
52
+ for (const [hName, hCfg] of Object.entries(env.hosts || {})) {
53
+ m.set(`${pName}/${eName}/${hName}`, { project: pName, environment: eName, host: hName, config: hCfg });
54
+ }
55
+ }
56
+ }
57
+ return m;
58
+ }
59
+ export function getFlatHostByName(name, cfg) {
60
+ const flat = getFlatHosts(cfg);
61
+ if (flat.has(name))
62
+ return { flatName: name, config: flat.get(name).config };
63
+ const candidates = [];
64
+ for (const [k, v] of flat)
65
+ if (v.host === name)
66
+ candidates.push(k);
67
+ if (candidates.length === 1)
68
+ return { flatName: candidates[0], config: flat.get(candidates[0]).config };
69
+ if (candidates.length > 1)
70
+ return { ambiguous: true, candidates };
71
+ return null;
72
+ }
73
+ export class ConfigStore {
74
+ opts;
75
+ watchers = [];
76
+ constructor(opts = {}) {
77
+ this.opts = opts;
78
+ }
79
+ get path() {
80
+ return this.opts.configPath || getGlobalConfigPath();
81
+ }
82
+ async getFlatHosts() {
83
+ const cfg = await this.load();
84
+ return getFlatHosts(cfg);
85
+ }
86
+ async getFlatHostByName(name) {
87
+ const cfg = await this.load();
88
+ return getFlatHostByName(name, cfg);
89
+ }
90
+ async load() {
91
+ try {
92
+ const raw = await fs.readFile(this.path, "utf-8");
93
+ const parsed = JSON.parse(raw, nullToJsonReviver);
94
+ const normalized = normalizeHostNames(parsed);
95
+ if (!normalized.audit)
96
+ normalized.audit = { ...DEFAULT_AUDIT_SETTINGS };
97
+ if (!normalized.backups)
98
+ normalized.backups = { ...DEFAULT_BACKUP_SETTINGS };
99
+ if (!normalized.security) {
100
+ normalized.security = {
101
+ commandWhitelist: [...DEFAULT_SECURITY.commandWhitelist],
102
+ commandBlacklist: [...DEFAULT_SECURITY.commandBlacklist],
103
+ allowedLocalPaths: [...DEFAULT_SECURITY.allowedLocalPaths],
104
+ allowedRemotePaths: [...DEFAULT_SECURITY.allowedRemotePaths],
105
+ };
106
+ }
107
+ return GlobalConfigSchema.parse(normalized);
108
+ }
109
+ catch (e) {
110
+ if (e.code === "ENOENT") {
111
+ return {
112
+ port: DEFAULT_ADMIN_PORT,
113
+ projects: {},
114
+ audit: { ...DEFAULT_AUDIT_SETTINGS },
115
+ backups: { ...DEFAULT_BACKUP_SETTINGS },
116
+ security: {
117
+ commandWhitelist: [...DEFAULT_SECURITY.commandWhitelist],
118
+ commandBlacklist: [...DEFAULT_SECURITY.commandBlacklist],
119
+ allowedLocalPaths: [...DEFAULT_SECURITY.allowedLocalPaths],
120
+ allowedRemotePaths: [...DEFAULT_SECURITY.allowedRemotePaths],
121
+ },
122
+ preConnect: false,
123
+ };
124
+ }
125
+ throw e;
126
+ }
127
+ }
128
+ async save(patch) {
129
+ const current = await this.load();
130
+ // 深合并 audit/backups/security,防止浅合并丢字段(R1-4)
131
+ const next = { ...current, ...patch };
132
+ if (patch.audit && current.audit)
133
+ next.audit = { ...current.audit, ...patch.audit };
134
+ if (patch.backups && current.backups)
135
+ next.backups = { ...current.backups, ...patch.backups };
136
+ if (patch.security && current.security)
137
+ next.security = { ...current.security, ...patch.security };
138
+ GlobalConfigSchema.parse(next);
139
+ const dir = path.dirname(this.path);
140
+ await fs.mkdir(dir, { recursive: true });
141
+ try {
142
+ await fs.copyFile(this.path, this.path + ".bak");
143
+ }
144
+ catch { }
145
+ const tmp = this.path + ".tmp";
146
+ await fs.writeFile(tmp, JSON.stringify(next, null, 2), "utf-8");
147
+ JSON.parse(await fs.readFile(tmp, "utf-8"));
148
+ await fs.rename(tmp, this.path);
149
+ }
150
+ onChange(cb) {
151
+ const watcher = chokidar.watch(this.path, { ignoreInitial: true });
152
+ let t = null;
153
+ watcher.on("all", () => {
154
+ if (t)
155
+ clearTimeout(t);
156
+ t = setTimeout(async () => {
157
+ cb(await this.load());
158
+ }, 200);
159
+ });
160
+ return () => watcher.close();
161
+ }
162
+ }
163
+ //# sourceMappingURL=config-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-store.js","sourceRoot":"","sources":["../../src/services/config-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ // 全局默认值的唯一来源:后端路由与前端(经 /admin/api/defaults)共用,
2
+ // 防止多处写死后漂移(安全黑名单漂移尤其危险)。
3
+ // 新建项目时自动创建的默认环境
4
+ export const DEFAULT_ENVIRONMENTS = ["开发环境", "测试环境", "生产环境", "UAT环境"];
5
+ // 未配置安全策略时的默认命令黑名单(行首锚定正则)
6
+ export const DEFAULT_COMMAND_BLACKLIST = [
7
+ "^rm\\s+.*",
8
+ "^shutdown.*",
9
+ "^reboot.*",
10
+ "^halt.*",
11
+ "^poweroff.*",
12
+ "^mkfs.*",
13
+ "^dd\\s+.*",
14
+ ];
15
+ // 默认审计策略:开启审计、开启成功执行记录、保留 30 天
16
+ export const DEFAULT_AUDIT_SETTINGS = {
17
+ enabled: true,
18
+ retentionDays: 30,
19
+ logResults: true,
20
+ };
21
+ // 默认备份策略:保留 30 天、最多 20 份、默认不开启定时自动备份、默认间隔 24 小时
22
+ export const DEFAULT_BACKUP_SETTINGS = {
23
+ retentionDays: 30,
24
+ maxCount: 20,
25
+ autoEnabled: false,
26
+ intervalHours: 24,
27
+ };
28
+ // 默认安全策略:默认预设高危命令黑名单,白名单与目录留空
29
+ export const DEFAULT_SECURITY = {
30
+ commandWhitelist: [],
31
+ commandBlacklist: [...DEFAULT_COMMAND_BLACKLIST],
32
+ allowedLocalPaths: [],
33
+ allowedRemotePaths: [],
34
+ };
35
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/services/defaults.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,0BAA0B;AAE1B,iBAAiB;AACjB,MAAM,CAAC,MAAM,oBAAoB,GAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhF,2BAA2B;AAC3B,MAAM,CAAC,MAAM,yBAAyB,GAAa;IACjD,WAAW;IACX,aAAa;IACb,WAAW;IACX,SAAS;IACT,aAAa;IACb,SAAS;IACT,WAAW;CACZ,CAAC;AAEF,+BAA+B;AAC/B,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,OAAO,EAAE,IAAI;IACb,aAAa,EAAE,EAAE;IACjB,UAAU,EAAE,IAAI;CACjB,CAAC;AAEF,gDAAgD;AAChD,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,aAAa,EAAE,EAAE;IACjB,QAAQ,EAAE,EAAE;IACZ,WAAW,EAAE,KAAK;IAClB,aAAa,EAAE,EAAE;CAClB,CAAC;AAEF,8BAA8B;AAC9B,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,gBAAgB,EAAE,EAAc;IAChC,gBAAgB,EAAE,CAAC,GAAG,yBAAyB,CAAC;IAChD,iBAAiB,EAAE,EAAc;IACjC,kBAAkB,EAAE,EAAc;CACnC,CAAC"}
@@ -0,0 +1,43 @@
1
+ import { spawn } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ // 应用控制(重启):移植自 MCP-DB-Tools 的 RestartHelper ——
6
+ // 起一个分离的隐藏 PowerShell 守候当前 PID 退出后再拉起新实例(避免端口占用冲突),
7
+ // 随后优雅关闭当前进程。新实例不带 --admin-port,从 config.json 读端口(端口修改由此生效)。
8
+ // 实现为临时 .ps1 文件执行:避免 -Command 的嵌套引号在含空格路径下被错误解析;
9
+ // 新实例 stdout/stderr 落盘到系统临时目录,启动失败时可查。
10
+ export function scheduleRestartAndExit(close, timeoutMs = 15000) {
11
+ const exe = process.execPath;
12
+ const script = process.argv[1] ? path.resolve(process.argv[1]) : "";
13
+ if (process.platform === "win32" && script) {
14
+ try {
15
+ const dir = os.tmpdir();
16
+ const ps1 = path.join(dir, `ssh-mcp-restart-${process.pid}.ps1`);
17
+ const outLog = path.join(dir, "ssh-mcp-restart-out.log");
18
+ const errLog = path.join(dir, "ssh-mcp-restart-err.log");
19
+ const ps = [
20
+ "$ErrorActionPreference='SilentlyContinue'",
21
+ `if (Get-Process -Id ${process.pid}) { Wait-Process -Id ${process.pid} -Timeout 30 }`,
22
+ "Start-Sleep -Milliseconds 600",
23
+ `Start-Process -FilePath '${exe.replace(/'/g, "''")}' -ArgumentList '"${script.replace(/'/g, "''")}"','--admin' -WorkingDirectory '${path.dirname(script).replace(/'/g, "''")}' -WindowStyle Hidden -RedirectStandardOutput '${outLog}' -RedirectStandardError '${errLog}'`,
24
+ ].join("; ");
25
+ fs.writeFileSync(ps1, ps, "utf-8");
26
+ spawn("powershell.exe", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-File", ps1], {
27
+ detached: true,
28
+ stdio: "ignore",
29
+ windowsHide: true,
30
+ }).unref();
31
+ }
32
+ catch {
33
+ // 守候脚本启动失败则仅退出(由外部进程管理器拉起)
34
+ }
35
+ }
36
+ // 非 Windows:无注册表/PowerShell 机制,仅优雅退出,依赖 systemd/pm2 等外部拉起
37
+ const timer = setTimeout(() => process.exit(0), timeoutMs);
38
+ timer.unref();
39
+ Promise.resolve(close())
40
+ .catch(() => { })
41
+ .finally(() => process.exit(0));
42
+ }
43
+ //# sourceMappingURL=restart-helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restart-helper.js","sourceRoot":"","sources":["../../src/services/restart-helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,+CAA+C;AAC/C,oDAAoD;AACpD,6DAA6D;AAC7D,iDAAiD;AACjD,uCAAuC;AACvC,MAAM,UAAU,sBAAsB,CAAC,KAA6B,EAAE,SAAS,GAAG,KAAK;IACrF,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC;IAC7B,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEpE,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,EAAE,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,mBAAmB,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC;YACjE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAC;YACzD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAC;YACzD,MAAM,EAAE,GAAG;gBACT,2CAA2C;gBAC3C,uBAAuB,OAAO,CAAC,GAAG,wBAAwB,OAAO,CAAC,GAAG,gBAAgB;gBACrF,+BAA+B;gBAC/B,4BAA4B,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,qBAAqB,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,mCAAmC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,kDAAkD,MAAM,6BAA6B,MAAM,GAAG;aAC5Q,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACb,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;YACnC,KAAK,CAAC,gBAAgB,EAAE,CAAC,YAAY,EAAE,kBAAkB,EAAE,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE;gBAC5G,QAAQ,EAAE,IAAI;gBACd,KAAK,EAAE,QAAQ;gBACf,WAAW,EAAE,IAAI;aAClB,CAAC,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;QAAC,MAAM,CAAC;YACP,2BAA2B;QAC7B,CAAC;IACH,CAAC;IACD,0DAA0D;IAE1D,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IAC3D,KAAK,CAAC,KAAK,EAAE,CAAC;IACd,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;SACrB,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;SACf,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC"}