service-setup-cokacdir 1.0.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/LICENSE +21 -0
- package/README.md +86 -0
- package/bin/setup.js +209 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 코드깎는노인
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# service-setup-cokacdir
|
|
2
|
+
|
|
3
|
+
[cokacdir](https://cokacdir.cokac.com) 텔레그램 봇 서버를 **명령어 한 줄**로 시스템 서비스에 등록합니다.
|
|
4
|
+
|
|
5
|
+
- Linux, macOS 자동 감지
|
|
6
|
+
- `sudo` 불필요
|
|
7
|
+
- 서버 재부팅 시 자동 시작
|
|
8
|
+
- 죽어도 자동 재시작
|
|
9
|
+
- 토큰 변경 시 같은 명령어로 업데이트
|
|
10
|
+
|
|
11
|
+
## 설치 & 실행
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx -y service-setup-cokacdir <봇토큰>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
토큰 여러 개도 가능합니다.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx -y service-setup-cokacdir <봇토큰1> <봇토큰2> <봇토큰3>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
이게 끝입니다. OS를 자동 감지하여 서비스가 바로 시작됩니다.
|
|
24
|
+
|
|
25
|
+
## 토큰 변경
|
|
26
|
+
|
|
27
|
+
같은 명령어를 다시 실행하면 됩니다. 기존 서비스를 자동으로 멈추고 새 설정으로 재시작합니다.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx -y service-setup-cokacdir <새로운토큰1> <새로운토큰2>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 서비스 관리
|
|
34
|
+
|
|
35
|
+
### Linux
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# 상태 확인
|
|
39
|
+
systemctl --user status cokacdir
|
|
40
|
+
|
|
41
|
+
# 로그 보기
|
|
42
|
+
tail -f ~/.local/log/cokacdir.log
|
|
43
|
+
|
|
44
|
+
# 중지
|
|
45
|
+
systemctl --user stop cokacdir
|
|
46
|
+
|
|
47
|
+
# 완전 삭제
|
|
48
|
+
systemctl --user disable cokacdir && rm ~/.config/systemd/user/cokacdir.service
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### macOS
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# 상태 확인
|
|
55
|
+
launchctl list | grep com.cokacdir.server
|
|
56
|
+
|
|
57
|
+
# 로그 보기
|
|
58
|
+
tail -f ~/.local/log/cokacdir.log
|
|
59
|
+
|
|
60
|
+
# 중지
|
|
61
|
+
launchctl unload ~/Library/LaunchAgents/com.cokacdir.server.plist
|
|
62
|
+
|
|
63
|
+
# 완전 삭제
|
|
64
|
+
launchctl unload ~/Library/LaunchAgents/com.cokacdir.server.plist && rm ~/Library/LaunchAgents/com.cokacdir.server.plist
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## 동작 원리
|
|
68
|
+
|
|
69
|
+
이 명령어를 실행하면 OS를 자동 감지하여 아래 과정이 수행됩니다.
|
|
70
|
+
|
|
71
|
+
| | Linux | macOS |
|
|
72
|
+
|--|--|--|
|
|
73
|
+
| 서비스 파일 | `~/.config/systemd/user/cokacdir.service` | `~/Library/LaunchAgents/com.cokacdir.server.plist` |
|
|
74
|
+
| 등록 명령 | `systemctl --user enable & start` | `launchctl load` |
|
|
75
|
+
| 자동 재시작 | `Restart=always` | `KeepAlive=true` |
|
|
76
|
+
| 부팅 시 시작 | `loginctl enable-linger` | `RunAtLoad=true` |
|
|
77
|
+
|
|
78
|
+
## 요구사항
|
|
79
|
+
|
|
80
|
+
- Linux (systemd) 또는 macOS
|
|
81
|
+
- [cokacdir](https://cokacdir.cokac.com) 설치 완료
|
|
82
|
+
- Node.js >= 14
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
MIT
|
package/bin/setup.js
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { execSync } = require("child_process");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const os = require("os");
|
|
7
|
+
|
|
8
|
+
// --- 인자 파싱 ---
|
|
9
|
+
const tokens = process.argv.slice(2);
|
|
10
|
+
|
|
11
|
+
if (tokens.length === 0) {
|
|
12
|
+
console.error("Usage: npx service-setup-cokacdir <BOT_TOKEN> [BOT_TOKEN2] ...");
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// --- cokacdir 바이너리 찾기 ---
|
|
17
|
+
let binaryPath;
|
|
18
|
+
try {
|
|
19
|
+
binaryPath = execSync("which cokacdir", { encoding: "utf-8" }).trim();
|
|
20
|
+
} catch {
|
|
21
|
+
console.error("Error: cokacdir not found in PATH.");
|
|
22
|
+
console.error("Install cokacdir first, then retry.");
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const platform = os.platform();
|
|
27
|
+
const serviceName = "cokacdir";
|
|
28
|
+
const homeDir = os.homedir();
|
|
29
|
+
const tokensArg = tokens.join(" ");
|
|
30
|
+
|
|
31
|
+
function escapeXml(str) {
|
|
32
|
+
return str
|
|
33
|
+
.replace(/&/g, "&")
|
|
34
|
+
.replace(/</g, "<")
|
|
35
|
+
.replace(/>/g, ">")
|
|
36
|
+
.replace(/"/g, """)
|
|
37
|
+
.replace(/'/g, "'");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (platform === "linux") {
|
|
41
|
+
setupLinux();
|
|
42
|
+
} else if (platform === "darwin") {
|
|
43
|
+
setupMacOS();
|
|
44
|
+
} else {
|
|
45
|
+
console.error(`Unsupported platform: ${platform}`);
|
|
46
|
+
console.error("This tool supports Linux and macOS only.");
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ============================================================
|
|
51
|
+
// Linux — systemd user service
|
|
52
|
+
// ============================================================
|
|
53
|
+
function setupLinux() {
|
|
54
|
+
const serviceDir = path.join(homeDir, ".config", "systemd", "user");
|
|
55
|
+
const serviceFile = path.join(serviceDir, `${serviceName}.service`);
|
|
56
|
+
const logDir = path.join(homeDir, ".local", "log");
|
|
57
|
+
|
|
58
|
+
const serviceContent = `[Unit]
|
|
59
|
+
Description=Cokacdir Server Service
|
|
60
|
+
After=network.target
|
|
61
|
+
|
|
62
|
+
[Service]
|
|
63
|
+
Type=simple
|
|
64
|
+
ExecStart=${binaryPath} --ccserver ${tokensArg}
|
|
65
|
+
Restart=always
|
|
66
|
+
RestartSec=5
|
|
67
|
+
StandardOutput=append:${logDir}/cokacdir.log
|
|
68
|
+
StandardError=append:${logDir}/cokacdir.error.log
|
|
69
|
+
|
|
70
|
+
[Install]
|
|
71
|
+
WantedBy=default.target
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
fs.mkdirSync(serviceDir, { recursive: true });
|
|
75
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
76
|
+
|
|
77
|
+
// 기존 서비스 확인 및 중지
|
|
78
|
+
const isUpdate = fs.existsSync(serviceFile);
|
|
79
|
+
if (isUpdate) {
|
|
80
|
+
console.log("Existing service found. Stopping for update...");
|
|
81
|
+
try {
|
|
82
|
+
execSync(`systemctl --user stop ${serviceName}`, { stdio: "inherit" });
|
|
83
|
+
} catch {
|
|
84
|
+
// 이미 멈춰있을 수 있음
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fs.writeFileSync(serviceFile, serviceContent, { mode: 0o644 });
|
|
89
|
+
console.log(
|
|
90
|
+
isUpdate
|
|
91
|
+
? `Service file updated: ${serviceFile}`
|
|
92
|
+
: `Service file created: ${serviceFile}`
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
execSync("systemctl --user daemon-reload", { stdio: "inherit" });
|
|
97
|
+
execSync(`systemctl --user enable ${serviceName}`, { stdio: "inherit" });
|
|
98
|
+
execSync(`systemctl --user restart ${serviceName}`, { stdio: "inherit" });
|
|
99
|
+
} catch (err) {
|
|
100
|
+
console.error("Failed to register systemd user service:", err.message);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
execSync(`loginctl enable-linger ${os.userInfo().username}`, {
|
|
106
|
+
stdio: "inherit",
|
|
107
|
+
});
|
|
108
|
+
console.log("Linger enabled: service will start on boot.");
|
|
109
|
+
} catch {
|
|
110
|
+
console.log(
|
|
111
|
+
"Warning: could not enable linger. Service may not auto-start on boot."
|
|
112
|
+
);
|
|
113
|
+
console.log(
|
|
114
|
+
`Run manually: loginctl enable-linger ${os.userInfo().username}`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
console.log("\n------------------------------------------------");
|
|
119
|
+
console.log("Setup complete!");
|
|
120
|
+
console.log(`Status : systemctl --user status ${serviceName}`);
|
|
121
|
+
console.log(`Logs : tail -f ${logDir}/cokacdir.log`);
|
|
122
|
+
console.log(`Stop : systemctl --user stop ${serviceName}`);
|
|
123
|
+
console.log(`Remove : systemctl --user disable ${serviceName} && rm ${serviceFile}`);
|
|
124
|
+
console.log("------------------------------------------------");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ============================================================
|
|
128
|
+
// macOS — launchd LaunchAgents
|
|
129
|
+
// ============================================================
|
|
130
|
+
function setupMacOS() {
|
|
131
|
+
const agentDir = path.join(homeDir, "Library", "LaunchAgents");
|
|
132
|
+
const label = `com.cokacdir.server`;
|
|
133
|
+
const plistFile = path.join(agentDir, `${label}.plist`);
|
|
134
|
+
const logDir = path.join(homeDir, ".local", "log");
|
|
135
|
+
|
|
136
|
+
const programArgs = [binaryPath, "--ccserver", ...tokens];
|
|
137
|
+
const argsXml = programArgs
|
|
138
|
+
.map((arg) => ` <string>${escapeXml(arg)}</string>`)
|
|
139
|
+
.join("\n");
|
|
140
|
+
|
|
141
|
+
const plistContent = `<?xml version="1.0" encoding="UTF-8"?>
|
|
142
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
143
|
+
<plist version="1.0">
|
|
144
|
+
<dict>
|
|
145
|
+
<key>Label</key>
|
|
146
|
+
<string>${label}</string>
|
|
147
|
+
<key>ProgramArguments</key>
|
|
148
|
+
<array>
|
|
149
|
+
${argsXml}
|
|
150
|
+
</array>
|
|
151
|
+
<key>RunAtLoad</key>
|
|
152
|
+
<true/>
|
|
153
|
+
<key>KeepAlive</key>
|
|
154
|
+
<true/>
|
|
155
|
+
<key>StandardOutPath</key>
|
|
156
|
+
<string>${logDir}/cokacdir.log</string>
|
|
157
|
+
<key>StandardErrorPath</key>
|
|
158
|
+
<string>${logDir}/cokacdir.error.log</string>
|
|
159
|
+
</dict>
|
|
160
|
+
</plist>
|
|
161
|
+
`;
|
|
162
|
+
|
|
163
|
+
fs.mkdirSync(agentDir, { recursive: true });
|
|
164
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
165
|
+
|
|
166
|
+
const uid = process.getuid();
|
|
167
|
+
const domain = `gui/${uid}`;
|
|
168
|
+
|
|
169
|
+
// 기존 서비스 확인 및 중지
|
|
170
|
+
const isUpdate = fs.existsSync(plistFile);
|
|
171
|
+
if (isUpdate) {
|
|
172
|
+
console.log("Existing service found. Stopping for update...");
|
|
173
|
+
try {
|
|
174
|
+
execSync(`launchctl bootout ${domain}/${label}`, { stdio: "inherit" });
|
|
175
|
+
} catch {
|
|
176
|
+
try {
|
|
177
|
+
execSync(`launchctl unload ${plistFile}`, { stdio: "inherit" });
|
|
178
|
+
} catch {
|
|
179
|
+
// 이미 멈춰있을 수 있음
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
fs.writeFileSync(plistFile, plistContent, { mode: 0o644 });
|
|
185
|
+
console.log(
|
|
186
|
+
isUpdate
|
|
187
|
+
? `Plist file updated: ${plistFile}`
|
|
188
|
+
: `Plist file created: ${plistFile}`
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
execSync(`launchctl bootstrap ${domain} ${plistFile}`, { stdio: "inherit" });
|
|
193
|
+
} catch {
|
|
194
|
+
try {
|
|
195
|
+
execSync(`launchctl load ${plistFile}`, { stdio: "inherit" });
|
|
196
|
+
} catch (err) {
|
|
197
|
+
console.error("Failed to register launchd service:", err.message);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
console.log("\n------------------------------------------------");
|
|
203
|
+
console.log("Setup complete!");
|
|
204
|
+
console.log(`Status : launchctl list | grep ${label}`);
|
|
205
|
+
console.log(`Logs : tail -f ${logDir}/cokacdir.log`);
|
|
206
|
+
console.log(`Stop : launchctl unload ${plistFile}`);
|
|
207
|
+
console.log(`Remove : launchctl unload ${plistFile} && rm ${plistFile}`);
|
|
208
|
+
console.log("------------------------------------------------");
|
|
209
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "service-setup-cokacdir",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Register cokacdir as a system service with a single command (Linux & macOS)",
|
|
5
|
+
"bin": {
|
|
6
|
+
"service-setup-cokacdir": "./bin/setup.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin"
|
|
10
|
+
],
|
|
11
|
+
"keywords": [
|
|
12
|
+
"cokacdir",
|
|
13
|
+
"systemd",
|
|
14
|
+
"service",
|
|
15
|
+
"telegram",
|
|
16
|
+
"bot",
|
|
17
|
+
"daemon",
|
|
18
|
+
"launchd",
|
|
19
|
+
"macos"
|
|
20
|
+
],
|
|
21
|
+
"author": {
|
|
22
|
+
"name": "코드깎는노인",
|
|
23
|
+
"email": "monogatree@gmail.com",
|
|
24
|
+
"url": "https://cokacdir.cokac.com"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://cokacdir.cokac.com",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/monogatree/service-setup-cokacdir"
|
|
30
|
+
},
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=14"
|
|
34
|
+
},
|
|
35
|
+
"os": [
|
|
36
|
+
"linux",
|
|
37
|
+
"darwin"
|
|
38
|
+
]
|
|
39
|
+
}
|