@questionbase/deskfree 0.6.10 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +511 -384
- package/dist/bin.js.map +1 -1
- package/dist/cli/start.d.ts +3 -0
- package/dist/cli/start.js +209 -0
- package/dist/cli/start.js.map +1 -0
- package/dist/cli/stop.d.ts +3 -0
- package/dist/cli/stop.js +80 -0
- package/dist/cli/stop.js.map +1 -0
- package/dist/cli/uninstall.d.ts +1 -1
- package/dist/cli/uninstall.js +20 -20
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/install.d.ts +0 -3
- package/dist/cli/install.js +0 -197
- package/dist/cli/install.js.map +0 -1
|
@@ -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"]}
|
package/dist/cli/stop.js
ADDED
|
@@ -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"]}
|
package/dist/cli/uninstall.d.ts
CHANGED
package/dist/cli/uninstall.js
CHANGED
|
@@ -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(
|
|
9
|
-
return `com.deskfree.agent.${
|
|
8
|
+
function getPlistLabel(botId) {
|
|
9
|
+
return `com.deskfree.agent.${botId}`;
|
|
10
10
|
}
|
|
11
|
-
function getServiceName(
|
|
12
|
-
return `deskfree-${
|
|
11
|
+
function getServiceName(botId) {
|
|
12
|
+
return `deskfree-${botId}`;
|
|
13
13
|
}
|
|
14
|
-
function getMacPaths(
|
|
14
|
+
function getMacPaths(botId) {
|
|
15
15
|
const home = homedir();
|
|
16
|
-
const deskfreeDir = join(home, ".deskfree",
|
|
17
|
-
const plistLabel = getPlistLabel(
|
|
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(
|
|
27
|
-
const serviceName = getServiceName(
|
|
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(
|
|
37
|
-
const paths = getMacPaths(
|
|
38
|
-
const plistLabel = getPlistLabel(
|
|
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(`
|
|
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(
|
|
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(
|
|
62
|
-
const serviceName = getServiceName(
|
|
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(`
|
|
80
|
+
console.log(`Bot ${botId} (${serviceName}) uninstalled.`);
|
|
81
81
|
}
|
|
82
|
-
function uninstall(
|
|
82
|
+
function uninstall(botId) {
|
|
83
83
|
if (process.platform === "darwin") {
|
|
84
|
-
uninstallMac(
|
|
84
|
+
uninstallMac(botId);
|
|
85
85
|
} else if (process.platform === "linux") {
|
|
86
|
-
uninstallLinux(
|
|
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":";;;;;;;
|
|
1
|
+
{"version":3,"sources":["../../src/cli/paths.ts","../../src/cli/uninstall.ts"],"names":[],"mappings":";;;;;;;AAGO,SAAS,cAAc,KAAA,EAAuB;AACnD,EAAA,OAAO,sBAAsB,KAAK,CAAA,CAAA;AACpC;AAEO,SAAS,eAAe,KAAA,EAAuB;AACpD,EAAA,OAAO,YAAY,KAAK,CAAA,CAAA;AAC1B;AAEO,SAAS,YAAY,KAAA,EAAe;AACzC,EAAA,MAAM,OAAO,OAAA,EAAQ;AACrB,EAAA,MAAM,WAAA,GAAc,IAAA,CAAK,IAAA,EAAM,WAAA,EAAa,KAAK,CAAA;AACjD,EAAA,MAAM,UAAA,GAAa,cAAc,KAAK,CAAA;AACtC,EAAA,OAAO;AAAA,IACL,WAAA;AAAA,IACA,OAAA,EAAS,IAAA,CAAK,WAAA,EAAa,MAAM,CAAA;AAAA,IACjC,QAAA,EAAU,IAAA,CAAK,WAAA,EAAa,aAAa,CAAA;AAAA,IACzC,MAAA,EAAQ,IAAA,CAAK,WAAA,EAAa,MAAM,CAAA;AAAA,IAChC,OAAO,IAAA,CAAK,IAAA,EAAM,WAAW,cAAA,EAAgB,CAAA,EAAG,UAAU,CAAA,MAAA,CAAQ;AAAA,GACpE;AACF;AAEO,SAAS,cAAc,KAAA,EAAe;AAC3C,EAAA,MAAM,WAAA,GAAc,eAAe,KAAK,CAAA;AACxC,EAAA,OAAO;AAAA,IACL,WAAA;AAAA,IACA,WAAA,EAAa,uBAAuB,WAAW,CAAA,QAAA,CAAA;AAAA,IAC/C,OAAA,EAAS,QAAQ,WAAW,CAAA,IAAA,CAAA;AAAA,IAC5B,QAAA,EAAU,YAAY,WAAW,CAAA,CAAA;AAAA,IACjC,MAAA,EAAQ,YAAY,WAAW,CAAA;AAAA,GACjC;AACF;ACtBA,SAAS,aAAa,KAAA,EAAqB;AACzC,EAAA,MAAM,KAAA,GAAQ,YAAY,KAAK,CAAA;AAC/B,EAAA,MAAM,UAAA,GAAa,cAAc,KAAK,CAAA;AAGtC,EAAA,IAAI;AACF,IAAA,QAAA,CAAS,CAAA,+BAAA,EAAkC,KAAA,CAAM,KAAK,CAAA,CAAA,EAAI;AAAA,MACxD,KAAA,EAAO;AAAA,KACR,CAAA;AAAA,EACH,CAAA,CAAA,MAAQ;AAAA,EAER;AAGA,EAAA,KAAA,MAAW,IAAA,IAAQ,CAAC,KAAA,CAAM,KAAA,EAAO,MAAM,OAAA,EAAS,KAAA,CAAM,QAAQ,CAAA,EAAG;AAC/D,IAAA,IAAI,UAAA,CAAW,IAAI,CAAA,EAAG;AACpB,MAAA,UAAA,CAAW,IAAI,CAAA;AACf,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,QAAA,EAAW,IAAI,CAAA,CAAE,CAAA;AAAA,IAC/B;AAAA,EACF;AAEA,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,IAAA,EAAO,KAAK,CAAA,EAAA,EAAK,UAAU,CAAA,cAAA,CAAgB,CAAA;AACvD,EAAA,OAAA,CAAQ,GAAA;AAAA,IACN,CAAA,wBAAA,EAA2B,MAAM,WAAW,CAAA,4CAAA;AAAA,GAC9C;AACF;AAIA,SAAS,eAAe,KAAA,EAAqB;AAC3C,EAAA,IAAI,OAAA,CAAQ,MAAA,IAAS,KAAM,CAAA,EAAG;AAC5B,IAAA,OAAA,CAAQ,MAAM,iDAAiD,CAAA;AAC/D,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAChB;AAEA,EAAA,MAAM,KAAA,GAAQ,cAAc,KAAK,CAAA;AACjC,EAAA,MAAM,WAAA,GAAc,eAAe,KAAK,CAAA;AAGxC,EAAA,IAAI;AACF,IAAA,QAAA,CAAS,kBAAkB,WAAW,CAAA,CAAA,EAAI,EAAE,KAAA,EAAO,UAAU,CAAA;AAAA,EAC/D,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,IAAI;AACF,IAAA,QAAA,CAAS,qBAAqB,WAAW,CAAA,CAAA,EAAI,EAAE,KAAA,EAAO,UAAU,CAAA;AAAA,EAClE,CAAA,CAAA,MAAQ;AAAA,EAER;AAGA,EAAA,IAAI,UAAA,CAAW,KAAA,CAAM,WAAW,CAAA,EAAG;AACjC,IAAA,UAAA,CAAW,MAAM,WAAW,CAAA;AAC5B,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,QAAA,EAAW,KAAA,CAAM,WAAW,CAAA,CAAE,CAAA;AAAA,EAC5C;AACA,EAAA,IAAI,UAAA,CAAW,KAAA,CAAM,OAAO,CAAA,EAAG;AAC7B,IAAA,UAAA,CAAW,MAAM,OAAO,CAAA;AACxB,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,QAAA,EAAW,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA,EACxC;AAEA,EAAA,QAAA,CAAS,yBAAyB,CAAA;AAClC,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,IAAA,EAAO,KAAK,CAAA,EAAA,EAAK,WAAW,CAAA,cAAA,CAAgB,CAAA;AAC1D;AAIO,SAAS,UAAU,KAAA,EAAqB;AAC7C,EAAA,IAAI,OAAA,CAAQ,aAAa,QAAA,EAAU;AACjC,IAAA,YAAA,CAAa,KAAK,CAAA;AAAA,EACpB,CAAA,MAAA,IAAW,OAAA,CAAQ,QAAA,KAAa,OAAA,EAAS;AACvC,IAAA,cAAA,CAAe,KAAK,CAAA;AAAA,EACtB,CAAA,MAAO;AACL,IAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,sBAAA,EAAyB,OAAA,CAAQ,QAAQ,CAAA,CAAE,CAAA;AACzD,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAChB;AACF","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
|
|
402
|
-
*
|
|
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
|
-
|
|
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.
|
|
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["
|
|
14532
|
+
instanceName: process.env["BOT"] || void 0
|
|
14543
14533
|
};
|
|
14544
14534
|
mkdirSync(config.stateDir, { recursive: true });
|
|
14545
14535
|
mkdirSync(config.toolsDir, { recursive: true });
|