@questionbase/deskfree 0.6.9 → 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
  };
@@ -14185,6 +14175,13 @@ ${userMessage}
14185
14175
  const delta = extractTextDelta2(message);
14186
14176
  if (delta) {
14187
14177
  fullText += delta;
14178
+ if (!fullText.trim()) {
14179
+ log.warn(`Skipping whitespace-only delta`, {
14180
+ delta: JSON.stringify(delta),
14181
+ fullText: JSON.stringify(fullText)
14182
+ });
14183
+ continue;
14184
+ }
14188
14185
  if (!streamStarted) {
14189
14186
  setInboundThreadId(taskId);
14190
14187
  await streamingSession.start(fullText, taskId);
@@ -14253,7 +14250,10 @@ ${userMessage}
14253
14250
  }
14254
14251
  }
14255
14252
  const errMsg = err instanceof Error ? err.message : String(err);
14256
- log.warn(`Worker drain loop error for task ${taskId}: ${errMsg}`);
14253
+ const errStack = err instanceof Error ? err.stack : void 0;
14254
+ log.warn(`Worker drain loop error for task ${taskId}: ${errMsg}`, {
14255
+ stack: errStack
14256
+ });
14257
14257
  try {
14258
14258
  await client.sendMessage({
14259
14259
  content: "Sorry, I encountered an error while working on this task. Please try again.",
@@ -14484,7 +14484,7 @@ async function startAgent(opts) {
14484
14484
  log.info("DeskFree Agent Runtime starting...");
14485
14485
  const { getRotationToken: getRotationToken2, setInitialRotationToken: setInitialRotationToken2 } = await Promise.resolve().then(() => (init_ws_gateway(), ws_gateway_exports));
14486
14486
  const { collectFingerprint: collectFingerprint2 } = await Promise.resolve().then(() => (init_fingerprint(), fingerprint_exports));
14487
- const runtimeVersion = "0.6.9";
14487
+ const runtimeVersion = "0.7.1";
14488
14488
  const fingerprint = collectFingerprint2(localConfig.stateDir, runtimeVersion);
14489
14489
  log.info("Connecting to DeskFree...", { wsUrl: localConfig.wsUrl });
14490
14490
  const connectResult = await initialConnect({
@@ -14529,7 +14529,7 @@ async function startAgent(opts) {
14529
14529
  runtimeVersion,
14530
14530
  maxConcurrentWorkers: 5,
14531
14531
  // updated after WorkerManager is created
14532
- instanceName: process.env["DESKFREE_INSTANCE_NAME"] || void 0
14532
+ instanceName: process.env["BOT"] || void 0
14533
14533
  };
14534
14534
  mkdirSync(config.stateDir, { recursive: true });
14535
14535
  mkdirSync(config.toolsDir, { recursive: true });