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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +86 -0
  3. package/bin/setup.js +209 -0
  4. 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, "&amp;")
34
+ .replace(/</g, "&lt;")
35
+ .replace(/>/g, "&gt;")
36
+ .replace(/"/g, "&quot;")
37
+ .replace(/'/g, "&apos;");
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
+ }