openilink-app-runner 0.2.1 → 0.3.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.
@@ -1,2 +1,2 @@
1
- export declare function install(configPath: string): void;
1
+ export declare function install(configPath?: string): void;
2
2
  export declare function uninstall(): void;
@@ -39,43 +39,27 @@ const fs = __importStar(require("fs"));
39
39
  const os = __importStar(require("os"));
40
40
  const path = __importStar(require("path"));
41
41
  const child_process_1 = require("child_process");
42
+ const config_1 = require("./config");
42
43
  const SERVICE_NAME = "openilink-app-runner";
43
44
  function getBinPath() {
44
45
  try {
45
46
  return (0, child_process_1.execSync)("which openilink-app-runner", { encoding: "utf-8" }).trim();
46
47
  }
47
48
  catch {
48
- // Fallback: resolve from node_modules
49
49
  return path.resolve(__dirname, "../bin/runner.js");
50
50
  }
51
51
  }
52
- function getNodePath() {
53
- return process.execPath;
54
- }
55
- // ========== Linux (systemd) ==========
56
- function getRealUser() {
57
- const sudoUser = process.env.SUDO_USER;
58
- if (sudoUser) {
59
- try {
60
- const home = (0, child_process_1.execSync)(`eval echo ~${sudoUser}`, { encoding: "utf-8", shell: "/bin/sh" }).trim();
61
- return { name: sudoUser, home };
62
- }
63
- catch { }
64
- }
65
- return { name: os.userInfo().username, home: os.homedir() };
66
- }
52
+ // ========== Linux (systemd --user) ==========
67
53
  function systemdUnit(configPath) {
68
54
  const absConfig = path.resolve(configPath);
69
55
  const binPath = getBinPath();
70
56
  const workDir = path.dirname(absConfig);
71
- const user = getRealUser();
72
57
  return `[Unit]
73
58
  Description=OpeniLink App Runner
74
59
  After=network.target
75
60
 
76
61
  [Service]
77
62
  Type=simple
78
- User=${user.name}
79
63
  ExecStart=${binPath} start --config ${absConfig}
80
64
  WorkingDirectory=${workDir}
81
65
  Restart=always
@@ -83,37 +67,43 @@ RestartSec=5
83
67
  Environment=NODE_ENV=production
84
68
 
85
69
  [Install]
86
- WantedBy=multi-user.target
70
+ WantedBy=default.target
87
71
  `;
88
72
  }
89
73
  function installSystemd(configPath) {
90
- const unitPath = `/etc/systemd/system/${SERVICE_NAME}.service`;
91
- const unit = systemdUnit(configPath);
92
- fs.writeFileSync(unitPath, unit);
93
- (0, child_process_1.execSync)("systemctl daemon-reload");
94
- (0, child_process_1.execSync)(`systemctl enable ${SERVICE_NAME}`);
95
- (0, child_process_1.execSync)(`systemctl start ${SERVICE_NAME}`);
96
- console.log(`✓ 已安装 systemd 服务`);
74
+ const userDir = path.join(os.homedir(), ".config/systemd/user");
75
+ fs.mkdirSync(userDir, { recursive: true });
76
+ const unitPath = path.join(userDir, `${SERVICE_NAME}.service`);
77
+ fs.writeFileSync(unitPath, systemdUnit(configPath));
78
+ (0, child_process_1.execSync)(`systemctl --user daemon-reload`);
79
+ (0, child_process_1.execSync)(`systemctl --user enable ${SERVICE_NAME}`);
80
+ (0, child_process_1.execSync)(`systemctl --user start ${SERVICE_NAME}`);
81
+ // Enable lingering so user services run without login
82
+ try {
83
+ (0, child_process_1.execSync)(`loginctl enable-linger ${os.userInfo().username}`, { stdio: "ignore" });
84
+ }
85
+ catch { }
86
+ console.log(`✓ 已安装 systemd 用户服务(无需 sudo)`);
97
87
  console.log(` 服务文件: ${unitPath}`);
98
88
  console.log(` 配置文件: ${path.resolve(configPath)}`);
99
- console.log(` 查看状态: systemctl status ${SERVICE_NAME}`);
100
- console.log(` 查看日志: journalctl -u ${SERVICE_NAME} -f`);
89
+ console.log(` 查看状态: systemctl --user status ${SERVICE_NAME}`);
90
+ console.log(` 查看日志: journalctl --user -u ${SERVICE_NAME} -f`);
101
91
  }
