@agentsoc/beacon 0.0.3 → 0.0.4
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/cli.js +2 -1
- package/dist/service-install.d.ts.map +1 -1
- package/dist/service-install.js +108 -7
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -60,7 +60,8 @@ program
|
|
|
60
60
|
await installService();
|
|
61
61
|
}
|
|
62
62
|
catch (err) {
|
|
63
|
-
|
|
63
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
64
|
+
console.error(`[beacon] Install failed:\n\n${message}\n`);
|
|
64
65
|
process.exit(1);
|
|
65
66
|
}
|
|
66
67
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service-install.d.ts","sourceRoot":"","sources":["../src/service-install.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"service-install.d.ts","sourceRoot":"","sources":["../src/service-install.ts"],"names":[],"mappings":"AA2GA,wBAAsB,cAAc,kBAuEnC"}
|
package/dist/service-install.js
CHANGED
|
@@ -1,7 +1,101 @@
|
|
|
1
|
+
import { constants as fsConstants } from 'node:fs';
|
|
1
2
|
import fs from 'node:fs/promises';
|
|
2
3
|
import path from 'node:path';
|
|
3
|
-
import { execSync } from 'node:child_process';
|
|
4
|
+
import { execFileSync, execSync } from 'node:child_process';
|
|
4
5
|
import { BEACON_CONFIG_REQUIRED_MESSAGE, loadConfig } from './config.js';
|
|
6
|
+
const PERMISSION_DENIED_HINT = `That location is only writable as root. Run the same command with sudo, for example:
|
|
7
|
+
|
|
8
|
+
sudo beacon install
|
|
9
|
+
|
|
10
|
+
You will be prompted for your administrator password.`;
|
|
11
|
+
function permissionDeniedInstallError(destPath) {
|
|
12
|
+
return new Error(`Cannot install the system service: permission denied for ${path.dirname(destPath)}.\n\n${PERMISSION_DENIED_HINT}`);
|
|
13
|
+
}
|
|
14
|
+
async function assertServiceInstallWritable(destPath) {
|
|
15
|
+
const dir = path.dirname(destPath);
|
|
16
|
+
try {
|
|
17
|
+
await fs.access(dir, fsConstants.W_OK);
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
const code = e && typeof e === 'object' && 'code' in e
|
|
21
|
+
? e.code
|
|
22
|
+
: undefined;
|
|
23
|
+
if (code === 'EACCES' || code === 'EPERM') {
|
|
24
|
+
throw permissionDeniedInstallError(destPath);
|
|
25
|
+
}
|
|
26
|
+
throw e;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async function writeServiceFile(destPath, contents) {
|
|
30
|
+
try {
|
|
31
|
+
await fs.writeFile(destPath, contents, 'utf8');
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
const code = e && typeof e === 'object' && 'code' in e
|
|
35
|
+
? e.code
|
|
36
|
+
: undefined;
|
|
37
|
+
if (code === 'EACCES' || code === 'EPERM') {
|
|
38
|
+
throw permissionDeniedInstallError(destPath);
|
|
39
|
+
}
|
|
40
|
+
throw e;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** launchd does not use your shell PATH — use an absolute interpreter path. */
|
|
44
|
+
function resolveLaunchdRunner() {
|
|
45
|
+
const isBun = process.execPath.endsWith('bun') || process.execPath.endsWith('bun.exe');
|
|
46
|
+
if (isBun) {
|
|
47
|
+
return process.execPath;
|
|
48
|
+
}
|
|
49
|
+
const base = path.basename(process.execPath);
|
|
50
|
+
if (/^node(\.exe)?$/i.test(base)) {
|
|
51
|
+
return process.execPath;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const nodePath = execFileSync('/usr/bin/which', ['node'], {
|
|
55
|
+
encoding: 'utf8',
|
|
56
|
+
}).trim();
|
|
57
|
+
if (nodePath) {
|
|
58
|
+
return nodePath;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// fall through
|
|
63
|
+
}
|
|
64
|
+
return 'node';
|
|
65
|
+
}
|
|
66
|
+
function launchctlBootoutSystem(plistPath, label) {
|
|
67
|
+
try {
|
|
68
|
+
execFileSync('launchctl', ['bootout', 'system', plistPath], {
|
|
69
|
+
stdio: 'ignore',
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// try service target (older registration style)
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
execFileSync('launchctl', ['bootout', `system/${label}`], {
|
|
78
|
+
stdio: 'ignore',
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// not loaded yet
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function launchctlBootstrapSystem(plistPath) {
|
|
86
|
+
try {
|
|
87
|
+
execFileSync('launchctl', ['bootstrap', 'system', plistPath], {
|
|
88
|
+
encoding: 'utf8',
|
|
89
|
+
stdio: ['ignore', 'inherit', 'pipe'],
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
const err = e;
|
|
94
|
+
const detail = err.stderr?.toString('utf8').trim() ?? '';
|
|
95
|
+
const suffix = detail ? `\n${detail}` : '';
|
|
96
|
+
throw new Error(`launchctl bootstrap failed for ${plistPath}.${suffix}\n\nTry:\n sudo launchctl bootout system ${plistPath}\n sudo beacon install`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
5
99
|
export async function installService() {
|
|
6
100
|
const config = await loadConfig();
|
|
7
101
|
// Service units do not inherit the install shell's env — key must be saved in config.
|
|
@@ -28,22 +122,25 @@ Environment=NODE_ENV=production
|
|
|
28
122
|
WantedBy=multi-user.target
|
|
29
123
|
`;
|
|
30
124
|
const dest = '/etc/systemd/system/syslog-beacon.service';
|
|
31
|
-
await
|
|
125
|
+
await assertServiceInstallWritable(dest);
|
|
126
|
+
await writeServiceFile(dest, serviceUnit);
|
|
32
127
|
execSync('systemctl daemon-reload');
|
|
33
128
|
execSync('systemctl enable syslog-beacon');
|
|
34
129
|
execSync('systemctl start syslog-beacon');
|
|
35
130
|
console.log('[syslog-beacon] Installed systemd service');
|
|
36
131
|
}
|
|
37
132
|
else if (process.platform === 'darwin') {
|
|
133
|
+
const label = 'com.agentsoc.syslog-beacon';
|
|
134
|
+
const launchdRunner = resolveLaunchdRunner();
|
|
38
135
|
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
39
136
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
40
137
|
<plist version="1.0">
|
|
41
138
|
<dict>
|
|
42
139
|
<key>Label</key>
|
|
43
|
-
<string
|
|
140
|
+
<string>${label}</string>
|
|
44
141
|
<key>ProgramArguments</key>
|
|
45
142
|
<array>
|
|
46
|
-
<string>${
|
|
143
|
+
<string>${launchdRunner}</string>
|
|
47
144
|
<string>${cliPath}</string>
|
|
48
145
|
<string>run</string>
|
|
49
146
|
</array>
|
|
@@ -54,9 +151,13 @@ WantedBy=multi-user.target
|
|
|
54
151
|
</dict>
|
|
55
152
|
</plist>
|
|
56
153
|
`;
|
|
57
|
-
const dest =
|
|
58
|
-
await
|
|
59
|
-
|
|
154
|
+
const dest = `/Library/LaunchDaemons/${label}.plist`;
|
|
155
|
+
await assertServiceInstallWritable(dest);
|
|
156
|
+
launchctlBootoutSystem(dest, label);
|
|
157
|
+
await writeServiceFile(dest, plist);
|
|
158
|
+
execFileSync('chown', ['root:wheel', dest], { stdio: 'inherit' });
|
|
159
|
+
await fs.chmod(dest, 0o644);
|
|
160
|
+
launchctlBootstrapSystem(dest);
|
|
60
161
|
console.log('[syslog-beacon] Installed launchd service');
|
|
61
162
|
}
|
|
62
163
|
else if (process.platform === 'win32') {
|