@questionbase/deskfree 0.6.10 → 0.7.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.js +511 -384
- package/dist/bin.js.map +1 -1
- package/dist/cli/start.d.ts +3 -0
- package/dist/cli/start.js +209 -0
- package/dist/cli/start.js.map +1 -0
- package/dist/cli/stop.d.ts +3 -0
- package/dist/cli/stop.js +80 -0
- package/dist/cli/stop.js.map +1 -0
- package/dist/cli/uninstall.d.ts +1 -1
- package/dist/cli/uninstall.js +20 -20
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/install.d.ts +0 -3
- package/dist/cli/install.js +0 -197
- package/dist/cli/install.js.map +0 -1
package/package.json
CHANGED
package/dist/cli/install.d.ts
DELETED
package/dist/cli/install.js
DELETED
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';
|
|
2
|
-
import { homedir } from 'os';
|
|
3
|
-
import { dirname, join } from 'path';
|
|
4
|
-
import { execSync } from 'child_process';
|
|
5
|
-
import { mkdirSync, writeFileSync, chmodSync } from 'fs';
|
|
6
|
-
|
|
7
|
-
createRequire(import.meta.url);
|
|
8
|
-
function getPlistLabel(name) {
|
|
9
|
-
return `com.deskfree.agent.${name}`;
|
|
10
|
-
}
|
|
11
|
-
function getServiceName(name) {
|
|
12
|
-
return `deskfree-${name}`;
|
|
13
|
-
}
|
|
14
|
-
function getMacPaths(name) {
|
|
15
|
-
const home = homedir();
|
|
16
|
-
const deskfreeDir = join(home, ".deskfree", name);
|
|
17
|
-
const plistLabel = getPlistLabel(name);
|
|
18
|
-
return {
|
|
19
|
-
deskfreeDir,
|
|
20
|
-
envFile: join(deskfreeDir, ".env"),
|
|
21
|
-
launcher: join(deskfreeDir, "deskfree.sh"),
|
|
22
|
-
logDir: join(deskfreeDir, "logs"),
|
|
23
|
-
plist: join(home, "Library", "LaunchAgents", `${plistLabel}.plist`)
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
function getLinuxPaths(name) {
|
|
27
|
-
const serviceName = getServiceName(name);
|
|
28
|
-
return {
|
|
29
|
-
serviceName,
|
|
30
|
-
serviceFile: `/etc/systemd/system/${serviceName}.service`,
|
|
31
|
-
envFile: `/etc/${serviceName}.env`,
|
|
32
|
-
stateDir: `/var/lib/${serviceName}`,
|
|
33
|
-
logDir: `/var/log/${serviceName}`
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
var PACKAGE = "@questionbase/deskfree@latest";
|
|
37
|
-
function installMac(botId, name, stage) {
|
|
38
|
-
const paths = getMacPaths(name);
|
|
39
|
-
const plistLabel = getPlistLabel(name);
|
|
40
|
-
let nodeBinDir;
|
|
41
|
-
try {
|
|
42
|
-
const nodePath = execSync("which node", { encoding: "utf8" }).trim();
|
|
43
|
-
nodeBinDir = dirname(nodePath);
|
|
44
|
-
} catch {
|
|
45
|
-
console.error("Error: node not found in PATH");
|
|
46
|
-
process.exit(1);
|
|
47
|
-
}
|
|
48
|
-
const extraPathDirs = (process.env["PATH"] ?? "").split(":").filter((d) => d && d !== nodeBinDir);
|
|
49
|
-
const fullPath = [nodeBinDir, ...extraPathDirs].join(":");
|
|
50
|
-
mkdirSync(paths.deskfreeDir, { recursive: true });
|
|
51
|
-
mkdirSync(paths.logDir, { recursive: true });
|
|
52
|
-
mkdirSync(dirname(paths.plist), { recursive: true });
|
|
53
|
-
const envLines = [`BOT=${botId}`, `DESKFREE_INSTANCE_NAME=${name}`];
|
|
54
|
-
if (stage) envLines.push(`STAGE=${stage}`);
|
|
55
|
-
writeFileSync(paths.envFile, envLines.join("\n") + "\n", { mode: 384 });
|
|
56
|
-
chmodSync(paths.envFile, 384);
|
|
57
|
-
console.log(`Wrote ${paths.envFile}`);
|
|
58
|
-
const launcher = `#!/bin/bash
|
|
59
|
-
set -euo pipefail
|
|
60
|
-
|
|
61
|
-
export PATH="${fullPath}"
|
|
62
|
-
|
|
63
|
-
# Update to latest version before starting
|
|
64
|
-
npm install -g ${PACKAGE} 2>/dev/null || true
|
|
65
|
-
|
|
66
|
-
# Source env
|
|
67
|
-
set -a
|
|
68
|
-
source "${paths.envFile}"
|
|
69
|
-
set +a
|
|
70
|
-
|
|
71
|
-
exec deskfree start
|
|
72
|
-
`;
|
|
73
|
-
writeFileSync(paths.launcher, launcher, { mode: 493 });
|
|
74
|
-
chmodSync(paths.launcher, 493);
|
|
75
|
-
console.log(`Wrote ${paths.launcher}`);
|
|
76
|
-
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
77
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
78
|
-
<plist version="1.0">
|
|
79
|
-
<dict>
|
|
80
|
-
<key>Label</key>
|
|
81
|
-
<string>${plistLabel}</string>
|
|
82
|
-
<key>ProgramArguments</key>
|
|
83
|
-
<array>
|
|
84
|
-
<string>${paths.launcher}</string>
|
|
85
|
-
</array>
|
|
86
|
-
<key>KeepAlive</key>
|
|
87
|
-
<true/>
|
|
88
|
-
<key>RunAtLoad</key>
|
|
89
|
-
<true/>
|
|
90
|
-
<key>StandardOutPath</key>
|
|
91
|
-
<string>${join(paths.logDir, "stdout.log")}</string>
|
|
92
|
-
<key>StandardErrorPath</key>
|
|
93
|
-
<string>${join(paths.logDir, "stderr.log")}</string>
|
|
94
|
-
<key>ThrottleInterval</key>
|
|
95
|
-
<integer>10</integer>
|
|
96
|
-
</dict>
|
|
97
|
-
</plist>
|
|
98
|
-
`;
|
|
99
|
-
writeFileSync(paths.plist, plist);
|
|
100
|
-
console.log(`Wrote ${paths.plist}`);
|
|
101
|
-
try {
|
|
102
|
-
execSync(`launchctl bootout gui/$(id -u) ${paths.plist}`, {
|
|
103
|
-
stdio: "ignore"
|
|
104
|
-
});
|
|
105
|
-
} catch {
|
|
106
|
-
}
|
|
107
|
-
execSync(`launchctl bootstrap gui/$(id -u) ${paths.plist}`);
|
|
108
|
-
console.log(`
|
|
109
|
-
Service ${plistLabel} installed and started.`);
|
|
110
|
-
console.log(`Check status: launchctl print gui/$(id -u)/${plistLabel}`);
|
|
111
|
-
console.log(`Logs: tail -f ${join(paths.logDir, "stdout.log")}`);
|
|
112
|
-
}
|
|
113
|
-
function installLinux(botId, name, stage) {
|
|
114
|
-
if (process.getuid?.() !== 0) {
|
|
115
|
-
console.error("Error: install must be run as root (use sudo)");
|
|
116
|
-
process.exit(1);
|
|
117
|
-
}
|
|
118
|
-
const paths = getLinuxPaths(name);
|
|
119
|
-
const serviceName = getServiceName(name);
|
|
120
|
-
const systemUser = serviceName;
|
|
121
|
-
let npmPath;
|
|
122
|
-
let nodeBinDir;
|
|
123
|
-
try {
|
|
124
|
-
npmPath = execSync("which npm", { encoding: "utf8" }).trim();
|
|
125
|
-
nodeBinDir = dirname(npmPath);
|
|
126
|
-
} catch {
|
|
127
|
-
console.error("Error: npm not found in PATH");
|
|
128
|
-
process.exit(1);
|
|
129
|
-
}
|
|
130
|
-
try {
|
|
131
|
-
execSync(`id ${systemUser}`, { stdio: "ignore" });
|
|
132
|
-
} catch {
|
|
133
|
-
execSync(
|
|
134
|
-
`useradd --system --no-create-home --shell /usr/sbin/nologin ${systemUser}`
|
|
135
|
-
);
|
|
136
|
-
console.log(`Created system user: ${systemUser}`);
|
|
137
|
-
}
|
|
138
|
-
mkdirSync(paths.stateDir, { recursive: true });
|
|
139
|
-
mkdirSync(paths.logDir, { recursive: true });
|
|
140
|
-
execSync(
|
|
141
|
-
`chown ${systemUser}:${systemUser} ${paths.stateDir} ${paths.logDir}`
|
|
142
|
-
);
|
|
143
|
-
console.log(`Created ${paths.stateDir} and ${paths.logDir}`);
|
|
144
|
-
const envLines = [`BOT=${botId}`, `DESKFREE_INSTANCE_NAME=${name}`];
|
|
145
|
-
if (stage) envLines.push(`STAGE=${stage}`);
|
|
146
|
-
writeFileSync(paths.envFile, envLines.join("\n") + "\n", { mode: 384 });
|
|
147
|
-
chmodSync(paths.envFile, 384);
|
|
148
|
-
console.log(`Wrote ${paths.envFile}`);
|
|
149
|
-
const unit = `[Unit]
|
|
150
|
-
Description=DeskFree Agent (${name})
|
|
151
|
-
After=network-online.target
|
|
152
|
-
Wants=network-online.target
|
|
153
|
-
|
|
154
|
-
[Service]
|
|
155
|
-
Type=simple
|
|
156
|
-
User=${systemUser}
|
|
157
|
-
Group=${systemUser}
|
|
158
|
-
WorkingDirectory=${paths.stateDir}
|
|
159
|
-
Environment=PATH=${nodeBinDir}:/usr/local/bin:/usr/bin:/bin
|
|
160
|
-
ExecStartPre=+${npmPath} install -g ${PACKAGE}
|
|
161
|
-
ExecStart=${nodeBinDir}/deskfree start
|
|
162
|
-
EnvironmentFile=${paths.envFile}
|
|
163
|
-
Environment=NODE_ENV=production
|
|
164
|
-
Environment=DESKFREE_STATE_DIR=${paths.stateDir}
|
|
165
|
-
Environment=DESKFREE_TOOLS_DIR=${paths.stateDir}/tools
|
|
166
|
-
Restart=always
|
|
167
|
-
RestartSec=10
|
|
168
|
-
StandardOutput=append:${paths.logDir}/stdout.log
|
|
169
|
-
StandardError=append:${paths.logDir}/stderr.log
|
|
170
|
-
|
|
171
|
-
[Install]
|
|
172
|
-
WantedBy=multi-user.target
|
|
173
|
-
`;
|
|
174
|
-
writeFileSync(paths.serviceFile, unit);
|
|
175
|
-
console.log(`Wrote ${paths.serviceFile}`);
|
|
176
|
-
execSync("systemctl daemon-reload");
|
|
177
|
-
execSync(`systemctl enable ${serviceName}`);
|
|
178
|
-
execSync(`systemctl start ${serviceName}`);
|
|
179
|
-
console.log(`
|
|
180
|
-
Service ${serviceName} installed and started.`);
|
|
181
|
-
console.log(`Check status: systemctl status ${serviceName}`);
|
|
182
|
-
console.log(`Logs: tail -f ${paths.logDir}/stdout.log`);
|
|
183
|
-
}
|
|
184
|
-
function install(botId, name, stage) {
|
|
185
|
-
if (process.platform === "darwin") {
|
|
186
|
-
installMac(botId, name, stage);
|
|
187
|
-
} else if (process.platform === "linux") {
|
|
188
|
-
installLinux(botId, name, stage);
|
|
189
|
-
} else {
|
|
190
|
-
console.error(`Unsupported platform: ${process.platform}`);
|
|
191
|
-
process.exit(1);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
export { install };
|
|
196
|
-
//# sourceMappingURL=install.js.map
|
|
197
|
-
//# sourceMappingURL=install.js.map
|
package/dist/cli/install.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/paths.ts","../../src/cli/install.ts"],"names":["join"],"mappings":"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","file":"install.js","sourcesContent":["import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nconst DEFAULT_NAME = 'main';\n\nexport function resolveName(name?: string): string {\n return name ?? DEFAULT_NAME;\n}\n\nexport function getPlistLabel(name: string): string {\n return `com.deskfree.agent.${name}`;\n}\n\nexport function getServiceName(name: string): string {\n return `deskfree-${name}`;\n}\n\nexport function getMacPaths(name: string) {\n const home = homedir();\n const deskfreeDir = join(home, '.deskfree', name);\n const plistLabel = getPlistLabel(name);\n return {\n deskfreeDir,\n envFile: join(deskfreeDir, '.env'),\n launcher: join(deskfreeDir, 'deskfree.sh'),\n logDir: join(deskfreeDir, 'logs'),\n plist: join(home, 'Library', 'LaunchAgents', `${plistLabel}.plist`),\n };\n}\n\nexport function getLinuxPaths(name: string) {\n const serviceName = getServiceName(name);\n return {\n serviceName,\n serviceFile: `/etc/systemd/system/${serviceName}.service`,\n envFile: `/etc/${serviceName}.env`,\n stateDir: `/var/lib/${serviceName}`,\n logDir: `/var/log/${serviceName}`,\n };\n}\n\n/** Parse --name <value> from argv, returning [resolvedName, remainingArgs]. */\nexport function parseName(args: string[]): [string, string[]] {\n const idx = args.indexOf('--name');\n if (idx === -1) return [DEFAULT_NAME, args];\n const name = args[idx + 1];\n if (!name || name.startsWith('-')) {\n console.error('Error: --name requires a value (e.g. --name kasper)');\n process.exit(1);\n }\n if (!/^[a-z0-9-]+$/i.test(name)) {\n console.error(\n 'Error: --name must contain only letters, numbers, and hyphens',\n );\n process.exit(1);\n }\n const remaining = [...args.slice(0, idx), ...args.slice(idx + 2)];\n return [name, remaining];\n}\n","import {\n getLinuxPaths,\n getMacPaths,\n getPlistLabel,\n getServiceName,\n} from './paths.js';\nimport { execSync } from 'node:child_process';\nimport { chmodSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nconst PACKAGE = '@questionbase/deskfree@latest';\n\n// ── macOS (launchd) ─────────────────────────────────────────────────────────\n\nfunction installMac(botId: string, name: string, stage?: string): void {\n const paths = getMacPaths(name);\n const plistLabel = getPlistLabel(name);\n\n // Resolve full path to node's bin directory (survives nvm/fnm across sessions)\n let nodeBinDir: string;\n try {\n const nodePath = execSync('which node', { encoding: 'utf8' }).trim();\n nodeBinDir = dirname(nodePath);\n } catch {\n console.error('Error: node not found in PATH');\n process.exit(1);\n }\n\n // Capture extra PATH dirs from the user's shell so the launcher can find\n // tools like `claude` that live outside the node bin directory.\n const extraPathDirs = (process.env['PATH'] ?? '')\n .split(':')\n .filter((d) => d && d !== nodeBinDir);\n const fullPath = [nodeBinDir, ...extraPathDirs].join(':');\n\n // Create directories\n mkdirSync(paths.deskfreeDir, { recursive: true });\n mkdirSync(paths.logDir, { recursive: true });\n mkdirSync(dirname(paths.plist), { recursive: true });\n\n // Write env file (chmod 600)\n const envLines = [`BOT=${botId}`, `DESKFREE_INSTANCE_NAME=${name}`];\n if (stage) envLines.push(`STAGE=${stage}`);\n writeFileSync(paths.envFile, envLines.join('\\n') + '\\n', { mode: 0o600 });\n chmodSync(paths.envFile, 0o600);\n console.log(`Wrote ${paths.envFile}`);\n\n // Write launcher script\n const launcher = `#!/bin/bash\nset -euo pipefail\n\nexport PATH=\"${fullPath}\"\n\n# Update to latest version before starting\nnpm install -g ${PACKAGE} 2>/dev/null || true\n\n# Source env\nset -a\nsource \"${paths.envFile}\"\nset +a\n\nexec deskfree start\n`;\n\n writeFileSync(paths.launcher, launcher, { mode: 0o755 });\n chmodSync(paths.launcher, 0o755);\n console.log(`Wrote ${paths.launcher}`);\n\n // Write LaunchAgent plist\n const plist = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${plistLabel}</string>\n <key>ProgramArguments</key>\n <array>\n <string>${paths.launcher}</string>\n </array>\n <key>KeepAlive</key>\n <true/>\n <key>RunAtLoad</key>\n <true/>\n <key>StandardOutPath</key>\n <string>${join(paths.logDir, 'stdout.log')}</string>\n <key>StandardErrorPath</key>\n <string>${join(paths.logDir, 'stderr.log')}</string>\n <key>ThrottleInterval</key>\n <integer>10</integer>\n</dict>\n</plist>\n`;\n\n writeFileSync(paths.plist, plist);\n console.log(`Wrote ${paths.plist}`);\n\n // Unload if already loaded (ignore errors)\n try {\n execSync(`launchctl bootout gui/$(id -u) ${paths.plist}`, {\n stdio: 'ignore',\n });\n } catch {\n // not loaded — fine\n }\n\n // Load the service\n execSync(`launchctl bootstrap gui/$(id -u) ${paths.plist}`);\n\n console.log(`\\nService ${plistLabel} installed and started.`);\n console.log(`Check status: launchctl print gui/$(id -u)/${plistLabel}`);\n console.log(`Logs: tail -f ${join(paths.logDir, 'stdout.log')}`);\n}\n\n// ── Linux (systemd) ─────────────────────────────────────────────────────────\n\nfunction installLinux(botId: string, name: string, stage?: string): void {\n if (process.getuid?.() !== 0) {\n console.error('Error: install must be run as root (use sudo)');\n process.exit(1);\n }\n\n const paths = getLinuxPaths(name);\n const serviceName = getServiceName(name);\n const systemUser = serviceName;\n\n // Resolve full npm/node bin dir so systemd doesn't depend on PATH\n let npmPath: string;\n let nodeBinDir: string;\n try {\n npmPath = execSync('which npm', { encoding: 'utf8' }).trim();\n nodeBinDir = dirname(npmPath);\n } catch {\n console.error('Error: npm not found in PATH');\n process.exit(1);\n }\n\n // Create dedicated system user (no login shell, no home dir)\n try {\n execSync(`id ${systemUser}`, { stdio: 'ignore' });\n } catch {\n execSync(\n `useradd --system --no-create-home --shell /usr/sbin/nologin ${systemUser}`,\n );\n console.log(`Created system user: ${systemUser}`);\n }\n\n // Create state and log directories owned by the service user\n mkdirSync(paths.stateDir, { recursive: true });\n mkdirSync(paths.logDir, { recursive: true });\n execSync(\n `chown ${systemUser}:${systemUser} ${paths.stateDir} ${paths.logDir}`,\n );\n console.log(`Created ${paths.stateDir} and ${paths.logDir}`);\n\n // Write env file (chmod 600 — only root can read)\n const envLines = [`BOT=${botId}`, `DESKFREE_INSTANCE_NAME=${name}`];\n if (stage) envLines.push(`STAGE=${stage}`);\n writeFileSync(paths.envFile, envLines.join('\\n') + '\\n', { mode: 0o600 });\n chmodSync(paths.envFile, 0o600);\n console.log(`Wrote ${paths.envFile}`);\n\n // Write systemd unit file\n const unit = `[Unit]\nDescription=DeskFree Agent (${name})\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=simple\nUser=${systemUser}\nGroup=${systemUser}\nWorkingDirectory=${paths.stateDir}\nEnvironment=PATH=${nodeBinDir}:/usr/local/bin:/usr/bin:/bin\nExecStartPre=+${npmPath} install -g ${PACKAGE}\nExecStart=${nodeBinDir}/deskfree start\nEnvironmentFile=${paths.envFile}\nEnvironment=NODE_ENV=production\nEnvironment=DESKFREE_STATE_DIR=${paths.stateDir}\nEnvironment=DESKFREE_TOOLS_DIR=${paths.stateDir}/tools\nRestart=always\nRestartSec=10\nStandardOutput=append:${paths.logDir}/stdout.log\nStandardError=append:${paths.logDir}/stderr.log\n\n[Install]\nWantedBy=multi-user.target\n`;\n\n writeFileSync(paths.serviceFile, unit);\n console.log(`Wrote ${paths.serviceFile}`);\n\n // Enable and start\n execSync('systemctl daemon-reload');\n execSync(`systemctl enable ${serviceName}`);\n execSync(`systemctl start ${serviceName}`);\n\n console.log(`\\nService ${serviceName} installed and started.`);\n console.log(`Check status: systemctl status ${serviceName}`);\n console.log(`Logs: tail -f ${paths.logDir}/stdout.log`);\n}\n\n// ── Public API ──────────────────────────────────────────────────────────────\n\nexport function install(botId: string, name: string, stage?: string): void {\n if (process.platform === 'darwin') {\n installMac(botId, name, stage);\n } else if (process.platform === 'linux') {\n installLinux(botId, name, stage);\n } else {\n console.error(`Unsupported platform: ${process.platform}`);\n process.exit(1);\n }\n}\n"]}
|