@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.
@@ -0,0 +1,3 @@
1
+ declare function start(botId: string, stage?: string): void;
2
+
3
+ export { start };
@@ -0,0 +1,209 @@
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 { existsSync, mkdirSync, writeFileSync, chmodSync } from 'fs';
6
+
7
+ createRequire(import.meta.url);
8
+ function getPlistLabel(botId) {
9
+ return `com.deskfree.agent.${botId}`;
10
+ }
11
+ function getServiceName(botId) {
12
+ return `deskfree-${botId}`;
13
+ }
14
+ function getMacPaths(botId) {
15
+ const home = homedir();
16
+ const deskfreeDir = join(home, ".deskfree", botId);
17
+ const plistLabel = getPlistLabel(botId);
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(botId) {
27
+ const serviceName = getServiceName(botId);
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 startMac(botId, stage) {
38
+ const paths = getMacPaths(botId);
39
+ const plistLabel = getPlistLabel(botId);
40
+ const isInstalled = existsSync(paths.plist);
41
+ if (!isInstalled) {
42
+ let nodeBinDir;
43
+ try {
44
+ const nodePath = execSync("which node", { encoding: "utf8" }).trim();
45
+ nodeBinDir = dirname(nodePath);
46
+ } catch {
47
+ console.error("Error: node not found in PATH");
48
+ process.exit(1);
49
+ }
50
+ const extraPathDirs = (process.env["PATH"] ?? "").split(":").filter((d) => d && d !== nodeBinDir);
51
+ const fullPath = [nodeBinDir, ...extraPathDirs].join(":");
52
+ mkdirSync(paths.deskfreeDir, { recursive: true });
53
+ mkdirSync(paths.logDir, { recursive: true });
54
+ mkdirSync(dirname(paths.plist), { recursive: true });
55
+ const envLines = [`BOT=${botId}`];
56
+ if (stage) envLines.push(`STAGE=${stage}`);
57
+ writeFileSync(paths.envFile, envLines.join("\n") + "\n", { mode: 384 });
58
+ chmodSync(paths.envFile, 384);
59
+ console.log(`Wrote ${paths.envFile}`);
60
+ const launcher = `#!/bin/bash
61
+ set -euo pipefail
62
+
63
+ export PATH="${fullPath}"
64
+
65
+ # Update to latest version before starting
66
+ npm install -g ${PACKAGE} 2>/dev/null || true
67
+
68
+ # Source env
69
+ set -a
70
+ source "${paths.envFile}"
71
+ set +a
72
+
73
+ exec deskfree run "$BOT"
74
+ `;
75
+ writeFileSync(paths.launcher, launcher, { mode: 493 });
76
+ chmodSync(paths.launcher, 493);
77
+ console.log(`Wrote ${paths.launcher}`);
78
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
79
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
80
+ <plist version="1.0">
81
+ <dict>
82
+ <key>Label</key>
83
+ <string>${plistLabel}</string>
84
+ <key>ProgramArguments</key>
85
+ <array>
86
+ <string>${paths.launcher}</string>
87
+ </array>
88
+ <key>KeepAlive</key>
89
+ <true/>
90
+ <key>RunAtLoad</key>
91
+ <true/>
92
+ <key>StandardOutPath</key>
93
+ <string>${join(paths.logDir, "stdout.log")}</string>
94
+ <key>StandardErrorPath</key>
95
+ <string>${join(paths.logDir, "stderr.log")}</string>
96
+ <key>ThrottleInterval</key>
97
+ <integer>10</integer>
98
+ </dict>
99
+ </plist>
100
+ `;
101
+ writeFileSync(paths.plist, plist);
102
+ console.log(`Wrote ${paths.plist}`);
103
+ }
104
+ try {
105
+ execSync(`launchctl bootout gui/$(id -u) ${paths.plist}`, {
106
+ stdio: "ignore"
107
+ });
108
+ } catch {
109
+ }
110
+ execSync(`launchctl bootstrap gui/$(id -u) ${paths.plist}`);
111
+ if (isInstalled) {
112
+ console.log(`Bot ${botId} started.`);
113
+ } else {
114
+ console.log(`
115
+ Bot ${botId} installed and started.`);
116
+ }
117
+ console.log(`Logs: tail -f ${join(paths.logDir, "stdout.log")}`);
118
+ }
119
+ function startLinux(botId, stage) {
120
+ const paths = getLinuxPaths(botId);
121
+ const serviceName = getServiceName(botId);
122
+ const isInstalled = existsSync(paths.serviceFile);
123
+ if (!isInstalled) {
124
+ if (process.getuid?.() !== 0) {
125
+ console.error("Error: first-time setup must be run as root (use sudo)");
126
+ process.exit(1);
127
+ }
128
+ const systemUser = serviceName;
129
+ let npmPath;
130
+ let nodeBinDir;
131
+ try {
132
+ npmPath = execSync("which npm", { encoding: "utf8" }).trim();
133
+ nodeBinDir = dirname(npmPath);
134
+ } catch {
135
+ console.error("Error: npm not found in PATH");
136
+ process.exit(1);
137
+ }
138
+ try {
139
+ execSync(`id ${systemUser}`, { stdio: "ignore" });
140
+ } catch {
141
+ execSync(
142
+ `useradd --system --no-create-home --shell /usr/sbin/nologin ${systemUser}`
143
+ );
144
+ console.log(`Created system user: ${systemUser}`);
145
+ }
146
+ mkdirSync(paths.stateDir, { recursive: true });
147
+ mkdirSync(paths.logDir, { recursive: true });
148
+ execSync(
149
+ `chown ${systemUser}:${systemUser} ${paths.stateDir} ${paths.logDir}`
150
+ );
151
+ console.log(`Created ${paths.stateDir} and ${paths.logDir}`);
152
+ const envLines = [`BOT=${botId}`];
153
+ if (stage) envLines.push(`STAGE=${stage}`);
154
+ writeFileSync(paths.envFile, envLines.join("\n") + "\n", { mode: 384 });
155
+ chmodSync(paths.envFile, 384);
156
+ console.log(`Wrote ${paths.envFile}`);
157
+ const unit = `[Unit]
158
+ Description=DeskFree Agent (${botId})
159
+ After=network-online.target
160
+ Wants=network-online.target
161
+
162
+ [Service]
163
+ Type=simple
164
+ User=${systemUser}
165
+ Group=${systemUser}
166
+ WorkingDirectory=${paths.stateDir}
167
+ Environment=PATH=${nodeBinDir}:/usr/local/bin:/usr/bin:/bin
168
+ ExecStartPre=+${npmPath} install -g ${PACKAGE}
169
+ ExecStart=${nodeBinDir}/deskfree run ${botId}
170
+ EnvironmentFile=${paths.envFile}
171
+ Environment=NODE_ENV=production
172
+ Environment=DESKFREE_STATE_DIR=${paths.stateDir}
173
+ Environment=DESKFREE_TOOLS_DIR=${paths.stateDir}/tools
174
+ Restart=always
175
+ RestartSec=10
176
+ StandardOutput=append:${paths.logDir}/stdout.log
177
+ StandardError=append:${paths.logDir}/stderr.log
178
+
179
+ [Install]
180
+ WantedBy=multi-user.target
181
+ `;
182
+ writeFileSync(paths.serviceFile, unit);
183
+ console.log(`Wrote ${paths.serviceFile}`);
184
+ execSync("systemctl daemon-reload");
185
+ execSync(`systemctl enable ${serviceName}`);
186
+ }
187
+ execSync(`systemctl start ${serviceName}`);
188
+ if (isInstalled) {
189
+ console.log(`Bot ${botId} started.`);
190
+ } else {
191
+ console.log(`
192
+ Bot ${botId} installed and started.`);
193
+ }
194
+ console.log(`Logs: tail -f ${paths.logDir}/stdout.log`);
195
+ }
196
+ function start(botId, stage) {
197
+ if (process.platform === "darwin") {
198
+ startMac(botId, stage);
199
+ } else if (process.platform === "linux") {
200
+ startLinux(botId, stage);
201
+ } else {
202
+ console.error(`Unsupported platform: ${process.platform}`);
203
+ process.exit(1);
204
+ }
205
+ }
206
+
207
+ export { start };
208
+ //# sourceMappingURL=start.js.map
209
+ //# sourceMappingURL=start.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/paths.ts","../../src/cli/start.ts"],"names":["join"],"mappings":"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","file":"start.js","sourcesContent":["import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nexport function getPlistLabel(botId: string): string {\n return `com.deskfree.agent.${botId}`;\n}\n\nexport function getServiceName(botId: string): string {\n return `deskfree-${botId}`;\n}\n\nexport function getMacPaths(botId: string) {\n const home = homedir();\n const deskfreeDir = join(home, '.deskfree', botId);\n const plistLabel = getPlistLabel(botId);\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(botId: string) {\n const serviceName = getServiceName(botId);\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/**\n * Parse a bot ID from argv.\n * Returns [botId | null, commandArgs].\n * Bot ID is the first positional arg that looks like a bot ID (starts with 'B').\n */\nexport function parseBotId(args: string[]): [string | null, string[]] {\n // Look for --stage and skip its value\n const result: string[] = [];\n let botId: string | null = null;\n let i = 0;\n while (i < args.length) {\n if (args[i] === '--stage') {\n result.push(args[i]!, args[i + 1] ?? '');\n i += 2;\n continue;\n }\n // First positional arg that starts with B is the bot ID\n if (\n !botId &&\n args[i] &&\n !args[i]!.startsWith('-') &&\n /^B[A-Z0-9]+$/i.test(args[i]!)\n ) {\n botId = args[i]!;\n } else {\n result.push(args[i]!);\n }\n i++;\n }\n return [botId, result];\n}\n","import {\n getLinuxPaths,\n getMacPaths,\n getPlistLabel,\n getServiceName,\n} from './paths.js';\nimport { execSync } from 'node:child_process';\nimport { chmodSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nconst PACKAGE = '@questionbase/deskfree@latest';\n\n// ── macOS (launchd) ─────────────────────────────────────────────────────────\n\nfunction startMac(botId: string, stage?: string): void {\n const paths = getMacPaths(botId);\n const plistLabel = getPlistLabel(botId);\n const isInstalled = existsSync(paths.plist);\n\n if (!isInstalled) {\n // First-time setup\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 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}`];\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 run \"$BOT\"\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\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 if (isInstalled) {\n console.log(`Bot ${botId} started.`);\n } else {\n console.log(`\\nBot ${botId} installed and started.`);\n }\n console.log(`Logs: tail -f ${join(paths.logDir, 'stdout.log')}`);\n}\n\n// ── Linux (systemd) ─────────────────────────────────────────────────────────\n\nfunction startLinux(botId: string, stage?: string): void {\n const paths = getLinuxPaths(botId);\n const serviceName = getServiceName(botId);\n const isInstalled = existsSync(paths.serviceFile);\n\n if (!isInstalled) {\n if (process.getuid?.() !== 0) {\n console.error('Error: first-time setup must be run as root (use sudo)');\n process.exit(1);\n }\n\n const systemUser = serviceName;\n\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\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\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\n const envLines = [`BOT=${botId}`];\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 (${botId})\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 run ${botId}\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 execSync('systemctl daemon-reload');\n execSync(`systemctl enable ${serviceName}`);\n }\n\n execSync(`systemctl start ${serviceName}`);\n\n if (isInstalled) {\n console.log(`Bot ${botId} started.`);\n } else {\n console.log(`\\nBot ${botId} installed and started.`);\n }\n console.log(`Logs: tail -f ${paths.logDir}/stdout.log`);\n}\n\n// ── Public API ──────────────────────────────────────────────────────────────\n\nexport function start(botId: string, stage?: string): void {\n if (process.platform === 'darwin') {\n startMac(botId, stage);\n } else if (process.platform === 'linux') {\n startLinux(botId, stage);\n } else {\n console.error(`Unsupported platform: ${process.platform}`);\n process.exit(1);\n }\n}\n"]}
@@ -0,0 +1,3 @@
1
+ declare function stop(botId: string): void;
2
+
3
+ export { stop };
@@ -0,0 +1,80 @@
1
+ import { createRequire } from 'node:module';
2
+ import { homedir } from 'os';
3
+ import { join } from 'path';
4
+ import { execSync } from 'child_process';
5
+ import { existsSync } from 'fs';
6
+
7
+ createRequire(import.meta.url);
8
+ function getPlistLabel(botId) {
9
+ return `com.deskfree.agent.${botId}`;
10
+ }
11
+ function getServiceName(botId) {
12
+ return `deskfree-${botId}`;
13
+ }
14
+ function getMacPaths(botId) {
15
+ const home = homedir();
16
+ const deskfreeDir = join(home, ".deskfree", botId);
17
+ const plistLabel = getPlistLabel(botId);
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(botId) {
27
+ const serviceName = getServiceName(botId);
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 stopMac(botId) {
37
+ const paths = getMacPaths(botId);
38
+ if (!existsSync(paths.plist)) {
39
+ console.error(`Bot ${botId} is not installed.`);
40
+ console.error(`Run: deskfree start ${botId}`);
41
+ process.exit(1);
42
+ }
43
+ try {
44
+ execSync(`launchctl bootout gui/$(id -u) ${paths.plist}`, {
45
+ stdio: "ignore"
46
+ });
47
+ console.log(`Bot ${botId} stopped.`);
48
+ } catch {
49
+ console.log(`Bot ${botId} is not running.`);
50
+ }
51
+ }
52
+ function stopLinux(botId) {
53
+ const paths = getLinuxPaths(botId);
54
+ const serviceName = getServiceName(botId);
55
+ if (!existsSync(paths.serviceFile)) {
56
+ console.error(`Bot ${botId} is not installed.`);
57
+ console.error(`Run: deskfree start ${botId}`);
58
+ process.exit(1);
59
+ }
60
+ try {
61
+ execSync(`systemctl stop ${serviceName}`, { stdio: "inherit" });
62
+ console.log(`Bot ${botId} stopped.`);
63
+ } catch {
64
+ console.log(`Bot ${botId} is not running.`);
65
+ }
66
+ }
67
+ function stop(botId) {
68
+ if (process.platform === "darwin") {
69
+ stopMac(botId);
70
+ } else if (process.platform === "linux") {
71
+ stopLinux(botId);
72
+ } else {
73
+ console.error(`Unsupported platform: ${process.platform}`);
74
+ process.exit(1);
75
+ }
76
+ }
77
+
78
+ export { stop };
79
+ //# sourceMappingURL=stop.js.map
80
+ //# sourceMappingURL=stop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/paths.ts","../../src/cli/stop.ts"],"names":[],"mappings":"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","file":"stop.js","sourcesContent":["import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nexport function getPlistLabel(botId: string): string {\n return `com.deskfree.agent.${botId}`;\n}\n\nexport function getServiceName(botId: string): string {\n return `deskfree-${botId}`;\n}\n\nexport function getMacPaths(botId: string) {\n const home = homedir();\n const deskfreeDir = join(home, '.deskfree', botId);\n const plistLabel = getPlistLabel(botId);\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(botId: string) {\n const serviceName = getServiceName(botId);\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/**\n * Parse a bot ID from argv.\n * Returns [botId | null, commandArgs].\n * Bot ID is the first positional arg that looks like a bot ID (starts with 'B').\n */\nexport function parseBotId(args: string[]): [string | null, string[]] {\n // Look for --stage and skip its value\n const result: string[] = [];\n let botId: string | null = null;\n let i = 0;\n while (i < args.length) {\n if (args[i] === '--stage') {\n result.push(args[i]!, args[i + 1] ?? '');\n i += 2;\n continue;\n }\n // First positional arg that starts with B is the bot ID\n if (\n !botId &&\n args[i] &&\n !args[i]!.startsWith('-') &&\n /^B[A-Z0-9]+$/i.test(args[i]!)\n ) {\n botId = args[i]!;\n } else {\n result.push(args[i]!);\n }\n i++;\n }\n return [botId, result];\n}\n","import { getLinuxPaths, getMacPaths, getServiceName } from './paths.js';\nimport { execSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\n\nfunction stopMac(botId: string): void {\n const paths = getMacPaths(botId);\n\n if (!existsSync(paths.plist)) {\n console.error(`Bot ${botId} is not installed.`);\n console.error(`Run: deskfree start ${botId}`);\n process.exit(1);\n }\n\n try {\n execSync(`launchctl bootout gui/$(id -u) ${paths.plist}`, {\n stdio: 'ignore',\n });\n console.log(`Bot ${botId} stopped.`);\n } catch {\n console.log(`Bot ${botId} is not running.`);\n }\n}\n\nfunction stopLinux(botId: string): void {\n const paths = getLinuxPaths(botId);\n const serviceName = getServiceName(botId);\n\n if (!existsSync(paths.serviceFile)) {\n console.error(`Bot ${botId} is not installed.`);\n console.error(`Run: deskfree start ${botId}`);\n process.exit(1);\n }\n\n try {\n execSync(`systemctl stop ${serviceName}`, { stdio: 'inherit' });\n console.log(`Bot ${botId} stopped.`);\n } catch {\n console.log(`Bot ${botId} is not running.`);\n }\n}\n\nexport function stop(botId: string): void {\n if (process.platform === 'darwin') {\n stopMac(botId);\n } else if (process.platform === 'linux') {\n stopLinux(botId);\n } else {\n console.error(`Unsupported platform: ${process.platform}`);\n process.exit(1);\n }\n}\n"]}
@@ -1,3 +1,3 @@
1
- declare function uninstall(name: string): void;
1
+ declare function uninstall(botId: string): void;
2
2
 
3
3
  export { uninstall };
@@ -5,16 +5,16 @@ import { execSync } from 'child_process';
5
5
  import { existsSync, unlinkSync } from 'fs';
6
6
 
7
7
  createRequire(import.meta.url);
8
- function getPlistLabel(name) {
9
- return `com.deskfree.agent.${name}`;
8
+ function getPlistLabel(botId) {
9
+ return `com.deskfree.agent.${botId}`;
10
10
  }
11
- function getServiceName(name) {
12
- return `deskfree-${name}`;
11
+ function getServiceName(botId) {
12
+ return `deskfree-${botId}`;
13
13
  }
14
- function getMacPaths(name) {
14
+ function getMacPaths(botId) {
15
15
  const home = homedir();
16
- const deskfreeDir = join(home, ".deskfree", name);
17
- const plistLabel = getPlistLabel(name);
16
+ const deskfreeDir = join(home, ".deskfree", botId);
17
+ const plistLabel = getPlistLabel(botId);
18
18
  return {
19
19
  deskfreeDir,
20
20
  envFile: join(deskfreeDir, ".env"),
@@ -23,8 +23,8 @@ function getMacPaths(name) {
23
23
  plist: join(home, "Library", "LaunchAgents", `${plistLabel}.plist`)
24
24
  };
25
25
  }
26
- function getLinuxPaths(name) {
27
- const serviceName = getServiceName(name);
26
+ function getLinuxPaths(botId) {
27
+ const serviceName = getServiceName(botId);
28
28
  return {
29
29
  serviceName,
30
30
  serviceFile: `/etc/systemd/system/${serviceName}.service`,
@@ -33,9 +33,9 @@ function getLinuxPaths(name) {
33
33
  logDir: `/var/log/${serviceName}`
34
34
  };
35
35
  }
36
- function uninstallMac(name) {
37
- const paths = getMacPaths(name);
38
- const plistLabel = getPlistLabel(name);
36
+ function uninstallMac(botId) {
37
+ const paths = getMacPaths(botId);
38
+ const plistLabel = getPlistLabel(botId);
39
39
  try {
40
40
  execSync(`launchctl bootout gui/$(id -u) ${paths.plist}`, {
41
41
  stdio: "ignore"
@@ -48,18 +48,18 @@ function uninstallMac(name) {
48
48
  console.log(`Removed ${file}`);
49
49
  }
50
50
  }
51
- console.log(`Service ${plistLabel} uninstalled.`);
51
+ console.log(`Bot ${botId} (${plistLabel}) uninstalled.`);
52
52
  console.log(
53
53
  `Note: logs and state in ${paths.deskfreeDir} were preserved. Remove manually if desired.`
54
54
  );
55
55
  }
56
- function uninstallLinux(name) {
56
+ function uninstallLinux(botId) {
57
57
  if (process.getuid?.() !== 0) {
58
58
  console.error("Error: uninstall must be run as root (use sudo)");
59
59
  process.exit(1);
60
60
  }
61
- const paths = getLinuxPaths(name);
62
- const serviceName = getServiceName(name);
61
+ const paths = getLinuxPaths(botId);
62
+ const serviceName = getServiceName(botId);
63
63
  try {
64
64
  execSync(`systemctl stop ${serviceName}`, { stdio: "ignore" });
65
65
  } catch {
@@ -77,13 +77,13 @@ function uninstallLinux(name) {
77
77
  console.log(`Removed ${paths.envFile}`);
78
78
  }
79
79
  execSync("systemctl daemon-reload");
80
- console.log(`Service ${serviceName} uninstalled.`);
80
+ console.log(`Bot ${botId} (${serviceName}) uninstalled.`);
81
81
  }
82
- function uninstall(name) {
82
+ function uninstall(botId) {
83
83
  if (process.platform === "darwin") {
84
- uninstallMac(name);
84
+ uninstallMac(botId);
85
85
  } else if (process.platform === "linux") {
86
- uninstallLinux(name);
86
+ uninstallLinux(botId);
87
87
  } else {
88
88
  console.error(`Unsupported platform: ${process.platform}`);
89
89
  process.exit(1);
@@ -1 +1 @@
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-${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 { 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"]}
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\nexport function getPlistLabel(botId: string): string {\n return `com.deskfree.agent.${botId}`;\n}\n\nexport function getServiceName(botId: string): string {\n return `deskfree-${botId}`;\n}\n\nexport function getMacPaths(botId: string) {\n const home = homedir();\n const deskfreeDir = join(home, '.deskfree', botId);\n const plistLabel = getPlistLabel(botId);\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(botId: string) {\n const serviceName = getServiceName(botId);\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/**\n * Parse a bot ID from argv.\n * Returns [botId | null, commandArgs].\n * Bot ID is the first positional arg that looks like a bot ID (starts with 'B').\n */\nexport function parseBotId(args: string[]): [string | null, string[]] {\n // Look for --stage and skip its value\n const result: string[] = [];\n let botId: string | null = null;\n let i = 0;\n while (i < args.length) {\n if (args[i] === '--stage') {\n result.push(args[i]!, args[i + 1] ?? '');\n i += 2;\n continue;\n }\n // First positional arg that starts with B is the bot ID\n if (\n !botId &&\n args[i] &&\n !args[i]!.startsWith('-') &&\n /^B[A-Z0-9]+$/i.test(args[i]!)\n ) {\n botId = args[i]!;\n } else {\n result.push(args[i]!);\n }\n i++;\n }\n return [botId, result];\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(botId: string): void {\n const paths = getMacPaths(botId);\n const plistLabel = getPlistLabel(botId);\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(`Bot ${botId} (${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(botId: 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(botId);\n const serviceName = getServiceName(botId);\n\n // Stop and disable\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(`Bot ${botId} (${serviceName}) uninstalled.`);\n}\n\n// ── Public API ──────────────────────────────────────────────────────────────\n\nexport function uninstall(botId: string): void {\n if (process.platform === 'darwin') {\n uninstallMac(botId);\n } else if (process.platform === 'linux') {\n uninstallLinux(botId);\n } else {\n console.error(`Unsupported platform: ${process.platform}`);\n process.exit(1);\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -398,8 +398,8 @@ declare function loadConfig(): LocalConfig;
398
398
  * Env var overrides take precedence over API values for debugging/testing.
399
399
  *
400
400
  * When running multiple agents on the same machine, stateDir and toolsDir
401
- * are namespaced by instance name (DESKFREE_INSTANCE_NAME, default "main").
402
- * The instance name is set during `deskfree install --name <name>`.
401
+ * are namespaced by bot ID (BOT env var).
402
+ * Each bot gets its own directory under ~/.deskfree/<botId>/.
403
403
  */
404
404
  declare function mergeWithRemoteConfig(local: LocalConfig, remote: RuntimeBootstrapConfig): RuntimeConfig;
405
405
 
package/dist/index.js CHANGED
@@ -12642,18 +12642,8 @@ function runOneShotWorker(opts) {
12642
12642
  init_dist();
12643
12643
  var isDocker = process.env["DOCKER"] === "1" || existsSync("/.dockerenv");
12644
12644
  var DEFAULTS = {
12645
- stateDir: isDocker ? "/app/state" : join(
12646
- homedir(),
12647
- ".deskfree",
12648
- process.env["DESKFREE_INSTANCE_NAME"] ?? "main",
12649
- "state"
12650
- ),
12651
- toolsDir: isDocker ? "/app/tools" : join(
12652
- homedir(),
12653
- ".deskfree",
12654
- process.env["DESKFREE_INSTANCE_NAME"] ?? "main",
12655
- "tools"
12656
- ),
12645
+ stateDir: isDocker ? "/app/state" : join(homedir(), ".deskfree", process.env["BOT"] ?? "default", "state"),
12646
+ toolsDir: isDocker ? "/app/tools" : join(homedir(), ".deskfree", process.env["BOT"] ?? "default", "tools"),
12657
12647
  logLevel: "info",
12658
12648
  healthPort: 3100
12659
12649
  };
@@ -14494,7 +14484,7 @@ async function startAgent(opts) {
14494
14484
  log.info("DeskFree Agent Runtime starting...");
14495
14485
  const { getRotationToken: getRotationToken2, setInitialRotationToken: setInitialRotationToken2 } = await Promise.resolve().then(() => (init_ws_gateway(), ws_gateway_exports));
14496
14486
  const { collectFingerprint: collectFingerprint2 } = await Promise.resolve().then(() => (init_fingerprint(), fingerprint_exports));
14497
- const runtimeVersion = "0.6.10";
14487
+ const runtimeVersion = "0.7.1";
14498
14488
  const fingerprint = collectFingerprint2(localConfig.stateDir, runtimeVersion);
14499
14489
  log.info("Connecting to DeskFree...", { wsUrl: localConfig.wsUrl });
14500
14490
  const connectResult = await initialConnect({
@@ -14539,7 +14529,7 @@ async function startAgent(opts) {
14539
14529
  runtimeVersion,
14540
14530
  maxConcurrentWorkers: 5,
14541
14531
  // updated after WorkerManager is created
14542
- instanceName: process.env["DESKFREE_INSTANCE_NAME"] || void 0
14532
+ instanceName: process.env["BOT"] || void 0
14543
14533
  };
14544
14534
  mkdirSync(config.stateDir, { recursive: true });
14545
14535
  mkdirSync(config.toolsDir, { recursive: true });