openilink-app-runner 0.1.0 → 0.2.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/bin/runner.js +14 -0
- package/dist/src/config.js +24 -2
- package/dist/src/daemon.d.ts +2 -0
- package/dist/src/daemon.js +212 -0
- package/package.json +1 -1
package/dist/bin/runner.js
CHANGED
|
@@ -6,6 +6,7 @@ const config_1 = require("../src/config");
|
|
|
6
6
|
const sync_1 = require("../src/sync");
|
|
7
7
|
const hub_1 = require("../src/hub");
|
|
8
8
|
const handler_1 = require("../src/handler");
|
|
9
|
+
const daemon_1 = require("../src/daemon");
|
|
9
10
|
const program = new commander_1.Command();
|
|
10
11
|
program
|
|
11
12
|
.name("openilink-app-runner")
|
|
@@ -131,6 +132,19 @@ program
|
|
|
131
132
|
});
|
|
132
133
|
hub.connect();
|
|
133
134
|
});
|
|
135
|
+
program
|
|
136
|
+
.command("install")
|
|
137
|
+
.description("注册为系统服务(systemd / launchd),开机自启")
|
|
138
|
+
.option("-c, --config <path>", "配置文件路径", "runner.yaml")
|
|
139
|
+
.action((opts) => {
|
|
140
|
+
(0, daemon_1.install)((0, config_1.getConfigPath)(opts.config));
|
|
141
|
+
});
|
|
142
|
+
program
|
|
143
|
+
.command("uninstall")
|
|
144
|
+
.description("卸载系统服务")
|
|
145
|
+
.action(() => {
|
|
146
|
+
(0, daemon_1.uninstall)();
|
|
147
|
+
});
|
|
134
148
|
// Default: show help
|
|
135
149
|
if (process.argv.length <= 2) {
|
|
136
150
|
program.help();
|
package/dist/src/config.js
CHANGED
|
@@ -38,10 +38,28 @@ exports.loadConfig = loadConfig;
|
|
|
38
38
|
exports.saveConfig = saveConfig;
|
|
39
39
|
exports.initConfig = initConfig;
|
|
40
40
|
const fs = __importStar(require("fs"));
|
|
41
|
+
const os = __importStar(require("os"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
41
43
|
const yaml = __importStar(require("js-yaml"));
|
|
42
|
-
|
|
44
|
+
function defaultConfigDir() {
|
|
45
|
+
const platform = os.platform();
|
|
46
|
+
const home = os.homedir();
|
|
47
|
+
if (process.getuid?.() === 0) {
|
|
48
|
+
return "/etc/openilink-app-runner";
|
|
49
|
+
}
|
|
50
|
+
if (platform === "darwin") {
|
|
51
|
+
return path.join(home, "Library/Application Support/openilink-app-runner");
|
|
52
|
+
}
|
|
53
|
+
return path.join(home, ".config/openilink-app-runner");
|
|
54
|
+
}
|
|
43
55
|
function getConfigPath(custom) {
|
|
44
|
-
|
|
56
|
+
if (custom)
|
|
57
|
+
return custom;
|
|
58
|
+
// Check current directory first
|
|
59
|
+
if (fs.existsSync("runner.yaml"))
|
|
60
|
+
return "runner.yaml";
|
|
61
|
+
// Then standard directory
|
|
62
|
+
return path.join(defaultConfigDir(), "runner.yaml");
|
|
45
63
|
}
|
|
46
64
|
function loadConfig(configPath) {
|
|
47
65
|
if (!fs.existsSync(configPath)) {
|
|
@@ -60,6 +78,10 @@ function loadConfig(configPath) {
|
|
|
60
78
|
return config;
|
|
61
79
|
}
|
|
62
80
|
function saveConfig(configPath, config) {
|
|
81
|
+
const dir = path.dirname(configPath);
|
|
82
|
+
if (!fs.existsSync(dir)) {
|
|
83
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
84
|
+
}
|
|
63
85
|
const raw = yaml.dump(config, { lineWidth: -1, noRefs: true });
|
|
64
86
|
fs.writeFileSync(configPath, raw, "utf-8");
|
|
65
87
|
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.install = install;
|
|
37
|
+
exports.uninstall = uninstall;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const child_process_1 = require("child_process");
|
|
42
|
+
const SERVICE_NAME = "openilink-app-runner";
|
|
43
|
+
function getBinPath() {
|
|
44
|
+
try {
|
|
45
|
+
return (0, child_process_1.execSync)("which openilink-app-runner", { encoding: "utf-8" }).trim();
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Fallback: resolve from node_modules
|
|
49
|
+
return path.resolve(__dirname, "../bin/runner.js");
|
|
50
|
+
}
|
|
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
|
+
}
|
|
67
|
+
function systemdUnit(configPath) {
|
|
68
|
+
const absConfig = path.resolve(configPath);
|
|
69
|
+
const binPath = getBinPath();
|
|
70
|
+
const workDir = path.dirname(absConfig);
|
|
71
|
+
const user = getRealUser();
|
|
72
|
+
return `[Unit]
|
|
73
|
+
Description=OpeniLink App Runner
|
|
74
|
+
After=network.target
|
|
75
|
+
|
|
76
|
+
[Service]
|
|
77
|
+
Type=simple
|
|
78
|
+
User=${user.name}
|
|
79
|
+
ExecStart=${binPath} start --config ${absConfig}
|
|
80
|
+
WorkingDirectory=${workDir}
|
|
81
|
+
Restart=always
|
|
82
|
+
RestartSec=5
|
|
83
|
+
Environment=NODE_ENV=production
|
|
84
|
+
|
|
85
|
+
[Install]
|
|
86
|
+
WantedBy=multi-user.target
|
|
87
|
+
`;
|
|
88
|
+
}
|
|
89
|
+
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 服务`);
|
|
97
|
+
console.log(` 服务文件: ${unitPath}`);
|
|
98
|
+
console.log(` 配置文件: ${path.resolve(configPath)}`);
|
|
99
|
+
console.log(` 查看状态: systemctl status ${SERVICE_NAME}`);
|
|
100
|
+
console.log(` 查看日志: journalctl -u ${SERVICE_NAME} -f`);
|
|
101
|
+
}
|
|
102
|
+
function uninstallSystemd() {
|
|
103
|
+
try {
|
|
104
|
+
(0, child_process_1.execSync)(`systemctl stop ${SERVICE_NAME}`, { stdio: "ignore" });
|
|
105
|
+
}
|
|
106
|
+
catch { }
|
|
107
|
+
try {
|
|
108
|
+
(0, child_process_1.execSync)(`systemctl disable ${SERVICE_NAME}`, { stdio: "ignore" });
|
|
109
|
+
}
|
|
110
|
+
catch { }
|
|
111
|
+
const unitPath = `/etc/systemd/system/${SERVICE_NAME}.service`;
|
|
112
|
+
if (fs.existsSync(unitPath)) {
|
|
113
|
+
fs.unlinkSync(unitPath);
|
|
114
|
+
(0, child_process_1.execSync)("systemctl daemon-reload");
|
|
115
|
+
}
|
|
116
|
+
console.log(`✓ 已卸载 systemd 服务`);
|
|
117
|
+
}
|
|
118
|
+
// ========== macOS (launchd) ==========
|
|
119
|
+
function launchdPlist(configPath) {
|
|
120
|
+
const absConfig = path.resolve(configPath);
|
|
121
|
+
const binPath = getBinPath();
|
|
122
|
+
const logDir = path.join(os.homedir(), "Library/Logs");
|
|
123
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
124
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
125
|
+
<plist version="1.0">
|
|
126
|
+
<dict>
|
|
127
|
+
<key>Label</key>
|
|
128
|
+
<string>com.openilink.app-runner</string>
|
|
129
|
+
<key>ProgramArguments</key>
|
|
130
|
+
<array>
|
|
131
|
+
<string>${binPath}</string>
|
|
132
|
+
<string>start</string>
|
|
133
|
+
<string>--config</string>
|
|
134
|
+
<string>${absConfig}</string>
|
|
135
|
+
</array>
|
|
136
|
+
<key>RunAtLoad</key>
|
|
137
|
+
<true/>
|
|
138
|
+
<key>KeepAlive</key>
|
|
139
|
+
<true/>
|
|
140
|
+
<key>StandardOutPath</key>
|
|
141
|
+
<string>${logDir}/openilink-app-runner.log</string>
|
|
142
|
+
<key>StandardErrorPath</key>
|
|
143
|
+
<string>${logDir}/openilink-app-runner.err</string>
|
|
144
|
+
</dict>
|
|
145
|
+
</plist>
|
|
146
|
+
`;
|
|
147
|
+
}
|
|
148
|
+
function installLaunchd(configPath) {
|
|
149
|
+
const plistPath = path.join(os.homedir(), `Library/LaunchAgents/com.openilink.app-runner.plist`);
|
|
150
|
+
const plist = launchdPlist(configPath);
|
|
151
|
+
fs.mkdirSync(path.dirname(plistPath), { recursive: true });
|
|
152
|
+
fs.writeFileSync(plistPath, plist);
|
|
153
|
+
try {
|
|
154
|
+
(0, child_process_1.execSync)(`launchctl unload ${plistPath}`, { stdio: "ignore" });
|
|
155
|
+
}
|
|
156
|
+
catch { }
|
|
157
|
+
(0, child_process_1.execSync)(`launchctl load ${plistPath}`);
|
|
158
|
+
console.log(`✓ 已安装 launchd 服务`);
|
|
159
|
+
console.log(` 配置文件: ${plistPath}`);
|
|
160
|
+
console.log(` 日志: ~/Library/Logs/openilink-app-runner.log`);
|
|
161
|
+
}
|
|
162
|
+
function uninstallLaunchd() {
|
|
163
|
+
const plistPath = path.join(os.homedir(), `Library/LaunchAgents/com.openilink.app-runner.plist`);
|
|
164
|
+
try {
|
|
165
|
+
(0, child_process_1.execSync)(`launchctl unload ${plistPath}`, { stdio: "ignore" });
|
|
166
|
+
}
|
|
167
|
+
catch { }
|
|
168
|
+
if (fs.existsSync(plistPath)) {
|
|
169
|
+
fs.unlinkSync(plistPath);
|
|
170
|
+
}
|
|
171
|
+
console.log(`✓ 已卸载 launchd 服务`);
|
|
172
|
+
}
|
|
173
|
+
// ========== Public API ==========
|
|
174
|
+
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
|
+
}
|
|
184
|
+
if (!fs.existsSync(absConfig)) {
|
|
185
|
+
console.error(`配置文件不存在: ${absConfig}`);
|
|
186
|
+
console.error(`请先运行: openilink-app-runner init --hub-url <url> --token <token>`);
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
if (os.platform() === "darwin") {
|
|
190
|
+
installLaunchd(configPath);
|
|
191
|
+
}
|
|
192
|
+
else if (os.platform() === "linux") {
|
|
193
|
+
installSystemd(configPath);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
console.error(`不支持的平台: ${os.platform()}`);
|
|
197
|
+
console.error(`请手动运行: openilink-app-runner start --config ${absConfig}`);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function uninstall() {
|
|
202
|
+
if (os.platform() === "darwin") {
|
|
203
|
+
uninstallLaunchd();
|
|
204
|
+
}
|
|
205
|
+
else if (os.platform() === "linux") {
|
|
206
|
+
uninstallSystemd();
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
console.error(`不支持的平台: ${os.platform()}`);
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
}
|