@questionbase/deskfree 0.4.5 → 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 +461 -140
- 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 +187 -69
- 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",
|
|
@@ -11316,18 +11366,7 @@ function runOrchestrator(opts) {
|
|
|
11316
11366
|
"deskfree-orchestrator": orchestratorServer
|
|
11317
11367
|
},
|
|
11318
11368
|
tools: [],
|
|
11319
|
-
allowedTools:
|
|
11320
|
-
"mcp__deskfree-orchestrator__*",
|
|
11321
|
-
"Read",
|
|
11322
|
-
"Write",
|
|
11323
|
-
"Edit",
|
|
11324
|
-
"Bash",
|
|
11325
|
-
"Glob",
|
|
11326
|
-
"Grep",
|
|
11327
|
-
"WebSearch",
|
|
11328
|
-
"WebFetch",
|
|
11329
|
-
"NotebookEdit"
|
|
11330
|
-
],
|
|
11369
|
+
allowedTools: ORCHESTRATOR_ALLOWED_TOOLS,
|
|
11331
11370
|
disallowedTools: DISALLOWED_BUILTIN_TOOLS
|
|
11332
11371
|
}
|
|
11333
11372
|
});
|
|
@@ -11348,18 +11387,7 @@ function runHeartbeat(opts) {
|
|
|
11348
11387
|
"deskfree-orchestrator": orchestratorServer
|
|
11349
11388
|
},
|
|
11350
11389
|
tools: [],
|
|
11351
|
-
allowedTools:
|
|
11352
|
-
"mcp__deskfree-orchestrator__*",
|
|
11353
|
-
"Read",
|
|
11354
|
-
"Write",
|
|
11355
|
-
"Edit",
|
|
11356
|
-
"Bash",
|
|
11357
|
-
"Glob",
|
|
11358
|
-
"Grep",
|
|
11359
|
-
"WebSearch",
|
|
11360
|
-
"WebFetch",
|
|
11361
|
-
"NotebookEdit"
|
|
11362
|
-
],
|
|
11390
|
+
allowedTools: ORCHESTRATOR_ALLOWED_TOOLS,
|
|
11363
11391
|
disallowedTools: DISALLOWED_BUILTIN_TOOLS
|
|
11364
11392
|
}
|
|
11365
11393
|
});
|
|
@@ -11521,6 +11549,72 @@ function getHealthState() {
|
|
|
11521
11549
|
uptimeSeconds: Math.floor((Date.now() - startTime) / 1e3)
|
|
11522
11550
|
};
|
|
11523
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");
|
|
11524
11618
|
|
|
11525
11619
|
// src/gateway/polling.ts
|
|
11526
11620
|
var deliveredMessageIds = /* @__PURE__ */ new Set();
|
|
@@ -11897,6 +11991,49 @@ async function runWebSocketConnection(opts) {
|
|
|
11897
11991
|
cleanup();
|
|
11898
11992
|
ws.close(1e3, "reload");
|
|
11899
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
|
+
}
|
|
11900
12037
|
}
|
|
11901
12038
|
} catch (err) {
|
|
11902
12039
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -12462,21 +12599,35 @@ function scheduleDailyCycle(label, run, hour, timezone, signal, log) {
|
|
|
12462
12599
|
}
|
|
12463
12600
|
function msUntilNextLocalHour(hour, timezone) {
|
|
12464
12601
|
const now = Date.now();
|
|
12465
|
-
const
|
|
12602
|
+
const hourFmt = new Intl.DateTimeFormat("en-US", {
|
|
12466
12603
|
timeZone: timezone,
|
|
12467
12604
|
hour: "numeric",
|
|
12468
12605
|
hour12: false
|
|
12469
12606
|
});
|
|
12607
|
+
const detailFmt = new Intl.DateTimeFormat("en-US", {
|
|
12608
|
+
timeZone: timezone,
|
|
12609
|
+
minute: "numeric",
|
|
12610
|
+
second: "numeric"
|
|
12611
|
+
});
|
|
12470
12612
|
for (let offsetMs = 6e4; offsetMs <= 25 * 36e5; offsetMs += 36e5) {
|
|
12471
12613
|
const candidateMs = now + offsetMs;
|
|
12472
|
-
const parts =
|
|
12614
|
+
const parts = hourFmt.formatToParts(new Date(candidateMs));
|
|
12473
12615
|
const hourPart = parts.find((p) => p.type === "hour");
|
|
12474
12616
|
const candidateHour = parseInt(hourPart?.value ?? "-1", 10);
|
|
12475
12617
|
if (candidateHour === hour) {
|
|
12476
|
-
const
|
|
12477
|
-
|
|
12478
|
-
|
|
12479
|
-
|
|
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
|
+
}
|
|
12480
12631
|
}
|
|
12481
12632
|
}
|
|
12482
12633
|
return 24 * 36e5;
|
|
@@ -12564,40 +12715,6 @@ async function loadToolModules(tools, toolsDir, log) {
|
|
|
12564
12715
|
}
|
|
12565
12716
|
return allTools;
|
|
12566
12717
|
}
|
|
12567
|
-
|
|
12568
|
-
// src/util/logger.ts
|
|
12569
|
-
var LEVELS = {
|
|
12570
|
-
debug: 0,
|
|
12571
|
-
info: 1,
|
|
12572
|
-
warn: 2,
|
|
12573
|
-
error: 3
|
|
12574
|
-
};
|
|
12575
|
-
function createLogger(component, minLevel = "info") {
|
|
12576
|
-
const minLevelNum = LEVELS[minLevel];
|
|
12577
|
-
function log(level, message, fields) {
|
|
12578
|
-
if (LEVELS[level] < minLevelNum) return;
|
|
12579
|
-
const entry = {
|
|
12580
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12581
|
-
level,
|
|
12582
|
-
component,
|
|
12583
|
-
msg: message,
|
|
12584
|
-
...fields
|
|
12585
|
-
};
|
|
12586
|
-
const line = JSON.stringify(entry);
|
|
12587
|
-
if (level === "error" || level === "warn") {
|
|
12588
|
-
process.stderr.write(line + "\n");
|
|
12589
|
-
} else {
|
|
12590
|
-
process.stdout.write(line + "\n");
|
|
12591
|
-
}
|
|
12592
|
-
}
|
|
12593
|
-
return {
|
|
12594
|
-
debug: (msg, fields) => log("debug", msg, fields),
|
|
12595
|
-
info: (msg, fields) => log("info", msg, fields),
|
|
12596
|
-
warn: (msg, fields) => log("warn", msg, fields),
|
|
12597
|
-
error: (msg, fields) => log("error", msg, fields)
|
|
12598
|
-
};
|
|
12599
|
-
}
|
|
12600
|
-
var logger = createLogger("runtime");
|
|
12601
12718
|
var MAX_MESSAGE_CONTENT_LENGTH = 32 * 1024;
|
|
12602
12719
|
var MAX_ATTACHMENT_SIZE = 10 * 1024 * 1024;
|
|
12603
12720
|
var MAX_TOTAL_DOWNLOAD_SIZE = 50 * 1024 * 1024;
|
|
@@ -13045,8 +13162,6 @@ function runWorker(opts) {
|
|
|
13045
13162
|
return query({
|
|
13046
13163
|
prompt,
|
|
13047
13164
|
options: {
|
|
13048
|
-
debug: true,
|
|
13049
|
-
debugFile: "/dev/stderr",
|
|
13050
13165
|
stderr: (data) => {
|
|
13051
13166
|
process.stderr.write(`[worker-sdk] ${data}
|
|
13052
13167
|
`);
|
|
@@ -13630,6 +13745,9 @@ async function startAgent(opts) {
|
|
|
13630
13745
|
}
|
|
13631
13746
|
mkdirSync(config.stateDir, { recursive: true });
|
|
13632
13747
|
mkdirSync(config.toolsDir, { recursive: true });
|
|
13748
|
+
const logFile = join(config.stateDir, "runtime.log");
|
|
13749
|
+
enableFileLogging(logFile);
|
|
13750
|
+
log.info(`File logging enabled: ${logFile}`);
|
|
13633
13751
|
if (config.tools.length > 0) {
|
|
13634
13752
|
log.info(`Installing ${config.tools.length} tool package(s)...`);
|
|
13635
13753
|
await installTools(config.tools, config.toolsDir, log);
|