rdsh-gateway 0.8.0 → 0.8.1

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/service.d.ts CHANGED
@@ -21,8 +21,9 @@ export interface ServiceSpec {
21
21
  }
22
22
  /** systemd 用户级 unit 模板。 */
23
23
  export declare function systemdUnit(execStart: string, spec: ServiceSpec): string;
24
- /** launchd plist 模板。 */
25
- export declare function launchdPlist(execStart: string, spec: ServiceSpec): string;
24
+ /** launchd plist 模板。program 为启动前缀 argv([node, 脚本]),命令参数由 spec 提供——
25
+ * launchd 不做空格切分,每个 <string> 是一个 argv 元素,故 node 与脚本必须分开。 */
26
+ export declare function launchdPlist(program: string[], spec: ServiceSpec): string;
26
27
  /** 安装并启动服务(用户级)。 */
27
28
  export declare function installService(spec: ServiceSpec): Promise<string>;
28
29
  /** 服务状态。 */
package/dist/service.js CHANGED
@@ -58,9 +58,10 @@ RestartSec=3
58
58
  WantedBy=default.target
59
59
  `;
60
60
  }
61
- /** launchd plist 模板。 */
62
- export function launchdPlist(execStart, spec) {
63
- const args = commandArgs(spec).map((a) => ` <string>${a}</string>`).join("\n");
61
+ /** launchd plist 模板。program 为启动前缀 argv([node, 脚本]),命令参数由 spec 提供——
62
+ * launchd 不做空格切分,每个 <string> 是一个 argv 元素,故 node 与脚本必须分开。 */
63
+ export function launchdPlist(program, spec) {
64
+ const argLines = [...program, ...commandArgs(spec)].map((a) => ` <string>${a}</string>`).join("\n");
64
65
  const envBlock = spec.pathEnv !== undefined
65
66
  ? ` <key>EnvironmentVariables</key>
66
67
  <dict>
@@ -77,13 +78,15 @@ export function launchdPlist(execStart, spec) {
77
78
  <string>com.${spec.name}</string>
78
79
  <key>ProgramArguments</key>
79
80
  <array>
80
- <string>${execStart}</string>
81
- ${args}
81
+ ${argLines}
82
82
  </array>
83
83
  <key>RunAtLoad</key>
84
84
  <true/>
85
85
  <key>KeepAlive</key>
86
- <true/>
86
+ <dict>
87
+ <key>SuccessfulExit</key>
88
+ <false/>
89
+ </dict>
87
90
  ${envBlock} <key>StandardOutPath</key>
88
91
  <string>${serviceLogPath(spec.name)}</string>
89
92
  <key>StandardErrorPath</key>
@@ -103,18 +106,25 @@ async function run(cmd, args) {
103
106
  }
104
107
  /** 安装并启动服务(用户级)。 */
105
108
  export async function installService(spec) {
106
- const execStart = `${process.execPath} ${process.argv[1]}`;
109
+ const bin = process.argv[1];
110
+ if (bin === undefined) {
111
+ throw new Error("cannot install a service: the running entry script (process.argv[1]) is unknown");
112
+ }
107
113
  if (isLinux()) {
114
+ const execStart = `${process.execPath} ${bin}`;
108
115
  await mkdir(SYSTEMD_DIR, { recursive: true });
109
116
  await writeFile(systemdUnitPath(spec.name), systemdUnit(execStart, spec), { mode: 0o600 });
110
117
  await run("systemctl", ["--user", "daemon-reload"]);
111
118
  await run("systemctl", ["--user", "enable", "--now", spec.name]);
112
119
  return `installed systemd user unit: ${systemdUnitPath(spec.name)}`;
113
120
  }
121
+ // macOS(launchd):ProgramArguments 不做空格切分 —— node 与脚本必须作为独立 argv 元素
122
+ const plistPath = launchdPlistPath(spec.name);
114
123
  await mkdir(LAUNCHD_DIR, { recursive: true });
115
- await writeFile(launchdPlistPath(spec.name), launchdPlist(execStart, spec), { mode: 0o600 });
116
- await run("launchctl", ["load", launchdPlistPath(spec.name)]);
117
- return `installed launchd plist: ${launchdPlistPath(spec.name)}`;
124
+ await writeFile(plistPath, launchdPlist([process.execPath, bin], spec), { mode: 0o600 });
125
+ await run("launchctl", ["unload", plistPath]).catch(() => undefined); // 重装幂等:先卸旧 job(不存在时忽略)
126
+ await run("launchctl", ["load", plistPath]);
127
+ return `installed launchd plist: ${plistPath}`;
118
128
  }
119
129
  /** 服务状态。 */
120
130
  export async function serviceStatus(name = SERVICE_NAME) {
@@ -128,8 +138,9 @@ export async function serviceStatus(name = SERVICE_NAME) {
128
138
  }
129
139
  }
130
140
  try {
131
- await run("launchctl", ["print", `com.${name}`]);
132
- return "active";
141
+ const out = await run("launchctl", ["print", `com.${name}`]);
142
+ // loaded 但进程已退出的 job(如 exec 失败)print 仍成功 —— 按 state 细分
143
+ return out.includes("state = running") ? "active" : "loaded (not running)";
133
144
  }
134
145
  catch {
135
146
  return "unloaded";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rdsh-gateway",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "rdsh host-side component: LAN auth gateway + outbound tunnel endpoint (spawns dsh web)",
5
5
  "license": "MIT",
6
6
  "type": "module",