102
92
  function uninstallSystemd() {
103
93
  try {
104
- (0, child_process_1.execSync)(`systemctl stop ${SERVICE_NAME}`, { stdio: "ignore" });
94
+ (0, child_process_1.execSync)(`systemctl --user stop ${SERVICE_NAME}`, { stdio: "ignore" });
105
95
  }
106
96
  catch { }
107
97
  try {
108
- (0, child_process_1.execSync)(`systemctl disable ${SERVICE_NAME}`, { stdio: "ignore" });
98
+ (0, child_process_1.execSync)(`systemctl --user disable ${SERVICE_NAME}`, { stdio: "ignore" });
109
99
  }
110
100
  catch { }
111
- const unitPath = `/etc/systemd/system/${SERVICE_NAME}.service`;
101
+ const unitPath = path.join(os.homedir(), `.config/systemd/user/${SERVICE_NAME}.service`);
112
102
  if (fs.existsSync(unitPath)) {
113
103
  fs.unlinkSync(unitPath);
114
- (0, child_process_1.execSync)("systemctl daemon-reload");
104
+ (0, child_process_1.execSync)(`systemctl --user daemon-reload`);
115
105
  }
116
- console.log(`✓ 已卸载 systemd 服务`);
106
+ console.log(`✓ 已卸载 systemd 用户服务`);
117
107
  }
118
108
  // ========== macOS (launchd) ==========
119
109
  function launchdPlist(configPath) {
@@ -146,55 +136,46 @@ function launchdPlist(configPath) {
146
136
  `;
147
137
  }
148
138
  function installLaunchd(configPath) {
149
- const plistPath = path.join(os.homedir(), `Library/LaunchAgents/com.openilink.app-runner.plist`);
150
- const plist = launchdPlist(configPath);
139
+ const plistPath = path.join(os.homedir(), "Library/LaunchAgents/com.openilink.app-runner.plist");
151
140
  fs.mkdirSync(path.dirname(plistPath), { recursive: true });
152
- fs.writeFileSync(plistPath, plist);
141
+ fs.writeFileSync(plistPath, launchdPlist(configPath));
153
142
  try {
154
143
  (0, child_process_1.execSync)(`launchctl unload ${plistPath}`, { stdio: "ignore" });
155
144
  }
156
145
  catch { }
157
146
  (0, child_process_1.execSync)(`launchctl load ${plistPath}`);
158
- console.log(`✓ 已安装 launchd 服务`);
147
+ console.log(`✓ 已安装 launchd 服务(无需 sudo)`);
159
148
  console.log(` 配置文件: ${plistPath}`);
160
149
  console.log(` 日志: ~/Library/Logs/openilink-app-runner.log`);
161
150
  }
162
151
  function uninstallLaunchd() {
163
- const plistPath = path.join(os.homedir(), `Library/LaunchAgents/com.openilink.app-runner.plist`);
152
+ const plistPath = path.join(os.homedir(), "Library/LaunchAgents/com.openilink.app-runner.plist");
164
153
  try {
165
154
  (0, child_process_1.execSync)(`launchctl unload ${plistPath}`, { stdio: "ignore" });
166
155
  }
167
156
  catch { }
168
- if (fs.existsSync(plistPath)) {
157
+ if (fs.existsSync(plistPath))
169
158
  fs.unlinkSync(plistPath);
170
- }
171
159
  console.log(`✓ 已卸载 launchd 服务`);
172
160
  }
173
161
  // ========== Public API ==========
174
162
  function install(configPath) {
175
- let absConfig = path.resolve(configPath);
176
- // If running under sudo and config not found, try the real user's config path
177
- if (!fs.existsSync(absConfig) && process.env.SUDO_USER) {
178
- const user = getRealUser();
179
- const userConfig = path.join(user.home, ".config/openilink-app-runner/runner.yaml");
180
- if (fs.existsSync(userConfig)) {
181
- absConfig = userConfig;
182
- }
183
- }
163
+ const resolved = configPath || (0, config_1.getConfigPath)();
164
+ const absConfig = path.resolve(resolved);
184
165
  if (!fs.existsSync(absConfig)) {
185
166
  console.error(`配置文件不存在: ${absConfig}`);
186
167
  console.error(`请先运行: openilink-app-runner init --hub-url <url> --token <token>`);
187
168
  process.exit(1);
188
169
  }
189
170
  if (os.platform() === "darwin") {
190
- installLaunchd(configPath);
171
+ installLaunchd(absConfig);
191
172
  }
192
173
  else if (os.platform() === "linux") {
193
- installSystemd(configPath);
174
+ installSystemd(absConfig);
194
175
  }
195
176
  else {
196
177
  console.error(`不支持的平台: ${os.platform()}`);
197
- console.error(`请手动运行: openilink-app-runner start --config ${absConfig}`);
178
+ console.error(`请手动运行: openilink-app-runner start`);
198
179
  process.exit(1);
199
180
  }
200
181
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openilink-app-runner",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Run local commands as OpeniLink Hub App tools — bridge CLI to WeChat",
5
5
  "bin": {
6
6
  "openilink-app-runner": "dist/bin/runner.js"