@questionbase/deskfree 0.4.7 → 0.5.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 +812 -413
- package/dist/bin.js.map +1 -1
- package/dist/cli/install.d.ts +1 -1
- package/dist/cli/install.js +90 -40
- package/dist/cli/install.js.map +1 -1
- package/dist/cli/uninstall.d.ts +1 -1
- package/dist/cli/uninstall.js +53 -28
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/index.d.ts +15 -3
- package/dist/index.js +272 -63
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/install.d.ts
CHANGED
package/dist/cli/install.js
CHANGED
|
@@ -1,26 +1,42 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
|
-
import { execSync } from 'child_process';
|
|
3
|
-
import { mkdirSync, writeFileSync, chmodSync } from 'fs';
|
|
4
2
|
import { homedir } from 'os';
|
|
5
3
|
import { dirname, join } from 'path';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import { mkdirSync, writeFileSync, chmodSync } from 'fs';
|
|
6
6
|
|
|
7
7
|
createRequire(import.meta.url);
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
function
|
|
8
|
+
function getPlistLabel(name) {
|
|
9
|
+
return `com.deskfree.agent.${name}`;
|
|
10
|
+
}
|
|
11
|
+
function getServiceName(name) {
|
|
12
|
+
return `deskfree-agent-${name}`;
|
|
13
|
+
}
|
|
14
|
+
function getMacPaths(name) {
|
|
12
15
|
const home = homedir();
|
|
13
|
-
const deskfreeDir = join(home, ".deskfree");
|
|
16
|
+
const deskfreeDir = join(home, ".deskfree", name);
|
|
17
|
+
const plistLabel = getPlistLabel(name);
|
|
14
18
|
return {
|
|
15
19
|
deskfreeDir,
|
|
16
20
|
envFile: join(deskfreeDir, ".env"),
|
|
17
21
|
launcher: join(deskfreeDir, "launcher.sh"),
|
|
18
22
|
logDir: join(deskfreeDir, "logs"),
|
|
19
|
-
plist: join(home, "Library", "LaunchAgents", `${
|
|
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}`
|
|
20
34
|
};
|
|
21
35
|
}
|
|
22
|
-
|
|
23
|
-
|
|
36
|
+
var PACKAGE = "@questionbase/deskfree@latest";
|
|
37
|
+
function installMac(token, name) {
|
|
38
|
+
const paths = getMacPaths(name);
|
|
39
|
+
const plistLabel = getPlistLabel(name);
|
|
24
40
|
let nodeBinDir;
|
|
25
41
|
try {
|
|
26
42
|
const nodePath = execSync("which node", { encoding: "utf8" }).trim();
|
|
@@ -32,8 +48,13 @@ function installMac(token) {
|
|
|
32
48
|
mkdirSync(paths.deskfreeDir, { recursive: true });
|
|
33
49
|
mkdirSync(paths.logDir, { recursive: true });
|
|
34
50
|
mkdirSync(dirname(paths.plist), { recursive: true });
|
|
35
|
-
writeFileSync(
|
|
36
|
-
|
|
51
|
+
writeFileSync(
|
|
52
|
+
paths.envFile,
|
|
53
|
+
`DESKFREE_LAUNCH=${token}
|
|
54
|
+
DESKFREE_INSTANCE_NAME=${name}
|
|
55
|
+
`,
|
|
56
|
+
{ mode: 384 }
|
|
57
|
+
);
|
|
37
58
|
chmodSync(paths.envFile, 384);
|
|
38
59
|
console.log(`Wrote ${paths.envFile}`);
|
|
39
60
|
const launcher = `#!/bin/bash
|
|
@@ -59,7 +80,7 @@ exec deskfree-agent start
|
|
|
59
80
|
<plist version="1.0">
|
|
60
81
|
<dict>
|
|
61
82
|
<key>Label</key>
|
|
62
|
-
<string>${
|
|
83
|
+
<string>${plistLabel}</string>
|
|
63
84
|
<key>ProgramArguments</key>
|
|
64
85
|
<array>
|
|
65
86
|
<string>${paths.launcher}</string>
|
|
@@ -87,61 +108,90 @@ exec deskfree-agent start
|
|
|
87
108
|
}
|
|
88
109
|
execSync(`launchctl bootstrap gui/$(id -u) ${paths.plist}`);
|
|
89
110
|
console.log(`
|
|
90
|
-
Service ${
|
|
91
|
-
console.log(`Check status: launchctl print gui/$(id -u)/${
|
|
111
|
+
Service ${plistLabel} installed and started.`);
|
|
112
|
+
console.log(`Check status: launchctl print gui/$(id -u)/${plistLabel}`);
|
|
92
113
|
console.log(`Logs: tail -f ${join(paths.logDir, "stdout.log")}`);
|
|
93
114
|
}
|
|
94
|
-
|
|
95
|
-
var SYSTEMD_ENV_FILE = `/etc/${SERVICE_NAME}.env`;
|
|
96
|
-
function installLinux(token) {
|
|
115
|
+
function installLinux(token, name) {
|
|
97
116
|
if (process.getuid?.() !== 0) {
|
|
98
117
|
console.error("Error: install must be run as root (use sudo)");
|
|
99
118
|
process.exit(1);
|
|
100
119
|
}
|
|
101
|
-
|
|
120
|
+
const paths = getLinuxPaths(name);
|
|
121
|
+
const serviceName = getServiceName(name);
|
|
122
|
+
const systemUser = serviceName;
|
|
123
|
+
let npmPath;
|
|
124
|
+
let nodeBinDir;
|
|
102
125
|
try {
|
|
103
|
-
|
|
126
|
+
npmPath = execSync("which npm", { encoding: "utf8" }).trim();
|
|
127
|
+
nodeBinDir = dirname(npmPath);
|
|
104
128
|
} catch {
|
|
105
|
-
console.error("Error:
|
|
129
|
+
console.error("Error: npm not found in PATH");
|
|
106
130
|
process.exit(1);
|
|
107
131
|
}
|
|
108
|
-
|
|
109
|
-
`, {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
132
|
+
try {
|
|
133
|
+
execSync(`id ${systemUser}`, { stdio: "ignore" });
|
|
134
|
+
} catch {
|
|
135
|
+
execSync(
|
|
136
|
+
`useradd --system --no-create-home --shell /usr/sbin/nologin ${systemUser}`
|
|
137
|
+
);
|
|
138
|
+
console.log(`Created system user: ${systemUser}`);
|
|
139
|
+
}
|
|
140
|
+
mkdirSync(paths.stateDir, { recursive: true });
|
|
141
|
+
mkdirSync(paths.logDir, { recursive: true });
|
|
142
|
+
execSync(
|
|
143
|
+
`chown ${systemUser}:${systemUser} ${paths.stateDir} ${paths.logDir}`
|
|
144
|
+
);
|
|
145
|
+
console.log(`Created ${paths.stateDir} and ${paths.logDir}`);
|
|
146
|
+
writeFileSync(
|
|
147
|
+
paths.envFile,
|
|
148
|
+
`DESKFREE_LAUNCH=${token}
|
|
149
|
+
DESKFREE_INSTANCE_NAME=${name}
|
|
150
|
+
`,
|
|
151
|
+
{ mode: 384 }
|
|
152
|
+
);
|
|
153
|
+
chmodSync(paths.envFile, 384);
|
|
154
|
+
console.log(`Wrote ${paths.envFile}`);
|
|
114
155
|
const unit = `[Unit]
|
|
115
|
-
Description=DeskFree Agent
|
|
156
|
+
Description=DeskFree Agent (${name})
|
|
116
157
|
After=network-online.target
|
|
117
158
|
Wants=network-online.target
|
|
118
159
|
|
|
119
160
|
[Service]
|
|
120
161
|
Type=simple
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
162
|
+
User=${systemUser}
|
|
163
|
+
Group=${systemUser}
|
|
164
|
+
WorkingDirectory=${paths.stateDir}
|
|
165
|
+
Environment=PATH=${nodeBinDir}:/usr/local/bin:/usr/bin:/bin
|
|
166
|
+
ExecStartPre=+${npmPath} install -g ${PACKAGE}
|
|
167
|
+
ExecStart=${nodeBinDir}/deskfree-agent start
|
|
168
|
+
EnvironmentFile=${paths.envFile}
|
|
124
169
|
Environment=NODE_ENV=production
|
|
170
|
+
Environment=DESKFREE_STATE_DIR=${paths.stateDir}
|
|
171
|
+
Environment=DESKFREE_TOOLS_DIR=${paths.stateDir}/tools
|
|
125
172
|
Restart=always
|
|
126
173
|
RestartSec=10
|
|
174
|
+
StandardOutput=append:${paths.logDir}/stdout.log
|
|
175
|
+
StandardError=append:${paths.logDir}/stderr.log
|
|
127
176
|
|
|
128
177
|
[Install]
|
|
129
178
|
WantedBy=multi-user.target
|
|
130
179
|
`;
|
|
131
|
-
writeFileSync(
|
|
132
|
-
console.log(`Wrote ${
|
|
180
|
+
writeFileSync(paths.serviceFile, unit);
|
|
181
|
+
console.log(`Wrote ${paths.serviceFile}`);
|
|
133
182
|
execSync("systemctl daemon-reload");
|
|
134
|
-
execSync(`systemctl enable ${
|
|
135
|
-
execSync(`systemctl start ${
|
|
183
|
+
execSync(`systemctl enable ${serviceName}`);
|
|
184
|
+
execSync(`systemctl start ${serviceName}`);
|
|
136
185
|
console.log(`
|
|
137
|
-
Service ${
|
|
138
|
-
console.log(`Check status: systemctl status ${
|
|
186
|
+
Service ${serviceName} installed and started.`);
|
|
187
|
+
console.log(`Check status: systemctl status ${serviceName}`);
|
|
188
|
+
console.log(`Logs: tail -f ${paths.logDir}/stdout.log`);
|
|
139
189
|
}
|
|
140
|
-
function install(token) {
|
|
190
|
+
function install(token, name) {
|
|
141
191
|
if (process.platform === "darwin") {
|
|
142
|
-
installMac(token);
|
|
192
|
+
installMac(token, name);
|
|
143
193
|
} else if (process.platform === "linux") {
|
|
144
|
-
installLinux(token);
|
|
194
|
+
installLinux(token, name);
|
|
145
195
|
} else {
|
|
146
196
|
console.error(`Unsupported platform: ${process.platform}`);
|
|
147
197
|
process.exit(1);
|
package/dist/cli/install.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/install.ts"],"names":[],"mappings":"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","file":"install.js","sourcesContent":["import { execSync } from 'node:child_process';\nimport { chmodSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\nconst SERVICE_NAME = 'deskfree-agent';\nconst PACKAGE = '@questionbase/deskfree@latest';\n\n// ── macOS (launchd) ─────────────────────────────────────────────────────────\n\nconst PLIST_LABEL = 'com.deskfree.agent';\n\nfunction getMacPaths() {\n const home = homedir();\n const deskfreeDir = join(home, '.deskfree');\n return {\n deskfreeDir,\n envFile: join(deskfreeDir, '.env'),\n launcher: join(deskfreeDir, 'launcher.sh'),\n logDir: join(deskfreeDir, 'logs'),\n plist: join(home, 'Library', 'LaunchAgents', `${PLIST_LABEL}.plist`),\n };\n}\n\nfunction installMac(token: string): void {\n const paths = getMacPaths();\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 // 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 writeFileSync(paths.envFile, `DESKFREE_LAUNCH=${token}\\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=\"${nodeBinDir}:$PATH\"\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-agent 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>${PLIST_LABEL}</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 ${PLIST_LABEL} installed and started.`);\n console.log(`Check status: launchctl print gui/$(id -u)/${PLIST_LABEL}`);\n console.log(`Logs: tail -f ${join(paths.logDir, 'stdout.log')}`);\n}\n\n// ── Linux (systemd) ─────────────────────────────────────────────────────────\n\nconst SYSTEMD_SERVICE_FILE = `/etc/systemd/system/${SERVICE_NAME}.service`;\nconst SYSTEMD_ENV_FILE = `/etc/${SERVICE_NAME}.env`;\n\nfunction installLinux(token: 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 // Resolve full npx path so systemd doesn't depend on PATH\n let npxPath: string;\n try {\n npxPath = execSync('which npx', { encoding: 'utf8' }).trim();\n } catch {\n console.error('Error: npx not found in PATH');\n process.exit(1);\n }\n\n // Write env file with token (chmod 600 — only root can read)\n writeFileSync(SYSTEMD_ENV_FILE, `DESKFREE_LAUNCH=${token}\\n`, {\n mode: 0o600,\n });\n chmodSync(SYSTEMD_ENV_FILE, 0o600);\n console.log(`Wrote ${SYSTEMD_ENV_FILE}`);\n\n // Write systemd unit file\n const unit = `[Unit]\nDescription=DeskFree Agent\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=simple\nExecStartPre=${npxPath} ${PACKAGE} --version\nExecStart=${npxPath} ${PACKAGE} start\nEnvironmentFile=${SYSTEMD_ENV_FILE}\nEnvironment=NODE_ENV=production\nRestart=always\nRestartSec=10\n\n[Install]\nWantedBy=multi-user.target\n`;\n\n writeFileSync(SYSTEMD_SERVICE_FILE, unit);\n console.log(`Wrote ${SYSTEMD_SERVICE_FILE}`);\n\n // Enable and start\n execSync('systemctl daemon-reload');\n execSync(`systemctl enable ${SERVICE_NAME}`);\n execSync(`systemctl start ${SERVICE_NAME}`);\n\n console.log(`\\nService ${SERVICE_NAME} installed and started.`);\n console.log(`Check status: systemctl status ${SERVICE_NAME}`);\n}\n\n// ── Public API ──────────────────────────────────────────────────────────────\n\nexport function install(token: string): void {\n if (process.platform === 'darwin') {\n installMac(token);\n } else if (process.platform === 'linux') {\n installLinux(token);\n } else {\n console.error(`Unsupported platform: ${process.platform}`);\n process.exit(1);\n }\n}\n"]}
|
|
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-agent-${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, 'launcher.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(token: string, name: 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 // 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 writeFileSync(\n paths.envFile,\n `DESKFREE_LAUNCH=${token}\\nDESKFREE_INSTANCE_NAME=${name}\\n`,\n { mode: 0o600 },\n );\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=\"${nodeBinDir}:$PATH\"\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-agent 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(token: string, name: 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 with token (chmod 600 — only root can read)\n writeFileSync(\n paths.envFile,\n `DESKFREE_LAUNCH=${token}\\nDESKFREE_INSTANCE_NAME=${name}\\n`,\n { mode: 0o600 },\n );\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-agent 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(token: string, name: string): void {\n if (process.platform === 'darwin') {\n installMac(token, name);\n } else if (process.platform === 'linux') {\n installLinux(token, name);\n } else {\n console.error(`Unsupported platform: ${process.platform}`);\n process.exit(1);\n }\n}\n"]}
|
package/dist/cli/uninstall.d.ts
CHANGED
package/dist/cli/uninstall.js
CHANGED
|
@@ -1,64 +1,89 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
|
-
import { execSync } from 'child_process';
|
|
3
|
-
import { existsSync, unlinkSync } from 'fs';
|
|
4
2
|
import { homedir } from 'os';
|
|
5
3
|
import { join } from 'path';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import { existsSync, unlinkSync } from 'fs';
|
|
6
6
|
|
|
7
7
|
createRequire(import.meta.url);
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
function getPlistLabel(name) {
|
|
9
|
+
return `com.deskfree.agent.${name}`;
|
|
10
|
+
}
|
|
11
|
+
function getServiceName(name) {
|
|
12
|
+
return `deskfree-agent-${name}`;
|
|
13
|
+
}
|
|
14
|
+
function getMacPaths(name) {
|
|
11
15
|
const home = homedir();
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
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, "launcher.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
|
+
function uninstallMac(name) {
|
|
37
|
+
const paths = getMacPaths(name);
|
|
38
|
+
const plistLabel = getPlistLabel(name);
|
|
16
39
|
try {
|
|
17
|
-
execSync(`launchctl bootout gui/$(id -u) ${plist}`, {
|
|
40
|
+
execSync(`launchctl bootout gui/$(id -u) ${paths.plist}`, {
|
|
41
|
+
stdio: "ignore"
|
|
42
|
+
});
|
|
18
43
|
} catch {
|
|
19
44
|
}
|
|
20
|
-
for (const file of [plist, envFile, launcher]) {
|
|
45
|
+
for (const file of [paths.plist, paths.envFile, paths.launcher]) {
|
|
21
46
|
if (existsSync(file)) {
|
|
22
47
|
unlinkSync(file);
|
|
23
48
|
console.log(`Removed ${file}`);
|
|
24
49
|
}
|
|
25
50
|
}
|
|
26
|
-
console.log(`Service ${
|
|
51
|
+
console.log(`Service ${plistLabel} uninstalled.`);
|
|
27
52
|
console.log(
|
|
28
|
-
`Note: logs and state in ${deskfreeDir} were preserved. Remove manually if desired.`
|
|
53
|
+
`Note: logs and state in ${paths.deskfreeDir} were preserved. Remove manually if desired.`
|
|
29
54
|
);
|
|
30
55
|
}
|
|
31
|
-
|
|
32
|
-
var SYSTEMD_ENV_FILE = `/etc/${SERVICE_NAME}.env`;
|
|
33
|
-
function uninstallLinux() {
|
|
56
|
+
function uninstallLinux(name) {
|
|
34
57
|
if (process.getuid?.() !== 0) {
|
|
35
58
|
console.error("Error: uninstall must be run as root (use sudo)");
|
|
36
59
|
process.exit(1);
|
|
37
60
|
}
|
|
61
|
+
const paths = getLinuxPaths(name);
|
|
62
|
+
const serviceName = getServiceName(name);
|
|
38
63
|
try {
|
|
39
|
-
execSync(`systemctl stop ${
|
|
64
|
+
execSync(`systemctl stop ${serviceName}`, { stdio: "ignore" });
|
|
40
65
|
} catch {
|
|
41
66
|
}
|
|
42
67
|
try {
|
|
43
|
-
execSync(`systemctl disable ${
|
|
68
|
+
execSync(`systemctl disable ${serviceName}`, { stdio: "ignore" });
|
|
44
69
|
} catch {
|
|
45
70
|
}
|
|
46
|
-
if (existsSync(
|
|
47
|
-
unlinkSync(
|
|
48
|
-
console.log(`Removed ${
|
|
71
|
+
if (existsSync(paths.serviceFile)) {
|
|
72
|
+
unlinkSync(paths.serviceFile);
|
|
73
|
+
console.log(`Removed ${paths.serviceFile}`);
|
|
49
74
|
}
|
|
50
|
-
if (existsSync(
|
|
51
|
-
unlinkSync(
|
|
52
|
-
console.log(`Removed ${
|
|
75
|
+
if (existsSync(paths.envFile)) {
|
|
76
|
+
unlinkSync(paths.envFile);
|
|
77
|
+
console.log(`Removed ${paths.envFile}`);
|
|
53
78
|
}
|
|
54
79
|
execSync("systemctl daemon-reload");
|
|
55
|
-
console.log(`Service ${
|
|
80
|
+
console.log(`Service ${serviceName} uninstalled.`);
|
|
56
81
|
}
|
|
57
|
-
function uninstall() {
|
|
82
|
+
function uninstall(name) {
|
|
58
83
|
if (process.platform === "darwin") {
|
|
59
|
-
uninstallMac();
|
|
84
|
+
uninstallMac(name);
|
|
60
85
|
} else if (process.platform === "linux") {
|
|
61
|
-
uninstallLinux();
|
|
86
|
+
uninstallLinux(name);
|
|
62
87
|
} else {
|
|
63
88
|
console.error(`Unsupported platform: ${process.platform}`);
|
|
64
89
|
process.exit(1);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/uninstall.ts"],"names":[],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"sources":["../../src/cli/paths.ts","../../src/cli/uninstall.ts"],"names":[],"mappings":"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","file":"uninstall.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-agent-${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, 'launcher.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 { existsSync, unlinkSync } from 'node:fs';\n\n// ── macOS (launchd) ─────────────────────────────────────────────────────────\n\nfunction uninstallMac(name: string): void {\n const paths = getMacPaths(name);\n const plistLabel = getPlistLabel(name);\n\n // Unload (ignore errors if not loaded)\n try {\n execSync(`launchctl bootout gui/$(id -u) ${paths.plist}`, {\n stdio: 'ignore',\n });\n } catch {\n // not loaded — fine\n }\n\n // Remove files\n for (const file of [paths.plist, paths.envFile, paths.launcher]) {\n if (existsSync(file)) {\n unlinkSync(file);\n console.log(`Removed ${file}`);\n }\n }\n\n console.log(`Service ${plistLabel} uninstalled.`);\n console.log(\n `Note: logs and state in ${paths.deskfreeDir} were preserved. Remove manually if desired.`,\n );\n}\n\n// ── Linux (systemd) ─────────────────────────────────────────────────────────\n\nfunction uninstallLinux(name: string): void {\n if (process.getuid?.() !== 0) {\n console.error('Error: uninstall must be run as root (use sudo)');\n process.exit(1);\n }\n\n const paths = getLinuxPaths(name);\n const serviceName = getServiceName(name);\n\n // Stop and disable (ignore errors if not running/enabled)\n try {\n execSync(`systemctl stop ${serviceName}`, { stdio: 'ignore' });\n } catch {\n // not running — fine\n }\n try {\n execSync(`systemctl disable ${serviceName}`, { stdio: 'ignore' });\n } catch {\n // not enabled — fine\n }\n\n // Remove files\n if (existsSync(paths.serviceFile)) {\n unlinkSync(paths.serviceFile);\n console.log(`Removed ${paths.serviceFile}`);\n }\n if (existsSync(paths.envFile)) {\n unlinkSync(paths.envFile);\n console.log(`Removed ${paths.envFile}`);\n }\n\n execSync('systemctl daemon-reload');\n console.log(`Service ${serviceName} uninstalled.`);\n}\n\n// ── Public API ──────────────────────────────────────────────────────────────\n\nexport function uninstall(name: string): void {\n if (process.platform === 'darwin') {\n uninstallMac(name);\n } else if (process.platform === 'linux') {\n uninstallLinux(name);\n } else {\n console.error(`Unsupported platform: ${process.platform}`);\n process.exit(1);\n }\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PluginLogger, DeskFreeClient, DeskFreeTool, ChatMessage, RuntimeBootstrapConfig } from '@deskfree/core';
|
|
1
|
+
import { PluginLogger, AgentContext, DeskFreeClient, DeskFreeTool, ChatMessage, RuntimeBootstrapConfig } from '@deskfree/core';
|
|
2
2
|
import * as _anthropic_ai_claude_agent_sdk from '@anthropic-ai/claude-agent-sdk';
|
|
3
3
|
import { McpSdkServerConfigWithInstance, SDKMessage, SDKUserMessage, Query, tool } from '@anthropic-ai/claude-agent-sdk';
|
|
4
4
|
|
|
@@ -28,6 +28,8 @@ interface OrchestratorQueryOptions {
|
|
|
28
28
|
sessionId?: string;
|
|
29
29
|
/** Path to user's Claude Code executable (uses bundled CLI if omitted). */
|
|
30
30
|
claudeCodePath?: string;
|
|
31
|
+
/** Runtime context for templated system prompt. Falls back to static directive if omitted. */
|
|
32
|
+
agentContext?: AgentContext;
|
|
31
33
|
}
|
|
32
34
|
/**
|
|
33
35
|
* Run the orchestrator agent. Returns an async iterable of SDKMessage events.
|
|
@@ -44,6 +46,8 @@ interface HeartbeatOptions {
|
|
|
44
46
|
model: string;
|
|
45
47
|
/** Path to user's Claude Code executable (uses bundled CLI if omitted). */
|
|
46
48
|
claudeCodePath?: string;
|
|
49
|
+
/** Runtime context for templated system prompt. Falls back to static directive if omitted. */
|
|
50
|
+
agentContext?: AgentContext;
|
|
47
51
|
}
|
|
48
52
|
/**
|
|
49
53
|
* Run a one-shot heartbeat query (no session persistence).
|
|
@@ -56,6 +60,8 @@ interface WorkerQueryOptions {
|
|
|
56
60
|
model: string;
|
|
57
61
|
/** Agent SDK session ID to resume (for multi-turn task conversations). */
|
|
58
62
|
sessionId?: string;
|
|
63
|
+
/** Runtime context for templated system prompt. Falls back to static directive if omitted. */
|
|
64
|
+
agentContext?: AgentContext;
|
|
59
65
|
}
|
|
60
66
|
/**
|
|
61
67
|
* Run the worker agent directly. Returns a Query (async iterable of
|
|
@@ -111,6 +117,8 @@ interface WorkerManagerDeps {
|
|
|
111
117
|
dailyLogCharBudget?: number;
|
|
112
118
|
/** Path to persist session history JSON (taskId → sessionId). */
|
|
113
119
|
sessionHistoryPath?: string;
|
|
120
|
+
/** Runtime context for templated system prompts. */
|
|
121
|
+
agentContext?: AgentContext;
|
|
114
122
|
}
|
|
115
123
|
declare class WorkerManager {
|
|
116
124
|
private workers;
|
|
@@ -386,6 +394,10 @@ interface RuntimeConfig extends LocalConfig {
|
|
|
386
394
|
baseUrl?: string;
|
|
387
395
|
/** Bot ID for this runtime instance */
|
|
388
396
|
botId: string;
|
|
397
|
+
/** Bot's display name (e.g. "Sofia") */
|
|
398
|
+
botName: string;
|
|
399
|
+
/** Deployment type (aws, docker, local) */
|
|
400
|
+
deploymentType: 'aws' | 'docker' | 'local' | null;
|
|
389
401
|
/** File ID for the bot's Memory file (null if not created yet) */
|
|
390
402
|
memoryFileId: string | null;
|
|
391
403
|
/** Hour of day (0-23) in local time for the nightly sleep cycle (null = disabled) */
|
|
@@ -409,8 +421,8 @@ declare function loadConfig(): LocalConfig;
|
|
|
409
421
|
* Env var overrides take precedence over API values for debugging/testing.
|
|
410
422
|
*
|
|
411
423
|
* When running multiple agents on the same machine, stateDir and toolsDir
|
|
412
|
-
* are
|
|
413
|
-
*
|
|
424
|
+
* are namespaced by instance name (DESKFREE_INSTANCE_NAME, default "main").
|
|
425
|
+
* The instance name is set during `deskfree-agent install --name <name>`.
|
|
414
426
|
*/
|
|
415
427
|
declare function mergeWithRemoteConfig(local: LocalConfig, remote: RuntimeBootstrapConfig): RuntimeConfig;
|
|
416
428
|
|