@questionbase/deskfree 0.6.1 → 0.6.3
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 +783 -521
- package/dist/bin.js.map +1 -1
- package/dist/cli/install.d.ts +1 -1
- package/dist/cli/install.js +11 -19
- package/dist/cli/install.js.map +1 -1
- package/dist/index.d.ts +28 -18
- package/dist/index.js +10508 -10171
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/dist/cli/install.d.ts
CHANGED
package/dist/cli/install.js
CHANGED
|
@@ -34,7 +34,7 @@ function getLinuxPaths(name) {
|
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
var PACKAGE = "@questionbase/deskfree@latest";
|
|
37
|
-
function installMac(
|
|
37
|
+
function installMac(botId, name, stage) {
|
|
38
38
|
const paths = getMacPaths(name);
|
|
39
39
|
const plistLabel = getPlistLabel(name);
|
|
40
40
|
let nodeBinDir;
|
|
@@ -50,13 +50,9 @@ function installMac(token, name) {
|
|
|
50
50
|
mkdirSync(paths.deskfreeDir, { recursive: true });
|
|
51
51
|
mkdirSync(paths.logDir, { recursive: true });
|
|
52
52
|
mkdirSync(dirname(paths.plist), { recursive: true });
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
DESKFREE_INSTANCE_NAME=${name}
|
|
57
|
-
`,
|
|
58
|
-
{ mode: 384 }
|
|
59
|
-
);
|
|
53
|
+
const envLines = [`BOT=${botId}`, `DESKFREE_INSTANCE_NAME=${name}`];
|
|
54
|
+
if (stage) envLines.push(`STAGE=${stage}`);
|
|
55
|
+
writeFileSync(paths.envFile, envLines.join("\n") + "\n", { mode: 384 });
|
|
60
56
|
chmodSync(paths.envFile, 384);
|
|
61
57
|
console.log(`Wrote ${paths.envFile}`);
|
|
62
58
|
const launcher = `#!/bin/bash
|
|
@@ -114,7 +110,7 @@ Service ${plistLabel} installed and started.`);
|
|
|
114
110
|
console.log(`Check status: launchctl print gui/$(id -u)/${plistLabel}`);
|
|
115
111
|
console.log(`Logs: tail -f ${join(paths.logDir, "stdout.log")}`);
|
|
116
112
|
}
|
|
117
|
-
function installLinux(
|
|
113
|
+
function installLinux(botId, name, stage) {
|
|
118
114
|
if (process.getuid?.() !== 0) {
|
|
119
115
|
console.error("Error: install must be run as root (use sudo)");
|
|
120
116
|
process.exit(1);
|
|
@@ -145,13 +141,9 @@ function installLinux(token, name) {
|
|
|
145
141
|
`chown ${systemUser}:${systemUser} ${paths.stateDir} ${paths.logDir}`
|
|
146
142
|
);
|
|
147
143
|
console.log(`Created ${paths.stateDir} and ${paths.logDir}`);
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
DESKFREE_INSTANCE_NAME=${name}
|
|
152
|
-
`,
|
|
153
|
-
{ mode: 384 }
|
|
154
|
-
);
|
|
144
|
+
const envLines = [`BOT=${botId}`, `DESKFREE_INSTANCE_NAME=${name}`];
|
|
145
|
+
if (stage) envLines.push(`STAGE=${stage}`);
|
|
146
|
+
writeFileSync(paths.envFile, envLines.join("\n") + "\n", { mode: 384 });
|
|
155
147
|
chmodSync(paths.envFile, 384);
|
|
156
148
|
console.log(`Wrote ${paths.envFile}`);
|
|
157
149
|
const unit = `[Unit]
|
|
@@ -189,11 +181,11 @@ Service ${serviceName} installed and started.`);
|
|
|
189
181
|
console.log(`Check status: systemctl status ${serviceName}`);
|
|
190
182
|
console.log(`Logs: tail -f ${paths.logDir}/stdout.log`);
|
|
191
183
|
}
|
|
192
|
-
function install(
|
|
184
|
+
function install(botId, name, stage) {
|
|
193
185
|
if (process.platform === "darwin") {
|
|
194
|
-
installMac(
|
|
186
|
+
installMac(botId, name, stage);
|
|
195
187
|
} else if (process.platform === "linux") {
|
|
196
|
-
installLinux(
|
|
188
|
+
installLinux(botId, name, stage);
|
|
197
189
|
} else {
|
|
198
190
|
console.error(`Unsupported platform: ${process.platform}`);
|
|
199
191
|
process.exit(1);
|
package/dist/cli/install.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/paths.ts","../../src/cli/install.ts"],"names":["join"],"mappings":"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","file":"install.js","sourcesContent":["import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nconst DEFAULT_NAME = 'main';\n\nexport function resolveName(name?: string): string {\n return name ?? DEFAULT_NAME;\n}\n\nexport function getPlistLabel(name: string): string {\n return `com.deskfree.agent.${name}`;\n}\n\nexport function getServiceName(name: string): string {\n return `deskfree-${name}`;\n}\n\nexport function getMacPaths(name: string) {\n const home = homedir();\n const deskfreeDir = join(home, '.deskfree', name);\n const plistLabel = getPlistLabel(name);\n return {\n deskfreeDir,\n envFile: join(deskfreeDir, '.env'),\n launcher: join(deskfreeDir, 'deskfree.sh'),\n logDir: join(deskfreeDir, 'logs'),\n plist: join(home, 'Library', 'LaunchAgents', `${plistLabel}.plist`),\n };\n}\n\nexport function getLinuxPaths(name: string) {\n const serviceName = getServiceName(name);\n return {\n serviceName,\n serviceFile: `/etc/systemd/system/${serviceName}.service`,\n envFile: `/etc/${serviceName}.env`,\n stateDir: `/var/lib/${serviceName}`,\n logDir: `/var/log/${serviceName}`,\n };\n}\n\n/** Parse --name <value> from argv, returning [resolvedName, remainingArgs]. */\nexport function parseName(args: string[]): [string, string[]] {\n const idx = args.indexOf('--name');\n if (idx === -1) return [DEFAULT_NAME, args];\n const name = args[idx + 1];\n if (!name || name.startsWith('-')) {\n console.error('Error: --name requires a value (e.g. --name kasper)');\n process.exit(1);\n }\n if (!/^[a-z0-9-]+$/i.test(name)) {\n console.error(\n 'Error: --name must contain only letters, numbers, and hyphens',\n );\n process.exit(1);\n }\n const remaining = [...args.slice(0, idx), ...args.slice(idx + 2)];\n return [name, remaining];\n}\n","import {\n getLinuxPaths,\n getMacPaths,\n getPlistLabel,\n getServiceName,\n} from './paths.js';\nimport { execSync } from 'node:child_process';\nimport { chmodSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nconst PACKAGE = '@questionbase/deskfree@latest';\n\n// ── macOS (launchd) ─────────────────────────────────────────────────────────\n\nfunction installMac(token: string, name: string): void {\n const paths = getMacPaths(name);\n const plistLabel = getPlistLabel(name);\n\n // Resolve full path to node's bin directory (survives nvm/fnm across sessions)\n let nodeBinDir: string;\n try {\n const nodePath = execSync('which node', { encoding: 'utf8' }).trim();\n nodeBinDir = dirname(nodePath);\n } catch {\n console.error('Error: node not found in PATH');\n process.exit(1);\n }\n\n // Capture extra PATH dirs from the user's shell so the launcher can find\n // tools like `claude` that live outside the node bin directory.\n const extraPathDirs = (process.env['PATH'] ?? '')\n .split(':')\n .filter((d) => d && d !== nodeBinDir);\n const fullPath = [nodeBinDir, ...extraPathDirs].join(':');\n\n // Create directories\n mkdirSync(paths.deskfreeDir, { recursive: true });\n mkdirSync(paths.logDir, { recursive: true });\n mkdirSync(dirname(paths.plist), { recursive: true });\n\n // Write env file (chmod 600)\n writeFileSync(\n paths.envFile,\n `DESKFREE_LAUNCH=${token}\\nDESKFREE_INSTANCE_NAME=${name}\\n`,\n { mode: 0o600 },\n );\n chmodSync(paths.envFile, 0o600);\n console.log(`Wrote ${paths.envFile}`);\n\n // Write launcher script\n const launcher = `#!/bin/bash\nset -euo pipefail\n\nexport PATH=\"${fullPath}\"\n\n# Update to latest version before starting\nnpm install -g ${PACKAGE} 2>/dev/null || true\n\n# Source env\nset -a\nsource \"${paths.envFile}\"\nset +a\n\nexec deskfree start\n`;\n\n writeFileSync(paths.launcher, launcher, { mode: 0o755 });\n chmodSync(paths.launcher, 0o755);\n console.log(`Wrote ${paths.launcher}`);\n\n // Write LaunchAgent plist\n const plist = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${plistLabel}</string>\n <key>ProgramArguments</key>\n <array>\n <string>${paths.launcher}</string>\n </array>\n <key>KeepAlive</key>\n <true/>\n <key>RunAtLoad</key>\n <true/>\n <key>StandardOutPath</key>\n <string>${join(paths.logDir, 'stdout.log')}</string>\n <key>StandardErrorPath</key>\n <string>${join(paths.logDir, 'stderr.log')}</string>\n <key>ThrottleInterval</key>\n <integer>10</integer>\n</dict>\n</plist>\n`;\n\n writeFileSync(paths.plist, plist);\n console.log(`Wrote ${paths.plist}`);\n\n // Unload if already loaded (ignore errors)\n try {\n execSync(`launchctl bootout gui/$(id -u) ${paths.plist}`, {\n stdio: 'ignore',\n });\n } catch {\n // not loaded — fine\n }\n\n // Load the service\n execSync(`launchctl bootstrap gui/$(id -u) ${paths.plist}`);\n\n console.log(`\\nService ${plistLabel} installed and started.`);\n console.log(`Check status: launchctl print gui/$(id -u)/${plistLabel}`);\n console.log(`Logs: tail -f ${join(paths.logDir, 'stdout.log')}`);\n}\n\n// ── Linux (systemd) ─────────────────────────────────────────────────────────\n\nfunction installLinux(token: string, name: string): void {\n if (process.getuid?.() !== 0) {\n console.error('Error: install must be run as root (use sudo)');\n process.exit(1);\n }\n\n const paths = getLinuxPaths(name);\n const serviceName = getServiceName(name);\n const systemUser = serviceName;\n\n // Resolve full npm/node bin dir so systemd doesn't depend on PATH\n let npmPath: string;\n let nodeBinDir: string;\n try {\n npmPath = execSync('which npm', { encoding: 'utf8' }).trim();\n nodeBinDir = dirname(npmPath);\n } catch {\n console.error('Error: npm not found in PATH');\n process.exit(1);\n }\n\n // Create dedicated system user (no login shell, no home dir)\n try {\n execSync(`id ${systemUser}`, { stdio: 'ignore' });\n } catch {\n execSync(\n `useradd --system --no-create-home --shell /usr/sbin/nologin ${systemUser}`,\n );\n console.log(`Created system user: ${systemUser}`);\n }\n\n // Create state and log directories owned by the service user\n mkdirSync(paths.stateDir, { recursive: true });\n mkdirSync(paths.logDir, { recursive: true });\n execSync(\n `chown ${systemUser}:${systemUser} ${paths.stateDir} ${paths.logDir}`,\n );\n console.log(`Created ${paths.stateDir} and ${paths.logDir}`);\n\n // Write env file with token (chmod 600 — only root can read)\n writeFileSync(\n paths.envFile,\n `DESKFREE_LAUNCH=${token}\\nDESKFREE_INSTANCE_NAME=${name}\\n`,\n { mode: 0o600 },\n );\n chmodSync(paths.envFile, 0o600);\n console.log(`Wrote ${paths.envFile}`);\n\n // Write systemd unit file\n const unit = `[Unit]\nDescription=DeskFree Agent (${name})\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=simple\nUser=${systemUser}\nGroup=${systemUser}\nWorkingDirectory=${paths.stateDir}\nEnvironment=PATH=${nodeBinDir}:/usr/local/bin:/usr/bin:/bin\nExecStartPre=+${npmPath} install -g ${PACKAGE}\nExecStart=${nodeBinDir}/deskfree start\nEnvironmentFile=${paths.envFile}\nEnvironment=NODE_ENV=production\nEnvironment=DESKFREE_STATE_DIR=${paths.stateDir}\nEnvironment=DESKFREE_TOOLS_DIR=${paths.stateDir}/tools\nRestart=always\nRestartSec=10\nStandardOutput=append:${paths.logDir}/stdout.log\nStandardError=append:${paths.logDir}/stderr.log\n\n[Install]\nWantedBy=multi-user.target\n`;\n\n writeFileSync(paths.serviceFile, unit);\n console.log(`Wrote ${paths.serviceFile}`);\n\n // Enable and start\n execSync('systemctl daemon-reload');\n execSync(`systemctl enable ${serviceName}`);\n execSync(`systemctl start ${serviceName}`);\n\n console.log(`\\nService ${serviceName} installed and started.`);\n console.log(`Check status: systemctl status ${serviceName}`);\n console.log(`Logs: tail -f ${paths.logDir}/stdout.log`);\n}\n\n// ── Public API ──────────────────────────────────────────────────────────────\n\nexport function install(token: string, name: string): void {\n if (process.platform === 'darwin') {\n installMac(token, name);\n } else if (process.platform === 'linux') {\n installLinux(token, name);\n } else {\n console.error(`Unsupported platform: ${process.platform}`);\n process.exit(1);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/paths.ts","../../src/cli/install.ts"],"names":["join"],"mappings":"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","file":"install.js","sourcesContent":["import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nconst DEFAULT_NAME = 'main';\n\nexport function resolveName(name?: string): string {\n return name ?? DEFAULT_NAME;\n}\n\nexport function getPlistLabel(name: string): string {\n return `com.deskfree.agent.${name}`;\n}\n\nexport function getServiceName(name: string): string {\n return `deskfree-${name}`;\n}\n\nexport function getMacPaths(name: string) {\n const home = homedir();\n const deskfreeDir = join(home, '.deskfree', name);\n const plistLabel = getPlistLabel(name);\n return {\n deskfreeDir,\n envFile: join(deskfreeDir, '.env'),\n launcher: join(deskfreeDir, 'deskfree.sh'),\n logDir: join(deskfreeDir, 'logs'),\n plist: join(home, 'Library', 'LaunchAgents', `${plistLabel}.plist`),\n };\n}\n\nexport function getLinuxPaths(name: string) {\n const serviceName = getServiceName(name);\n return {\n serviceName,\n serviceFile: `/etc/systemd/system/${serviceName}.service`,\n envFile: `/etc/${serviceName}.env`,\n stateDir: `/var/lib/${serviceName}`,\n logDir: `/var/log/${serviceName}`,\n };\n}\n\n/** Parse --name <value> from argv, returning [resolvedName, remainingArgs]. */\nexport function parseName(args: string[]): [string, string[]] {\n const idx = args.indexOf('--name');\n if (idx === -1) return [DEFAULT_NAME, args];\n const name = args[idx + 1];\n if (!name || name.startsWith('-')) {\n console.error('Error: --name requires a value (e.g. --name kasper)');\n process.exit(1);\n }\n if (!/^[a-z0-9-]+$/i.test(name)) {\n console.error(\n 'Error: --name must contain only letters, numbers, and hyphens',\n );\n process.exit(1);\n }\n const remaining = [...args.slice(0, idx), ...args.slice(idx + 2)];\n return [name, remaining];\n}\n","import {\n getLinuxPaths,\n getMacPaths,\n getPlistLabel,\n getServiceName,\n} from './paths.js';\nimport { execSync } from 'node:child_process';\nimport { chmodSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nconst PACKAGE = '@questionbase/deskfree@latest';\n\n// ── macOS (launchd) ─────────────────────────────────────────────────────────\n\nfunction installMac(botId: string, name: string, stage?: string): void {\n const paths = getMacPaths(name);\n const plistLabel = getPlistLabel(name);\n\n // Resolve full path to node's bin directory (survives nvm/fnm across sessions)\n let nodeBinDir: string;\n try {\n const nodePath = execSync('which node', { encoding: 'utf8' }).trim();\n nodeBinDir = dirname(nodePath);\n } catch {\n console.error('Error: node not found in PATH');\n process.exit(1);\n }\n\n // Capture extra PATH dirs from the user's shell so the launcher can find\n // tools like `claude` that live outside the node bin directory.\n const extraPathDirs = (process.env['PATH'] ?? '')\n .split(':')\n .filter((d) => d && d !== nodeBinDir);\n const fullPath = [nodeBinDir, ...extraPathDirs].join(':');\n\n // Create directories\n mkdirSync(paths.deskfreeDir, { recursive: true });\n mkdirSync(paths.logDir, { recursive: true });\n mkdirSync(dirname(paths.plist), { recursive: true });\n\n // Write env file (chmod 600)\n const envLines = [`BOT=${botId}`, `DESKFREE_INSTANCE_NAME=${name}`];\n if (stage) envLines.push(`STAGE=${stage}`);\n writeFileSync(paths.envFile, envLines.join('\\n') + '\\n', { mode: 0o600 });\n chmodSync(paths.envFile, 0o600);\n console.log(`Wrote ${paths.envFile}`);\n\n // Write launcher script\n const launcher = `#!/bin/bash\nset -euo pipefail\n\nexport PATH=\"${fullPath}\"\n\n# Update to latest version before starting\nnpm install -g ${PACKAGE} 2>/dev/null || true\n\n# Source env\nset -a\nsource \"${paths.envFile}\"\nset +a\n\nexec deskfree start\n`;\n\n writeFileSync(paths.launcher, launcher, { mode: 0o755 });\n chmodSync(paths.launcher, 0o755);\n console.log(`Wrote ${paths.launcher}`);\n\n // Write LaunchAgent plist\n const plist = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${plistLabel}</string>\n <key>ProgramArguments</key>\n <array>\n <string>${paths.launcher}</string>\n </array>\n <key>KeepAlive</key>\n <true/>\n <key>RunAtLoad</key>\n <true/>\n <key>StandardOutPath</key>\n <string>${join(paths.logDir, 'stdout.log')}</string>\n <key>StandardErrorPath</key>\n <string>${join(paths.logDir, 'stderr.log')}</string>\n <key>ThrottleInterval</key>\n <integer>10</integer>\n</dict>\n</plist>\n`;\n\n writeFileSync(paths.plist, plist);\n console.log(`Wrote ${paths.plist}`);\n\n // Unload if already loaded (ignore errors)\n try {\n execSync(`launchctl bootout gui/$(id -u) ${paths.plist}`, {\n stdio: 'ignore',\n });\n } catch {\n // not loaded — fine\n }\n\n // Load the service\n execSync(`launchctl bootstrap gui/$(id -u) ${paths.plist}`);\n\n console.log(`\\nService ${plistLabel} installed and started.`);\n console.log(`Check status: launchctl print gui/$(id -u)/${plistLabel}`);\n console.log(`Logs: tail -f ${join(paths.logDir, 'stdout.log')}`);\n}\n\n// ── Linux (systemd) ─────────────────────────────────────────────────────────\n\nfunction installLinux(botId: string, name: string, stage?: string): void {\n if (process.getuid?.() !== 0) {\n console.error('Error: install must be run as root (use sudo)');\n process.exit(1);\n }\n\n const paths = getLinuxPaths(name);\n const serviceName = getServiceName(name);\n const systemUser = serviceName;\n\n // Resolve full npm/node bin dir so systemd doesn't depend on PATH\n let npmPath: string;\n let nodeBinDir: string;\n try {\n npmPath = execSync('which npm', { encoding: 'utf8' }).trim();\n nodeBinDir = dirname(npmPath);\n } catch {\n console.error('Error: npm not found in PATH');\n process.exit(1);\n }\n\n // Create dedicated system user (no login shell, no home dir)\n try {\n execSync(`id ${systemUser}`, { stdio: 'ignore' });\n } catch {\n execSync(\n `useradd --system --no-create-home --shell /usr/sbin/nologin ${systemUser}`,\n );\n console.log(`Created system user: ${systemUser}`);\n }\n\n // Create state and log directories owned by the service user\n mkdirSync(paths.stateDir, { recursive: true });\n mkdirSync(paths.logDir, { recursive: true });\n execSync(\n `chown ${systemUser}:${systemUser} ${paths.stateDir} ${paths.logDir}`,\n );\n console.log(`Created ${paths.stateDir} and ${paths.logDir}`);\n\n // Write env file (chmod 600 — only root can read)\n const envLines = [`BOT=${botId}`, `DESKFREE_INSTANCE_NAME=${name}`];\n if (stage) envLines.push(`STAGE=${stage}`);\n writeFileSync(paths.envFile, envLines.join('\\n') + '\\n', { mode: 0o600 });\n chmodSync(paths.envFile, 0o600);\n console.log(`Wrote ${paths.envFile}`);\n\n // Write systemd unit file\n const unit = `[Unit]\nDescription=DeskFree Agent (${name})\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=simple\nUser=${systemUser}\nGroup=${systemUser}\nWorkingDirectory=${paths.stateDir}\nEnvironment=PATH=${nodeBinDir}:/usr/local/bin:/usr/bin:/bin\nExecStartPre=+${npmPath} install -g ${PACKAGE}\nExecStart=${nodeBinDir}/deskfree start\nEnvironmentFile=${paths.envFile}\nEnvironment=NODE_ENV=production\nEnvironment=DESKFREE_STATE_DIR=${paths.stateDir}\nEnvironment=DESKFREE_TOOLS_DIR=${paths.stateDir}/tools\nRestart=always\nRestartSec=10\nStandardOutput=append:${paths.logDir}/stdout.log\nStandardError=append:${paths.logDir}/stderr.log\n\n[Install]\nWantedBy=multi-user.target\n`;\n\n writeFileSync(paths.serviceFile, unit);\n console.log(`Wrote ${paths.serviceFile}`);\n\n // Enable and start\n execSync('systemctl daemon-reload');\n execSync(`systemctl enable ${serviceName}`);\n execSync(`systemctl start ${serviceName}`);\n\n console.log(`\\nService ${serviceName} installed and started.`);\n console.log(`Check status: systemctl status ${serviceName}`);\n console.log(`Logs: tail -f ${paths.logDir}/stdout.log`);\n}\n\n// ── Public API ──────────────────────────────────────────────────────────────\n\nexport function install(botId: string, name: string, stage?: string): void {\n if (process.platform === 'darwin') {\n installMac(botId, name, stage);\n } else if (process.platform === 'linux') {\n installLinux(botId, name, stage);\n } else {\n console.error(`Unsupported platform: ${process.platform}`);\n process.exit(1);\n }\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PluginLogger, AgentContext, DeskFreeClient, DeskFreeTool, ChatMessage, RuntimeBootstrapConfig } from '@deskfree/core';
|
|
2
2
|
import * as _anthropic_ai_claude_agent_sdk from '@anthropic-ai/claude-agent-sdk';
|
|
3
3
|
import { McpSdkServerConfigWithInstance, SDKMessage, SDKUserMessage, Query, tool } from '@anthropic-ai/claude-agent-sdk';
|
|
4
|
+
import WebSocket from 'ws';
|
|
4
5
|
|
|
5
6
|
interface StartAgentOptions {
|
|
6
7
|
log?: PluginLogger;
|
|
@@ -10,13 +11,14 @@ type DisposeAgent = () => void;
|
|
|
10
11
|
/**
|
|
11
12
|
* Full agent startup sequence:
|
|
12
13
|
*
|
|
13
|
-
* 1. Load local config (
|
|
14
|
-
* 2.
|
|
15
|
-
* 3.
|
|
16
|
-
* 4.
|
|
17
|
-
* 5.
|
|
18
|
-
* 6.
|
|
19
|
-
* 7.
|
|
14
|
+
* 1. Load local config (botId + apiUrl from env)
|
|
15
|
+
* 2. Connect to DeskFree (WS-first → rotation token)
|
|
16
|
+
* 3. Create authenticated DeskFree client
|
|
17
|
+
* 4. Bootstrap: fetch config from API (wsUrl, model, region, tools)
|
|
18
|
+
* 5. Install enabled tool packages
|
|
19
|
+
* 6. Configure Agent SDK env vars (Bedrock, region, model)
|
|
20
|
+
* 7. Create MCP servers
|
|
21
|
+
* 8. Start health server + WS gateway + heartbeat
|
|
20
22
|
*/
|
|
21
23
|
declare function startAgent(opts?: StartAgentOptions): Promise<DisposeAgent>;
|
|
22
24
|
|
|
@@ -283,13 +285,19 @@ interface GatewayConfig {
|
|
|
283
285
|
getWorkerStatus?: () => WorkerStatus;
|
|
284
286
|
/** Trigger memory consolidation on demand. Returns status string. */
|
|
285
287
|
onConsolidate?: () => Promise<string>;
|
|
288
|
+
/** Bot ID for WS-first connection */
|
|
289
|
+
botId: string;
|
|
290
|
+
/** Deployment stage (e.g. "dev", "alpha") */
|
|
291
|
+
stage: string;
|
|
292
|
+
/** Live WebSocket from initial connect (first iteration only) */
|
|
293
|
+
initialWs: WebSocket;
|
|
294
|
+
/** Rotation token from initial connect */
|
|
295
|
+
initialRotationToken: string;
|
|
286
296
|
}
|
|
287
297
|
/**
|
|
288
298
|
* Start the WS gateway — reconnection loop with backoff.
|
|
289
299
|
*
|
|
290
|
-
*
|
|
291
|
-
* On disconnect: backoff → reconnect
|
|
292
|
-
* Fallback: interval-based polling when WS is unavailable
|
|
300
|
+
* Flow: WS connect → init → "go" → run connection → proactive reconnect
|
|
293
301
|
*/
|
|
294
302
|
declare function startGateway(config: GatewayConfig): Promise<void>;
|
|
295
303
|
|
|
@@ -318,13 +326,17 @@ declare function loadToolModules(tools: ToolPackageConfig[], toolsDir: string, l
|
|
|
318
326
|
|
|
319
327
|
/**
|
|
320
328
|
* Local config: values known before connecting to the API.
|
|
321
|
-
* Only
|
|
329
|
+
* Only `botId` is required. The API URL is derived from stage + domain.
|
|
322
330
|
*/
|
|
323
331
|
interface LocalConfig {
|
|
324
|
-
/** Bot
|
|
325
|
-
|
|
326
|
-
/** DeskFree backend API URL
|
|
332
|
+
/** Bot ID (install key, e.g. "BFRH3VXHQR7") */
|
|
333
|
+
botId: string;
|
|
334
|
+
/** DeskFree backend API URL */
|
|
327
335
|
apiUrl: string;
|
|
336
|
+
/** Deployment stage (e.g. "dev", "alpha") */
|
|
337
|
+
stage: string;
|
|
338
|
+
/** WebSocket URL (derived from domain or explicit override) */
|
|
339
|
+
wsUrl: string;
|
|
328
340
|
/** Directory for cursor + media storage */
|
|
329
341
|
stateDir: string;
|
|
330
342
|
/** Directory for dynamic tool installs */
|
|
@@ -361,8 +373,6 @@ interface RuntimeConfig extends LocalConfig {
|
|
|
361
373
|
anthropicApiKey?: string;
|
|
362
374
|
/** Base URL for Ollama-compatible API (only set when provider is 'ollama') */
|
|
363
375
|
baseUrl?: string;
|
|
364
|
-
/** Bot ID for this runtime instance */
|
|
365
|
-
botId: string;
|
|
366
376
|
/** Bot's display name (e.g. "Sofia") */
|
|
367
377
|
botName: string;
|
|
368
378
|
/** Deployment type (aws, docker, local) */
|
|
@@ -379,8 +389,8 @@ interface RuntimeConfig extends LocalConfig {
|
|
|
379
389
|
/**
|
|
380
390
|
* Load local config from environment variables.
|
|
381
391
|
*
|
|
382
|
-
*
|
|
383
|
-
*
|
|
392
|
+
* Requires `BOT=BFRH3VXHQR7` (or `DESKFREE_BOT_ID`).
|
|
393
|
+
* API URL is derived from STAGE + DESKFREE_DOMAIN, or set explicitly via DESKFREE_API_URL.
|
|
384
394
|
*/
|
|
385
395
|
declare function loadConfig(): LocalConfig;
|
|
386
396
|
/**
|