@questionbase/deskfree 0.4.7 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +812 -413
- package/dist/bin.js.map +1 -1
- package/dist/cli/install.d.ts +1 -1
- package/dist/cli/install.js +90 -40
- package/dist/cli/install.js.map +1 -1
- package/dist/cli/uninstall.d.ts +1 -1
- package/dist/cli/uninstall.js +53 -28
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/index.d.ts +15 -3
- package/dist/index.js +272 -63
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { execSync, execFileSync, execFile } from 'child_process';
|
|
4
|
-
import { mkdirSync, writeFileSync, chmodSync, existsSync, unlinkSync, appendFileSync, readdirSync, readFileSync, statSync, createWriteStream } from 'fs';
|
|
5
3
|
import { homedir } from 'os';
|
|
6
4
|
import { dirname, join, extname } from 'path';
|
|
5
|
+
import { spawn, execSync, execFileSync, execFile } from 'child_process';
|
|
6
|
+
import { mkdirSync, writeFileSync, chmodSync, existsSync, unlinkSync, appendFileSync, readdirSync, readFileSync, statSync, createWriteStream } from 'fs';
|
|
7
7
|
import { createRequire as createRequire$1 } from 'module';
|
|
8
8
|
import { query, createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk';
|
|
9
9
|
import { z } from 'zod';
|
|
@@ -33,8 +33,8 @@ var __commonJS = (cb, mod) => function __require3() {
|
|
|
33
33
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
34
34
|
};
|
|
35
35
|
var __export = (target, all) => {
|
|
36
|
-
for (var
|
|
37
|
-
__defProp(target,
|
|
36
|
+
for (var name2 in all)
|
|
37
|
+
__defProp(target, name2, { get: all[name2], enumerable: true });
|
|
38
38
|
};
|
|
39
39
|
var __copyProps = (to, from, except, desc) => {
|
|
40
40
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
@@ -52,25 +52,66 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
52
52
|
__defProp(target, "default", { value: mod, enumerable: true }) ,
|
|
53
53
|
mod
|
|
54
54
|
));
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
function getMacPaths() {
|
|
55
|
+
function getPlistLabel(name2) {
|
|
56
|
+
return `com.deskfree.agent.${name2}`;
|
|
57
|
+
}
|
|
58
|
+
function getServiceName(name2) {
|
|
59
|
+
return `deskfree-agent-${name2}`;
|
|
60
|
+
}
|
|
61
|
+
function getMacPaths(name2) {
|
|
62
62
|
const home = homedir();
|
|
63
|
-
const deskfreeDir = join(home, ".deskfree");
|
|
63
|
+
const deskfreeDir = join(home, ".deskfree", name2);
|
|
64
|
+
const plistLabel = getPlistLabel(name2);
|
|
64
65
|
return {
|
|
65
66
|
deskfreeDir,
|
|
66
67
|
envFile: join(deskfreeDir, ".env"),
|
|
67
68
|
launcher: join(deskfreeDir, "launcher.sh"),
|
|
68
69
|
logDir: join(deskfreeDir, "logs"),
|
|
69
|
-
plist: join(home, "Library", "LaunchAgents", `${
|
|
70
|
+
plist: join(home, "Library", "LaunchAgents", `${plistLabel}.plist`)
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function getLinuxPaths(name2) {
|
|
74
|
+
const serviceName = getServiceName(name2);
|
|
75
|
+
return {
|
|
76
|
+
serviceName,
|
|
77
|
+
serviceFile: `/etc/systemd/system/${serviceName}.service`,
|
|
78
|
+
envFile: `/etc/${serviceName}.env`,
|
|
79
|
+
stateDir: `/var/lib/${serviceName}`,
|
|
80
|
+
logDir: `/var/log/${serviceName}`
|
|
70
81
|
};
|
|
71
82
|
}
|
|
72
|
-
function
|
|
73
|
-
const
|
|
83
|
+
function parseName(args) {
|
|
84
|
+
const idx = args.indexOf("--name");
|
|
85
|
+
if (idx === -1) return [DEFAULT_NAME, args];
|
|
86
|
+
const name2 = args[idx + 1];
|
|
87
|
+
if (!name2 || name2.startsWith("-")) {
|
|
88
|
+
console.error("Error: --name requires a value (e.g. --name kasper)");
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
if (!/^[a-z0-9-]+$/i.test(name2)) {
|
|
92
|
+
console.error(
|
|
93
|
+
"Error: --name must contain only letters, numbers, and hyphens"
|
|
94
|
+
);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
const remaining = [...args.slice(0, idx), ...args.slice(idx + 2)];
|
|
98
|
+
return [name2, remaining];
|
|
99
|
+
}
|
|
100
|
+
var DEFAULT_NAME;
|
|
101
|
+
var init_paths = __esm({
|
|
102
|
+
"src/cli/paths.ts"() {
|
|
103
|
+
DEFAULT_NAME = "main";
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// src/cli/install.ts
|
|
108
|
+
var install_exports = {};
|
|
109
|
+
__export(install_exports, {
|
|
110
|
+
install: () => install
|
|
111
|
+
});
|
|
112
|
+
function installMac(token, name2) {
|
|
113
|
+
const paths = getMacPaths(name2);
|
|
114
|
+
const plistLabel = getPlistLabel(name2);
|
|
74
115
|
let nodeBinDir;
|
|
75
116
|
try {
|
|
76
117
|
const nodePath = execSync("which node", { encoding: "utf8" }).trim();
|
|
@@ -82,8 +123,13 @@ function installMac(token) {
|
|
|
82
123
|
mkdirSync(paths.deskfreeDir, { recursive: true });
|
|
83
124
|
mkdirSync(paths.logDir, { recursive: true });
|
|
84
125
|
mkdirSync(dirname(paths.plist), { recursive: true });
|
|
85
|
-
writeFileSync(
|
|
86
|
-
|
|
126
|
+
writeFileSync(
|
|
127
|
+
paths.envFile,
|
|
128
|
+
`DESKFREE_LAUNCH=${token}
|
|
129
|
+
DESKFREE_INSTANCE_NAME=${name2}
|
|
130
|
+
`,
|
|
131
|
+
{ mode: 384 }
|
|
132
|
+
);
|
|
87
133
|
chmodSync(paths.envFile, 384);
|
|
88
134
|
console.log(`Wrote ${paths.envFile}`);
|
|
89
135
|
const launcher = `#!/bin/bash
|
|
@@ -109,7 +155,7 @@ exec deskfree-agent start
|
|
|
109
155
|
<plist version="1.0">
|
|
110
156
|
<dict>
|
|
111
157
|
<key>Label</key>
|
|
112
|
-
<string>${
|
|
158
|
+
<string>${plistLabel}</string>
|
|
113
159
|
<key>ProgramArguments</key>
|
|
114
160
|
<array>
|
|
115
161
|
<string>${paths.launcher}</string>
|
|
@@ -137,72 +183,100 @@ exec deskfree-agent start
|
|
|
137
183
|
}
|
|
138
184
|
execSync(`launchctl bootstrap gui/$(id -u) ${paths.plist}`);
|
|
139
185
|
console.log(`
|
|
140
|
-
Service ${
|
|
141
|
-
console.log(`Check status: launchctl print gui/$(id -u)/${
|
|
186
|
+
Service ${plistLabel} installed and started.`);
|
|
187
|
+
console.log(`Check status: launchctl print gui/$(id -u)/${plistLabel}`);
|
|
142
188
|
console.log(`Logs: tail -f ${join(paths.logDir, "stdout.log")}`);
|
|
143
189
|
}
|
|
144
|
-
function installLinux(token) {
|
|
190
|
+
function installLinux(token, name2) {
|
|
145
191
|
if (process.getuid?.() !== 0) {
|
|
146
192
|
console.error("Error: install must be run as root (use sudo)");
|
|
147
193
|
process.exit(1);
|
|
148
194
|
}
|
|
149
|
-
|
|
195
|
+
const paths = getLinuxPaths(name2);
|
|
196
|
+
const serviceName = getServiceName(name2);
|
|
197
|
+
const systemUser = serviceName;
|
|
198
|
+
let npmPath;
|
|
199
|
+
let nodeBinDir;
|
|
150
200
|
try {
|
|
151
|
-
|
|
201
|
+
npmPath = execSync("which npm", { encoding: "utf8" }).trim();
|
|
202
|
+
nodeBinDir = dirname(npmPath);
|
|
152
203
|
} catch {
|
|
153
|
-
console.error("Error:
|
|
204
|
+
console.error("Error: npm not found in PATH");
|
|
154
205
|
process.exit(1);
|
|
155
206
|
}
|
|
156
|
-
|
|
157
|
-
`, {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
207
|
+
try {
|
|
208
|
+
execSync(`id ${systemUser}`, { stdio: "ignore" });
|
|
209
|
+
} catch {
|
|
210
|
+
execSync(
|
|
211
|
+
`useradd --system --no-create-home --shell /usr/sbin/nologin ${systemUser}`
|
|
212
|
+
);
|
|
213
|
+
console.log(`Created system user: ${systemUser}`);
|
|
214
|
+
}
|
|
215
|
+
mkdirSync(paths.stateDir, { recursive: true });
|
|
216
|
+
mkdirSync(paths.logDir, { recursive: true });
|
|
217
|
+
execSync(
|
|
218
|
+
`chown ${systemUser}:${systemUser} ${paths.stateDir} ${paths.logDir}`
|
|
219
|
+
);
|
|
220
|
+
console.log(`Created ${paths.stateDir} and ${paths.logDir}`);
|
|
221
|
+
writeFileSync(
|
|
222
|
+
paths.envFile,
|
|
223
|
+
`DESKFREE_LAUNCH=${token}
|
|
224
|
+
DESKFREE_INSTANCE_NAME=${name2}
|
|
225
|
+
`,
|
|
226
|
+
{ mode: 384 }
|
|
227
|
+
);
|
|
228
|
+
chmodSync(paths.envFile, 384);
|
|
229
|
+
console.log(`Wrote ${paths.envFile}`);
|
|
162
230
|
const unit = `[Unit]
|
|
163
|
-
Description=DeskFree Agent
|
|
231
|
+
Description=DeskFree Agent (${name2})
|
|
164
232
|
After=network-online.target
|
|
165
233
|
Wants=network-online.target
|
|
166
234
|
|
|
167
235
|
[Service]
|
|
168
236
|
Type=simple
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
237
|
+
User=${systemUser}
|
|
238
|
+
Group=${systemUser}
|
|
239
|
+
WorkingDirectory=${paths.stateDir}
|
|
240
|
+
Environment=PATH=${nodeBinDir}:/usr/local/bin:/usr/bin:/bin
|
|
241
|
+
ExecStartPre=+${npmPath} install -g ${PACKAGE}
|
|
242
|
+
ExecStart=${nodeBinDir}/deskfree-agent start
|
|
243
|
+
EnvironmentFile=${paths.envFile}
|
|
172
244
|
Environment=NODE_ENV=production
|
|
245
|
+
Environment=DESKFREE_STATE_DIR=${paths.stateDir}
|
|
246
|
+
Environment=DESKFREE_TOOLS_DIR=${paths.stateDir}/tools
|
|
173
247
|
Restart=always
|
|
174
248
|
RestartSec=10
|
|
249
|
+
StandardOutput=append:${paths.logDir}/stdout.log
|
|
250
|
+
StandardError=append:${paths.logDir}/stderr.log
|
|
175
251
|
|
|
176
252
|
[Install]
|
|
177
253
|
WantedBy=multi-user.target
|
|
178
254
|
`;
|
|
179
|
-
writeFileSync(
|
|
180
|
-
console.log(`Wrote ${
|
|
255
|
+
writeFileSync(paths.serviceFile, unit);
|
|
256
|
+
console.log(`Wrote ${paths.serviceFile}`);
|
|
181
257
|
execSync("systemctl daemon-reload");
|
|
182
|
-
execSync(`systemctl enable ${
|
|
183
|
-
execSync(`systemctl start ${
|
|
258
|
+
execSync(`systemctl enable ${serviceName}`);
|
|
259
|
+
execSync(`systemctl start ${serviceName}`);
|
|
184
260
|
console.log(`
|
|
185
|
-
Service ${
|
|
186
|
-
console.log(`Check status: systemctl status ${
|
|
261
|
+
Service ${serviceName} installed and started.`);
|
|
262
|
+
console.log(`Check status: systemctl status ${serviceName}`);
|
|
263
|
+
console.log(`Logs: tail -f ${paths.logDir}/stdout.log`);
|
|
187
264
|
}
|
|
188
|
-
function install(token) {
|
|
265
|
+
function install(token, name2) {
|
|
189
266
|
if (process.platform === "darwin") {
|
|
190
|
-
installMac(token);
|
|
267
|
+
installMac(token, name2);
|
|
191
268
|
} else if (process.platform === "linux") {
|
|
192
|
-
installLinux(token);
|
|
269
|
+
installLinux(token, name2);
|
|
193
270
|
} else {
|
|
194
271
|
console.error(`Unsupported platform: ${process.platform}`);
|
|
195
272
|
process.exit(1);
|
|
196
273
|
}
|
|
197
274
|
}
|
|
198
|
-
var
|
|
275
|
+
var PACKAGE;
|
|
199
276
|
var init_install = __esm({
|
|
200
277
|
"src/cli/install.ts"() {
|
|
201
|
-
|
|
278
|
+
init_paths();
|
|
202
279
|
PACKAGE = "@questionbase/deskfree@latest";
|
|
203
|
-
PLIST_LABEL = "com.deskfree.agent";
|
|
204
|
-
SYSTEMD_SERVICE_FILE = `/etc/systemd/system/${SERVICE_NAME}.service`;
|
|
205
|
-
SYSTEMD_ENV_FILE = `/etc/${SERVICE_NAME}.env`;
|
|
206
280
|
}
|
|
207
281
|
});
|
|
208
282
|
|
|
@@ -211,68 +285,65 @@ var uninstall_exports = {};
|
|
|
211
285
|
__export(uninstall_exports, {
|
|
212
286
|
uninstall: () => uninstall
|
|
213
287
|
});
|
|
214
|
-
function uninstallMac() {
|
|
215
|
-
const
|
|
216
|
-
const
|
|
217
|
-
const deskfreeDir = join(home, ".deskfree");
|
|
218
|
-
const envFile = join(deskfreeDir, ".env");
|
|
219
|
-
const launcher = join(deskfreeDir, "launcher.sh");
|
|
288
|
+
function uninstallMac(name2) {
|
|
289
|
+
const paths = getMacPaths(name2);
|
|
290
|
+
const plistLabel = getPlistLabel(name2);
|
|
220
291
|
try {
|
|
221
|
-
execSync(`launchctl bootout gui/$(id -u) ${plist}`, {
|
|
292
|
+
execSync(`launchctl bootout gui/$(id -u) ${paths.plist}`, {
|
|
293
|
+
stdio: "ignore"
|
|
294
|
+
});
|
|
222
295
|
} catch {
|
|
223
296
|
}
|
|
224
|
-
for (const file of [plist, envFile, launcher]) {
|
|
297
|
+
for (const file of [paths.plist, paths.envFile, paths.launcher]) {
|
|
225
298
|
if (existsSync(file)) {
|
|
226
299
|
unlinkSync(file);
|
|
227
300
|
console.log(`Removed ${file}`);
|
|
228
301
|
}
|
|
229
302
|
}
|
|
230
|
-
console.log(`Service ${
|
|
303
|
+
console.log(`Service ${plistLabel} uninstalled.`);
|
|
231
304
|
console.log(
|
|
232
|
-
`Note: logs and state in ${deskfreeDir} were preserved. Remove manually if desired.`
|
|
305
|
+
`Note: logs and state in ${paths.deskfreeDir} were preserved. Remove manually if desired.`
|
|
233
306
|
);
|
|
234
307
|
}
|
|
235
|
-
function uninstallLinux() {
|
|
308
|
+
function uninstallLinux(name2) {
|
|
236
309
|
if (process.getuid?.() !== 0) {
|
|
237
310
|
console.error("Error: uninstall must be run as root (use sudo)");
|
|
238
311
|
process.exit(1);
|
|
239
312
|
}
|
|
313
|
+
const paths = getLinuxPaths(name2);
|
|
314
|
+
const serviceName = getServiceName(name2);
|
|
240
315
|
try {
|
|
241
|
-
execSync(`systemctl stop ${
|
|
316
|
+
execSync(`systemctl stop ${serviceName}`, { stdio: "ignore" });
|
|
242
317
|
} catch {
|
|
243
318
|
}
|
|
244
319
|
try {
|
|
245
|
-
execSync(`systemctl disable ${
|
|
320
|
+
execSync(`systemctl disable ${serviceName}`, { stdio: "ignore" });
|
|
246
321
|
} catch {
|
|
247
322
|
}
|
|
248
|
-
if (existsSync(
|
|
249
|
-
unlinkSync(
|
|
250
|
-
console.log(`Removed ${
|
|
323
|
+
if (existsSync(paths.serviceFile)) {
|
|
324
|
+
unlinkSync(paths.serviceFile);
|
|
325
|
+
console.log(`Removed ${paths.serviceFile}`);
|
|
251
326
|
}
|
|
252
|
-
if (existsSync(
|
|
253
|
-
unlinkSync(
|
|
254
|
-
console.log(`Removed ${
|
|
327
|
+
if (existsSync(paths.envFile)) {
|
|
328
|
+
unlinkSync(paths.envFile);
|
|
329
|
+
console.log(`Removed ${paths.envFile}`);
|
|
255
330
|
}
|
|
256
331
|
execSync("systemctl daemon-reload");
|
|
257
|
-
console.log(`Service ${
|
|
332
|
+
console.log(`Service ${serviceName} uninstalled.`);
|
|
258
333
|
}
|
|
259
|
-
function uninstall() {
|
|
334
|
+
function uninstall(name2) {
|
|
260
335
|
if (process.platform === "darwin") {
|
|
261
|
-
uninstallMac();
|
|
336
|
+
uninstallMac(name2);
|
|
262
337
|
} else if (process.platform === "linux") {
|
|
263
|
-
uninstallLinux();
|
|
338
|
+
uninstallLinux(name2);
|
|
264
339
|
} else {
|
|
265
340
|
console.error(`Unsupported platform: ${process.platform}`);
|
|
266
341
|
process.exit(1);
|
|
267
342
|
}
|
|
268
343
|
}
|
|
269
|
-
var SERVICE_NAME2, PLIST_LABEL2, SYSTEMD_SERVICE_FILE2, SYSTEMD_ENV_FILE2;
|
|
270
344
|
var init_uninstall = __esm({
|
|
271
345
|
"src/cli/uninstall.ts"() {
|
|
272
|
-
|
|
273
|
-
PLIST_LABEL2 = "com.deskfree.agent";
|
|
274
|
-
SYSTEMD_SERVICE_FILE2 = `/etc/systemd/system/${SERVICE_NAME2}.service`;
|
|
275
|
-
SYSTEMD_ENV_FILE2 = `/etc/${SERVICE_NAME2}.env`;
|
|
346
|
+
init_paths();
|
|
276
347
|
}
|
|
277
348
|
});
|
|
278
349
|
|
|
@@ -281,44 +352,46 @@ var status_exports = {};
|
|
|
281
352
|
__export(status_exports, {
|
|
282
353
|
status: () => status
|
|
283
354
|
});
|
|
284
|
-
function statusMac() {
|
|
285
|
-
const
|
|
286
|
-
const
|
|
287
|
-
if (!existsSync(plist)) {
|
|
288
|
-
console.log(
|
|
289
|
-
console.log(`Run:
|
|
355
|
+
function statusMac(name2) {
|
|
356
|
+
const paths = getMacPaths(name2);
|
|
357
|
+
const plistLabel = getPlistLabel(name2);
|
|
358
|
+
if (!existsSync(paths.plist)) {
|
|
359
|
+
console.log(`DeskFree Agent "${name2}" is not installed.`);
|
|
360
|
+
console.log(`Run: deskfree-agent install <token> --name ${name2}`);
|
|
290
361
|
return;
|
|
291
362
|
}
|
|
292
|
-
console.log(
|
|
363
|
+
console.log(`DeskFree Agent "${name2}" (macOS LaunchAgent)
|
|
364
|
+
`);
|
|
293
365
|
try {
|
|
294
|
-
const output = execSync(
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
);
|
|
366
|
+
const output = execSync(`launchctl print gui/$(id -u)/${plistLabel} 2>&1`, {
|
|
367
|
+
encoding: "utf8"
|
|
368
|
+
});
|
|
298
369
|
const pidMatch = output.match(/pid\s*=\s*(\d+)/);
|
|
299
370
|
const stateMatch = output.match(/state\s*=\s*(\w+)/);
|
|
300
371
|
if (pidMatch) console.log(` PID: ${pidMatch[1]}`);
|
|
301
372
|
if (stateMatch) console.log(` State: ${stateMatch[1]}`);
|
|
302
|
-
|
|
303
|
-
console.log(`
|
|
304
|
-
console.log(` Plist: ${plist}`);
|
|
373
|
+
console.log(` Logs: ${paths.logDir}/stdout.log`);
|
|
374
|
+
console.log(` Plist: ${paths.plist}`);
|
|
305
375
|
} catch {
|
|
306
376
|
console.log(" Status: not running (service may be unloaded)");
|
|
307
377
|
}
|
|
308
378
|
}
|
|
309
|
-
function statusLinux() {
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
console.log(`
|
|
379
|
+
function statusLinux(name2) {
|
|
380
|
+
const paths = getLinuxPaths(name2);
|
|
381
|
+
const serviceName = getServiceName(name2);
|
|
382
|
+
if (!existsSync(paths.serviceFile)) {
|
|
383
|
+
console.log(`DeskFree Agent "${name2}" is not installed.`);
|
|
384
|
+
console.log(
|
|
385
|
+
`Run: sudo npx @questionbase/deskfree@latest install <token> --name ${name2}`
|
|
386
|
+
);
|
|
314
387
|
return;
|
|
315
388
|
}
|
|
316
|
-
console.log(
|
|
389
|
+
console.log(`DeskFree Agent "${name2}" (systemd service)
|
|
390
|
+
`);
|
|
317
391
|
try {
|
|
318
|
-
const output = execSync(
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
);
|
|
392
|
+
const output = execSync(`systemctl status ${serviceName} --no-pager 2>&1`, {
|
|
393
|
+
encoding: "utf8"
|
|
394
|
+
});
|
|
322
395
|
console.log(output);
|
|
323
396
|
} catch (err) {
|
|
324
397
|
if (err && typeof err === "object" && "stdout" in err) {
|
|
@@ -327,22 +400,116 @@ function statusLinux() {
|
|
|
327
400
|
console.log(" Status: unknown (could not query systemd)");
|
|
328
401
|
}
|
|
329
402
|
}
|
|
403
|
+
console.log(` Logs: ${paths.logDir}/stdout.log`);
|
|
330
404
|
}
|
|
331
|
-
function status() {
|
|
405
|
+
function status(name2) {
|
|
332
406
|
if (process.platform === "darwin") {
|
|
333
|
-
statusMac();
|
|
407
|
+
statusMac(name2);
|
|
334
408
|
} else if (process.platform === "linux") {
|
|
335
|
-
statusLinux();
|
|
409
|
+
statusLinux(name2);
|
|
336
410
|
} else {
|
|
337
411
|
console.error(`Unsupported platform: ${process.platform}`);
|
|
338
412
|
process.exit(1);
|
|
339
413
|
}
|
|
340
414
|
}
|
|
341
|
-
var PLIST_LABEL3, SYSTEMD_SERVICE_NAME;
|
|
342
415
|
var init_status = __esm({
|
|
343
416
|
"src/cli/status.ts"() {
|
|
344
|
-
|
|
345
|
-
|
|
417
|
+
init_paths();
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// src/cli/restart.ts
|
|
422
|
+
var restart_exports = {};
|
|
423
|
+
__export(restart_exports, {
|
|
424
|
+
restart: () => restart
|
|
425
|
+
});
|
|
426
|
+
function restartMac(name2) {
|
|
427
|
+
const paths = getMacPaths(name2);
|
|
428
|
+
if (!existsSync(paths.plist)) {
|
|
429
|
+
console.error(`DeskFree Agent "${name2}" is not installed.`);
|
|
430
|
+
console.error(`Run: deskfree-agent install <token> --name ${name2}`);
|
|
431
|
+
process.exit(1);
|
|
432
|
+
}
|
|
433
|
+
try {
|
|
434
|
+
execSync(`launchctl bootout gui/$(id -u) ${paths.plist}`, {
|
|
435
|
+
stdio: "ignore"
|
|
436
|
+
});
|
|
437
|
+
} catch {
|
|
438
|
+
}
|
|
439
|
+
execSync(`launchctl bootstrap gui/$(id -u) ${paths.plist}`);
|
|
440
|
+
console.log(`DeskFree Agent "${name2}" restarted.`);
|
|
441
|
+
console.log(`Logs: tail -f ${paths.logDir}/stdout.log`);
|
|
442
|
+
}
|
|
443
|
+
function restartLinux(name2) {
|
|
444
|
+
const paths = getLinuxPaths(name2);
|
|
445
|
+
const serviceName = getServiceName(name2);
|
|
446
|
+
if (!existsSync(paths.serviceFile)) {
|
|
447
|
+
console.error(`DeskFree Agent "${name2}" is not installed.`);
|
|
448
|
+
console.error(
|
|
449
|
+
`Run: sudo npx @questionbase/deskfree@latest install <token> --name ${name2}`
|
|
450
|
+
);
|
|
451
|
+
process.exit(1);
|
|
452
|
+
}
|
|
453
|
+
execSync(`systemctl restart ${serviceName}`, { stdio: "inherit" });
|
|
454
|
+
console.log(`DeskFree Agent "${name2}" restarted.`);
|
|
455
|
+
console.log(`Logs: tail -f ${paths.logDir}/stdout.log`);
|
|
456
|
+
}
|
|
457
|
+
function restart(name2) {
|
|
458
|
+
if (process.platform === "darwin") {
|
|
459
|
+
restartMac(name2);
|
|
460
|
+
} else if (process.platform === "linux") {
|
|
461
|
+
restartLinux(name2);
|
|
462
|
+
} else {
|
|
463
|
+
console.error(`Unsupported platform: ${process.platform}`);
|
|
464
|
+
process.exit(1);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
var init_restart = __esm({
|
|
468
|
+
"src/cli/restart.ts"() {
|
|
469
|
+
init_paths();
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// src/cli/logs.ts
|
|
474
|
+
var logs_exports = {};
|
|
475
|
+
__export(logs_exports, {
|
|
476
|
+
logs: () => logs
|
|
477
|
+
});
|
|
478
|
+
function getLogPath(name2) {
|
|
479
|
+
if (process.platform === "darwin") {
|
|
480
|
+
const logFile = join(getMacPaths(name2).logDir, "stdout.log");
|
|
481
|
+
return existsSync(logFile) ? logFile : null;
|
|
482
|
+
} else if (process.platform === "linux") {
|
|
483
|
+
const logFile = join(getLinuxPaths(name2).logDir, "stdout.log");
|
|
484
|
+
return existsSync(logFile) ? logFile : null;
|
|
485
|
+
}
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
function logs(follow, name2) {
|
|
489
|
+
if (process.platform !== "darwin" && process.platform !== "linux") {
|
|
490
|
+
console.error(`Unsupported platform: ${process.platform}`);
|
|
491
|
+
process.exit(1);
|
|
492
|
+
}
|
|
493
|
+
const logPath = getLogPath(name2);
|
|
494
|
+
if (!logPath) {
|
|
495
|
+
console.error(
|
|
496
|
+
`No log file found for "${name2}". Is DeskFree Agent installed with --name ${name2}?`
|
|
497
|
+
);
|
|
498
|
+
process.exit(1);
|
|
499
|
+
}
|
|
500
|
+
const args = follow ? ["-f", "-n", "50", logPath] : ["-n", "50", logPath];
|
|
501
|
+
const child = spawn("tail", args, { stdio: "inherit" });
|
|
502
|
+
child.on("error", (err) => {
|
|
503
|
+
console.error(`Failed to read logs: ${err.message}`);
|
|
504
|
+
process.exit(1);
|
|
505
|
+
});
|
|
506
|
+
child.on("exit", (code) => {
|
|
507
|
+
process.exit(code ?? 0);
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
var init_logs = __esm({
|
|
511
|
+
"src/cli/logs.ts"() {
|
|
512
|
+
init_paths();
|
|
346
513
|
}
|
|
347
514
|
});
|
|
348
515
|
function toErrorMessage(error) {
|
|
@@ -1373,8 +1540,8 @@ function Intersect(types, options) {
|
|
|
1373
1540
|
throw new Error("Cannot intersect transform types");
|
|
1374
1541
|
return IntersectCreate(types, options);
|
|
1375
1542
|
}
|
|
1376
|
-
function Ref(...
|
|
1377
|
-
const [$ref, options] = typeof
|
|
1543
|
+
function Ref(...args) {
|
|
1544
|
+
const [$ref, options] = typeof args[0] === "string" ? [args[0], args[1]] : [args[0].$id, args[1]];
|
|
1378
1545
|
if (typeof $ref !== "string")
|
|
1379
1546
|
throw new TypeBoxError("Ref: $ref must be a string");
|
|
1380
1547
|
return CreateType({ [Kind]: "Ref", $ref }, options);
|
|
@@ -1933,78 +2100,78 @@ function RecordKey2(type) {
|
|
|
1933
2100
|
function RecordValue2(type) {
|
|
1934
2101
|
return type.patternProperties[RecordPattern(type)];
|
|
1935
2102
|
}
|
|
1936
|
-
function FromConstructor2(
|
|
1937
|
-
type.parameters = FromTypes(
|
|
1938
|
-
type.returns = FromType(
|
|
2103
|
+
function FromConstructor2(args, type) {
|
|
2104
|
+
type.parameters = FromTypes(args, type.parameters);
|
|
2105
|
+
type.returns = FromType(args, type.returns);
|
|
1939
2106
|
return type;
|
|
1940
2107
|
}
|
|
1941
|
-
function FromFunction2(
|
|
1942
|
-
type.parameters = FromTypes(
|
|
1943
|
-
type.returns = FromType(
|
|
2108
|
+
function FromFunction2(args, type) {
|
|
2109
|
+
type.parameters = FromTypes(args, type.parameters);
|
|
2110
|
+
type.returns = FromType(args, type.returns);
|
|
1944
2111
|
return type;
|
|
1945
2112
|
}
|
|
1946
|
-
function FromIntersect5(
|
|
1947
|
-
type.allOf = FromTypes(
|
|
2113
|
+
function FromIntersect5(args, type) {
|
|
2114
|
+
type.allOf = FromTypes(args, type.allOf);
|
|
1948
2115
|
return type;
|
|
1949
2116
|
}
|
|
1950
|
-
function FromUnion7(
|
|
1951
|
-
type.anyOf = FromTypes(
|
|
2117
|
+
function FromUnion7(args, type) {
|
|
2118
|
+
type.anyOf = FromTypes(args, type.anyOf);
|
|
1952
2119
|
return type;
|
|
1953
2120
|
}
|
|
1954
|
-
function FromTuple4(
|
|
2121
|
+
function FromTuple4(args, type) {
|
|
1955
2122
|
if (IsUndefined(type.items))
|
|
1956
2123
|
return type;
|
|
1957
|
-
type.items = FromTypes(
|
|
2124
|
+
type.items = FromTypes(args, type.items);
|
|
1958
2125
|
return type;
|
|
1959
2126
|
}
|
|
1960
|
-
function FromArray5(
|
|
1961
|
-
type.items = FromType(
|
|
2127
|
+
function FromArray5(args, type) {
|
|
2128
|
+
type.items = FromType(args, type.items);
|
|
1962
2129
|
return type;
|
|
1963
2130
|
}
|
|
1964
|
-
function FromAsyncIterator2(
|
|
1965
|
-
type.items = FromType(
|
|
2131
|
+
function FromAsyncIterator2(args, type) {
|
|
2132
|
+
type.items = FromType(args, type.items);
|
|
1966
2133
|
return type;
|
|
1967
2134
|
}
|
|
1968
|
-
function FromIterator2(
|
|
1969
|
-
type.items = FromType(
|
|
2135
|
+
function FromIterator2(args, type) {
|
|
2136
|
+
type.items = FromType(args, type.items);
|
|
1970
2137
|
return type;
|
|
1971
2138
|
}
|
|
1972
|
-
function FromPromise3(
|
|
1973
|
-
type.item = FromType(
|
|
2139
|
+
function FromPromise3(args, type) {
|
|
2140
|
+
type.item = FromType(args, type.item);
|
|
1974
2141
|
return type;
|
|
1975
2142
|
}
|
|
1976
|
-
function FromObject2(
|
|
1977
|
-
const mappedProperties = FromProperties11(
|
|
2143
|
+
function FromObject2(args, type) {
|
|
2144
|
+
const mappedProperties = FromProperties11(args, type.properties);
|
|
1978
2145
|
return { ...type, ...Object2(mappedProperties) };
|
|
1979
2146
|
}
|
|
1980
|
-
function FromRecord2(
|
|
1981
|
-
const mappedKey = FromType(
|
|
1982
|
-
const mappedValue = FromType(
|
|
2147
|
+
function FromRecord2(args, type) {
|
|
2148
|
+
const mappedKey = FromType(args, RecordKey2(type));
|
|
2149
|
+
const mappedValue = FromType(args, RecordValue2(type));
|
|
1983
2150
|
const result = Record(mappedKey, mappedValue);
|
|
1984
2151
|
return { ...type, ...result };
|
|
1985
2152
|
}
|
|
1986
|
-
function FromArgument(
|
|
1987
|
-
return argument.index in
|
|
2153
|
+
function FromArgument(args, argument) {
|
|
2154
|
+
return argument.index in args ? args[argument.index] : Unknown();
|
|
1988
2155
|
}
|
|
1989
|
-
function FromProperty2(
|
|
2156
|
+
function FromProperty2(args, type) {
|
|
1990
2157
|
const isReadonly = IsReadonly(type);
|
|
1991
2158
|
const isOptional = IsOptional(type);
|
|
1992
|
-
const mapped = FromType(
|
|
2159
|
+
const mapped = FromType(args, type);
|
|
1993
2160
|
return isReadonly && isOptional ? ReadonlyOptional(mapped) : isReadonly && !isOptional ? Readonly(mapped) : !isReadonly && isOptional ? Optional(mapped) : mapped;
|
|
1994
2161
|
}
|
|
1995
|
-
function FromProperties11(
|
|
2162
|
+
function FromProperties11(args, properties) {
|
|
1996
2163
|
return globalThis.Object.getOwnPropertyNames(properties).reduce((result, key) => {
|
|
1997
|
-
return { ...result, [key]: FromProperty2(
|
|
2164
|
+
return { ...result, [key]: FromProperty2(args, properties[key]) };
|
|
1998
2165
|
}, {});
|
|
1999
2166
|
}
|
|
2000
|
-
function FromTypes(
|
|
2001
|
-
return types.map((type) => FromType(
|
|
2167
|
+
function FromTypes(args, types) {
|
|
2168
|
+
return types.map((type) => FromType(args, type));
|
|
2002
2169
|
}
|
|
2003
|
-
function FromType(
|
|
2004
|
-
return IsConstructor(type) ? FromConstructor2(
|
|
2170
|
+
function FromType(args, type) {
|
|
2171
|
+
return IsConstructor(type) ? FromConstructor2(args, type) : IsFunction2(type) ? FromFunction2(args, type) : IsIntersect(type) ? FromIntersect5(args, type) : IsUnion(type) ? FromUnion7(args, type) : IsTuple(type) ? FromTuple4(args, type) : IsArray3(type) ? FromArray5(args, type) : IsAsyncIterator2(type) ? FromAsyncIterator2(args, type) : IsIterator2(type) ? FromIterator2(args, type) : IsPromise(type) ? FromPromise3(args, type) : IsObject3(type) ? FromObject2(args, type) : IsRecord(type) ? FromRecord2(args, type) : IsArgument(type) ? FromArgument(args, type) : type;
|
|
2005
2172
|
}
|
|
2006
|
-
function Instantiate(type,
|
|
2007
|
-
return FromType(
|
|
2173
|
+
function Instantiate(type, args) {
|
|
2174
|
+
return FromType(args, CloneType(type));
|
|
2008
2175
|
}
|
|
2009
2176
|
function Integer(options) {
|
|
2010
2177
|
return CreateType({ [Kind]: "Integer", type: "integer" }, options);
|
|
@@ -2480,6 +2647,9 @@ function validateStringParam(params, key, required) {
|
|
|
2480
2647
|
}
|
|
2481
2648
|
function validateEnumParam(params, key, values, required) {
|
|
2482
2649
|
const value = params?.[key];
|
|
2650
|
+
if (required && (value === void 0 || value === null)) {
|
|
2651
|
+
throw new Error(`Missing required parameter: ${key}`);
|
|
2652
|
+
}
|
|
2483
2653
|
if (value !== void 0 && value !== null && !values.includes(value)) {
|
|
2484
2654
|
throw new Error(
|
|
2485
2655
|
`Parameter ${key} must be one of: ${values.join(", ")}. Got: ${value}`
|
|
@@ -2663,7 +2833,18 @@ function createWorkerTools(client, options) {
|
|
|
2663
2833
|
try {
|
|
2664
2834
|
const content = validateStringParam(params, "content", true);
|
|
2665
2835
|
const taskId = validateStringParam(params, "taskId", false);
|
|
2836
|
+
const type = validateEnumParam(params, "type", ["notify", "ask"], true);
|
|
2666
2837
|
await client.sendMessage({ content, taskId });
|
|
2838
|
+
if (type === "ask") {
|
|
2839
|
+
return {
|
|
2840
|
+
content: [
|
|
2841
|
+
{
|
|
2842
|
+
type: "text",
|
|
2843
|
+
text: "Ask sent \u2014 task is now awaiting human response. Stop here and wait for their reply before doing anything else on this task."
|
|
2844
|
+
}
|
|
2845
|
+
]
|
|
2846
|
+
};
|
|
2847
|
+
}
|
|
2667
2848
|
return {
|
|
2668
2849
|
content: [{ type: "text", text: "Message sent successfully" }]
|
|
2669
2850
|
};
|
|
@@ -2684,7 +2865,7 @@ function createWorkerTools(client, options) {
|
|
|
2684
2865
|
}),
|
|
2685
2866
|
createTool(WORKER_TOOLS.CREATE_FILE, async (params) => {
|
|
2686
2867
|
try {
|
|
2687
|
-
const
|
|
2868
|
+
const name2 = validateStringParam(params, "name", true);
|
|
2688
2869
|
const description = validateStringParam(params, "description", false);
|
|
2689
2870
|
const content = validateStringParam(params, "content", false);
|
|
2690
2871
|
const contentFormat = validateEnumParam(
|
|
@@ -2695,7 +2876,7 @@ function createWorkerTools(client, options) {
|
|
|
2695
2876
|
);
|
|
2696
2877
|
const taskId = validateStringParam(params, "taskId", false);
|
|
2697
2878
|
const result = await client.createFile({
|
|
2698
|
-
name,
|
|
2879
|
+
name: name2,
|
|
2699
2880
|
description,
|
|
2700
2881
|
content,
|
|
2701
2882
|
contentFormat,
|
|
@@ -2813,6 +2994,330 @@ function createWorkerTools(client, options) {
|
|
|
2813
2994
|
})
|
|
2814
2995
|
];
|
|
2815
2996
|
}
|
|
2997
|
+
function identityBlock(ctx) {
|
|
2998
|
+
const providerLabel = ctx.provider === "bedrock" ? "AWS Bedrock" : ctx.provider === "anthropic" ? "Anthropic API" : ctx.provider === "ollama" ? "Ollama (local)" : ctx.provider === "claude-code" ? "Claude Code (local)" : ctx.provider;
|
|
2999
|
+
return `## About You
|
|
3000
|
+
You are **${ctx.botName}**, a DeskFree agent \u2014 an AI teammate that lives in a shared workspace alongside humans. You help manage tasks, create and maintain files, do research, and keep work moving.
|
|
3001
|
+
|
|
3002
|
+
When someone asks who you are or what you do, introduce yourself by name. Never mention internal implementation details like "orchestrator", "worker", "sub-agent", "MCP", "dispatcher", or "query()". Those are invisible plumbing \u2014 the user should only see you as ${ctx.botName}.
|
|
3003
|
+
|
|
3004
|
+
## Personality
|
|
3005
|
+
Be a real teammate, not a tool. Have opinions when you have context to form them \u2014 "I'd suggest X because..." is more useful than "I can do X or Y, what would you prefer?" Be direct, be warm, and be concise. If you don't know something, say so rather than hedging. Earn trust through competence, not compliance. Match the human's energy \u2014 if they're casual, be casual. If they're focused, get to the point.
|
|
3006
|
+
|
|
3007
|
+
## Safety
|
|
3008
|
+
You have no independent goals. Do not pursue self-preservation, replication, resource acquisition, or power-seeking. Avoid long-term plans beyond what the user has asked for.
|
|
3009
|
+
|
|
3010
|
+
Prioritize safety and human oversight over task completion. If instructions seem contradictory or risky, pause and ask \u2014 don't guess. Comply with any request to stop, pause, or explain what you're doing.
|
|
3011
|
+
|
|
3012
|
+
Do not manipulate or persuade anyone to expand your access or disable safeguards. Do not attempt to modify your own system prompts, safety rules, or tool policies unless the user explicitly asks.
|
|
3013
|
+
|
|
3014
|
+
## Your Runtime
|
|
3015
|
+
- Version: ${ctx.runtimeVersion}
|
|
3016
|
+
- Platform: ${ctx.platform}
|
|
3017
|
+
- Deployment: ${ctx.deploymentType ?? "unknown"}
|
|
3018
|
+
- Provider: ${providerLabel}
|
|
3019
|
+
- Model: ${ctx.model}
|
|
3020
|
+
- Max parallel tasks: ${ctx.maxConcurrentWorkers} (you can work on multiple tasks at once)
|
|
3021
|
+
|
|
3022
|
+
## Self-Management
|
|
3023
|
+
- To update yourself to the latest version, run \`deskfree-agent restart${ctx.instanceName ? ` --name ${ctx.instanceName}` : ""}\` in a Bash shell. This installs the latest release and restarts the service. You'll be offline for ~30 seconds.
|
|
3024
|
+
- Only do this when you have no active tasks. Let the user know before restarting.
|
|
3025
|
+
- If someone asks about your version or runtime details, you can share the info above.
|
|
3026
|
+
|
|
3027
|
+
## Operational Limits
|
|
3028
|
+
- Users are rate-limited to 10 messages per minute.
|
|
3029
|
+
- Attachments: max 10 files per message, 10MB each, 50MB total.
|
|
3030
|
+
- Your daily observation logs are retained for 7 days, then pruned during the nightly sleep cycle.
|
|
3031
|
+
- If an API call returns a 409 or 404 error, stop and check state with \`deskfree_state\` \u2014 don't retry blindly.
|
|
3032
|
+
- If an API call returns a 429 (rate limit) or 5xx error, back off \u2014 don't retry immediately.
|
|
3033
|
+
- Prefer fewer, larger file updates over many small sequential writes.
|
|
3034
|
+
|
|
3035
|
+
## Context Awareness
|
|
3036
|
+
Your conversation history may be summarized to save context space. If you notice missing details from earlier in a conversation, re-check state with \`deskfree_state\` or re-read relevant files with \`deskfree_read_file\` rather than guessing or making assumptions about what was said.
|
|
3037
|
+
|
|
3038
|
+
## Working With Humans
|
|
3039
|
+
Human attention is finite. You have unlimited stamina \u2014 they don't. Optimize for their review experience, not just output quality.
|
|
3040
|
+
|
|
3041
|
+
- **Don't pile on.** If the board already has 3+ open tasks, think twice before proposing more. Help finish and clear existing work before adding new items.
|
|
3042
|
+
- **Incremental over monolithic.** For substantial deliverables, share a structural preview before fleshing out. A quick "here's the outline \u2014 does this direction work?" saves everyone time versus a finished wall of text to review.
|
|
3043
|
+
- **Separate FYI from action needed.** Never make the human triage what needs their input. \`notify\` = no action needed. \`ask\` = needs their input. Be precise about which you're sending.
|
|
3044
|
+
- **Fewer, better decisions.** Don't present 5 options when you can recommend 1 with reasoning. Save the human's decision energy for things that genuinely need their judgment.
|
|
3045
|
+
- **Prefer simple output.** A focused 500-word draft beats a comprehensive 2000-word one the human has to pare down. Don't add sections, caveats, or "bonus" content unless asked.`;
|
|
3046
|
+
}
|
|
3047
|
+
function buildAgentDirective(ctx) {
|
|
3048
|
+
return `${identityBlock(ctx)}
|
|
3049
|
+
|
|
3050
|
+
## How You Work
|
|
3051
|
+
|
|
3052
|
+
**Main thread = short and snappy.** Keep responses to 1-3 sentences. Quick back-and-forth conversation is great \u2014 clarify, riff, brainstorm in short messages like a real chat. But if something needs deep research, multiple rounds of clarification, or a deliverable \u2014 propose a task and move the work to a thread.
|
|
3053
|
+
|
|
3054
|
+
**The core loop:**
|
|
3055
|
+
|
|
3056
|
+
1. **Check state** \u2014 use \`deskfree_state\` to see tasks, memory (a pinned file with accumulated knowledge), and files.
|
|
3057
|
+
2. **Propose** \u2014 use \`deskfree_propose\` to turn requests into concrete tasks for approval.
|
|
3058
|
+
3. **Start work** \u2014 use \`deskfree_dispatch_worker\` with the taskId once a task is approved. You'll then continue the work in the task thread.
|
|
3059
|
+
4. **Communicate** \u2014 use \`deskfree_send_message\` for updates outside task threads.
|
|
3060
|
+
|
|
3061
|
+
**Before proposing, qualify the request.** Figure out what kind of thing this is:
|
|
3062
|
+
- **One-off task** ("proofread this") \u2014 propose a task directly.
|
|
3063
|
+
- **New aspiration** ("I want to start posting on LinkedIn") \u2014 don't rush to propose. Ask 1-2 short qualifying questions to understand the real goal.
|
|
3064
|
+
- Never call \`deskfree_propose\` as your very first action \u2014 qualify first, even if briefly.
|
|
3065
|
+
|
|
3066
|
+
**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.
|
|
3067
|
+
|
|
3068
|
+
In the main thread you propose and coordinate \u2014 the actual work happens in task threads. Use \`deskfree_dispatch_worker\` to start working on approved tasks.
|
|
3069
|
+
- When a human writes in a task thread, decide:
|
|
3070
|
+
- **Continuation of the same task?** \u2192 reopen and pick it back up.
|
|
3071
|
+
- **New/different work request?** \u2192 propose it as a new task (don't reopen the old one).
|
|
3072
|
+
- **Just confirmation or deferred?** \u2192 leave it for now.
|
|
3073
|
+
- Estimate token cost per task \u2014 consider files to read, reasoning, output.`;
|
|
3074
|
+
}
|
|
3075
|
+
function buildWorkerDirective(ctx) {
|
|
3076
|
+
return `${identityBlock(ctx)}
|
|
3077
|
+
|
|
3078
|
+
## You're In a Task Thread
|
|
3079
|
+
You're the same ${ctx.botName} from the main thread, now focused on a specific task. Same voice, same personality \u2014 just heads-down on the work.
|
|
3080
|
+
|
|
3081
|
+
Tools: deskfree_state, deskfree_start_task, deskfree_read_file, deskfree_create_file, deskfree_update_file, deskfree_learning, deskfree_complete_task, deskfree_send_message, deskfree_propose.
|
|
3082
|
+
|
|
3083
|
+
**Context loading:**
|
|
3084
|
+
- If your first message contains \`<task_context>\`, the task is already loaded. Start working immediately \u2014 do NOT call deskfree_start_task.
|
|
3085
|
+
- If your first message contains \`<workspace_state>\`, use it for situational awareness (other tasks, memory, files).
|
|
3086
|
+
- If no pre-loaded context (edge case/fallback), call \`deskfree_start_task\` with your taskId to load it.
|
|
3087
|
+
- If continuing from a previous conversation (you can see prior tool calls and context), respond directly to the human's latest message \u2014 do NOT call deskfree_start_task again.
|
|
3088
|
+
|
|
3089
|
+
**Orient \u2192 Align \u2192 Work.** Every new task follows this rhythm:
|
|
3090
|
+
|
|
3091
|
+
1. **Orient** \u2014 Scan workspace state for relevant files. Read the Memory file for context on preferences and past patterns. Read any other files that are useful context with \`deskfree_read_file\`. Don't read everything \u2014 just what's relevant to this task.
|
|
3092
|
+
2. **Align** \u2014 Send a brief \`notify\` message: what you found, what you'll produce. One or two sentences. ("I'll build on the existing brand guide and create a new tone reference.")
|
|
3093
|
+
- **Judgment calls or creative direction?** State your assumptions and approach, send as \`ask\`, and wait for confirmation before proceeding. Getting alignment early prevents costly rework.
|
|
3094
|
+
- **Straightforward execution?** Proceed immediately after the notify \u2014 don't wait for a response.
|
|
3095
|
+
3. **Work** \u2014 Execute the task. Update existing files with \`deskfree_update_file\` or create new ones with \`deskfree_create_file\`. Pass your taskId so updates appear in the thread. For large deliverables, build incrementally \u2014 share structure/outline first, then flesh out. Don't produce a finished 2000-word document and ask for review in one shot.
|
|
3096
|
+
4. **Deliver** \u2014 Send an \`ask\` message when work is ready for review. Only complete (\`deskfree_complete_task\` with humanApproved: true) after the human has confirmed. Never self-complete.
|
|
3097
|
+
|
|
3098
|
+
**Push back when warranted:**
|
|
3099
|
+
- If task instructions seem unclear, contradictory, or misguided \u2014 say so. "This task asks for X, but based on [context], Y might work better because..." is more useful than silently executing a flawed plan.
|
|
3100
|
+
- If you hit genuine ambiguity mid-task, send an \`ask\` message and wait. Don't guess on important decisions \u2014 guessing creates review debt the human has to pay later.
|
|
3101
|
+
- You're a teammate, not a task executor. Have an opinion when you have the context to form one.
|
|
3102
|
+
|
|
3103
|
+
**File rules:**
|
|
3104
|
+
- Create files when your task naturally produces them. Don't be afraid to create multiple files if the work calls for it.
|
|
3105
|
+
- Always pass \`taskId\` when creating or updating files \u2014 this threads notifications into the task.
|
|
3106
|
+
- If you discover work that falls outside your task's scope, use \`deskfree_propose\` to suggest follow-up tasks immediately \u2014 don't wait until completion. Propose as you discover, then stay focused on your current task.
|
|
3107
|
+
|
|
3108
|
+
**Learnings:**
|
|
3109
|
+
- Use \`deskfree_learning\` to record observations worth remembering. A nightly cycle consolidates these into the Memory file. Record:
|
|
3110
|
+
- **Preferences**: how the human wants things done ("prefers X over Y")
|
|
3111
|
+
- **Corrections**: when the human corrects you ("actually, do X not Y")
|
|
3112
|
+
- **Patterns**: recurring approaches that work ("for this type of task, always...")
|
|
3113
|
+
- **Domain facts**: business-specific knowledge not in project docs
|
|
3114
|
+
- Prefix critical observations with [!] (corrections, constraints, errors).
|
|
3115
|
+
- Prefix notable observations with [~] (preferences, patterns).
|
|
3116
|
+
- Leave routine observations unprefixed.
|
|
3117
|
+
- Do NOT record one-time task details, things in project docs, or obvious/generic knowledge.
|
|
3118
|
+
- If your first message contains \`<daily_observations>\`, these are recent raw observations not yet consolidated into Memory. Use them as additional context.
|
|
3119
|
+
|
|
3120
|
+
**Delegation:**
|
|
3121
|
+
- Your context window is finite. Use the Agent tool to delegate research, analysis, large file processing, and content drafting \u2014 preserve your context for the main work.
|
|
3122
|
+
- Delegated work gets a fresh context window with standard tools (Read, Write, Bash, Grep, WebSearch, etc.) but NO DeskFree tools. Pre-load any file content they need into the prompt.
|
|
3123
|
+
- Use \`run_in_background: true\` for parallel independent work.
|
|
3124
|
+
- During Orient, check Memory for delegation patterns. Inject relevant ones into the prompt alongside the task.
|
|
3125
|
+
- After delegated work completes, reflect: did this reveal a useful pattern? Record via \`deskfree_learning\` so it's consolidated into Memory.
|
|
3126
|
+
- Don't over-delegate: quick reads, simple lookups, and anything requiring DeskFree tools are faster inline.
|
|
3127
|
+
|
|
3128
|
+
**Completing tasks:**
|
|
3129
|
+
- On 409 or 404 errors: STOP. Do not retry. Call deskfree_state to find available tasks.`;
|
|
3130
|
+
}
|
|
3131
|
+
function buildHeartbeatDirective(ctx) {
|
|
3132
|
+
return `${identityBlock(ctx)}
|
|
3133
|
+
|
|
3134
|
+
## Heartbeat Check
|
|
3135
|
+
On each heartbeat, run through this checklist:
|
|
3136
|
+
|
|
3137
|
+
### 1. Work the queue
|
|
3138
|
+
- Run \`deskfree_state\` to get the full workspace snapshot.
|
|
3139
|
+
- **Check board load.** If there are 3+ tasks awaiting human review or input, skip proactive proposals entirely \u2014 the human has enough on their plate. Focus only on dispatching approved work.
|
|
3140
|
+
- Any open tasks with awaiting=bot? Use \`deskfree_dispatch_worker\` to start working on each one. Pass the taskId.
|
|
3141
|
+
- Any open tasks that seem stalled (no recent activity)? Check on them.
|
|
3142
|
+
|
|
3143
|
+
### 2. Proactive assessment
|
|
3144
|
+
After handling the queue, step back and think about the bigger picture. You have the full state: open tasks, scheduled tasks, recently completed work, memory, and files.
|
|
3145
|
+
|
|
3146
|
+
**Think through:**
|
|
3147
|
+
- What momentum exists? What was recently accomplished, and what naturally follows?
|
|
3148
|
+
- What is stalled or falling behind? Anything open too long without progress?
|
|
3149
|
+
- What is scheduled soon? Does anything need prep work before it lands?
|
|
3150
|
+
- What is the single highest-leverage thing that could happen next?
|
|
3151
|
+
|
|
3152
|
+
**Then act \u2014 but only if you have something genuinely useful:**
|
|
3153
|
+
|
|
3154
|
+
*Things you can do* \u2014 research, drafts, analysis, prep. Propose as a task via \`deskfree_propose\`. One focused task, not a batch.
|
|
3155
|
+
|
|
3156
|
+
*Things the human should do* \u2014 nudges, reminders, conversation starters. Send via \`deskfree_send_message\`. Keep it brief and genuinely helpful, not nagging.
|
|
3157
|
+
|
|
3158
|
+
**Rules:**
|
|
3159
|
+
- Do not suggest things that are already open or scheduled \u2014 check first.
|
|
3160
|
+
- Do not repeat suggestions the human ignored or rejected recently.
|
|
3161
|
+
- Quality over quantity. One good insight beats five generic nudges.
|
|
3162
|
+
- If everything looks healthy and active, do nothing. Silence is fine.`;
|
|
3163
|
+
}
|
|
3164
|
+
function buildSleepDirective(ctx) {
|
|
3165
|
+
return `${identityBlock(ctx)}
|
|
3166
|
+
|
|
3167
|
+
## Nightly Sleep Cycle
|
|
3168
|
+
You're running your nightly cycle to reflect, consolidate memory, and prepare for tomorrow.
|
|
3169
|
+
|
|
3170
|
+
Tools available: deskfree_state, deskfree_propose, deskfree_send_message, deskfree_read_file, deskfree_update_file.
|
|
3171
|
+
|
|
3172
|
+
---
|
|
3173
|
+
|
|
3174
|
+
### 1. REFLECT & CONSOLIDATE
|
|
3175
|
+
|
|
3176
|
+
Your primary job: merge daily observations into the Memory file using strength-scored entries.
|
|
3177
|
+
|
|
3178
|
+
Your prompt contains both \`<current_memory>\` (the current Memory file content) and \`<daily_observations>\` (raw daily logs) inline \u2014 you have everything you need upfront.
|
|
3179
|
+
|
|
3180
|
+
**Steps:**
|
|
3181
|
+
1. Review the current memory and daily observations from your prompt.
|
|
3182
|
+
2. Apply the memory curation rules below.
|
|
3183
|
+
3. Update the Memory file via \`deskfree_update_file\` using the file ID from your prompt.
|
|
3184
|
+
4. After updating, send a brief main-thread message via \`deskfree_send_message\` noting what was consolidated (e.g. "Reflected on yesterday \u2014 updated my notes on your brand voice preferences and the weekly review pattern."). Keep it to 1-2 sentences.
|
|
3185
|
+
5. If nothing meaningful changed, skip the update and send a brief message like "Quiet day \u2014 nothing new to consolidate."
|
|
3186
|
+
|
|
3187
|
+
**Memory Types**
|
|
3188
|
+
|
|
3189
|
+
Each memory item carries a type tag and strength score: \`[type:X, strength:N]\`
|
|
3190
|
+
|
|
3191
|
+
| Type | What it captures | Decay rate |
|
|
3192
|
+
|------|-----------------|------------|
|
|
3193
|
+
| \`correction\` | Explicit "do X not Y" from human | Very slow (\u22121 when strength >=10, else no decay) |
|
|
3194
|
+
| \`preference\` | How the human wants things done | Slow (\u22121 when strength >=6, else no decay) |
|
|
3195
|
+
| \`pattern\` | Approaches/workflows that work | Normal (see decay rules) |
|
|
3196
|
+
| \`domain\` | Business/project-specific facts | Slow (\u22121 when strength >=6, else no decay) |
|
|
3197
|
+
| \`insight\` | Meta-observations from reflection | Normal (see decay rules) |
|
|
3198
|
+
|
|
3199
|
+
Corrections and domain facts are durable \u2014 they rarely become irrelevant.
|
|
3200
|
+
Patterns and insights decay normally \u2014 stale approaches should be forgotten.
|
|
3201
|
+
Preferences sit in between \u2014 slow decay, but they can evolve.
|
|
3202
|
+
|
|
3203
|
+
**Strength Scoring Rules**
|
|
3204
|
+
|
|
3205
|
+
Strength uses Ebbinghaus-inspired logarithmic decay \u2014 strong memories resist forgetting:
|
|
3206
|
+
|
|
3207
|
+
**Reinforcement (observation matches existing memory):**
|
|
3208
|
+
- strength +2
|
|
3209
|
+
|
|
3210
|
+
**Explicit correction ("actually do X not Y"):**
|
|
3211
|
+
- Replace old memory, new one starts at [strength: 3, type: correction]
|
|
3212
|
+
|
|
3213
|
+
**Decay (memory NOT referenced by any daily observation):**
|
|
3214
|
+
- strength >= 10: decay by \u22121 (deeply encoded, slow forgetting)
|
|
3215
|
+
- strength 5-9: decay by \u22122 (moderately encoded)
|
|
3216
|
+
- strength 1-4: decay by \u22123 (weakly encoded, fast forgetting)
|
|
3217
|
+
- EXCEPT: corrections and domain facts with strength >=6 decay at \u22121 max (durable memories)
|
|
3218
|
+
- EXCEPT: preferences with strength >=6 decay at \u22121 max
|
|
3219
|
+
|
|
3220
|
+
**Removal:**
|
|
3221
|
+
- Strength reaches 0 \u2192 move to a ## Fading section at the bottom (one-line summaries only, no strength tags). Keep max 10 fading memories. If Fading section is full, oldest entries are permanently forgotten. You may rescue a fading memory back to active if it becomes relevant again (re-add with [strength: 2]).
|
|
3222
|
+
|
|
3223
|
+
**New observation:**
|
|
3224
|
+
- Assess importance: how consequential is this for future tasks?
|
|
3225
|
+
- Look for [!] prefix (critical) or [~] prefix (notable) as importance signals.
|
|
3226
|
+
- Low importance (casual mention, routine) \u2192 [strength: 1, type: <appropriate>]
|
|
3227
|
+
- Medium importance (useful preference, [~] prefix) \u2192 [strength: 2, type: <appropriate>]
|
|
3228
|
+
- High importance (explicit correction, strong constraint, [!] prefix) \u2192 [strength: 4, type: <appropriate>]
|
|
3229
|
+
- Critical (production error, absolute rule) \u2192 [strength: 6, type: <appropriate>]
|
|
3230
|
+
|
|
3231
|
+
**Consolidation rules:**
|
|
3232
|
+
- Classify each memory with the correct type. Only keep genuinely reusable knowledge.
|
|
3233
|
+
- Discard noise and one-off task details.
|
|
3234
|
+
- If the same episodic observation appears 3+ times across different days, promote it to a \`pattern\` or \`domain\` fact with [strength: 5]. Remove the individual episodes.
|
|
3235
|
+
- Look for meta-patterns: approaches that consistently work/fail, implicit preferences, recurring corrections.
|
|
3236
|
+
- If you find non-obvious insights, add them as \`[type: insight, strength: 2]\` memories.
|
|
3237
|
+
- Merge near-duplicates, remove items at [strength: 0], trim verbose entries.
|
|
3238
|
+
- Keep total document under ~4000 words.
|
|
3239
|
+
- Use ## headers for sections \u2014 let them emerge organically from content.
|
|
3240
|
+
|
|
3241
|
+
### 2. CHECK RECURRING COMMITMENTS
|
|
3242
|
+
|
|
3243
|
+
After memory consolidation:
|
|
3244
|
+
1. Call \`deskfree_state\` to see the board.
|
|
3245
|
+
2. Cross-reference memory for recurring patterns (daily audits, weekly reviews, etc.).
|
|
3246
|
+
3. If a recurring commitment has no upcoming scheduled task, propose it via \`deskfree_propose\` with appropriate \`scheduledFor\`.
|
|
3247
|
+
4. Don't propose things already on the board or that were recently completed/ignored.
|
|
3248
|
+
|
|
3249
|
+
### 3. PROACTIVE OPPORTUNITIES
|
|
3250
|
+
|
|
3251
|
+
Based on recent work, completed tasks, and patterns in memory \u2014 is there something genuinely useful to propose or a message worth sending?
|
|
3252
|
+
- **Check board load first.** If the human already has 3+ items needing their attention, skip proposals entirely.
|
|
3253
|
+
- One focused proposal max. Skip if nothing merits it.
|
|
3254
|
+
- Quality over quantity. Don't force it.
|
|
3255
|
+
|
|
3256
|
+
### Rules
|
|
3257
|
+
- Keep main thread messages short (1-2 sentences).
|
|
3258
|
+
- Do NOT propose things the human has previously ignored or rejected.
|
|
3259
|
+
- Use \`deskfree_read_file\` only if you need to re-read the Memory file after your own update (verification). The current content is already in your prompt.`;
|
|
3260
|
+
}
|
|
3261
|
+
function buildDuskDirective(ctx) {
|
|
3262
|
+
return `${identityBlock(ctx)}
|
|
3263
|
+
|
|
3264
|
+
## Evening Dusk Cycle
|
|
3265
|
+
You're running your evening cycle to review the day, propose overnight work, and brief the human.
|
|
3266
|
+
|
|
3267
|
+
Tools available: deskfree_state, deskfree_propose, deskfree_send_message, deskfree_read_file, deskfree_update_file.
|
|
3268
|
+
|
|
3269
|
+
---
|
|
3270
|
+
|
|
3271
|
+
### 1. REVIEW THE DAY
|
|
3272
|
+
|
|
3273
|
+
Your prompt contains both \`<current_memory>\` (the current Memory file content) and \`<daily_observations>\` (raw daily logs) inline \u2014 you have everything you need upfront.
|
|
3274
|
+
|
|
3275
|
+
**Steps:**
|
|
3276
|
+
1. Review the current memory and daily observations from your prompt.
|
|
3277
|
+
2. Call \`deskfree_state\` to see the current board \u2014 open tasks, recently completed work, files.
|
|
3278
|
+
3. Build a mental model of what happened today: what was accomplished, what's in progress, what's stalled.
|
|
3279
|
+
|
|
3280
|
+
### 2. IDENTIFY OVERNIGHT OPPORTUNITIES
|
|
3281
|
+
|
|
3282
|
+
Think about work that can be done autonomously overnight \u2014 WITHOUT human judgment or approval mid-task.
|
|
3283
|
+
|
|
3284
|
+
**Good overnight work:**
|
|
3285
|
+
- Research and analysis (market research, competitive analysis, reading material)
|
|
3286
|
+
- Content drafts (blog posts, reports, documentation)
|
|
3287
|
+
- File updates (updating recurring reports, preparing templates)
|
|
3288
|
+
- Prep work for tomorrow (gathering context, pre-reading for scheduled meetings)
|
|
3289
|
+
- Recurring tasks that are due (weekly reviews, daily audits)
|
|
3290
|
+
|
|
3291
|
+
**NOT good overnight work:**
|
|
3292
|
+
- Anything requiring human decisions mid-task
|
|
3293
|
+
- Tasks that depend on external input not yet received
|
|
3294
|
+
- Creative work where the human has strong opinions on direction
|
|
3295
|
+
- Anything the human explicitly said to wait on
|
|
3296
|
+
|
|
3297
|
+
### 3. PROPOSE THE PLAN
|
|
3298
|
+
|
|
3299
|
+
If you identified useful overnight work:
|
|
3300
|
+
1. **Check board load first.** Count open and awaiting-review tasks. If the human already has 3+ items needing their attention, limit to 1 proposal max \u2014 or skip entirely. Don't pile on.
|
|
3301
|
+
2. Use \`deskfree_propose\` with 1-3 well-scoped tasks. Quality over quantity.
|
|
3302
|
+
3. Each task should be self-contained \u2014 it must be completable without human input.
|
|
3303
|
+
4. Set \`scheduledFor\` if work should start at a specific time (e.g. early morning).
|
|
3304
|
+
5. If nothing genuinely useful can be done overnight, skip the proposal entirely. Don't force it.
|
|
3305
|
+
|
|
3306
|
+
### 4. BRIEF THE HUMAN
|
|
3307
|
+
|
|
3308
|
+
Send a brief main-thread message via \`deskfree_send_message\`:
|
|
3309
|
+
- 2-3 sentence summary: what happened today + what you're proposing for overnight (if anything).
|
|
3310
|
+
- Keep it conversational and useful \u2014 this is the human's "end of day" touchpoint.
|
|
3311
|
+
- If no proposals, still send a brief day summary.
|
|
3312
|
+
|
|
3313
|
+
### Rules
|
|
3314
|
+
- Do NOT propose things already on the board or recently completed.
|
|
3315
|
+
- Do NOT repeat suggestions the human previously ignored or rejected.
|
|
3316
|
+
- Quality over quantity. One good task beats three mediocre ones.
|
|
3317
|
+
- Keep the briefing message short and actionable.
|
|
3318
|
+
- Cross-reference memory for recurring patterns \u2014 if something is due, propose it.
|
|
3319
|
+
- Use \`deskfree_read_file\` only if you need file content beyond what's in your prompt.`;
|
|
3320
|
+
}
|
|
2816
3321
|
function setActiveWs(ws) {
|
|
2817
3322
|
activeWs = ws;
|
|
2818
3323
|
}
|
|
@@ -2940,18 +3445,18 @@ function saveCursor(ctx, cursor, storagePath, log) {
|
|
|
2940
3445
|
}
|
|
2941
3446
|
}
|
|
2942
3447
|
function validateField(opts) {
|
|
2943
|
-
const { value, name, minLength, maxLength, pattern, patternMessage } = opts;
|
|
2944
|
-
if (!value) return `${
|
|
2945
|
-
if (typeof value !== "string") return `${
|
|
3448
|
+
const { value, name: name2, minLength, maxLength, pattern, patternMessage } = opts;
|
|
3449
|
+
if (!value) return `${name2} is required`;
|
|
3450
|
+
if (typeof value !== "string") return `${name2} must be a string`;
|
|
2946
3451
|
const trimmed = value.trim();
|
|
2947
3452
|
if (trimmed !== value)
|
|
2948
|
-
return `${
|
|
3453
|
+
return `${name2} must not have leading or trailing whitespace`;
|
|
2949
3454
|
if (minLength !== void 0 && trimmed.length < minLength)
|
|
2950
|
-
return `${
|
|
3455
|
+
return `${name2} appears to be incomplete (minimum ${minLength} characters expected)`;
|
|
2951
3456
|
if (maxLength !== void 0 && trimmed.length > maxLength)
|
|
2952
|
-
return `${
|
|
3457
|
+
return `${name2} appears to be invalid (maximum ${maxLength} characters expected)`;
|
|
2953
3458
|
if (pattern !== void 0 && !pattern.test(trimmed))
|
|
2954
|
-
return patternMessage ?? `${
|
|
3459
|
+
return patternMessage ?? `${name2} contains invalid characters`;
|
|
2955
3460
|
return null;
|
|
2956
3461
|
}
|
|
2957
3462
|
function isLocalDevelopmentHost(hostname) {
|
|
@@ -2981,8 +3486,8 @@ function validateBotToken(value) {
|
|
|
2981
3486
|
}
|
|
2982
3487
|
return null;
|
|
2983
3488
|
}
|
|
2984
|
-
function validateUrl(value,
|
|
2985
|
-
const fieldError = validateField({ value, name });
|
|
3489
|
+
function validateUrl(value, name2, allowedProtocols, protocolError) {
|
|
3490
|
+
const fieldError = validateField({ value, name: name2 });
|
|
2986
3491
|
if (fieldError) return fieldError;
|
|
2987
3492
|
const trimmed = value.trim();
|
|
2988
3493
|
let url;
|
|
@@ -2990,21 +3495,21 @@ function validateUrl(value, name, allowedProtocols, protocolError) {
|
|
|
2990
3495
|
url = new URL(trimmed);
|
|
2991
3496
|
} catch (err) {
|
|
2992
3497
|
const message = err instanceof Error ? err.message : "Invalid URL format";
|
|
2993
|
-
return `${
|
|
3498
|
+
return `${name2} must be a valid URL: ${message}`;
|
|
2994
3499
|
}
|
|
2995
3500
|
if (!allowedProtocols.includes(url.protocol)) {
|
|
2996
3501
|
return protocolError;
|
|
2997
3502
|
}
|
|
2998
3503
|
if (!url.hostname) {
|
|
2999
|
-
return `${
|
|
3504
|
+
return `${name2} must have a valid hostname`;
|
|
3000
3505
|
}
|
|
3001
3506
|
if (isLocalDevelopmentHost(url.hostname)) {
|
|
3002
3507
|
if (process.env.NODE_ENV === "production") {
|
|
3003
|
-
return `${
|
|
3508
|
+
return `${name2} cannot use localhost or private IP addresses in production. Please use a publicly accessible URL.`;
|
|
3004
3509
|
}
|
|
3005
3510
|
}
|
|
3006
3511
|
if (url.hostname.includes("..") || url.hostname.startsWith(".")) {
|
|
3007
|
-
return `${
|
|
3512
|
+
return `${name2} hostname appears to be malformed. Please check for typos.`;
|
|
3008
3513
|
}
|
|
3009
3514
|
return null;
|
|
3010
3515
|
}
|
|
@@ -3016,7 +3521,7 @@ function validateApiUrl(value) {
|
|
|
3016
3521
|
"API URL must use HTTPS protocol for security. Make sure your DeskFree deployment supports HTTPS."
|
|
3017
3522
|
);
|
|
3018
3523
|
}
|
|
3019
|
-
var require2, __create2, __defProp2, __getOwnPropDesc2, __getOwnPropNames2, __getProtoOf2, __hasOwnProp2, __require2, __commonJS2, __export2, __copyProps2, __toESM2, require_constants, require_buffer_util, require_limiter, require_permessage_deflate, require_validation, require_receiver, require_sender, require_event_target, require_extension, require_websocket, require_stream, require_subprotocol, require_websocket_server, DEFAULT_REQUEST_TIMEOUT_MS, DeskFreeError, DeskFreeClient, value_exports, TypeSystemPolicy, TypeBoxError, TransformKind, ReadonlyKind, OptionalKind, Hint, Kind, type_exports, TypeGuardUnknownTypeError, KnownTypes, PatternBoolean, PatternNumber, PatternString, PatternNever, PatternNumberExact, PatternStringExact, PatternNeverExact, TemplateLiteralParserError, TemplateLiteralFiniteError, TemplateLiteralGenerateError, TemplateLiteralPatternError, Object2, includePatternProperties, ExtendsResolverError, ExtendsResult, TModule, Ordinal, TransformDecodeBuilder, TransformEncodeBuilder, type_exports2, Type, ORCHESTRATOR_TOOLS, SHARED_TOOLS, WORKER_TOOLS, MAX_FULL_MESSAGES, DESKFREE_AGENT_DIRECTIVE, DESKFREE_WORKER_DIRECTIVE,
|
|
3524
|
+
var require2, __create2, __defProp2, __getOwnPropDesc2, __getOwnPropNames2, __getProtoOf2, __hasOwnProp2, __require2, __commonJS2, __export2, __copyProps2, __toESM2, require_constants, require_buffer_util, require_limiter, require_permessage_deflate, require_validation, require_receiver, require_sender, require_event_target, require_extension, require_websocket, require_stream, require_subprotocol, require_websocket_server, DEFAULT_REQUEST_TIMEOUT_MS, DeskFreeError, DeskFreeClient, value_exports, TypeSystemPolicy, TypeBoxError, TransformKind, ReadonlyKind, OptionalKind, Hint, Kind, type_exports, TypeGuardUnknownTypeError, KnownTypes, PatternBoolean, PatternNumber, PatternString, PatternNever, PatternNumberExact, PatternStringExact, PatternNeverExact, TemplateLiteralParserError, TemplateLiteralFiniteError, TemplateLiteralGenerateError, TemplateLiteralPatternError, Object2, includePatternProperties, ExtendsResolverError, ExtendsResult, TModule, Ordinal, TransformDecodeBuilder, TransformEncodeBuilder, type_exports2, Type, ORCHESTRATOR_TOOLS, SHARED_TOOLS, WORKER_TOOLS, MAX_FULL_MESSAGES, DESKFREE_AGENT_DIRECTIVE, DESKFREE_WORKER_DIRECTIVE, THROTTLE_MS, CHAR_BUFFER_SIZE, CLOSE_MAX_RETRIES, DeskFreeStreamingSession, import_websocket, wrapper_default, activeWs, activeTaskId, completedTaskId, inboundThreadId, FLUSH_INTERVAL_MS, MAX_BATCH_SIZE, MAX_QUEUE_SIZE, _instance, ErrorReporter, accountHealth;
|
|
3020
3525
|
var init_dist = __esm({
|
|
3021
3526
|
"../core/dist/index.js"() {
|
|
3022
3527
|
require2 = createRequire$1(import.meta.url);
|
|
@@ -3036,8 +3541,8 @@ var init_dist = __esm({
|
|
|
3036
3541
|
return mod || (0, cb[__getOwnPropNames2(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
3037
3542
|
};
|
|
3038
3543
|
__export2 = (target, all) => {
|
|
3039
|
-
for (var
|
|
3040
|
-
__defProp2(target,
|
|
3544
|
+
for (var name2 in all)
|
|
3545
|
+
__defProp2(target, name2, { get: all[name2], enumerable: true });
|
|
3041
3546
|
};
|
|
3042
3547
|
__copyProps2 = (to, from, except, desc) => {
|
|
3043
3548
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
@@ -5075,9 +5580,9 @@ var init_dist = __esm({
|
|
|
5075
5580
|
require_extension = __commonJS2({
|
|
5076
5581
|
"../../node_modules/ws/lib/extension.js"(exports$1, module) {
|
|
5077
5582
|
var { tokenChars } = require_validation();
|
|
5078
|
-
function push(dest,
|
|
5079
|
-
if (dest[
|
|
5080
|
-
else dest[
|
|
5583
|
+
function push(dest, name2, elem) {
|
|
5584
|
+
if (dest[name2] === void 0) dest[name2] = [elem];
|
|
5585
|
+
else dest[name2].push(elem);
|
|
5081
5586
|
}
|
|
5082
5587
|
function parse(header) {
|
|
5083
5588
|
const offers = /* @__PURE__ */ Object.create(null);
|
|
@@ -5103,12 +5608,12 @@ var init_dist = __esm({
|
|
|
5103
5608
|
throw new SyntaxError(`Unexpected character at index ${i}`);
|
|
5104
5609
|
}
|
|
5105
5610
|
if (end === -1) end = i;
|
|
5106
|
-
const
|
|
5611
|
+
const name2 = header.slice(start, end);
|
|
5107
5612
|
if (code === 44) {
|
|
5108
|
-
push(offers,
|
|
5613
|
+
push(offers, name2, params);
|
|
5109
5614
|
params = /* @__PURE__ */ Object.create(null);
|
|
5110
5615
|
} else {
|
|
5111
|
-
extensionName =
|
|
5616
|
+
extensionName = name2;
|
|
5112
5617
|
}
|
|
5113
5618
|
start = end = -1;
|
|
5114
5619
|
} else {
|
|
@@ -6818,13 +7323,13 @@ var init_dist = __esm({
|
|
|
6818
7323
|
* Validates that a string parameter is non-empty.
|
|
6819
7324
|
* Catches invalid inputs before they hit the network.
|
|
6820
7325
|
*/
|
|
6821
|
-
requireNonEmpty(value,
|
|
7326
|
+
requireNonEmpty(value, name2) {
|
|
6822
7327
|
if (!value || value.trim() === "") {
|
|
6823
7328
|
throw new DeskFreeError(
|
|
6824
7329
|
"client",
|
|
6825
|
-
|
|
6826
|
-
`${
|
|
6827
|
-
`Missing required parameter: ${
|
|
7330
|
+
name2,
|
|
7331
|
+
`${name2} is required and cannot be empty`,
|
|
7332
|
+
`Missing required parameter: ${name2}. Please provide a valid value.`
|
|
6828
7333
|
);
|
|
6829
7334
|
}
|
|
6830
7335
|
}
|
|
@@ -7520,7 +8025,7 @@ var init_dist = __esm({
|
|
|
7520
8025
|
WORKER_TOOLS = {
|
|
7521
8026
|
START_TASK: {
|
|
7522
8027
|
name: "deskfree_start_task",
|
|
7523
|
-
description: "
|
|
8028
|
+
description: "Load a task and start working on it. Returns full context (instructions, message history). Use deskfree_read_file to load any relevant files.",
|
|
7524
8029
|
parameters: Type.Object({
|
|
7525
8030
|
taskId: Type.String({ description: "Task UUID to claim" })
|
|
7526
8031
|
})
|
|
@@ -7577,8 +8082,8 @@ var init_dist = __esm({
|
|
|
7577
8082
|
PROPOSE: SHARED_TOOLS.PROPOSE
|
|
7578
8083
|
};
|
|
7579
8084
|
MAX_FULL_MESSAGES = 15;
|
|
7580
|
-
DESKFREE_AGENT_DIRECTIVE = `## DeskFree \u2014
|
|
7581
|
-
You
|
|
8085
|
+
DESKFREE_AGENT_DIRECTIVE = `## DeskFree \u2014 Main Thread
|
|
8086
|
+
You handle the main conversation thread. Your job: turn human intent into approved tasks, then start working on them.
|
|
7582
8087
|
|
|
7583
8088
|
**Main thread = short and snappy.** Keep responses to 1-3 sentences. Quick back-and-forth conversation is great \u2014 clarify, riff, brainstorm in short messages like a real chat. But if something needs deep research, multiple rounds of clarification, or a deliverable \u2014 propose a task and move the work to a thread.
|
|
7584
8089
|
|
|
@@ -7586,7 +8091,7 @@ You are the orchestrator. Your job: turn human intent into approved tasks, then
|
|
|
7586
8091
|
|
|
7587
8092
|
1. **Check state** \u2192 \`deskfree_state\` \u2014 see tasks, memory (a pinned file with accumulated knowledge), and files.
|
|
7588
8093
|
2. **Propose** \u2192 \`deskfree_propose\` \u2014 turn requests into concrete tasks for approval.
|
|
7589
|
-
3. **
|
|
8094
|
+
3. **Start work** \u2192 \`deskfree_dispatch_worker\` with the taskId. You'll then continue the work in the task thread.
|
|
7590
8095
|
4. **Communicate** \u2192 \`deskfree_send_message\` for updates outside task threads.
|
|
7591
8096
|
|
|
7592
8097
|
**Before proposing, qualify the request.** Figure out what kind of thing this is:
|
|
@@ -7596,36 +8101,44 @@ You are the orchestrator. Your job: turn human intent into approved tasks, then
|
|
|
7596
8101
|
|
|
7597
8102
|
**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.
|
|
7598
8103
|
|
|
7599
|
-
|
|
8104
|
+
In the main thread you propose and coordinate \u2014 the actual work happens in task threads. Use \`deskfree_dispatch_worker\` to start working on approved tasks.
|
|
7600
8105
|
- When a human writes in a task thread, decide:
|
|
7601
|
-
- **Continuation of the same task?** \u2192 reopen and
|
|
7602
|
-
- **New/different work request?** \u2192 propose it as a new task (don't reopen the old one
|
|
8106
|
+
- **Continuation of the same task?** \u2192 reopen and pick it back up.
|
|
8107
|
+
- **New/different work request?** \u2192 propose it as a new task (don't reopen the old one).
|
|
7603
8108
|
- **Just confirmation or deferred?** \u2192 leave it for now.
|
|
7604
8109
|
- Estimate token cost per task \u2014 consider files to read, reasoning, output.`;
|
|
7605
|
-
DESKFREE_WORKER_DIRECTIVE = `## DeskFree
|
|
7606
|
-
You
|
|
8110
|
+
DESKFREE_WORKER_DIRECTIVE = `## DeskFree \u2014 Task Thread
|
|
8111
|
+
You're in a task thread, focused on a specific piece of work. Same you as in the main thread \u2014 same voice, same personality.
|
|
8112
|
+
|
|
7607
8113
|
Tools: deskfree_state, deskfree_start_task, deskfree_read_file, deskfree_create_file, deskfree_update_file, deskfree_learning, deskfree_complete_task, deskfree_send_message, deskfree_propose.
|
|
7608
8114
|
|
|
7609
8115
|
**Context loading:**
|
|
7610
|
-
- If your first message contains \`<task_context>\`, the task is already
|
|
8116
|
+
- If your first message contains \`<task_context>\`, the task is already loaded. Start working immediately \u2014 do NOT call deskfree_start_task.
|
|
7611
8117
|
- If your first message contains \`<workspace_state>\`, use it for situational awareness (other tasks, memory, files).
|
|
7612
|
-
- If no pre-loaded context (edge case/fallback), call \`deskfree_start_task\` with your taskId to
|
|
8118
|
+
- If no pre-loaded context (edge case/fallback), call \`deskfree_start_task\` with your taskId to load it.
|
|
7613
8119
|
- If continuing from a previous conversation (you can see prior tool calls and context), respond directly to the human's latest message \u2014 do NOT call deskfree_start_task again.
|
|
7614
8120
|
|
|
7615
8121
|
**Orient \u2192 Align \u2192 Work.** Every new task follows this rhythm:
|
|
7616
8122
|
|
|
7617
8123
|
1. **Orient** \u2014 Scan workspace state for relevant files. Read the Memory file for context on preferences and past patterns. Read any other files that are useful context with \`deskfree_read_file\`. Don't read everything \u2014 just what's relevant to this task.
|
|
7618
|
-
2. **Align** \u2014 Send a brief \`notify\` message: what you found, what you'll produce. One or two sentences. ("I'll build on the existing brand guide and create a new tone reference.")
|
|
7619
|
-
|
|
8124
|
+
2. **Align** \u2014 Send a brief \`notify\` message: what you found, what you'll produce. One or two sentences. ("I'll build on the existing brand guide and create a new tone reference.")
|
|
8125
|
+
- **Judgment calls or creative direction?** State your assumptions and approach, send as \`ask\`, and wait for confirmation before proceeding. Getting alignment early prevents costly rework.
|
|
8126
|
+
- **Straightforward execution?** Proceed immediately after the notify \u2014 don't wait for a response.
|
|
8127
|
+
3. **Work** \u2014 Execute the task. Update existing files with \`deskfree_update_file\` or create new ones with \`deskfree_create_file\`. Pass your taskId so updates appear in the thread. For large deliverables, build incrementally \u2014 share structure/outline first, then flesh out. Don't produce a finished 2000-word document and ask for review in one shot.
|
|
7620
8128
|
4. **Deliver** \u2014 Send an \`ask\` message when work is ready for review. Only complete (\`deskfree_complete_task\` with humanApproved: true) after the human has confirmed. Never self-complete.
|
|
7621
8129
|
|
|
8130
|
+
**Push back when warranted:**
|
|
8131
|
+
- If task instructions seem unclear, contradictory, or misguided \u2014 say so. "This task asks for X, but based on [context], Y might work better because..." is more useful than silently executing a flawed plan.
|
|
8132
|
+
- If you hit genuine ambiguity mid-task, send an \`ask\` message and wait. Don't guess on important decisions \u2014 guessing creates review debt the human has to pay later.
|
|
8133
|
+
- You're a teammate, not a task executor. Have an opinion when you have the context to form one.
|
|
8134
|
+
|
|
7622
8135
|
**File rules:**
|
|
7623
8136
|
- Create files when your task naturally produces them. Don't be afraid to create multiple files if the work calls for it.
|
|
7624
8137
|
- Always pass \`taskId\` when creating or updating files \u2014 this threads notifications into the task.
|
|
7625
8138
|
- If you discover work that falls outside your task's scope, use \`deskfree_propose\` to suggest follow-up tasks immediately \u2014 don't wait until completion. Propose as you discover, then stay focused on your current task.
|
|
7626
8139
|
|
|
7627
8140
|
**Learnings:**
|
|
7628
|
-
- Use \`deskfree_learning\` to record observations worth remembering. A nightly
|
|
8141
|
+
- Use \`deskfree_learning\` to record observations worth remembering. A nightly cycle consolidates these into the Memory file. Record:
|
|
7629
8142
|
- **Preferences**: how the human wants things done ("prefers X over Y")
|
|
7630
8143
|
- **Corrections**: when the human corrects you ("actually, do X not Y")
|
|
7631
8144
|
- **Patterns**: recurring approaches that work ("for this type of task, always...")
|
|
@@ -7636,191 +8149,16 @@ Tools: deskfree_state, deskfree_start_task, deskfree_read_file, deskfree_create_
|
|
|
7636
8149
|
- Do NOT record one-time task details, things in project docs, or obvious/generic knowledge.
|
|
7637
8150
|
- If your first message contains \`<daily_observations>\`, these are recent raw observations not yet consolidated into Memory. Use them as additional context.
|
|
7638
8151
|
|
|
7639
|
-
**
|
|
7640
|
-
- Your context window is finite.
|
|
7641
|
-
-
|
|
8152
|
+
**Delegation:**
|
|
8153
|
+
- Your context window is finite. Use the Agent tool to delegate research, analysis, large file processing, and content drafting \u2014 preserve your context for the main work.
|
|
8154
|
+
- Delegated work gets a fresh context window with standard tools (Read, Write, Bash, Grep, WebSearch, etc.) but NO DeskFree tools. Pre-load any file content they need into the prompt.
|
|
7642
8155
|
- Use \`run_in_background: true\` for parallel independent work.
|
|
7643
|
-
- During Orient, check Memory for
|
|
7644
|
-
- After
|
|
8156
|
+
- During Orient, check Memory for delegation patterns. Inject relevant ones into the prompt alongside the task.
|
|
8157
|
+
- After delegated work completes, reflect: did this reveal a useful pattern? Record via \`deskfree_learning\` so it's consolidated into Memory.
|
|
7645
8158
|
- Don't over-delegate: quick reads, simple lookups, and anything requiring DeskFree tools are faster inline.
|
|
7646
8159
|
|
|
7647
8160
|
**Completing tasks:**
|
|
7648
8161
|
- On 409 or 404 errors: STOP. Do not retry. Call deskfree_state to find available tasks.`;
|
|
7649
|
-
DESKFREE_HEARTBEAT_DIRECTIVE = `## DeskFree Heartbeat
|
|
7650
|
-
On each heartbeat, run through this checklist:
|
|
7651
|
-
|
|
7652
|
-
### 1. Work the queue
|
|
7653
|
-
- Run \`deskfree_state\` to get the full workspace snapshot.
|
|
7654
|
-
- Any open tasks with awaiting=bot? Use \`deskfree_dispatch_worker\` to dispatch a worker for each. Pass the taskId.
|
|
7655
|
-
- Any open tasks that seem stalled (claimed but no recent activity)? Check on them.
|
|
7656
|
-
|
|
7657
|
-
### 2. Proactive assessment
|
|
7658
|
-
After handling the queue, step back and think about the bigger picture. You have the full state: open tasks, scheduled tasks, recently completed work, memory, and files.
|
|
7659
|
-
|
|
7660
|
-
**Think through:**
|
|
7661
|
-
- What momentum exists? What was recently accomplished, and what naturally follows?
|
|
7662
|
-
- What is stalled or falling behind? Anything open too long without progress?
|
|
7663
|
-
- What is scheduled soon? Does anything need prep work before it lands?
|
|
7664
|
-
- What is the single highest-leverage thing that could happen next?
|
|
7665
|
-
|
|
7666
|
-
**Then act \u2014 but only if you have something genuinely useful:**
|
|
7667
|
-
|
|
7668
|
-
*Things the bot can do* \u2014 research, drafts, analysis, prep. Propose as a task via \`deskfree_propose\`. One focused task, not a batch.
|
|
7669
|
-
|
|
7670
|
-
*Things the human should do* \u2014 nudges, reminders, conversation starters. Send via \`deskfree_send_message\`. Keep it brief and genuinely helpful, not nagging.
|
|
7671
|
-
|
|
7672
|
-
**Rules:**
|
|
7673
|
-
- Do not suggest things that are already open or scheduled \u2014 check first.
|
|
7674
|
-
- Do not repeat suggestions the human ignored or rejected recently.
|
|
7675
|
-
- Quality over quantity. One good insight beats five generic nudges.
|
|
7676
|
-
- If everything looks healthy and active, do nothing. Silence is fine.`;
|
|
7677
|
-
DESKFREE_SLEEP_DIRECTIVE = `## DeskFree \u2014 Nightly Sleep Cycle
|
|
7678
|
-
You are the sleep agent. You run once per day to reflect, consolidate memory, and prepare for tomorrow.
|
|
7679
|
-
|
|
7680
|
-
Tools available: deskfree_state, deskfree_propose, deskfree_send_message, deskfree_read_file, deskfree_update_file.
|
|
7681
|
-
|
|
7682
|
-
---
|
|
7683
|
-
|
|
7684
|
-
### 1. REFLECT & CONSOLIDATE
|
|
7685
|
-
|
|
7686
|
-
Your primary job: merge daily observations into the Memory file using strength-scored entries.
|
|
7687
|
-
|
|
7688
|
-
Your prompt contains both \`<current_memory>\` (the current Memory file content) and \`<daily_observations>\` (raw daily logs) inline \u2014 you have everything you need upfront.
|
|
7689
|
-
|
|
7690
|
-
**Steps:**
|
|
7691
|
-
1. Review the current memory and daily observations from your prompt.
|
|
7692
|
-
2. Apply the memory curation rules below.
|
|
7693
|
-
3. Update the Memory file via \`deskfree_update_file\` using the file ID from your prompt.
|
|
7694
|
-
4. After updating, send a brief main-thread message via \`deskfree_send_message\` noting what was consolidated (e.g. "Reflected on yesterday \u2014 updated my notes on your brand voice preferences and the weekly review pattern."). Keep it to 1-2 sentences.
|
|
7695
|
-
5. If nothing meaningful changed, skip the update and send a brief message like "Quiet day \u2014 nothing new to consolidate."
|
|
7696
|
-
|
|
7697
|
-
**Memory Types**
|
|
7698
|
-
|
|
7699
|
-
Each memory item carries a type tag and strength score: \`[type:X, strength:N]\`
|
|
7700
|
-
|
|
7701
|
-
| Type | What it captures | Decay rate |
|
|
7702
|
-
|------|-----------------|------------|
|
|
7703
|
-
| \`correction\` | Explicit "do X not Y" from human | Very slow (\u22121 when strength \u226510, else no decay) |
|
|
7704
|
-
| \`preference\` | How the human wants things done | Slow (\u22121 when strength \u22656, else no decay) |
|
|
7705
|
-
| \`pattern\` | Approaches/workflows that work | Normal (see decay rules) |
|
|
7706
|
-
| \`domain\` | Business/project-specific facts | Slow (\u22121 when strength \u22656, else no decay) |
|
|
7707
|
-
| \`insight\` | Meta-observations from reflection | Normal (see decay rules) |
|
|
7708
|
-
|
|
7709
|
-
Corrections and domain facts are durable \u2014 they rarely become irrelevant.
|
|
7710
|
-
Patterns and insights decay normally \u2014 stale approaches should be forgotten.
|
|
7711
|
-
Preferences sit in between \u2014 slow decay, but they can evolve.
|
|
7712
|
-
|
|
7713
|
-
**Strength Scoring Rules**
|
|
7714
|
-
|
|
7715
|
-
Strength uses Ebbinghaus-inspired logarithmic decay \u2014 strong memories resist forgetting:
|
|
7716
|
-
|
|
7717
|
-
**Reinforcement (observation matches existing memory):**
|
|
7718
|
-
- strength +2
|
|
7719
|
-
|
|
7720
|
-
**Explicit correction ("actually do X not Y"):**
|
|
7721
|
-
- Replace old memory, new one starts at [strength: 3, type: correction]
|
|
7722
|
-
|
|
7723
|
-
**Decay (memory NOT referenced by any daily observation):**
|
|
7724
|
-
- strength \u2265 10: decay by \u22121 (deeply encoded, slow forgetting)
|
|
7725
|
-
- strength 5-9: decay by \u22122 (moderately encoded)
|
|
7726
|
-
- strength 1-4: decay by \u22123 (weakly encoded, fast forgetting)
|
|
7727
|
-
- EXCEPT: corrections and domain facts with strength \u22656 decay at \u22121 max (durable memories)
|
|
7728
|
-
- EXCEPT: preferences with strength \u22656 decay at \u22121 max
|
|
7729
|
-
|
|
7730
|
-
**Removal:**
|
|
7731
|
-
- Strength reaches 0 \u2192 move to a ## Fading section at the bottom (one-line summaries only, no strength tags). Keep max 10 fading memories. If Fading section is full, oldest entries are permanently forgotten. You may rescue a fading memory back to active if it becomes relevant again (re-add with [strength: 2]).
|
|
7732
|
-
|
|
7733
|
-
**New observation:**
|
|
7734
|
-
- Assess importance: how consequential is this for future tasks?
|
|
7735
|
-
- Look for [!] prefix (critical) or [~] prefix (notable) as importance signals from the worker.
|
|
7736
|
-
- Low importance (casual mention, routine) \u2192 [strength: 1, type: <appropriate>]
|
|
7737
|
-
- Medium importance (useful preference, [~] prefix) \u2192 [strength: 2, type: <appropriate>]
|
|
7738
|
-
- High importance (explicit correction, strong constraint, [!] prefix) \u2192 [strength: 4, type: <appropriate>]
|
|
7739
|
-
- Critical (production error, absolute rule) \u2192 [strength: 6, type: <appropriate>]
|
|
7740
|
-
|
|
7741
|
-
**Consolidation rules:**
|
|
7742
|
-
- Classify each memory with the correct type. Only keep genuinely reusable knowledge.
|
|
7743
|
-
- Discard noise and one-off task details.
|
|
7744
|
-
- If the same episodic observation appears 3+ times across different days, promote it to a \`pattern\` or \`domain\` fact with [strength: 5]. Remove the individual episodes.
|
|
7745
|
-
- Look for meta-patterns: approaches that consistently work/fail, implicit preferences, recurring corrections.
|
|
7746
|
-
- If you find non-obvious insights, add them as \`[type: insight, strength: 2]\` memories.
|
|
7747
|
-
- Merge near-duplicates, remove items at [strength: 0], trim verbose entries.
|
|
7748
|
-
- Keep total document under ~4000 words.
|
|
7749
|
-
- Use ## headers for sections \u2014 let them emerge organically from content.
|
|
7750
|
-
|
|
7751
|
-
### 2. CHECK RECURRING COMMITMENTS
|
|
7752
|
-
|
|
7753
|
-
After memory consolidation:
|
|
7754
|
-
1. Call \`deskfree_state\` to see the board.
|
|
7755
|
-
2. Cross-reference memory for recurring patterns (daily audits, weekly reviews, etc.).
|
|
7756
|
-
3. If a recurring commitment has no upcoming scheduled task, propose it via \`deskfree_propose\` with appropriate \`scheduledFor\`.
|
|
7757
|
-
4. Don't propose things already on the board or that were recently completed/ignored.
|
|
7758
|
-
|
|
7759
|
-
### 3. PROACTIVE OPPORTUNITIES
|
|
7760
|
-
|
|
7761
|
-
Based on recent work, completed tasks, and patterns in memory \u2014 is there something genuinely useful to propose or a message worth sending?
|
|
7762
|
-
- One focused proposal max. Skip if nothing merits it.
|
|
7763
|
-
- Quality over quantity. Don't force it.
|
|
7764
|
-
|
|
7765
|
-
### Rules
|
|
7766
|
-
- Keep main thread messages short (1-2 sentences).
|
|
7767
|
-
- Do NOT propose things the human has previously ignored or rejected.
|
|
7768
|
-
- Use \`deskfree_read_file\` only if you need to re-read the Memory file after your own update (verification). The current content is already in your prompt.`;
|
|
7769
|
-
DESKFREE_DUSK_DIRECTIVE = `## DeskFree \u2014 Evening Dusk Cycle
|
|
7770
|
-
You are the dusk agent. You run once per day in the evening to review the day, propose overnight work, and brief the human.
|
|
7771
|
-
|
|
7772
|
-
Tools available: deskfree_state, deskfree_propose, deskfree_send_message, deskfree_read_file, deskfree_update_file.
|
|
7773
|
-
|
|
7774
|
-
---
|
|
7775
|
-
|
|
7776
|
-
### 1. REVIEW THE DAY
|
|
7777
|
-
|
|
7778
|
-
Your prompt contains both \`<current_memory>\` (the current Memory file content) and \`<daily_observations>\` (raw daily logs) inline \u2014 you have everything you need upfront.
|
|
7779
|
-
|
|
7780
|
-
**Steps:**
|
|
7781
|
-
1. Review the current memory and daily observations from your prompt.
|
|
7782
|
-
2. Call \`deskfree_state\` to see the current board \u2014 open tasks, recently completed work, files.
|
|
7783
|
-
3. Build a mental model of what happened today: what was accomplished, what's in progress, what's stalled.
|
|
7784
|
-
|
|
7785
|
-
### 2. IDENTIFY OVERNIGHT OPPORTUNITIES
|
|
7786
|
-
|
|
7787
|
-
Think about work that can be done autonomously overnight \u2014 WITHOUT human judgment or approval mid-task.
|
|
7788
|
-
|
|
7789
|
-
**Good overnight work:**
|
|
7790
|
-
- Research and analysis (market research, competitive analysis, reading material)
|
|
7791
|
-
- Content drafts (blog posts, reports, documentation)
|
|
7792
|
-
- File updates (updating recurring reports, preparing templates)
|
|
7793
|
-
- Prep work for tomorrow (gathering context, pre-reading for scheduled meetings)
|
|
7794
|
-
- Recurring tasks that are due (weekly reviews, daily audits)
|
|
7795
|
-
|
|
7796
|
-
**NOT good overnight work:**
|
|
7797
|
-
- Anything requiring human decisions mid-task
|
|
7798
|
-
- Tasks that depend on external input not yet received
|
|
7799
|
-
- Creative work where the human has strong opinions on direction
|
|
7800
|
-
- Anything the human explicitly said to wait on
|
|
7801
|
-
|
|
7802
|
-
### 3. PROPOSE THE PLAN
|
|
7803
|
-
|
|
7804
|
-
If you identified useful overnight work:
|
|
7805
|
-
1. Use \`deskfree_propose\` with 1-3 well-scoped tasks. Quality over quantity.
|
|
7806
|
-
2. Each task should be self-contained \u2014 a worker must be able to complete it without human input.
|
|
7807
|
-
3. Set \`scheduledFor\` if work should start at a specific time (e.g. early morning).
|
|
7808
|
-
4. If nothing genuinely useful can be done overnight, skip the proposal entirely. Don't force it.
|
|
7809
|
-
|
|
7810
|
-
### 4. BRIEF THE HUMAN
|
|
7811
|
-
|
|
7812
|
-
Send a brief main-thread message via \`deskfree_send_message\`:
|
|
7813
|
-
- 2-3 sentence summary: what happened today + what you're proposing for overnight (if anything).
|
|
7814
|
-
- Keep it conversational and useful \u2014 this is the human's "end of day" touchpoint.
|
|
7815
|
-
- If no proposals, still send a brief day summary.
|
|
7816
|
-
|
|
7817
|
-
### Rules
|
|
7818
|
-
- Do NOT propose things already on the board or recently completed.
|
|
7819
|
-
- Do NOT repeat suggestions the human previously ignored or rejected.
|
|
7820
|
-
- Quality over quantity. One good task beats three mediocre ones.
|
|
7821
|
-
- Keep the briefing message short and actionable.
|
|
7822
|
-
- Cross-reference memory for recurring patterns \u2014 if something is due, propose it.
|
|
7823
|
-
- Use \`deskfree_read_file\` only if you need file content beyond what's in your prompt.`;
|
|
7824
8162
|
THROTTLE_MS = 300;
|
|
7825
8163
|
CHAR_BUFFER_SIZE = 256;
|
|
7826
8164
|
CLOSE_MAX_RETRIES = 3;
|
|
@@ -8032,7 +8370,15 @@ Send a brief main-thread message via \`deskfree_send_message\`:
|
|
|
8032
8370
|
}
|
|
8033
8371
|
});
|
|
8034
8372
|
function runOrchestrator(opts) {
|
|
8035
|
-
const {
|
|
8373
|
+
const {
|
|
8374
|
+
prompt,
|
|
8375
|
+
orchestratorServer,
|
|
8376
|
+
model,
|
|
8377
|
+
sessionId,
|
|
8378
|
+
claudeCodePath,
|
|
8379
|
+
agentContext
|
|
8380
|
+
} = opts;
|
|
8381
|
+
const systemPrompt = agentContext ? buildAgentDirective(agentContext) : DESKFREE_AGENT_DIRECTIVE;
|
|
8036
8382
|
return query({
|
|
8037
8383
|
prompt,
|
|
8038
8384
|
options: {
|
|
@@ -8040,7 +8386,7 @@ function runOrchestrator(opts) {
|
|
|
8040
8386
|
process.stderr.write(`[orchestrator-sdk] ${data}
|
|
8041
8387
|
`);
|
|
8042
8388
|
},
|
|
8043
|
-
systemPrompt
|
|
8389
|
+
systemPrompt,
|
|
8044
8390
|
model,
|
|
8045
8391
|
...claudeCodePath ? { pathToClaudeCodeExecutable: claudeCodePath } : {},
|
|
8046
8392
|
maxTurns: MAX_ORCHESTRATOR_TURNS,
|
|
@@ -8059,11 +8405,12 @@ function runOrchestrator(opts) {
|
|
|
8059
8405
|
});
|
|
8060
8406
|
}
|
|
8061
8407
|
function runHeartbeat(opts) {
|
|
8062
|
-
const { prompt, orchestratorServer, model, claudeCodePath } = opts;
|
|
8408
|
+
const { prompt, orchestratorServer, model, claudeCodePath, agentContext } = opts;
|
|
8409
|
+
const systemPrompt = agentContext ? buildHeartbeatDirective(agentContext) : DESKFREE_AGENT_DIRECTIVE;
|
|
8063
8410
|
return query({
|
|
8064
8411
|
prompt,
|
|
8065
8412
|
options: {
|
|
8066
|
-
systemPrompt
|
|
8413
|
+
systemPrompt,
|
|
8067
8414
|
model,
|
|
8068
8415
|
...claudeCodePath ? { pathToClaudeCodeExecutable: claudeCodePath } : {},
|
|
8069
8416
|
maxTurns: MAX_ORCHESTRATOR_TURNS,
|
|
@@ -8184,10 +8531,6 @@ function loadConfig() {
|
|
|
8184
8531
|
};
|
|
8185
8532
|
}
|
|
8186
8533
|
function mergeWithRemoteConfig(local, remote) {
|
|
8187
|
-
const stateDirOverridden = !!process.env["DESKFREE_STATE_DIR"];
|
|
8188
|
-
const toolsDirOverridden = !!process.env["DESKFREE_TOOLS_DIR"];
|
|
8189
|
-
const stateDir = stateDirOverridden ? local.stateDir : isDocker ? local.stateDir : `.deskfree/${remote.botId}/state`;
|
|
8190
|
-
const toolsDir = toolsDirOverridden ? local.toolsDir : isDocker ? local.toolsDir : `.deskfree/${remote.botId}/tools`;
|
|
8191
8534
|
let claudeCodePath;
|
|
8192
8535
|
if (remote.provider === "claude-code") {
|
|
8193
8536
|
try {
|
|
@@ -8202,8 +8545,6 @@ function mergeWithRemoteConfig(local, remote) {
|
|
|
8202
8545
|
}
|
|
8203
8546
|
return {
|
|
8204
8547
|
...local,
|
|
8205
|
-
stateDir,
|
|
8206
|
-
toolsDir,
|
|
8207
8548
|
claudeCodePath,
|
|
8208
8549
|
wsUrl: process.env["DESKFREE_WS_URL"] ?? remote.wsUrl,
|
|
8209
8550
|
model: process.env["DESKFREE_MODEL"] ?? remote.model,
|
|
@@ -8215,6 +8556,8 @@ function mergeWithRemoteConfig(local, remote) {
|
|
|
8215
8556
|
anthropicApiKey: remote.anthropicApiKey,
|
|
8216
8557
|
baseUrl: process.env["DESKFREE_BASE_URL"] ?? remote.baseUrl,
|
|
8217
8558
|
botId: remote.botId,
|
|
8559
|
+
botName: remote.botName,
|
|
8560
|
+
deploymentType: remote.deploymentType,
|
|
8218
8561
|
memoryFileId: remote.memoryFileId,
|
|
8219
8562
|
sleepHour: remote.sleepHour,
|
|
8220
8563
|
duskHour: remote.duskHour,
|
|
@@ -8227,8 +8570,18 @@ var init_config = __esm({
|
|
|
8227
8570
|
init_dist();
|
|
8228
8571
|
isDocker = process.env["DOCKER"] === "1" || existsSync("/.dockerenv");
|
|
8229
8572
|
DEFAULTS = {
|
|
8230
|
-
stateDir: isDocker ? "/app/state" :
|
|
8231
|
-
|
|
8573
|
+
stateDir: isDocker ? "/app/state" : join(
|
|
8574
|
+
homedir(),
|
|
8575
|
+
".deskfree",
|
|
8576
|
+
process.env["DESKFREE_INSTANCE_NAME"] ?? "main",
|
|
8577
|
+
"state"
|
|
8578
|
+
),
|
|
8579
|
+
toolsDir: isDocker ? "/app/tools" : join(
|
|
8580
|
+
homedir(),
|
|
8581
|
+
".deskfree",
|
|
8582
|
+
process.env["DESKFREE_INSTANCE_NAME"] ?? "main",
|
|
8583
|
+
"tools"
|
|
8584
|
+
),
|
|
8232
8585
|
logLevel: "info",
|
|
8233
8586
|
healthPort: 3100
|
|
8234
8587
|
};
|
|
@@ -10480,9 +10833,9 @@ var require_event_target2 = __commonJS({
|
|
|
10480
10833
|
var require_extension2 = __commonJS({
|
|
10481
10834
|
"../../node_modules/ws/lib/extension.js"(exports$1, module) {
|
|
10482
10835
|
var { tokenChars } = require_validation2();
|
|
10483
|
-
function push(dest,
|
|
10484
|
-
if (dest[
|
|
10485
|
-
else dest[
|
|
10836
|
+
function push(dest, name2, elem) {
|
|
10837
|
+
if (dest[name2] === void 0) dest[name2] = [elem];
|
|
10838
|
+
else dest[name2].push(elem);
|
|
10486
10839
|
}
|
|
10487
10840
|
function parse(header) {
|
|
10488
10841
|
const offers = /* @__PURE__ */ Object.create(null);
|
|
@@ -10508,12 +10861,12 @@ var require_extension2 = __commonJS({
|
|
|
10508
10861
|
throw new SyntaxError(`Unexpected character at index ${i}`);
|
|
10509
10862
|
}
|
|
10510
10863
|
if (end === -1) end = i;
|
|
10511
|
-
const
|
|
10864
|
+
const name2 = header.slice(start, end);
|
|
10512
10865
|
if (code === 44) {
|
|
10513
|
-
push(offers,
|
|
10866
|
+
push(offers, name2, params);
|
|
10514
10867
|
params = /* @__PURE__ */ Object.create(null);
|
|
10515
10868
|
} else {
|
|
10516
|
-
extensionName =
|
|
10869
|
+
extensionName = name2;
|
|
10517
10870
|
}
|
|
10518
10871
|
start = end = -1;
|
|
10519
10872
|
} else {
|
|
@@ -12594,9 +12947,9 @@ function adaptTool(deskfreeTool) {
|
|
|
12594
12947
|
deskfreeTool.name,
|
|
12595
12948
|
deskfreeTool.description,
|
|
12596
12949
|
zodShape,
|
|
12597
|
-
async (
|
|
12950
|
+
async (args) => {
|
|
12598
12951
|
const result = await deskfreeTool.execute(
|
|
12599
|
-
|
|
12952
|
+
args
|
|
12600
12953
|
);
|
|
12601
12954
|
return {
|
|
12602
12955
|
content: result.content,
|
|
@@ -12627,7 +12980,7 @@ function createOrchestratorMcpServer(client, customTools = [], workerManager) {
|
|
|
12627
12980
|
function createDispatchWorkerTool(workerManager) {
|
|
12628
12981
|
return {
|
|
12629
12982
|
name: "deskfree_dispatch_worker",
|
|
12630
|
-
description: "
|
|
12983
|
+
description: "Start working on an approved task in its thread. You will pick up the task, load context, and handle follow-up messages there. Pass the taskId of the approved task.",
|
|
12631
12984
|
parameters: {
|
|
12632
12985
|
type: "object",
|
|
12633
12986
|
properties: {
|
|
@@ -12695,15 +13048,15 @@ function containsInjectionPattern(content) {
|
|
|
12695
13048
|
return INJECTION_PATTERNS.some((pattern) => pattern.test(content));
|
|
12696
13049
|
}
|
|
12697
13050
|
function withContentScan(execute, extractContent, scanner) {
|
|
12698
|
-
return async (
|
|
12699
|
-
const content = extractContent(
|
|
13051
|
+
return async (args) => {
|
|
13052
|
+
const content = extractContent(args);
|
|
12700
13053
|
if (content) {
|
|
12701
13054
|
const result = await scanner.scan(content);
|
|
12702
13055
|
if (!result.safe) {
|
|
12703
13056
|
return `Security check failed: ${result.reason ?? "content rejected"}. File not updated.`;
|
|
12704
13057
|
}
|
|
12705
13058
|
}
|
|
12706
|
-
return execute(
|
|
13059
|
+
return execute(args);
|
|
12707
13060
|
};
|
|
12708
13061
|
}
|
|
12709
13062
|
function validateDownloadUrl(url) {
|
|
@@ -12838,7 +13191,7 @@ function createWorkerMcpServer(client, customTools = [], contentScanner, dailyLo
|
|
|
12838
13191
|
if ((t.name === "deskfree_update_file" || t.name === "deskfree_create_file") && contentScanner) {
|
|
12839
13192
|
const wrappedExecute = withContentScan(
|
|
12840
13193
|
t.execute,
|
|
12841
|
-
(
|
|
13194
|
+
(args) => typeof args["content"] === "string" ? args["content"] : null,
|
|
12842
13195
|
contentScanner
|
|
12843
13196
|
);
|
|
12844
13197
|
return {
|
|
@@ -13374,7 +13727,8 @@ async function routeMessage(message, client, deps, sessionStore, config) {
|
|
|
13374
13727
|
orchestratorServer: deps.createOrchestratorServer(),
|
|
13375
13728
|
model: deps.model,
|
|
13376
13729
|
sessionId: existingSessionId,
|
|
13377
|
-
claudeCodePath: deps.claudeCodePath
|
|
13730
|
+
claudeCodePath: deps.claudeCodePath,
|
|
13731
|
+
agentContext: deps.agentContext
|
|
13378
13732
|
});
|
|
13379
13733
|
let fullText = "";
|
|
13380
13734
|
let capturedSessionId = null;
|
|
@@ -13557,7 +13911,8 @@ var init_sessions = __esm({
|
|
|
13557
13911
|
}
|
|
13558
13912
|
});
|
|
13559
13913
|
function runWorker(opts) {
|
|
13560
|
-
const { prompt, workerServer, model, sessionId } = opts;
|
|
13914
|
+
const { prompt, workerServer, model, sessionId, agentContext } = opts;
|
|
13915
|
+
const systemPrompt = agentContext ? buildWorkerDirective(agentContext) : DESKFREE_WORKER_DIRECTIVE;
|
|
13561
13916
|
return query({
|
|
13562
13917
|
prompt,
|
|
13563
13918
|
options: {
|
|
@@ -13565,7 +13920,7 @@ function runWorker(opts) {
|
|
|
13565
13920
|
process.stderr.write(`[worker-sdk] ${data}
|
|
13566
13921
|
`);
|
|
13567
13922
|
},
|
|
13568
|
-
systemPrompt
|
|
13923
|
+
systemPrompt,
|
|
13569
13924
|
model,
|
|
13570
13925
|
maxTurns: MAX_WORKER_TURNS,
|
|
13571
13926
|
permissionMode: "bypassPermissions",
|
|
@@ -13877,7 +14232,8 @@ ${userMessage}
|
|
|
13877
14232
|
prompt: channel,
|
|
13878
14233
|
workerServer,
|
|
13879
14234
|
model,
|
|
13880
|
-
sessionId: previousSessionId
|
|
14235
|
+
sessionId: previousSessionId,
|
|
14236
|
+
agentContext: this.deps.agentContext
|
|
13881
14237
|
});
|
|
13882
14238
|
const idleTimer = this.startIdleTimer(taskId);
|
|
13883
14239
|
const drainPromise = this.drainLoop(
|
|
@@ -14123,7 +14479,7 @@ function startHealthServer(port, log) {
|
|
|
14123
14479
|
}
|
|
14124
14480
|
};
|
|
14125
14481
|
}
|
|
14126
|
-
function scheduleHeartbeat(createOrchServer, model, intervalMs, signal, log, claudeCodePath) {
|
|
14482
|
+
function scheduleHeartbeat(createOrchServer, model, intervalMs, signal, log, claudeCodePath, agentContext) {
|
|
14127
14483
|
if (intervalMs <= 0) return;
|
|
14128
14484
|
let running = false;
|
|
14129
14485
|
async function tick() {
|
|
@@ -14131,11 +14487,13 @@ function scheduleHeartbeat(createOrchServer, model, intervalMs, signal, log, cla
|
|
|
14131
14487
|
running = true;
|
|
14132
14488
|
try {
|
|
14133
14489
|
log.debug("Heartbeat tick: checking for pending work...");
|
|
14490
|
+
const heartbeatPrompt = agentContext ? buildHeartbeatDirective(agentContext) : "Run your heartbeat check now.";
|
|
14134
14491
|
const result = runHeartbeat({
|
|
14135
|
-
prompt:
|
|
14492
|
+
prompt: heartbeatPrompt,
|
|
14136
14493
|
orchestratorServer: createOrchServer(),
|
|
14137
14494
|
model,
|
|
14138
|
-
claudeCodePath
|
|
14495
|
+
claudeCodePath,
|
|
14496
|
+
agentContext
|
|
14139
14497
|
});
|
|
14140
14498
|
for await (const _ of result) {
|
|
14141
14499
|
}
|
|
@@ -14176,6 +14534,19 @@ async function startAgent(opts) {
|
|
|
14176
14534
|
const msg = err instanceof Error ? err.message : String(err);
|
|
14177
14535
|
throw new Error(`Failed to bootstrap config from API: ${msg}`);
|
|
14178
14536
|
}
|
|
14537
|
+
const isDocker2 = process.env["DOCKER"] === "1" || (await import('fs')).existsSync("/.dockerenv");
|
|
14538
|
+
const runtimeVersion = process.env["npm_package_version"] ?? "unknown";
|
|
14539
|
+
const agentContext = {
|
|
14540
|
+
botName: config.botName,
|
|
14541
|
+
deploymentType: config.deploymentType,
|
|
14542
|
+
provider: config.provider,
|
|
14543
|
+
model: config.model,
|
|
14544
|
+
platform: isDocker2 ? "Docker" : process.platform === "darwin" ? "macOS" : "Linux",
|
|
14545
|
+
runtimeVersion,
|
|
14546
|
+
maxConcurrentWorkers: 5,
|
|
14547
|
+
// updated after WorkerManager is created
|
|
14548
|
+
instanceName: process.env["DESKFREE_INSTANCE_NAME"] || void 0
|
|
14549
|
+
};
|
|
14179
14550
|
mkdirSync(config.stateDir, { recursive: true });
|
|
14180
14551
|
mkdirSync(config.toolsDir, { recursive: true });
|
|
14181
14552
|
const logFile = join(config.stateDir, "runtime.log");
|
|
@@ -14234,8 +14605,10 @@ async function startAgent(opts) {
|
|
|
14234
14605
|
"memory",
|
|
14235
14606
|
config.botId,
|
|
14236
14607
|
"session-history.json"
|
|
14237
|
-
)
|
|
14608
|
+
),
|
|
14609
|
+
agentContext
|
|
14238
14610
|
});
|
|
14611
|
+
agentContext.maxConcurrentWorkers = workerManager.maxConcurrentWorkers;
|
|
14239
14612
|
const createOrchServer = () => createOrchestratorMcpServer(client, customTools, workerManager);
|
|
14240
14613
|
const healthServer = startHealthServer(config.healthPort, log);
|
|
14241
14614
|
const sessionStore = new SessionStore();
|
|
@@ -14262,7 +14635,8 @@ async function startAgent(opts) {
|
|
|
14262
14635
|
createOrchestratorServer: createOrchServer,
|
|
14263
14636
|
workerManager,
|
|
14264
14637
|
model: config.model,
|
|
14265
|
-
claudeCodePath: config.claudeCodePath
|
|
14638
|
+
claudeCodePath: config.claudeCodePath,
|
|
14639
|
+
agentContext
|
|
14266
14640
|
},
|
|
14267
14641
|
sessionStore,
|
|
14268
14642
|
{
|
|
@@ -14278,7 +14652,8 @@ async function startAgent(opts) {
|
|
|
14278
14652
|
config.heartbeatIntervalMs,
|
|
14279
14653
|
abortController.signal,
|
|
14280
14654
|
log,
|
|
14281
|
-
config.claudeCodePath
|
|
14655
|
+
config.claudeCodePath,
|
|
14656
|
+
agentContext
|
|
14282
14657
|
);
|
|
14283
14658
|
if (config.memoryFileId && config.sleepHour !== null && config.timezone) {
|
|
14284
14659
|
const memoryFileId = config.memoryFileId;
|
|
@@ -14315,7 +14690,7 @@ async function startAgent(opts) {
|
|
|
14315
14690
|
const workerServer = createWorkServer();
|
|
14316
14691
|
const result = runOneShotWorker({
|
|
14317
14692
|
prompt,
|
|
14318
|
-
systemPrompt:
|
|
14693
|
+
systemPrompt: buildSleepDirective(agentContext),
|
|
14319
14694
|
workerServer,
|
|
14320
14695
|
model: config.model
|
|
14321
14696
|
});
|
|
@@ -14361,7 +14736,7 @@ async function startAgent(opts) {
|
|
|
14361
14736
|
const workerServer = createWorkServer();
|
|
14362
14737
|
const result = runOneShotWorker({
|
|
14363
14738
|
prompt,
|
|
14364
|
-
systemPrompt:
|
|
14739
|
+
systemPrompt: buildDuskDirective(agentContext),
|
|
14365
14740
|
workerServer,
|
|
14366
14741
|
model: config.model
|
|
14367
14742
|
});
|
|
@@ -14408,22 +14783,46 @@ var init_entrypoint = __esm({
|
|
|
14408
14783
|
});
|
|
14409
14784
|
|
|
14410
14785
|
// src/bin.ts
|
|
14411
|
-
|
|
14412
|
-
var
|
|
14786
|
+
init_paths();
|
|
14787
|
+
var [name, cleanArgs] = parseName(process.argv.slice(2));
|
|
14788
|
+
var command = cleanArgs[0];
|
|
14413
14789
|
if (command === "install") {
|
|
14414
|
-
|
|
14790
|
+
let token = cleanArgs[1];
|
|
14415
14791
|
if (!token) {
|
|
14416
|
-
|
|
14417
|
-
|
|
14792
|
+
const { createInterface } = await import('readline');
|
|
14793
|
+
const rl = createInterface({
|
|
14794
|
+
input: process.stdin,
|
|
14795
|
+
output: process.stdout
|
|
14796
|
+
});
|
|
14797
|
+
token = await new Promise((resolve) => {
|
|
14798
|
+
rl.question(
|
|
14799
|
+
"Paste your bot token (from the DeskFree dashboard):\n> ",
|
|
14800
|
+
(answer) => {
|
|
14801
|
+
rl.close();
|
|
14802
|
+
resolve(answer.trim());
|
|
14803
|
+
}
|
|
14804
|
+
);
|
|
14805
|
+
});
|
|
14806
|
+
if (!token) {
|
|
14807
|
+
console.error("No token provided.");
|
|
14808
|
+
process.exit(1);
|
|
14809
|
+
}
|
|
14418
14810
|
}
|
|
14419
14811
|
const { install: install2 } = await Promise.resolve().then(() => (init_install(), install_exports));
|
|
14420
|
-
install2(token);
|
|
14812
|
+
install2(token, name);
|
|
14421
14813
|
} else if (command === "uninstall") {
|
|
14422
14814
|
const { uninstall: uninstall2 } = await Promise.resolve().then(() => (init_uninstall(), uninstall_exports));
|
|
14423
|
-
uninstall2();
|
|
14815
|
+
uninstall2(name);
|
|
14424
14816
|
} else if (command === "status") {
|
|
14425
14817
|
const { status: status2 } = await Promise.resolve().then(() => (init_status(), status_exports));
|
|
14426
|
-
status2();
|
|
14818
|
+
status2(name);
|
|
14819
|
+
} else if (command === "restart") {
|
|
14820
|
+
const { restart: restart2 } = await Promise.resolve().then(() => (init_restart(), restart_exports));
|
|
14821
|
+
restart2(name);
|
|
14822
|
+
} else if (command === "logs") {
|
|
14823
|
+
const follow = cleanArgs.includes("-f") || cleanArgs.includes("--follow");
|
|
14824
|
+
const { logs: logs2 } = await Promise.resolve().then(() => (init_logs(), logs_exports));
|
|
14825
|
+
logs2(follow, name);
|
|
14427
14826
|
} else {
|
|
14428
14827
|
let handleShutdown = function(signal) {
|
|
14429
14828
|
log.info(`Received ${signal} \u2014 shutting down...`);
|
|
@@ -14438,7 +14837,7 @@ if (command === "install") {
|
|
|
14438
14837
|
};
|
|
14439
14838
|
let token;
|
|
14440
14839
|
if (command === "start") {
|
|
14441
|
-
token =
|
|
14840
|
+
token = cleanArgs[1];
|
|
14442
14841
|
} else if (command && !command.startsWith("-")) {
|
|
14443
14842
|
token = command;
|
|
14444
14843
|
}
|