@questionbase/deskfree 0.4.4 → 0.4.7
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 +462 -143
- package/dist/bin.js.map +1 -1
- package/dist/cli/install.js +112 -12
- package/dist/cli/install.js.map +1 -1
- package/dist/cli/uninstall.js +43 -9
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/index.js +188 -72
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/install.js
CHANGED
|
@@ -1,13 +1,99 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { execSync } from 'child_process';
|
|
3
|
-
import { writeFileSync, chmodSync } from 'fs';
|
|
3
|
+
import { mkdirSync, writeFileSync, chmodSync } from 'fs';
|
|
4
|
+
import { homedir } from 'os';
|
|
5
|
+
import { dirname, join } from 'path';
|
|
4
6
|
|
|
5
7
|
createRequire(import.meta.url);
|
|
6
8
|
var SERVICE_NAME = "deskfree-agent";
|
|
7
|
-
var SERVICE_FILE = `/etc/systemd/system/${SERVICE_NAME}.service`;
|
|
8
|
-
var ENV_FILE = `/etc/${SERVICE_NAME}.env`;
|
|
9
9
|
var PACKAGE = "@questionbase/deskfree@latest";
|
|
10
|
-
|
|
10
|
+
var PLIST_LABEL = "com.deskfree.agent";
|
|
11
|
+
function getMacPaths() {
|
|
12
|
+
const home = homedir();
|
|
13
|
+
const deskfreeDir = join(home, ".deskfree");
|
|
14
|
+
return {
|
|
15
|
+
deskfreeDir,
|
|
16
|
+
envFile: join(deskfreeDir, ".env"),
|
|
17
|
+
launcher: join(deskfreeDir, "launcher.sh"),
|
|
18
|
+
logDir: join(deskfreeDir, "logs"),
|
|
19
|
+
plist: join(home, "Library", "LaunchAgents", `${PLIST_LABEL}.plist`)
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function installMac(token) {
|
|
23
|
+
const paths = getMacPaths();
|
|
24
|
+
let nodeBinDir;
|
|
25
|
+
try {
|
|
26
|
+
const nodePath = execSync("which node", { encoding: "utf8" }).trim();
|
|
27
|
+
nodeBinDir = dirname(nodePath);
|
|
28
|
+
} catch {
|
|
29
|
+
console.error("Error: node not found in PATH");
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
mkdirSync(paths.deskfreeDir, { recursive: true });
|
|
33
|
+
mkdirSync(paths.logDir, { recursive: true });
|
|
34
|
+
mkdirSync(dirname(paths.plist), { recursive: true });
|
|
35
|
+
writeFileSync(paths.envFile, `DESKFREE_LAUNCH=${token}
|
|
36
|
+
`, { mode: 384 });
|
|
37
|
+
chmodSync(paths.envFile, 384);
|
|
38
|
+
console.log(`Wrote ${paths.envFile}`);
|
|
39
|
+
const launcher = `#!/bin/bash
|
|
40
|
+
set -euo pipefail
|
|
41
|
+
|
|
42
|
+
export PATH="${nodeBinDir}:$PATH"
|
|
43
|
+
|
|
44
|
+
# Update to latest version before starting
|
|
45
|
+
npm install -g ${PACKAGE} 2>/dev/null || true
|
|
46
|
+
|
|
47
|
+
# Source env
|
|
48
|
+
set -a
|
|
49
|
+
source "${paths.envFile}"
|
|
50
|
+
set +a
|
|
51
|
+
|
|
52
|
+
exec deskfree-agent start
|
|
53
|
+
`;
|
|
54
|
+
writeFileSync(paths.launcher, launcher, { mode: 493 });
|
|
55
|
+
chmodSync(paths.launcher, 493);
|
|
56
|
+
console.log(`Wrote ${paths.launcher}`);
|
|
57
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
58
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
59
|
+
<plist version="1.0">
|
|
60
|
+
<dict>
|
|
61
|
+
<key>Label</key>
|
|
62
|
+
<string>${PLIST_LABEL}</string>
|
|
63
|
+
<key>ProgramArguments</key>
|
|
64
|
+
<array>
|
|
65
|
+
<string>${paths.launcher}</string>
|
|
66
|
+
</array>
|
|
67
|
+
<key>KeepAlive</key>
|
|
68
|
+
<true/>
|
|
69
|
+
<key>RunAtLoad</key>
|
|
70
|
+
<true/>
|
|
71
|
+
<key>StandardOutPath</key>
|
|
72
|
+
<string>${join(paths.logDir, "stdout.log")}</string>
|
|
73
|
+
<key>StandardErrorPath</key>
|
|
74
|
+
<string>${join(paths.logDir, "stderr.log")}</string>
|
|
75
|
+
<key>ThrottleInterval</key>
|
|
76
|
+
<integer>10</integer>
|
|
77
|
+
</dict>
|
|
78
|
+
</plist>
|
|
79
|
+
`;
|
|
80
|
+
writeFileSync(paths.plist, plist);
|
|
81
|
+
console.log(`Wrote ${paths.plist}`);
|
|
82
|
+
try {
|
|
83
|
+
execSync(`launchctl bootout gui/$(id -u) ${paths.plist}`, {
|
|
84
|
+
stdio: "ignore"
|
|
85
|
+
});
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
execSync(`launchctl bootstrap gui/$(id -u) ${paths.plist}`);
|
|
89
|
+
console.log(`
|
|
90
|
+
Service ${PLIST_LABEL} installed and started.`);
|
|
91
|
+
console.log(`Check status: launchctl print gui/$(id -u)/${PLIST_LABEL}`);
|
|
92
|
+
console.log(`Logs: tail -f ${join(paths.logDir, "stdout.log")}`);
|
|
93
|
+
}
|
|
94
|
+
var SYSTEMD_SERVICE_FILE = `/etc/systemd/system/${SERVICE_NAME}.service`;
|
|
95
|
+
var SYSTEMD_ENV_FILE = `/etc/${SERVICE_NAME}.env`;
|
|
96
|
+
function installLinux(token) {
|
|
11
97
|
if (process.getuid?.() !== 0) {
|
|
12
98
|
console.error("Error: install must be run as root (use sudo)");
|
|
13
99
|
process.exit(1);
|
|
@@ -19,10 +105,12 @@ function install(token) {
|
|
|
19
105
|
console.error("Error: npx not found in PATH");
|
|
20
106
|
process.exit(1);
|
|
21
107
|
}
|
|
22
|
-
writeFileSync(
|
|
23
|
-
`, {
|
|
24
|
-
|
|
25
|
-
|
|
108
|
+
writeFileSync(SYSTEMD_ENV_FILE, `DESKFREE_LAUNCH=${token}
|
|
109
|
+
`, {
|
|
110
|
+
mode: 384
|
|
111
|
+
});
|
|
112
|
+
chmodSync(SYSTEMD_ENV_FILE, 384);
|
|
113
|
+
console.log(`Wrote ${SYSTEMD_ENV_FILE}`);
|
|
26
114
|
const unit = `[Unit]
|
|
27
115
|
Description=DeskFree Agent
|
|
28
116
|
After=network-online.target
|
|
@@ -30,8 +118,9 @@ Wants=network-online.target
|
|
|
30
118
|
|
|
31
119
|
[Service]
|
|
32
120
|
Type=simple
|
|
121
|
+
ExecStartPre=${npxPath} ${PACKAGE} --version
|
|
33
122
|
ExecStart=${npxPath} ${PACKAGE} start
|
|
34
|
-
EnvironmentFile=${
|
|
123
|
+
EnvironmentFile=${SYSTEMD_ENV_FILE}
|
|
35
124
|
Environment=NODE_ENV=production
|
|
36
125
|
Restart=always
|
|
37
126
|
RestartSec=10
|
|
@@ -39,14 +128,25 @@ RestartSec=10
|
|
|
39
128
|
[Install]
|
|
40
129
|
WantedBy=multi-user.target
|
|
41
130
|
`;
|
|
42
|
-
writeFileSync(
|
|
43
|
-
console.log(`Wrote ${
|
|
131
|
+
writeFileSync(SYSTEMD_SERVICE_FILE, unit);
|
|
132
|
+
console.log(`Wrote ${SYSTEMD_SERVICE_FILE}`);
|
|
44
133
|
execSync("systemctl daemon-reload");
|
|
45
134
|
execSync(`systemctl enable ${SERVICE_NAME}`);
|
|
46
135
|
execSync(`systemctl start ${SERVICE_NAME}`);
|
|
47
|
-
console.log(`
|
|
136
|
+
console.log(`
|
|
137
|
+
Service ${SERVICE_NAME} installed and started.`);
|
|
48
138
|
console.log(`Check status: systemctl status ${SERVICE_NAME}`);
|
|
49
139
|
}
|
|
140
|
+
function install(token) {
|
|
141
|
+
if (process.platform === "darwin") {
|
|
142
|
+
installMac(token);
|
|
143
|
+
} else if (process.platform === "linux") {
|
|
144
|
+
installLinux(token);
|
|
145
|
+
} else {
|
|
146
|
+
console.error(`Unsupported platform: ${process.platform}`);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
50
150
|
|
|
51
151
|
export { install };
|
|
52
152
|
//# sourceMappingURL=install.js.map
|
package/dist/cli/install.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/install.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/cli/install.ts"],"names":[],"mappings":"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","file":"install.js","sourcesContent":["import { execSync } from 'node:child_process';\nimport { chmodSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\nconst SERVICE_NAME = 'deskfree-agent';\nconst PACKAGE = '@questionbase/deskfree@latest';\n\n// ── macOS (launchd) ─────────────────────────────────────────────────────────\n\nconst PLIST_LABEL = 'com.deskfree.agent';\n\nfunction getMacPaths() {\n const home = homedir();\n const deskfreeDir = join(home, '.deskfree');\n return {\n deskfreeDir,\n envFile: join(deskfreeDir, '.env'),\n launcher: join(deskfreeDir, 'launcher.sh'),\n logDir: join(deskfreeDir, 'logs'),\n plist: join(home, 'Library', 'LaunchAgents', `${PLIST_LABEL}.plist`),\n };\n}\n\nfunction installMac(token: string): void {\n const paths = getMacPaths();\n\n // Resolve full path to node's bin directory (survives nvm/fnm across sessions)\n let nodeBinDir: string;\n try {\n const nodePath = execSync('which node', { encoding: 'utf8' }).trim();\n nodeBinDir = dirname(nodePath);\n } catch {\n console.error('Error: node not found in PATH');\n process.exit(1);\n }\n\n // Create directories\n mkdirSync(paths.deskfreeDir, { recursive: true });\n mkdirSync(paths.logDir, { recursive: true });\n mkdirSync(dirname(paths.plist), { recursive: true });\n\n // Write env file (chmod 600)\n writeFileSync(paths.envFile, `DESKFREE_LAUNCH=${token}\\n`, { mode: 0o600 });\n chmodSync(paths.envFile, 0o600);\n console.log(`Wrote ${paths.envFile}`);\n\n // Write launcher script\n const launcher = `#!/bin/bash\nset -euo pipefail\n\nexport PATH=\"${nodeBinDir}:$PATH\"\n\n# Update to latest version before starting\nnpm install -g ${PACKAGE} 2>/dev/null || true\n\n# Source env\nset -a\nsource \"${paths.envFile}\"\nset +a\n\nexec deskfree-agent start\n`;\n\n writeFileSync(paths.launcher, launcher, { mode: 0o755 });\n chmodSync(paths.launcher, 0o755);\n console.log(`Wrote ${paths.launcher}`);\n\n // Write LaunchAgent plist\n const plist = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${PLIST_LABEL}</string>\n <key>ProgramArguments</key>\n <array>\n <string>${paths.launcher}</string>\n </array>\n <key>KeepAlive</key>\n <true/>\n <key>RunAtLoad</key>\n <true/>\n <key>StandardOutPath</key>\n <string>${join(paths.logDir, 'stdout.log')}</string>\n <key>StandardErrorPath</key>\n <string>${join(paths.logDir, 'stderr.log')}</string>\n <key>ThrottleInterval</key>\n <integer>10</integer>\n</dict>\n</plist>\n`;\n\n writeFileSync(paths.plist, plist);\n console.log(`Wrote ${paths.plist}`);\n\n // Unload if already loaded (ignore errors)\n try {\n execSync(`launchctl bootout gui/$(id -u) ${paths.plist}`, {\n stdio: 'ignore',\n });\n } catch {\n // not loaded — fine\n }\n\n // Load the service\n execSync(`launchctl bootstrap gui/$(id -u) ${paths.plist}`);\n\n console.log(`\\nService ${PLIST_LABEL} installed and started.`);\n console.log(`Check status: launchctl print gui/$(id -u)/${PLIST_LABEL}`);\n console.log(`Logs: tail -f ${join(paths.logDir, 'stdout.log')}`);\n}\n\n// ── Linux (systemd) ─────────────────────────────────────────────────────────\n\nconst SYSTEMD_SERVICE_FILE = `/etc/systemd/system/${SERVICE_NAME}.service`;\nconst SYSTEMD_ENV_FILE = `/etc/${SERVICE_NAME}.env`;\n\nfunction installLinux(token: string): void {\n if (process.getuid?.() !== 0) {\n console.error('Error: install must be run as root (use sudo)');\n process.exit(1);\n }\n\n // Resolve full npx path so systemd doesn't depend on PATH\n let npxPath: string;\n try {\n npxPath = execSync('which npx', { encoding: 'utf8' }).trim();\n } catch {\n console.error('Error: npx not found in PATH');\n process.exit(1);\n }\n\n // Write env file with token (chmod 600 — only root can read)\n writeFileSync(SYSTEMD_ENV_FILE, `DESKFREE_LAUNCH=${token}\\n`, {\n mode: 0o600,\n });\n chmodSync(SYSTEMD_ENV_FILE, 0o600);\n console.log(`Wrote ${SYSTEMD_ENV_FILE}`);\n\n // Write systemd unit file\n const unit = `[Unit]\nDescription=DeskFree Agent\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=simple\nExecStartPre=${npxPath} ${PACKAGE} --version\nExecStart=${npxPath} ${PACKAGE} start\nEnvironmentFile=${SYSTEMD_ENV_FILE}\nEnvironment=NODE_ENV=production\nRestart=always\nRestartSec=10\n\n[Install]\nWantedBy=multi-user.target\n`;\n\n writeFileSync(SYSTEMD_SERVICE_FILE, unit);\n console.log(`Wrote ${SYSTEMD_SERVICE_FILE}`);\n\n // Enable and start\n execSync('systemctl daemon-reload');\n execSync(`systemctl enable ${SERVICE_NAME}`);\n execSync(`systemctl start ${SERVICE_NAME}`);\n\n console.log(`\\nService ${SERVICE_NAME} installed and started.`);\n console.log(`Check status: systemctl status ${SERVICE_NAME}`);\n}\n\n// ── Public API ──────────────────────────────────────────────────────────────\n\nexport function install(token: string): void {\n if (process.platform === 'darwin') {\n installMac(token);\n } else if (process.platform === 'linux') {\n installLinux(token);\n } else {\n console.error(`Unsupported platform: ${process.platform}`);\n process.exit(1);\n }\n}\n"]}
|
package/dist/cli/uninstall.js
CHANGED
|
@@ -1,12 +1,36 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { execSync } from 'child_process';
|
|
3
3
|
import { existsSync, unlinkSync } from 'fs';
|
|
4
|
+
import { homedir } from 'os';
|
|
5
|
+
import { join } from 'path';
|
|
4
6
|
|
|
5
7
|
createRequire(import.meta.url);
|
|
6
8
|
var SERVICE_NAME = "deskfree-agent";
|
|
7
|
-
var
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
var PLIST_LABEL = "com.deskfree.agent";
|
|
10
|
+
function uninstallMac() {
|
|
11
|
+
const home = homedir();
|
|
12
|
+
const plist = join(home, "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
13
|
+
const deskfreeDir = join(home, ".deskfree");
|
|
14
|
+
const envFile = join(deskfreeDir, ".env");
|
|
15
|
+
const launcher = join(deskfreeDir, "launcher.sh");
|
|
16
|
+
try {
|
|
17
|
+
execSync(`launchctl bootout gui/$(id -u) ${plist}`, { stdio: "ignore" });
|
|
18
|
+
} catch {
|
|
19
|
+
}
|
|
20
|
+
for (const file of [plist, envFile, launcher]) {
|
|
21
|
+
if (existsSync(file)) {
|
|
22
|
+
unlinkSync(file);
|
|
23
|
+
console.log(`Removed ${file}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
console.log(`Service ${PLIST_LABEL} uninstalled.`);
|
|
27
|
+
console.log(
|
|
28
|
+
`Note: logs and state in ${deskfreeDir} were preserved. Remove manually if desired.`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
var SYSTEMD_SERVICE_FILE = `/etc/systemd/system/${SERVICE_NAME}.service`;
|
|
32
|
+
var SYSTEMD_ENV_FILE = `/etc/${SERVICE_NAME}.env`;
|
|
33
|
+
function uninstallLinux() {
|
|
10
34
|
if (process.getuid?.() !== 0) {
|
|
11
35
|
console.error("Error: uninstall must be run as root (use sudo)");
|
|
12
36
|
process.exit(1);
|
|
@@ -19,17 +43,27 @@ function uninstall() {
|
|
|
19
43
|
execSync(`systemctl disable ${SERVICE_NAME}`, { stdio: "ignore" });
|
|
20
44
|
} catch {
|
|
21
45
|
}
|
|
22
|
-
if (existsSync(
|
|
23
|
-
unlinkSync(
|
|
24
|
-
console.log(`Removed ${
|
|
46
|
+
if (existsSync(SYSTEMD_SERVICE_FILE)) {
|
|
47
|
+
unlinkSync(SYSTEMD_SERVICE_FILE);
|
|
48
|
+
console.log(`Removed ${SYSTEMD_SERVICE_FILE}`);
|
|
25
49
|
}
|
|
26
|
-
if (existsSync(
|
|
27
|
-
unlinkSync(
|
|
28
|
-
console.log(`Removed ${
|
|
50
|
+
if (existsSync(SYSTEMD_ENV_FILE)) {
|
|
51
|
+
unlinkSync(SYSTEMD_ENV_FILE);
|
|
52
|
+
console.log(`Removed ${SYSTEMD_ENV_FILE}`);
|
|
29
53
|
}
|
|
30
54
|
execSync("systemctl daemon-reload");
|
|
31
55
|
console.log(`Service ${SERVICE_NAME} uninstalled.`);
|
|
32
56
|
}
|
|
57
|
+
function uninstall() {
|
|
58
|
+
if (process.platform === "darwin") {
|
|
59
|
+
uninstallMac();
|
|
60
|
+
} else if (process.platform === "linux") {
|
|
61
|
+
uninstallLinux();
|
|
62
|
+
} else {
|
|
63
|
+
console.error(`Unsupported platform: ${process.platform}`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
33
67
|
|
|
34
68
|
export { uninstall };
|
|
35
69
|
//# sourceMappingURL=uninstall.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/uninstall.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/cli/uninstall.ts"],"names":[],"mappings":"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","file":"uninstall.js","sourcesContent":["import { execSync } from 'node:child_process';\nimport { existsSync, unlinkSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nconst SERVICE_NAME = 'deskfree-agent';\n\n// ── macOS (launchd) ─────────────────────────────────────────────────────────\n\nconst PLIST_LABEL = 'com.deskfree.agent';\n\nfunction uninstallMac(): void {\n const home = homedir();\n const plist = join(home, 'Library', 'LaunchAgents', `${PLIST_LABEL}.plist`);\n const deskfreeDir = join(home, '.deskfree');\n const envFile = join(deskfreeDir, '.env');\n const launcher = join(deskfreeDir, 'launcher.sh');\n\n // Unload (ignore errors if not loaded)\n try {\n execSync(`launchctl bootout gui/$(id -u) ${plist}`, { stdio: 'ignore' });\n } catch {\n // not loaded — fine\n }\n\n // Remove files\n for (const file of [plist, envFile, launcher]) {\n if (existsSync(file)) {\n unlinkSync(file);\n console.log(`Removed ${file}`);\n }\n }\n\n console.log(`Service ${PLIST_LABEL} uninstalled.`);\n console.log(\n `Note: logs and state in ${deskfreeDir} were preserved. Remove manually if desired.`,\n );\n}\n\n// ── Linux (systemd) ─────────────────────────────────────────────────────────\n\nconst SYSTEMD_SERVICE_FILE = `/etc/systemd/system/${SERVICE_NAME}.service`;\nconst SYSTEMD_ENV_FILE = `/etc/${SERVICE_NAME}.env`;\n\nfunction uninstallLinux(): 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 // Stop and disable (ignore errors if not running/enabled)\n try {\n execSync(`systemctl stop ${SERVICE_NAME}`, { stdio: 'ignore' });\n } catch {\n // not running — fine\n }\n try {\n execSync(`systemctl disable ${SERVICE_NAME}`, { stdio: 'ignore' });\n } catch {\n // not enabled — fine\n }\n\n // Remove files\n if (existsSync(SYSTEMD_SERVICE_FILE)) {\n unlinkSync(SYSTEMD_SERVICE_FILE);\n console.log(`Removed ${SYSTEMD_SERVICE_FILE}`);\n }\n if (existsSync(SYSTEMD_ENV_FILE)) {\n unlinkSync(SYSTEMD_ENV_FILE);\n console.log(`Removed ${SYSTEMD_ENV_FILE}`);\n }\n\n execSync('systemctl daemon-reload');\n console.log(`Service ${SERVICE_NAME} uninstalled.`);\n}\n\n// ── Public API ──────────────────────────────────────────────────────────────\n\nexport function uninstall(): void {\n if (process.platform === 'darwin') {\n uninstallMac();\n } else if (process.platform === 'linux') {\n uninstallLinux();\n } else {\n console.error(`Unsupported platform: ${process.platform}`);\n process.exit(1);\n }\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { query, tool, createSdkMcpServer } from '@anthropic-ai/claude-agent-sdk';
|
|
3
3
|
import { createRequire as createRequire$1 } from 'module';
|
|
4
|
-
import { existsSync, mkdirSync, writeFileSync,
|
|
4
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync, unlinkSync, createWriteStream, appendFileSync, statSync } from 'fs';
|
|
5
5
|
import { join, dirname, extname } from 'path';
|
|
6
6
|
import { execFileSync, execFile } from 'child_process';
|
|
7
7
|
import { z } from 'zod';
|
|
@@ -10174,6 +10174,8 @@ var SHARED_TOOLS = {
|
|
|
10174
10174
|
})
|
|
10175
10175
|
},
|
|
10176
10176
|
PROPOSE: {
|
|
10177
|
+
name: "deskfree_propose",
|
|
10178
|
+
description: "Propose a plan for human approval. Nothing is created until the human reviews and approves in a modal. Put all details in the instructions field using simple markdown (bold, lists, inline code). Do NOT use markdown headers (#) \u2014 use **bold text** instead for section labels.",
|
|
10177
10179
|
parameters: Type.Object({
|
|
10178
10180
|
context: Type.Optional(
|
|
10179
10181
|
Type.String({
|
|
@@ -10289,7 +10291,9 @@ var WORKER_TOOLS = {
|
|
|
10289
10291
|
})
|
|
10290
10292
|
},
|
|
10291
10293
|
COMPLETE_TASK: SHARED_TOOLS.COMPLETE_TASK,
|
|
10292
|
-
SEND_MESSAGE: SHARED_TOOLS.SEND_MESSAGE
|
|
10294
|
+
SEND_MESSAGE: SHARED_TOOLS.SEND_MESSAGE,
|
|
10295
|
+
PROPOSE: SHARED_TOOLS.PROPOSE
|
|
10296
|
+
};
|
|
10293
10297
|
var MAX_FULL_MESSAGES = 15;
|
|
10294
10298
|
function trimTaskContext(result) {
|
|
10295
10299
|
const trimmed = {
|
|
@@ -10606,6 +10610,41 @@ function createWorkerTools(client, options) {
|
|
|
10606
10610
|
return errorResult(err);
|
|
10607
10611
|
}
|
|
10608
10612
|
}),
|
|
10613
|
+
createTool(WORKER_TOOLS.PROPOSE, async (params) => {
|
|
10614
|
+
try {
|
|
10615
|
+
const context = validateStringParam(params, "context", false);
|
|
10616
|
+
const taskId = validateStringParam(params, "taskId", false);
|
|
10617
|
+
const rawTasks = params.tasks;
|
|
10618
|
+
if (!Array.isArray(rawTasks) || rawTasks.length === 0) {
|
|
10619
|
+
throw new Error("tasks must be a non-empty array of task objects");
|
|
10620
|
+
}
|
|
10621
|
+
const tasks = rawTasks;
|
|
10622
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
10623
|
+
const task = tasks[i];
|
|
10624
|
+
if (!task || typeof task !== "object") {
|
|
10625
|
+
throw new Error(`tasks[${i}] must be an object`);
|
|
10626
|
+
}
|
|
10627
|
+
if (!task.title || typeof task.title !== "string" || task.title.trim() === "") {
|
|
10628
|
+
throw new Error(`tasks[${i}].title must be a non-empty string`);
|
|
10629
|
+
}
|
|
10630
|
+
}
|
|
10631
|
+
await client.proposePlan({
|
|
10632
|
+
context,
|
|
10633
|
+
tasks,
|
|
10634
|
+
taskId
|
|
10635
|
+
});
|
|
10636
|
+
return {
|
|
10637
|
+
content: [
|
|
10638
|
+
{
|
|
10639
|
+
type: "text",
|
|
10640
|
+
text: `Proposal created with ${tasks.length} task(s)`
|
|
10641
|
+
}
|
|
10642
|
+
]
|
|
10643
|
+
};
|
|
10644
|
+
} catch (err) {
|
|
10645
|
+
return errorResult(err);
|
|
10646
|
+
}
|
|
10647
|
+
}),
|
|
10609
10648
|
createTool(WORKER_TOOLS.COMPLETE_TASK, async (params) => {
|
|
10610
10649
|
try {
|
|
10611
10650
|
const taskId = validateStringParam(params, "taskId", true);
|
|
@@ -10649,7 +10688,10 @@ You are the orchestrator. Your job: turn human intent into approved tasks, then
|
|
|
10649
10688
|
**Match the human's energy.** Short message \u2192 short reply. Casual tone \u2192 casual response. Don't over-explain, don't lecture, don't pad responses.
|
|
10650
10689
|
|
|
10651
10690
|
You do NOT claim tasks, complete tasks, or do work directly \u2014 you have no access to deskfree_start_task or deskfree_complete_task. Use \`deskfree_dispatch_worker\` to dispatch a worker for each approved task.
|
|
10652
|
-
- When a human writes in a task thread, decide:
|
|
10691
|
+
- When a human writes in a task thread, decide:
|
|
10692
|
+
- **Continuation of the same task?** \u2192 reopen and dispatch a worker.
|
|
10693
|
+
- **New/different work request?** \u2192 propose it as a new task (don't reopen the old one or do the work yourself).
|
|
10694
|
+
- **Just confirmation or deferred?** \u2192 leave it for now.
|
|
10653
10695
|
- Estimate token cost per task \u2014 consider files to read, reasoning, output.`;
|
|
10654
10696
|
var DESKFREE_WORKER_DIRECTIVE = `## DeskFree Worker
|
|
10655
10697
|
You are a worker sub-agent. Your first message contains pre-loaded context \u2014 use it directly.
|
|
@@ -11283,6 +11325,14 @@ function validateApiUrl(value) {
|
|
|
11283
11325
|
|
|
11284
11326
|
// src/agents/orchestrator.ts
|
|
11285
11327
|
var MAX_ORCHESTRATOR_TURNS = 20;
|
|
11328
|
+
var ORCHESTRATOR_ALLOWED_TOOLS = [
|
|
11329
|
+
"mcp__deskfree-orchestrator__*",
|
|
11330
|
+
"Read",
|
|
11331
|
+
"Glob",
|
|
11332
|
+
"Grep",
|
|
11333
|
+
"WebSearch",
|
|
11334
|
+
"WebFetch"
|
|
11335
|
+
];
|
|
11286
11336
|
var DISALLOWED_BUILTIN_TOOLS = [
|
|
11287
11337
|
"TodoWrite",
|
|
11288
11338
|
"AskUserQuestion",
|
|
@@ -11299,8 +11349,6 @@ function runOrchestrator(opts) {
|
|
|
11299
11349
|
return query({
|
|
11300
11350
|
prompt,
|
|
11301
11351
|
options: {
|
|
11302
|
-
debug: true,
|
|
11303
|
-
debugFile: "/dev/stderr",
|
|
11304
11352
|
stderr: (data) => {
|
|
11305
11353
|
process.stderr.write(`[orchestrator-sdk] ${data}
|
|
11306
11354
|
`);
|
|
@@ -11318,18 +11366,7 @@ function runOrchestrator(opts) {
|
|
|
11318
11366
|
"deskfree-orchestrator": orchestratorServer
|
|
11319
11367
|
},
|
|
11320
11368
|
tools: [],
|
|
11321
|
-
allowedTools:
|
|
11322
|
-
"mcp__deskfree-orchestrator__*",
|
|
11323
|
-
"Read",
|
|
11324
|
-
"Write",
|
|
11325
|
-
"Edit",
|
|
11326
|
-
"Bash",
|
|
11327
|
-
"Glob",
|
|
11328
|
-
"Grep",
|
|
11329
|
-
"WebSearch",
|
|
11330
|
-
"WebFetch",
|
|
11331
|
-
"NotebookEdit"
|
|
11332
|
-
],
|
|
11369
|
+
allowedTools: ORCHESTRATOR_ALLOWED_TOOLS,
|
|
11333
11370
|
disallowedTools: DISALLOWED_BUILTIN_TOOLS
|
|
11334
11371
|
}
|
|
11335
11372
|
});
|
|
@@ -11350,18 +11387,7 @@ function runHeartbeat(opts) {
|
|
|
11350
11387
|
"deskfree-orchestrator": orchestratorServer
|
|
11351
11388
|
},
|
|
11352
11389
|
tools: [],
|
|
11353
|
-
allowedTools:
|
|
11354
|
-
"mcp__deskfree-orchestrator__*",
|
|
11355
|
-
"Read",
|
|
11356
|
-
"Write",
|
|
11357
|
-
"Edit",
|
|
11358
|
-
"Bash",
|
|
11359
|
-
"Glob",
|
|
11360
|
-
"Grep",
|
|
11361
|
-
"WebSearch",
|
|
11362
|
-
"WebFetch",
|
|
11363
|
-
"NotebookEdit"
|
|
11364
|
-
],
|
|
11390
|
+
allowedTools: ORCHESTRATOR_ALLOWED_TOOLS,
|
|
11365
11391
|
disallowedTools: DISALLOWED_BUILTIN_TOOLS
|
|
11366
11392
|
}
|
|
11367
11393
|
});
|
|
@@ -11523,6 +11549,72 @@ function getHealthState() {
|
|
|
11523
11549
|
uptimeSeconds: Math.floor((Date.now() - startTime) / 1e3)
|
|
11524
11550
|
};
|
|
11525
11551
|
}
|
|
11552
|
+
var LEVELS = {
|
|
11553
|
+
debug: 0,
|
|
11554
|
+
info: 1,
|
|
11555
|
+
warn: 2,
|
|
11556
|
+
error: 3
|
|
11557
|
+
};
|
|
11558
|
+
var MAX_LOG_FILE_BYTES = 5 * 1024 * 1024;
|
|
11559
|
+
var ROTATION_CHECK_INTERVAL = 500;
|
|
11560
|
+
var logFilePath = null;
|
|
11561
|
+
var writesSinceCheck = 0;
|
|
11562
|
+
function enableFileLogging(filePath) {
|
|
11563
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
11564
|
+
logFilePath = filePath;
|
|
11565
|
+
}
|
|
11566
|
+
function getLogFilePath() {
|
|
11567
|
+
return logFilePath;
|
|
11568
|
+
}
|
|
11569
|
+
function rotateIfNeeded() {
|
|
11570
|
+
if (!logFilePath) return;
|
|
11571
|
+
try {
|
|
11572
|
+
const stat = statSync(logFilePath);
|
|
11573
|
+
if (stat.size <= MAX_LOG_FILE_BYTES) return;
|
|
11574
|
+
const content = readFileSync(logFilePath, "utf8");
|
|
11575
|
+
const half = Math.floor(content.length / 2);
|
|
11576
|
+
const newlineIdx = content.indexOf("\n", half);
|
|
11577
|
+
const trimmed = newlineIdx >= 0 ? content.slice(newlineIdx + 1) : content.slice(half);
|
|
11578
|
+
writeFileSync(logFilePath, trimmed);
|
|
11579
|
+
} catch {
|
|
11580
|
+
}
|
|
11581
|
+
}
|
|
11582
|
+
function createLogger(component, minLevel = "info") {
|
|
11583
|
+
const minLevelNum = LEVELS[minLevel];
|
|
11584
|
+
function log(level, message, fields) {
|
|
11585
|
+
if (LEVELS[level] < minLevelNum) return;
|
|
11586
|
+
const entry = {
|
|
11587
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11588
|
+
level,
|
|
11589
|
+
component,
|
|
11590
|
+
msg: message,
|
|
11591
|
+
...fields
|
|
11592
|
+
};
|
|
11593
|
+
const line = JSON.stringify(entry);
|
|
11594
|
+
if (level === "error" || level === "warn") {
|
|
11595
|
+
process.stderr.write(line + "\n");
|
|
11596
|
+
} else {
|
|
11597
|
+
process.stdout.write(line + "\n");
|
|
11598
|
+
}
|
|
11599
|
+
if (logFilePath) {
|
|
11600
|
+
try {
|
|
11601
|
+
appendFileSync(logFilePath, line + "\n");
|
|
11602
|
+
if (++writesSinceCheck >= ROTATION_CHECK_INTERVAL) {
|
|
11603
|
+
writesSinceCheck = 0;
|
|
11604
|
+
rotateIfNeeded();
|
|
11605
|
+
}
|
|
11606
|
+
} catch {
|
|
11607
|
+
}
|
|
11608
|
+
}
|
|
11609
|
+
}
|
|
11610
|
+
return {
|
|
11611
|
+
debug: (msg, fields) => log("debug", msg, fields),
|
|
11612
|
+
info: (msg, fields) => log("info", msg, fields),
|
|
11613
|
+
warn: (msg, fields) => log("warn", msg, fields),
|
|
11614
|
+
error: (msg, fields) => log("error", msg, fields)
|
|
11615
|
+
};
|
|
11616
|
+
}
|
|
11617
|
+
var logger = createLogger("runtime");
|
|
11526
11618
|
|
|
11527
11619
|
// src/gateway/polling.ts
|
|
11528
11620
|
var deliveredMessageIds = /* @__PURE__ */ new Set();
|
|
@@ -11899,6 +11991,49 @@ async function runWebSocketConnection(opts) {
|
|
|
11899
11991
|
cleanup();
|
|
11900
11992
|
ws.close(1e3, "reload");
|
|
11901
11993
|
process.kill(process.pid, "SIGTERM");
|
|
11994
|
+
} else if (msg.action === "logs") {
|
|
11995
|
+
const MAX_RESPONSE_BYTES = 12e4;
|
|
11996
|
+
const lineCount = msg.lines ?? 200;
|
|
11997
|
+
try {
|
|
11998
|
+
const logFile = getLogFilePath();
|
|
11999
|
+
if (!logFile) {
|
|
12000
|
+
ws.send(
|
|
12001
|
+
JSON.stringify({
|
|
12002
|
+
action: "logsResponse",
|
|
12003
|
+
lines: [],
|
|
12004
|
+
error: "File logging not enabled"
|
|
12005
|
+
})
|
|
12006
|
+
);
|
|
12007
|
+
} else {
|
|
12008
|
+
const content = readFileSync(logFile, "utf8");
|
|
12009
|
+
const allLines = content.split("\n").filter(Boolean);
|
|
12010
|
+
let tail = allLines.slice(-lineCount);
|
|
12011
|
+
while (tail.length > 1) {
|
|
12012
|
+
const payload = JSON.stringify({
|
|
12013
|
+
action: "logsResponse",
|
|
12014
|
+
lines: tail
|
|
12015
|
+
});
|
|
12016
|
+
if (Buffer.byteLength(payload) <= MAX_RESPONSE_BYTES) break;
|
|
12017
|
+
tail = tail.slice(Math.ceil(tail.length * 0.25));
|
|
12018
|
+
}
|
|
12019
|
+
ws.send(
|
|
12020
|
+
JSON.stringify({
|
|
12021
|
+
action: "logsResponse",
|
|
12022
|
+
lines: tail
|
|
12023
|
+
})
|
|
12024
|
+
);
|
|
12025
|
+
}
|
|
12026
|
+
} catch (err) {
|
|
12027
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
12028
|
+
log.warn(`Failed to read logs: ${errMsg}`);
|
|
12029
|
+
ws.send(
|
|
12030
|
+
JSON.stringify({
|
|
12031
|
+
action: "logsResponse",
|
|
12032
|
+
lines: [],
|
|
12033
|
+
error: errMsg
|
|
12034
|
+
})
|
|
12035
|
+
);
|
|
12036
|
+
}
|
|
11902
12037
|
}
|
|
11903
12038
|
} catch (err) {
|
|
11904
12039
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -12464,21 +12599,35 @@ function scheduleDailyCycle(label, run, hour, timezone, signal, log) {
|
|
|
12464
12599
|
}
|
|
12465
12600
|
function msUntilNextLocalHour(hour, timezone) {
|
|
12466
12601
|
const now = Date.now();
|
|
12467
|
-
const
|
|
12602
|
+
const hourFmt = new Intl.DateTimeFormat("en-US", {
|
|
12468
12603
|
timeZone: timezone,
|
|
12469
12604
|
hour: "numeric",
|
|
12470
12605
|
hour12: false
|
|
12471
12606
|
});
|
|
12607
|
+
const detailFmt = new Intl.DateTimeFormat("en-US", {
|
|
12608
|
+
timeZone: timezone,
|
|
12609
|
+
minute: "numeric",
|
|
12610
|
+
second: "numeric"
|
|
12611
|
+
});
|
|
12472
12612
|
for (let offsetMs = 6e4; offsetMs <= 25 * 36e5; offsetMs += 36e5) {
|
|
12473
12613
|
const candidateMs = now + offsetMs;
|
|
12474
|
-
const parts =
|
|
12614
|
+
const parts = hourFmt.formatToParts(new Date(candidateMs));
|
|
12475
12615
|
const hourPart = parts.find((p) => p.type === "hour");
|
|
12476
12616
|
const candidateHour = parseInt(hourPart?.value ?? "-1", 10);
|
|
12477
12617
|
if (candidateHour === hour) {
|
|
12478
|
-
const
|
|
12479
|
-
|
|
12480
|
-
|
|
12481
|
-
|
|
12618
|
+
const detailParts = detailFmt.formatToParts(new Date(candidateMs));
|
|
12619
|
+
const localMinute = parseInt(
|
|
12620
|
+
detailParts.find((p) => p.type === "minute")?.value ?? "0",
|
|
12621
|
+
10
|
|
12622
|
+
);
|
|
12623
|
+
const localSecond = parseInt(
|
|
12624
|
+
detailParts.find((p) => p.type === "second")?.value ?? "0",
|
|
12625
|
+
10
|
|
12626
|
+
);
|
|
12627
|
+
const snappedMs = candidateMs - localMinute * 6e4 - localSecond * 1e3;
|
|
12628
|
+
if (snappedMs > now + 6e4) {
|
|
12629
|
+
return snappedMs - now;
|
|
12630
|
+
}
|
|
12482
12631
|
}
|
|
12483
12632
|
}
|
|
12484
12633
|
return 24 * 36e5;
|
|
@@ -12566,40 +12715,6 @@ async function loadToolModules(tools, toolsDir, log) {
|
|
|
12566
12715
|
}
|
|
12567
12716
|
return allTools;
|
|
12568
12717
|
}
|
|
12569
|
-
|
|
12570
|
-
// src/util/logger.ts
|
|
12571
|
-
var LEVELS = {
|
|
12572
|
-
debug: 0,
|
|
12573
|
-
info: 1,
|
|
12574
|
-
warn: 2,
|
|
12575
|
-
error: 3
|
|
12576
|
-
};
|
|
12577
|
-
function createLogger(component, minLevel = "info") {
|
|
12578
|
-
const minLevelNum = LEVELS[minLevel];
|
|
12579
|
-
function log(level, message, fields) {
|
|
12580
|
-
if (LEVELS[level] < minLevelNum) return;
|
|
12581
|
-
const entry = {
|
|
12582
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12583
|
-
level,
|
|
12584
|
-
component,
|
|
12585
|
-
msg: message,
|
|
12586
|
-
...fields
|
|
12587
|
-
};
|
|
12588
|
-
const line = JSON.stringify(entry);
|
|
12589
|
-
if (level === "error" || level === "warn") {
|
|
12590
|
-
process.stderr.write(line + "\n");
|
|
12591
|
-
} else {
|
|
12592
|
-
process.stdout.write(line + "\n");
|
|
12593
|
-
}
|
|
12594
|
-
}
|
|
12595
|
-
return {
|
|
12596
|
-
debug: (msg, fields) => log("debug", msg, fields),
|
|
12597
|
-
info: (msg, fields) => log("info", msg, fields),
|
|
12598
|
-
warn: (msg, fields) => log("warn", msg, fields),
|
|
12599
|
-
error: (msg, fields) => log("error", msg, fields)
|
|
12600
|
-
};
|
|
12601
|
-
}
|
|
12602
|
-
var logger = createLogger("runtime");
|
|
12603
12718
|
var MAX_MESSAGE_CONTENT_LENGTH = 32 * 1024;
|
|
12604
12719
|
var MAX_ATTACHMENT_SIZE = 10 * 1024 * 1024;
|
|
12605
12720
|
var MAX_TOTAL_DOWNLOAD_SIZE = 50 * 1024 * 1024;
|
|
@@ -13047,8 +13162,6 @@ function runWorker(opts) {
|
|
|
13047
13162
|
return query({
|
|
13048
13163
|
prompt,
|
|
13049
13164
|
options: {
|
|
13050
|
-
debug: true,
|
|
13051
|
-
debugFile: "/dev/stderr",
|
|
13052
13165
|
stderr: (data) => {
|
|
13053
13166
|
process.stderr.write(`[worker-sdk] ${data}
|
|
13054
13167
|
`);
|
|
@@ -13426,7 +13539,7 @@ ${userMessage}
|
|
|
13426
13539
|
try {
|
|
13427
13540
|
await client.reportUsage({
|
|
13428
13541
|
taskId,
|
|
13429
|
-
estimatedCost: result.total_cost_usd
|
|
13542
|
+
estimatedCost: Math.round(result.total_cost_usd * 1e6) / 1e6
|
|
13430
13543
|
});
|
|
13431
13544
|
} catch {
|
|
13432
13545
|
log.warn(`Failed to report usage for task ${taskId}`);
|
|
@@ -13632,6 +13745,9 @@ async function startAgent(opts) {
|
|
|
13632
13745
|
}
|
|
13633
13746
|
mkdirSync(config.stateDir, { recursive: true });
|
|
13634
13747
|
mkdirSync(config.toolsDir, { recursive: true });
|
|
13748
|
+
const logFile = join(config.stateDir, "runtime.log");
|
|
13749
|
+
enableFileLogging(logFile);
|
|
13750
|
+
log.info(`File logging enabled: ${logFile}`);
|
|
13635
13751
|
if (config.tools.length > 0) {
|
|
13636
13752
|
log.info(`Installing ${config.tools.length} tool package(s)...`);
|
|
13637
13753
|
await installTools(config.tools, config.toolsDir, log);
|