sparkecoder 0.1.116 → 0.1.118
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/agent/index.d.ts +3 -3
- package/dist/agent/index.js +136 -697
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +621 -1038
- package/dist/cli.js.map +1 -1
- package/dist/db/index.d.ts +2 -2
- package/dist/{index-Biy5JTop.d.ts → index-Bcz0aCAR.d.ts} +104 -113
- package/dist/index.d.ts +5 -5
- package/dist/index.js +353 -935
- package/dist/index.js.map +1 -1
- package/dist/{schema-CYSKJZ3m.d.ts → schema-BWbWmfDQ.d.ts} +3 -5
- package/dist/{search-CVVfuBPZ.d.ts → search-DOzC4ojH.d.ts} +4 -4
- package/dist/server/index.js +353 -935
- package/dist/server/index.js.map +1 -1
- package/dist/skills/default/desktop-automation.md +290 -0
- package/dist/skills/default/recording.md +3 -3
- package/dist/tools/index.d.ts +4 -170
- package/dist/tools/index.js +5 -590
- package/dist/tools/index.js.map +1 -1
- package/package.json +1 -1
- package/src/skills/default/desktop-automation.md +290 -0
- package/src/skills/default/recording.md +3 -3
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/build-manifest.json +2 -2
- package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
- package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.html +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.html +1 -1
- package/web/.next/standalone/web/.next/server/app/index.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.html +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
- package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
- package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
- package/dist/skills/default/computer-use.md +0 -225
- package/src/skills/default/computer-use.md +0 -225
- /package/web/.next/standalone/web/.next/static/{7tYFi20tEUZNGXmy2DJ1K → T8x1J_CS0n9FaWBr5GhLe}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{7tYFi20tEUZNGXmy2DJ1K → T8x1J_CS0n9FaWBr5GhLe}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{7tYFi20tEUZNGXmy2DJ1K → T8x1J_CS0n9FaWBr5GhLe}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{7tYFi20tEUZNGXmy2DJ1K → T8x1J_CS0n9FaWBr5GhLe}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{7tYFi20tEUZNGXmy2DJ1K → T8x1J_CS0n9FaWBr5GhLe}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/static/{7tYFi20tEUZNGXmy2DJ1K → T8x1J_CS0n9FaWBr5GhLe}/_ssgManifest.js +0 -0
- /package/web/.next/static/{7tYFi20tEUZNGXmy2DJ1K → T8x1J_CS0n9FaWBr5GhLe}/_buildManifest.js +0 -0
- /package/web/.next/static/{7tYFi20tEUZNGXmy2DJ1K → T8x1J_CS0n9FaWBr5GhLe}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{7tYFi20tEUZNGXmy2DJ1K → T8x1J_CS0n9FaWBr5GhLe}/_ssgManifest.js +0 -0
package/dist/server/index.js
CHANGED
|
@@ -724,12 +724,6 @@ var init_types = __esm({
|
|
|
724
724
|
skillsDirectory: z.string().optional(),
|
|
725
725
|
maxContextChars: z.number().optional().default(2e5),
|
|
726
726
|
task: TaskConfigSchema.optional(),
|
|
727
|
-
// Anthropic computer use tool — opt-in. When true, the `computer` tool is
|
|
728
|
-
// included in the toolset for Anthropic models. Default false.
|
|
729
|
-
computerUseEnabled: z.boolean().optional(),
|
|
730
|
-
// Display dimensions for the computer use tool (defaults: 1280x800).
|
|
731
|
-
computerUseDisplayWidth: z.number().int().positive().optional(),
|
|
732
|
-
computerUseDisplayHeight: z.number().int().positive().optional(),
|
|
733
727
|
// 'orchestrator' = supervisor session; 'worker' = task spawned by an orchestrator.
|
|
734
728
|
role: z.enum(["orchestrator", "worker", "chat"]).optional(),
|
|
735
729
|
// Optional persona / extra system-prompt text appended to the orchestrator's
|
|
@@ -2714,12 +2708,12 @@ function findNearestRoot(startDir, markers) {
|
|
|
2714
2708
|
}
|
|
2715
2709
|
async function commandExists(cmd) {
|
|
2716
2710
|
try {
|
|
2717
|
-
const { exec:
|
|
2718
|
-
const { promisify:
|
|
2719
|
-
const
|
|
2711
|
+
const { exec: exec7 } = await import("child_process");
|
|
2712
|
+
const { promisify: promisify7 } = await import("util");
|
|
2713
|
+
const execAsync7 = promisify7(exec7);
|
|
2720
2714
|
const isWindows = process.platform === "win32";
|
|
2721
2715
|
const checkCmd = isWindows ? `where ${cmd}` : `which ${cmd}`;
|
|
2722
|
-
await
|
|
2716
|
+
await execAsync7(checkCmd);
|
|
2723
2717
|
return true;
|
|
2724
2718
|
} catch {
|
|
2725
2719
|
return false;
|
|
@@ -6364,581 +6358,6 @@ var init_upload_file = __esm({
|
|
|
6364
6358
|
}
|
|
6365
6359
|
});
|
|
6366
6360
|
|
|
6367
|
-
// src/tools/computer-use.ts
|
|
6368
|
-
import { anthropic } from "@ai-sdk/anthropic";
|
|
6369
|
-
import { exec as exec5 } from "child_process";
|
|
6370
|
-
import { promisify as promisify5 } from "util";
|
|
6371
|
-
import { mkdirSync as mkdirSync5, existsSync as existsSync15, readFileSync as readFileSync7, unlinkSync as unlinkSync2 } from "fs";
|
|
6372
|
-
import { join as join8 } from "path";
|
|
6373
|
-
import { tmpdir } from "os";
|
|
6374
|
-
import { nanoid as nanoid4 } from "nanoid";
|
|
6375
|
-
function isMacOs() {
|
|
6376
|
-
return process.platform === "darwin";
|
|
6377
|
-
}
|
|
6378
|
-
async function isCliclickInstalled() {
|
|
6379
|
-
try {
|
|
6380
|
-
await execAsync5("command -v cliclick", { timeout: 2e3 });
|
|
6381
|
-
return true;
|
|
6382
|
-
} catch {
|
|
6383
|
-
return false;
|
|
6384
|
-
}
|
|
6385
|
-
}
|
|
6386
|
-
async function runJxa(script) {
|
|
6387
|
-
try {
|
|
6388
|
-
const escaped = script.replace(/'/g, `'\\''`);
|
|
6389
|
-
const { stdout } = await execAsync5(`osascript -l JavaScript -e '${escaped}'`, {
|
|
6390
|
-
timeout: 5e3
|
|
6391
|
-
});
|
|
6392
|
-
return JSON.parse(stdout.trim());
|
|
6393
|
-
} catch {
|
|
6394
|
-
return null;
|
|
6395
|
-
}
|
|
6396
|
-
}
|
|
6397
|
-
async function hasAccessibilityPermissions() {
|
|
6398
|
-
try {
|
|
6399
|
-
const { stderr } = await execAsync5("cliclick p:.", { timeout: 3e3 });
|
|
6400
|
-
if (/accessibility privileges not enabled/i.test(stderr)) {
|
|
6401
|
-
return { ok: false, error: stderr.trim().split("\n")[0] };
|
|
6402
|
-
}
|
|
6403
|
-
return { ok: true };
|
|
6404
|
-
} catch (err) {
|
|
6405
|
-
return { ok: false, error: err?.message || String(err) };
|
|
6406
|
-
}
|
|
6407
|
-
}
|
|
6408
|
-
async function hasScreenRecordingPermissions() {
|
|
6409
|
-
const result = await runJxa(
|
|
6410
|
-
`ObjC.import("Cocoa");
|
|
6411
|
-
ObjC.import("CoreGraphics");
|
|
6412
|
-
ObjC.bindFunction("CGPreflightScreenCaptureAccess", ["bool", []]);
|
|
6413
|
-
JSON.stringify({ hasAccess: !!$.CGPreflightScreenCaptureAccess() });`
|
|
6414
|
-
);
|
|
6415
|
-
return result?.hasAccess ?? false;
|
|
6416
|
-
}
|
|
6417
|
-
async function requestAccessibilityPrompt() {
|
|
6418
|
-
const result = await runJxa(
|
|
6419
|
-
`ObjC.import("ApplicationServices");
|
|
6420
|
-
var key = $.kAXTrustedCheckOptionPrompt;
|
|
6421
|
-
var dict = $.NSDictionary.dictionaryWithObjectForKey($.kCFBooleanTrue, key);
|
|
6422
|
-
var trusted = $.AXIsProcessTrustedWithOptions(dict);
|
|
6423
|
-
JSON.stringify({ trusted: !!trusted });`
|
|
6424
|
-
);
|
|
6425
|
-
return result?.trusted ?? false;
|
|
6426
|
-
}
|
|
6427
|
-
async function requestScreenRecordingPrompt() {
|
|
6428
|
-
const result = await runJxa(
|
|
6429
|
-
`ObjC.import("Cocoa");
|
|
6430
|
-
ObjC.import("CoreGraphics");
|
|
6431
|
-
ObjC.bindFunction("CGRequestScreenCaptureAccess", ["bool", []]);
|
|
6432
|
-
JSON.stringify({ granted: !!$.CGRequestScreenCaptureAccess() });`
|
|
6433
|
-
);
|
|
6434
|
-
return result?.granted ?? false;
|
|
6435
|
-
}
|
|
6436
|
-
async function openSystemSettings(pane) {
|
|
6437
|
-
const url = pane === "accessibility" ? "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility" : "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
|
|
6438
|
-
try {
|
|
6439
|
-
await execAsync5(`open '${url}'`, { timeout: 3e3 });
|
|
6440
|
-
} catch {
|
|
6441
|
-
}
|
|
6442
|
-
}
|
|
6443
|
-
async function detectScreenSize() {
|
|
6444
|
-
try {
|
|
6445
|
-
const { stdout } = await execAsync5(
|
|
6446
|
-
`osascript -e 'tell application "Finder" to get bounds of window of desktop'`,
|
|
6447
|
-
{ timeout: 3e3 }
|
|
6448
|
-
);
|
|
6449
|
-
const parts = stdout.trim().split(",").map((s) => parseInt(s.trim(), 10));
|
|
6450
|
-
if (parts.length >= 4 && parts.every((n) => Number.isFinite(n))) {
|
|
6451
|
-
const [x1, y1, x2, y2] = parts;
|
|
6452
|
-
return { width: x2 - x1, height: y2 - y1 };
|
|
6453
|
-
}
|
|
6454
|
-
} catch {
|
|
6455
|
-
}
|
|
6456
|
-
return null;
|
|
6457
|
-
}
|
|
6458
|
-
async function runCliclick(args) {
|
|
6459
|
-
const quoted = args.map((a) => `'${a.replace(/'/g, `'\\''`)}'`).join(" ");
|
|
6460
|
-
const { stdout, stderr } = await execAsync5(`cliclick ${quoted}`, {
|
|
6461
|
-
timeout: 15e3,
|
|
6462
|
-
maxBuffer: 1024 * 1024
|
|
6463
|
-
});
|
|
6464
|
-
if (/accessibility privileges not enabled/i.test(stderr)) {
|
|
6465
|
-
throw new Error(
|
|
6466
|
-
"Accessibility permissions not granted to cliclick. Open System Settings \u2192 Privacy & Security \u2192 Accessibility, add cliclick (or the agent runtime), and toggle it on."
|
|
6467
|
-
);
|
|
6468
|
-
}
|
|
6469
|
-
if (stderr && !stdout) throw new Error(stderr.trim());
|
|
6470
|
-
return (stdout || "").trim();
|
|
6471
|
-
}
|
|
6472
|
-
async function runScreencapture(path) {
|
|
6473
|
-
await execAsync5(`screencapture -x -t png '${path.replace(/'/g, `'\\''`)}'`, {
|
|
6474
|
-
timeout: 5e3
|
|
6475
|
-
});
|
|
6476
|
-
}
|
|
6477
|
-
async function resizeScreenshotToPoints(path, targetWidth, targetHeight) {
|
|
6478
|
-
const sharpModule = await import("sharp");
|
|
6479
|
-
const sharp2 = sharpModule.default || sharpModule;
|
|
6480
|
-
const meta = await sharp2(path).metadata();
|
|
6481
|
-
if (meta.width === targetWidth && meta.height === targetHeight) {
|
|
6482
|
-
return readFileSync7(path);
|
|
6483
|
-
}
|
|
6484
|
-
return await sharp2(path).resize(targetWidth, targetHeight, { fit: "fill" }).png().toBuffer();
|
|
6485
|
-
}
|
|
6486
|
-
async function runScroll(dx, dy) {
|
|
6487
|
-
const wheelY = -Math.round(dy);
|
|
6488
|
-
const wheelX = -Math.round(dx);
|
|
6489
|
-
const script = `ObjC.import('CoreGraphics');var ev = $.CGEventCreateScrollWheelEvent(null, 0, 2, ${wheelY}, ${wheelX});$.CGEventPost(0, ev);`;
|
|
6490
|
-
await execAsync5(
|
|
6491
|
-
`osascript -l JavaScript -e '${script.replace(/'/g, `'\\''`)}'`,
|
|
6492
|
-
{ timeout: 5e3 }
|
|
6493
|
-
);
|
|
6494
|
-
}
|
|
6495
|
-
function translateKeyForCliclick(key2) {
|
|
6496
|
-
if (!key2) return [];
|
|
6497
|
-
const parts = key2.split("+").map((p) => p.trim()).filter(Boolean);
|
|
6498
|
-
if (parts.length === 0) return [];
|
|
6499
|
-
const modMap = {
|
|
6500
|
-
ctrl: "ctrl",
|
|
6501
|
-
control: "ctrl",
|
|
6502
|
-
alt: "alt",
|
|
6503
|
-
option: "alt",
|
|
6504
|
-
shift: "shift",
|
|
6505
|
-
cmd: "cmd",
|
|
6506
|
-
super: "cmd",
|
|
6507
|
-
meta: "cmd",
|
|
6508
|
-
win: "cmd",
|
|
6509
|
-
fn: "fn"
|
|
6510
|
-
};
|
|
6511
|
-
const keyMap = {
|
|
6512
|
-
return: "enter",
|
|
6513
|
-
enter: "enter",
|
|
6514
|
-
esc: "esc",
|
|
6515
|
-
escape: "esc",
|
|
6516
|
-
backspace: "delete",
|
|
6517
|
-
back_space: "delete",
|
|
6518
|
-
delete: "fwd-delete",
|
|
6519
|
-
fwd_delete: "fwd-delete",
|
|
6520
|
-
forward_delete: "fwd-delete",
|
|
6521
|
-
tab: "tab",
|
|
6522
|
-
space: "space",
|
|
6523
|
-
up: "arrow-up",
|
|
6524
|
-
arrow_up: "arrow-up",
|
|
6525
|
-
down: "arrow-down",
|
|
6526
|
-
arrow_down: "arrow-down",
|
|
6527
|
-
left: "arrow-left",
|
|
6528
|
-
arrow_left: "arrow-left",
|
|
6529
|
-
right: "arrow-right",
|
|
6530
|
-
arrow_right: "arrow-right",
|
|
6531
|
-
page_up: "page-up",
|
|
6532
|
-
pageup: "page-up",
|
|
6533
|
-
page_down: "page-down",
|
|
6534
|
-
pagedown: "page-down",
|
|
6535
|
-
home: "home",
|
|
6536
|
-
end: "end",
|
|
6537
|
-
f1: "f1",
|
|
6538
|
-
f2: "f2",
|
|
6539
|
-
f3: "f3",
|
|
6540
|
-
f4: "f4",
|
|
6541
|
-
f5: "f5",
|
|
6542
|
-
f6: "f6",
|
|
6543
|
-
f7: "f7",
|
|
6544
|
-
f8: "f8",
|
|
6545
|
-
f9: "f9",
|
|
6546
|
-
f10: "f10",
|
|
6547
|
-
f11: "f11",
|
|
6548
|
-
f12: "f12"
|
|
6549
|
-
};
|
|
6550
|
-
const modifiers = [];
|
|
6551
|
-
let mainKey = null;
|
|
6552
|
-
for (let i = 0; i < parts.length; i++) {
|
|
6553
|
-
const lower = parts[i].toLowerCase().replace(/-/g, "_");
|
|
6554
|
-
if (i < parts.length - 1 && modMap[lower]) {
|
|
6555
|
-
modifiers.push(modMap[lower]);
|
|
6556
|
-
} else {
|
|
6557
|
-
mainKey = keyMap[lower] || lower;
|
|
6558
|
-
}
|
|
6559
|
-
}
|
|
6560
|
-
const args = [];
|
|
6561
|
-
if (modifiers.length > 0) args.push(`kd:${modifiers.join(",")}`);
|
|
6562
|
-
if (mainKey) {
|
|
6563
|
-
const isNamedKey = Object.values(keyMap).includes(mainKey) || /^f([1-9]|1[0-9]|20)$/.test(mainKey) || /^num-/.test(mainKey);
|
|
6564
|
-
if (isNamedKey) {
|
|
6565
|
-
args.push(`kp:${mainKey}`);
|
|
6566
|
-
} else {
|
|
6567
|
-
args.push(`t:${mainKey}`);
|
|
6568
|
-
}
|
|
6569
|
-
}
|
|
6570
|
-
if (modifiers.length > 0) args.push(`ku:${modifiers.join(",")}`);
|
|
6571
|
-
return args;
|
|
6572
|
-
}
|
|
6573
|
-
function modifierStringToCliclick(text) {
|
|
6574
|
-
return text.split("+").map((p) => p.trim().toLowerCase()).map((p) => {
|
|
6575
|
-
if (p === "ctrl" || p === "control") return "ctrl";
|
|
6576
|
-
if (p === "alt" || p === "option") return "alt";
|
|
6577
|
-
if (p === "shift") return "shift";
|
|
6578
|
-
if (p === "super" || p === "meta" || p === "cmd") return "cmd";
|
|
6579
|
-
return "";
|
|
6580
|
-
}).filter(Boolean);
|
|
6581
|
-
}
|
|
6582
|
-
function createComputerUseTool(options) {
|
|
6583
|
-
const displayWidth = options.displayWidth ?? DEFAULT_WIDTH;
|
|
6584
|
-
const displayHeight = options.displayHeight ?? DEFAULT_HEIGHT;
|
|
6585
|
-
return anthropic.tools.computer_20251124({
|
|
6586
|
-
displayWidthPx: displayWidth,
|
|
6587
|
-
displayHeightPx: displayHeight,
|
|
6588
|
-
enableZoom: true,
|
|
6589
|
-
execute: async (input) => {
|
|
6590
|
-
try {
|
|
6591
|
-
switch (input.action) {
|
|
6592
|
-
case "screenshot": {
|
|
6593
|
-
const path = join8(tmpdir(), `cu-${nanoid4(8)}.png`);
|
|
6594
|
-
await runScreencapture(path);
|
|
6595
|
-
const resized = await resizeScreenshotToPoints(path, displayWidth, displayHeight);
|
|
6596
|
-
try {
|
|
6597
|
-
unlinkSync2(path);
|
|
6598
|
-
} catch {
|
|
6599
|
-
}
|
|
6600
|
-
return { type: "image", data: resized.toString("base64") };
|
|
6601
|
-
}
|
|
6602
|
-
case "left_click": {
|
|
6603
|
-
const [x, y] = input.coordinate ?? [0, 0];
|
|
6604
|
-
if (input.text) {
|
|
6605
|
-
const mods = modifierStringToCliclick(input.text);
|
|
6606
|
-
if (mods.length > 0) {
|
|
6607
|
-
await runCliclick([`kd:${mods.join(",")}`, `c:${x},${y}`, `ku:${mods.join(",")}`]);
|
|
6608
|
-
} else {
|
|
6609
|
-
await runCliclick([`c:${x},${y}`]);
|
|
6610
|
-
}
|
|
6611
|
-
} else {
|
|
6612
|
-
await runCliclick([`c:${x},${y}`]);
|
|
6613
|
-
}
|
|
6614
|
-
return `clicked at (${x}, ${y})${input.text ? ` with ${input.text}` : ""}`;
|
|
6615
|
-
}
|
|
6616
|
-
case "right_click": {
|
|
6617
|
-
const [x, y] = input.coordinate ?? [0, 0];
|
|
6618
|
-
await runCliclick([`rc:${x},${y}`]);
|
|
6619
|
-
return `right-clicked at (${x}, ${y})`;
|
|
6620
|
-
}
|
|
6621
|
-
case "middle_click": {
|
|
6622
|
-
const [x, y] = input.coordinate ?? [0, 0];
|
|
6623
|
-
const script = `ObjC.import('CoreGraphics');var loc = $.CGPointMake(${x}, ${y});var down = $.CGEventCreateMouseEvent(null, 25, loc, 2);var up = $.CGEventCreateMouseEvent(null, 26, loc, 2);$.CGEventPost(0, down); $.CGEventPost(0, up);`;
|
|
6624
|
-
await execAsync5(
|
|
6625
|
-
`osascript -l JavaScript -e '${script.replace(/'/g, `'\\''`)}'`,
|
|
6626
|
-
{ timeout: 3e3 }
|
|
6627
|
-
);
|
|
6628
|
-
return `middle-clicked at (${x}, ${y})`;
|
|
6629
|
-
}
|
|
6630
|
-
case "double_click": {
|
|
6631
|
-
const [x, y] = input.coordinate ?? [0, 0];
|
|
6632
|
-
await runCliclick([`dc:${x},${y}`]);
|
|
6633
|
-
return `double-clicked at (${x}, ${y})`;
|
|
6634
|
-
}
|
|
6635
|
-
case "triple_click": {
|
|
6636
|
-
const [x, y] = input.coordinate ?? [0, 0];
|
|
6637
|
-
await runCliclick([`tc:${x},${y}`]);
|
|
6638
|
-
return `triple-clicked at (${x}, ${y})`;
|
|
6639
|
-
}
|
|
6640
|
-
case "mouse_move": {
|
|
6641
|
-
const [x, y] = input.coordinate ?? [0, 0];
|
|
6642
|
-
await runCliclick([`m:${x},${y}`]);
|
|
6643
|
-
return `moved cursor to (${x}, ${y})`;
|
|
6644
|
-
}
|
|
6645
|
-
case "left_mouse_down": {
|
|
6646
|
-
const [x, y] = input.coordinate ?? [0, 0];
|
|
6647
|
-
await runCliclick([`dd:${x},${y}`]);
|
|
6648
|
-
return `left mouse button pressed at (${x}, ${y})`;
|
|
6649
|
-
}
|
|
6650
|
-
case "left_mouse_up": {
|
|
6651
|
-
const [x, y] = input.coordinate ?? [0, 0];
|
|
6652
|
-
await runCliclick([`du:${x},${y}`]);
|
|
6653
|
-
return `left mouse button released at (${x}, ${y})`;
|
|
6654
|
-
}
|
|
6655
|
-
case "left_click_drag": {
|
|
6656
|
-
const [sx, sy] = input.start_coordinate ?? [0, 0];
|
|
6657
|
-
const [ex, ey] = input.coordinate ?? [0, 0];
|
|
6658
|
-
await runCliclick([`dd:${sx},${sy}`, `m:${ex},${ey}`, `du:${ex},${ey}`]);
|
|
6659
|
-
return `dragged from (${sx}, ${sy}) to (${ex}, ${ey})`;
|
|
6660
|
-
}
|
|
6661
|
-
case "type": {
|
|
6662
|
-
const text = input.text ?? "";
|
|
6663
|
-
await runCliclick([`t:${text}`]);
|
|
6664
|
-
return `typed ${text.length} character(s)`;
|
|
6665
|
-
}
|
|
6666
|
-
case "key": {
|
|
6667
|
-
const args = translateKeyForCliclick(input.text ?? "");
|
|
6668
|
-
if (args.length === 0) return "no key specified";
|
|
6669
|
-
await runCliclick(args);
|
|
6670
|
-
return `pressed ${input.text}`;
|
|
6671
|
-
}
|
|
6672
|
-
case "hold_key": {
|
|
6673
|
-
const text = (input.text ?? "").toLowerCase();
|
|
6674
|
-
const duration = input.duration ?? 1;
|
|
6675
|
-
const modMap = {
|
|
6676
|
-
ctrl: "ctrl",
|
|
6677
|
-
control: "ctrl",
|
|
6678
|
-
alt: "alt",
|
|
6679
|
-
option: "alt",
|
|
6680
|
-
shift: "shift",
|
|
6681
|
-
cmd: "cmd",
|
|
6682
|
-
super: "cmd",
|
|
6683
|
-
meta: "cmd",
|
|
6684
|
-
fn: "fn"
|
|
6685
|
-
};
|
|
6686
|
-
const cliName = modMap[text] || text;
|
|
6687
|
-
await runCliclick([`kd:${cliName}`]);
|
|
6688
|
-
await new Promise((r) => setTimeout(r, duration * 1e3));
|
|
6689
|
-
await runCliclick([`ku:${cliName}`]);
|
|
6690
|
-
return `held ${text} for ${duration}s`;
|
|
6691
|
-
}
|
|
6692
|
-
case "scroll": {
|
|
6693
|
-
const direction = input.scroll_direction ?? "down";
|
|
6694
|
-
const amount = input.scroll_amount ?? 3;
|
|
6695
|
-
const px = amount * 100;
|
|
6696
|
-
const dx = direction === "left" ? -px : direction === "right" ? px : 0;
|
|
6697
|
-
const dy = direction === "up" ? -px : direction === "down" ? px : 0;
|
|
6698
|
-
if (input.coordinate) {
|
|
6699
|
-
const [x, y] = input.coordinate;
|
|
6700
|
-
await runCliclick([`m:${x},${y}`]);
|
|
6701
|
-
}
|
|
6702
|
-
const mods = input.text ? modifierStringToCliclick(input.text) : [];
|
|
6703
|
-
if (mods.length > 0) {
|
|
6704
|
-
await runCliclick([`kd:${mods.join(",")}`]);
|
|
6705
|
-
}
|
|
6706
|
-
await runScroll(dx, dy);
|
|
6707
|
-
if (mods.length > 0) {
|
|
6708
|
-
await runCliclick([`ku:${mods.join(",")}`]);
|
|
6709
|
-
}
|
|
6710
|
-
return `scrolled ${direction} by ${amount}`;
|
|
6711
|
-
}
|
|
6712
|
-
case "wait": {
|
|
6713
|
-
const duration = input.duration ?? 1;
|
|
6714
|
-
await new Promise((r) => setTimeout(r, duration * 1e3));
|
|
6715
|
-
return `waited ${duration}s`;
|
|
6716
|
-
}
|
|
6717
|
-
case "cursor_position": {
|
|
6718
|
-
const out = await runCliclick(["p:."]);
|
|
6719
|
-
return `cursor at ${out}`;
|
|
6720
|
-
}
|
|
6721
|
-
case "zoom": {
|
|
6722
|
-
const region = input.region ?? [0, 0, displayWidth, displayHeight];
|
|
6723
|
-
const [x1, y1, x2, y2] = region;
|
|
6724
|
-
const tmpPath = join8(tmpdir(), `cu-zoom-${nanoid4(8)}.png`);
|
|
6725
|
-
await runScreencapture(tmpPath);
|
|
6726
|
-
const sharpModule = await import("sharp");
|
|
6727
|
-
const sharp2 = sharpModule.default || sharpModule;
|
|
6728
|
-
const meta = await sharp2(tmpPath).metadata();
|
|
6729
|
-
const scaleX = (meta.width || displayWidth) / displayWidth;
|
|
6730
|
-
const scaleY = (meta.height || displayHeight) / displayHeight;
|
|
6731
|
-
const px = {
|
|
6732
|
-
left: Math.max(0, Math.round(x1 * scaleX)),
|
|
6733
|
-
top: Math.max(0, Math.round(y1 * scaleY)),
|
|
6734
|
-
width: Math.max(1, Math.round((x2 - x1) * scaleX)),
|
|
6735
|
-
height: Math.max(1, Math.round((y2 - y1) * scaleY))
|
|
6736
|
-
};
|
|
6737
|
-
const buf = await sharp2(tmpPath).extract(px).png().toBuffer();
|
|
6738
|
-
try {
|
|
6739
|
-
unlinkSync2(tmpPath);
|
|
6740
|
-
} catch {
|
|
6741
|
-
}
|
|
6742
|
-
return { type: "image", data: buf.toString("base64") };
|
|
6743
|
-
}
|
|
6744
|
-
default: {
|
|
6745
|
-
const exhaustive = input.action;
|
|
6746
|
-
return `unsupported action: ${String(exhaustive)}`;
|
|
6747
|
-
}
|
|
6748
|
-
}
|
|
6749
|
-
} catch (err) {
|
|
6750
|
-
const msg = err?.message || String(err);
|
|
6751
|
-
let hint = "";
|
|
6752
|
-
if (/accessibility|not authorized|tcc|operation not permitted/i.test(msg)) {
|
|
6753
|
-
hint = " (Hint: call enable_computer_use to (re-)check permissions and open System Settings)";
|
|
6754
|
-
} else if (/command not found/i.test(msg)) {
|
|
6755
|
-
hint = " (Hint: install cliclick with `brew install cliclick`)";
|
|
6756
|
-
}
|
|
6757
|
-
return `Error: ${msg}${hint}`;
|
|
6758
|
-
}
|
|
6759
|
-
},
|
|
6760
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6761
|
-
toModelOutput({ output }) {
|
|
6762
|
-
if (typeof output === "string") {
|
|
6763
|
-
return { type: "content", value: [{ type: "text", text: output }] };
|
|
6764
|
-
}
|
|
6765
|
-
return {
|
|
6766
|
-
type: "content",
|
|
6767
|
-
value: [{ type: "media", data: output.data, mediaType: "image/png" }]
|
|
6768
|
-
};
|
|
6769
|
-
}
|
|
6770
|
-
});
|
|
6771
|
-
}
|
|
6772
|
-
var execAsync5, DEFAULT_WIDTH, DEFAULT_HEIGHT;
|
|
6773
|
-
var init_computer_use = __esm({
|
|
6774
|
-
"src/tools/computer-use.ts"() {
|
|
6775
|
-
"use strict";
|
|
6776
|
-
execAsync5 = promisify5(exec5);
|
|
6777
|
-
DEFAULT_WIDTH = 1280;
|
|
6778
|
-
DEFAULT_HEIGHT = 800;
|
|
6779
|
-
}
|
|
6780
|
-
});
|
|
6781
|
-
|
|
6782
|
-
// src/tools/enable-computer-use.ts
|
|
6783
|
-
import { tool as tool13 } from "ai";
|
|
6784
|
-
import { z as z14 } from "zod";
|
|
6785
|
-
function createEnableComputerUseTool(options) {
|
|
6786
|
-
return tool13({
|
|
6787
|
-
description: "Enable Anthropic's computer use beta tool for this session. macOS only. Drives the actual desktop (mouse, keyboard, screenshots) at pixel coordinates. Requires `cliclick` (brew install cliclick), Accessibility permissions, and Screen Recording permissions. When called, this tool will automatically request any missing permissions and open System Settings to the right pane. Only works on Anthropic Claude models. After this tool succeeds, you MUST stop the current turn and ask the user to send another message \u2014 the `computer` tool only becomes available on the NEXT message because the toolset is fixed for the current turn.",
|
|
6788
|
-
inputSchema,
|
|
6789
|
-
execute: async ({ display_width, display_height, request_permissions }) => {
|
|
6790
|
-
try {
|
|
6791
|
-
if (!isMacOs()) {
|
|
6792
|
-
return {
|
|
6793
|
-
success: false,
|
|
6794
|
-
error: "Computer use is currently only supported on macOS.",
|
|
6795
|
-
platform: process.platform
|
|
6796
|
-
};
|
|
6797
|
-
}
|
|
6798
|
-
if (!await isCliclickInstalled()) {
|
|
6799
|
-
return {
|
|
6800
|
-
success: false,
|
|
6801
|
-
error: "`cliclick` is not installed. It is required for mouse/keyboard control on macOS.",
|
|
6802
|
-
installCommand: "brew install cliclick",
|
|
6803
|
-
fixSteps: [
|
|
6804
|
-
"In a terminal on this Mac, run: brew install cliclick",
|
|
6805
|
-
"(If Homebrew is not installed, install it first from https://brew.sh)",
|
|
6806
|
-
"Then call enable_computer_use again"
|
|
6807
|
-
]
|
|
6808
|
-
};
|
|
6809
|
-
}
|
|
6810
|
-
const acc = await hasAccessibilityPermissions();
|
|
6811
|
-
const screen = await hasScreenRecordingPermissions();
|
|
6812
|
-
const missing = [];
|
|
6813
|
-
if (!acc.ok) {
|
|
6814
|
-
let prompted = false;
|
|
6815
|
-
let panelOpened = false;
|
|
6816
|
-
if (request_permissions) {
|
|
6817
|
-
prompted = await requestAccessibilityPrompt().then(() => true).catch(() => false);
|
|
6818
|
-
await openSystemSettings("accessibility").then(() => {
|
|
6819
|
-
panelOpened = true;
|
|
6820
|
-
}).catch(() => void 0);
|
|
6821
|
-
}
|
|
6822
|
-
missing.push({
|
|
6823
|
-
name: "Accessibility",
|
|
6824
|
-
reason: "cliclick failed: " + (acc.error?.split("\n")[0] || "no permission"),
|
|
6825
|
-
pane: "accessibility",
|
|
6826
|
-
settingsUrl: ACCESSIBILITY_URL,
|
|
6827
|
-
fixSteps: [
|
|
6828
|
-
"In the System Settings \u2192 Privacy & Security \u2192 Accessibility pane that opened",
|
|
6829
|
-
"Click the + button",
|
|
6830
|
-
"Add the application running the agent (Terminal, iTerm, your IDE, or `node`)",
|
|
6831
|
-
"Toggle the switch ON",
|
|
6832
|
-
"Restart the agent process so the new permission takes effect",
|
|
6833
|
-
"Then call enable_computer_use again"
|
|
6834
|
-
],
|
|
6835
|
-
prompted,
|
|
6836
|
-
panelOpened
|
|
6837
|
-
});
|
|
6838
|
-
}
|
|
6839
|
-
if (!screen) {
|
|
6840
|
-
let prompted = false;
|
|
6841
|
-
let panelOpened = false;
|
|
6842
|
-
if (request_permissions) {
|
|
6843
|
-
prompted = await requestScreenRecordingPrompt().then(() => true).catch(() => false);
|
|
6844
|
-
await openSystemSettings("screen-recording").then(() => {
|
|
6845
|
-
panelOpened = true;
|
|
6846
|
-
}).catch(() => void 0);
|
|
6847
|
-
}
|
|
6848
|
-
missing.push({
|
|
6849
|
-
name: "Screen Recording",
|
|
6850
|
-
reason: "CGPreflightScreenCaptureAccess returned false",
|
|
6851
|
-
pane: "screen-recording",
|
|
6852
|
-
settingsUrl: SCREEN_RECORDING_URL,
|
|
6853
|
-
fixSteps: [
|
|
6854
|
-
"In the System Settings \u2192 Privacy & Security \u2192 Screen Recording pane that opened",
|
|
6855
|
-
"Click the + button",
|
|
6856
|
-
"Add the application running the agent (Terminal, iTerm, your IDE, or `node`)",
|
|
6857
|
-
"Toggle the switch ON",
|
|
6858
|
-
"Restart the agent process so the new permission takes effect",
|
|
6859
|
-
"Then call enable_computer_use again"
|
|
6860
|
-
],
|
|
6861
|
-
prompted,
|
|
6862
|
-
panelOpened
|
|
6863
|
-
});
|
|
6864
|
-
}
|
|
6865
|
-
if (missing.length > 0) {
|
|
6866
|
-
return {
|
|
6867
|
-
success: false,
|
|
6868
|
-
error: `Missing permission${missing.length > 1 ? "s" : ""}: ` + missing.map((m) => m.name).join(" and ") + ".",
|
|
6869
|
-
missingPermissions: missing,
|
|
6870
|
-
note: request_permissions ? "System permission prompts have been triggered (best-effort) and System Settings has been opened to the relevant pane(s). After granting and restarting the agent, call enable_computer_use again." : "Re-run with request_permissions: true to auto-open System Settings."
|
|
6871
|
-
};
|
|
6872
|
-
}
|
|
6873
|
-
let width = display_width;
|
|
6874
|
-
let height = display_height;
|
|
6875
|
-
let detected = null;
|
|
6876
|
-
if (width === void 0 || height === void 0) {
|
|
6877
|
-
detected = await detectScreenSize();
|
|
6878
|
-
width = width ?? detected?.width ?? 1280;
|
|
6879
|
-
height = height ?? detected?.height ?? 800;
|
|
6880
|
-
}
|
|
6881
|
-
const session = await sessionQueries.getById(options.sessionId);
|
|
6882
|
-
if (!session) {
|
|
6883
|
-
return { success: false, error: "Session not found" };
|
|
6884
|
-
}
|
|
6885
|
-
const config = session.config || {};
|
|
6886
|
-
if (config.computerUseEnabled === true && config.computerUseDisplayWidth === width && config.computerUseDisplayHeight === height) {
|
|
6887
|
-
return {
|
|
6888
|
-
success: true,
|
|
6889
|
-
alreadyEnabled: true,
|
|
6890
|
-
message: "Computer use was already enabled for this session.",
|
|
6891
|
-
displayWidth: width,
|
|
6892
|
-
displayHeight: height
|
|
6893
|
-
};
|
|
6894
|
-
}
|
|
6895
|
-
const updated = {
|
|
6896
|
-
...config,
|
|
6897
|
-
computerUseEnabled: true,
|
|
6898
|
-
computerUseDisplayWidth: width,
|
|
6899
|
-
computerUseDisplayHeight: height
|
|
6900
|
-
};
|
|
6901
|
-
await sessionQueries.update(options.sessionId, { config: updated });
|
|
6902
|
-
return {
|
|
6903
|
-
success: true,
|
|
6904
|
-
enabled: true,
|
|
6905
|
-
platform: "darwin",
|
|
6906
|
-
displayWidth: width,
|
|
6907
|
-
displayHeight: height,
|
|
6908
|
-
detectedScreenSize: detected || void 0,
|
|
6909
|
-
permissions: {
|
|
6910
|
-
accessibility: "granted",
|
|
6911
|
-
screenRecording: "granted"
|
|
6912
|
-
},
|
|
6913
|
-
message: `Computer use is now enabled for this session. IMPORTANT: The \`computer\` tool is NOT yet available in this turn. Stop here, send a brief message to the user telling them computer use is enabled (display: ${width}x${height}), and ask them to send their next message to begin using it.`
|
|
6914
|
-
};
|
|
6915
|
-
} catch (err) {
|
|
6916
|
-
return {
|
|
6917
|
-
success: false,
|
|
6918
|
-
error: err?.message || String(err)
|
|
6919
|
-
};
|
|
6920
|
-
}
|
|
6921
|
-
}
|
|
6922
|
-
});
|
|
6923
|
-
}
|
|
6924
|
-
var inputSchema, ACCESSIBILITY_URL, SCREEN_RECORDING_URL;
|
|
6925
|
-
var init_enable_computer_use = __esm({
|
|
6926
|
-
"src/tools/enable-computer-use.ts"() {
|
|
6927
|
-
"use strict";
|
|
6928
|
-
init_db();
|
|
6929
|
-
init_computer_use();
|
|
6930
|
-
inputSchema = z14.object({
|
|
6931
|
-
display_width: z14.number().int().positive().optional().describe("Display width in pixels (defaults to detected primary display, fallback 1280)"),
|
|
6932
|
-
display_height: z14.number().int().positive().optional().describe("Display height in pixels (defaults to detected primary display, fallback 800)"),
|
|
6933
|
-
request_permissions: z14.boolean().optional().default(true).describe(
|
|
6934
|
-
"When true (default), proactively trigger macOS permission prompts and open System Settings panes for any missing permissions."
|
|
6935
|
-
)
|
|
6936
|
-
});
|
|
6937
|
-
ACCESSIBILITY_URL = "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility";
|
|
6938
|
-
SCREEN_RECORDING_URL = "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
|
|
6939
|
-
}
|
|
6940
|
-
});
|
|
6941
|
-
|
|
6942
6361
|
// src/tools/index.ts
|
|
6943
6362
|
async function createTools(options) {
|
|
6944
6363
|
const tools = {
|
|
@@ -6983,20 +6402,6 @@ async function createTools(options) {
|
|
|
6983
6402
|
sessionId: options.sessionId
|
|
6984
6403
|
});
|
|
6985
6404
|
}
|
|
6986
|
-
if (process.platform === "darwin") {
|
|
6987
|
-
if (options.enableComputerUse) {
|
|
6988
|
-
tools.computer = createComputerUseTool({
|
|
6989
|
-
workingDirectory: options.workingDirectory,
|
|
6990
|
-
sessionId: options.sessionId,
|
|
6991
|
-
displayWidth: options.computerUseDisplayWidth,
|
|
6992
|
-
displayHeight: options.computerUseDisplayHeight
|
|
6993
|
-
});
|
|
6994
|
-
} else {
|
|
6995
|
-
tools.enable_computer_use = createEnableComputerUseTool({
|
|
6996
|
-
sessionId: options.sessionId
|
|
6997
|
-
});
|
|
6998
|
-
}
|
|
6999
|
-
}
|
|
7000
6405
|
if (options.enableSemanticSearch !== false) {
|
|
7001
6406
|
try {
|
|
7002
6407
|
if (isVectorGatewayConfigured()) {
|
|
@@ -7031,8 +6436,6 @@ var init_tools = __esm({
|
|
|
7031
6436
|
init_code_graph();
|
|
7032
6437
|
init_task();
|
|
7033
6438
|
init_upload_file();
|
|
7034
|
-
init_computer_use();
|
|
7035
|
-
init_enable_computer_use();
|
|
7036
6439
|
init_semantic();
|
|
7037
6440
|
init_remote();
|
|
7038
6441
|
init_bash();
|
|
@@ -7046,8 +6449,6 @@ var init_tools = __esm({
|
|
|
7046
6449
|
init_code_graph();
|
|
7047
6450
|
init_task();
|
|
7048
6451
|
init_upload_file();
|
|
7049
|
-
init_computer_use();
|
|
7050
|
-
init_enable_computer_use();
|
|
7051
6452
|
}
|
|
7052
6453
|
});
|
|
7053
6454
|
|
|
@@ -7523,8 +6924,7 @@ ${JSON.stringify(outputSchema, null, 2)}
|
|
|
7523
6924
|
`;
|
|
7524
6925
|
}
|
|
7525
6926
|
function buildOrchestratorPromptAddendum() {
|
|
7526
|
-
const
|
|
7527
|
-
const computerUseAvailable = platform3 === "darwin";
|
|
6927
|
+
const desktopAvailable = process.platform === "darwin";
|
|
7528
6928
|
return `
|
|
7529
6929
|
## Orchestrator Mode
|
|
7530
6930
|
|
|
@@ -7623,14 +7023,14 @@ When NOT to split (keep as one worker):
|
|
|
7623
7023
|
When spawning a worker, push it toward the *cheapest tool that gets the job done*:
|
|
7624
7024
|
|
|
7625
7025
|
1. **Bash / file tools** for anything with a CLI (git, npm, brew, builds, tests, file editing, HTTP via curl, scripting).
|
|
7626
|
-
2. **agent-browser** (\`load_skill browser\`) for *anything* in a web browser \u2014 refs from \`snapshot -i\` are deterministic, ~100\xD7 cheaper in tokens than pixel coordinates, work cross-platform, and don't need any host permissions.${
|
|
7627
|
-
3. **
|
|
7026
|
+
2. **agent-browser** (\`load_skill browser\`) for *anything* in a web browser \u2014 refs from \`snapshot -i\` are deterministic, ~100\xD7 cheaper in tokens than pixel coordinates, work cross-platform, and don't need any host permissions.${desktopAvailable ? `
|
|
7027
|
+
3. **Desktop automation** (\`load_skill desktop-automation\`) is the last resort \u2014 only when the task genuinely requires a native macOS GUI app with no CLI / API equivalent (System Settings, Calculator, Finder operations that don't have CLI flags, complex cross-app drag/drop, demos where the user wants to *see* the screen). It's all shell \u2014 \`cliclick\`, \`screencapture\`, and \`osascript\` \u2014 invoked from \`bash\`. No special tool registration; no vendor lock-in.
|
|
7628
7028
|
|
|
7629
|
-
A common anti-pattern: a worker reaches for
|
|
7029
|
+
A common anti-pattern: a worker reaches for desktop automation because the user phrased the request visually ("open the website and click the button"). Almost always wrong \u2014 that's a job for the browser skill, not the desktop. Coach the worker in its goal text: *"Use the browser skill (\`load_skill browser\` + \`agent-browser\` with refs from \`snapshot -i\`) to open the site and click the button. Don't use desktop automation for browser work."*
|
|
7630
7030
|
|
|
7631
|
-
### Serialize desktop
|
|
7031
|
+
### Serialize desktop-automation tasks
|
|
7632
7032
|
|
|
7633
|
-
There is exactly **one** desktop, mouse, and keyboard on the host. If two or more workers both
|
|
7033
|
+
There is exactly **one** desktop, mouse, and keyboard on the host. If two or more workers both drive the desktop (clicking with \`cliclick\`, taking screenshots with \`screencapture\`, opening apps, switching windows), they will **fight over the same screen** \u2014 windows will steal focus from each other, screenshots will catch the wrong app, mouse clicks will land on the wrong target.
|
|
7634
7034
|
|
|
7635
7035
|
**Rule**: when spawning workers, look at each one's goal:
|
|
7636
7036
|
|
|
@@ -7651,10 +7051,30 @@ Example: *"Take a screenshot of Calculator AND run the test suite AND open Syste
|
|
|
7651
7051
|
|
|
7652
7052
|
Headless workers never interfere with desktop workers (they don't touch the screen), so they always run in parallel.
|
|
7653
7053
|
|
|
7654
|
-
When you spawn a **desktop worker**,
|
|
7054
|
+
When you spawn a **desktop worker**, tell it to bracket the work with \`sparkecoder record start\` / \`sparkecoder record stop\` (per the \`recording\` skill) so the user can replay what happened on screen, unless the task is long-running / boring / contains sensitive content. When the worker reports back, mention the recording path in your reply via the original channel.` : ""}
|
|
7655
7055
|
|
|
7656
7056
|
Default bias: **when in doubt, decompose**. Two workers running in parallel and reporting independently is almost always better UX than one worker doing things sequentially.
|
|
7657
7057
|
|
|
7058
|
+
### How to TALK to the user (versus how you reason internally)
|
|
7059
|
+
|
|
7060
|
+
All of the rules below \u2014 decomposition, parallel spawning, "don't invent commands", load-the-skill, etc. \u2014 are **your internal operating procedure**. They are how you decide what to do. They are NOT something to recite back to the user.
|
|
7061
|
+
|
|
7062
|
+
When replying to the user (Slack, web, or any channel), be a normal helpful assistant:
|
|
7063
|
+
|
|
7064
|
+
- Tell them *what you're doing*, not *how you're doing it internally*.
|
|
7065
|
+
- Don't quote your own prompt language ("just the goal", "read the skill", "no step-by-step from me", "decomposing into parallel workers"). The user doesn't care about your delegation pattern.
|
|
7066
|
+
- Don't narrate "handing off to a worker now" unless it's actually relevant (e.g. they asked how you work, or the work is going to take a noticeably long time).
|
|
7067
|
+
- Skip ceremony. A short acknowledgement + a brief description of the actual task + (optionally) a heads-up if there's anything they need to do or avoid is plenty.
|
|
7068
|
+
|
|
7069
|
+
| What you might be tempted to say | What you should say instead |
|
|
7070
|
+
|---|---|
|
|
7071
|
+
| *"Got it \u2014 handing it off with just the goal + 'read the skills and figure it out.' No step-by-step from me."* | *"On it. Recording a screen capture of the Weather app showing Anchorage. Don't touch the keyboard while it's running \u2014 I'll post the video when it's done."* |
|
|
7072
|
+
| *"Decomposing into two parallel workers: one headless, one desktop."* | *"Running the tests and pulling the diff at the same time. Back in ~30s."* |
|
|
7073
|
+
| *"Spawning worker \`screenshot-calc\` with goal: \u2026"* | *"Taking a screenshot of Calculator now."* |
|
|
7074
|
+
| *"I'll relay the user's instructions to the worker verbatim."* | *(say nothing \u2014 just do it)* |
|
|
7075
|
+
|
|
7076
|
+
If the user explicitly asks how you work, *then* you can explain the orchestrator/worker split. Otherwise: less is more.
|
|
7077
|
+
|
|
7658
7078
|
### How to write a worker goal (and what NOT to put in it)
|
|
7659
7079
|
|
|
7660
7080
|
You delegate; the worker executes. Stay at the **what** level, not the **how**.
|
|
@@ -7662,7 +7082,7 @@ You delegate; the worker executes. Stay at the **what** level, not the **how**.
|
|
|
7662
7082
|
**DO** put in the goal:
|
|
7663
7083
|
|
|
7664
7084
|
- The end objective ("open the macOS Weather app and capture the forecast for Anchorage as a screen recording").
|
|
7665
|
-
- Which skills the worker should load up-front (\`load_skill recording\`, \`load_skill
|
|
7085
|
+
- Which skills the worker should load up-front (\`load_skill recording\`, \`load_skill desktop-automation\`).
|
|
7666
7086
|
- Acceptance criteria ("verify the city name is visible in the screenshot before reporting done").
|
|
7667
7087
|
- Routing back ("post the recording URL to Slack channel C0123 thread 1700.001").
|
|
7668
7088
|
|
|
@@ -7695,7 +7115,7 @@ Bad goal (don't do this):
|
|
|
7695
7115
|
> "Start a screen recording with \`screencapture -v -V 45 -C /tmp/weather.mov &\`, then open Weather with \`open -a Weather\`, then click the search bar with cliclick, type 'Anchorage'..."
|
|
7696
7116
|
|
|
7697
7117
|
Good goal (do this):
|
|
7698
|
-
> "Capture a 30\u201360s screen recording of opening the macOS Weather app and viewing the Anchorage, AK forecast. \`load_skill recording\` and \`load_skill
|
|
7118
|
+
> "Capture a 30\u201360s screen recording of opening the macOS Weather app and viewing the Anchorage, AK forecast. \`load_skill recording\` and \`load_skill desktop-automation\` first; use the canonical commands from those skills. Verify the Anchorage temperature is visible in a final screenshot before completing. Return the recording path + a one-line summary of the forecast."
|
|
7699
7119
|
`;
|
|
7700
7120
|
}
|
|
7701
7121
|
function createSummaryPrompt(conversationHistory) {
|
|
@@ -7914,17 +7334,17 @@ __export(conversation_archive_exports, {
|
|
|
7914
7334
|
getHistoryDir: () => getHistoryDir,
|
|
7915
7335
|
listSessionArchives: () => listSessionArchives
|
|
7916
7336
|
});
|
|
7917
|
-
import { existsSync as
|
|
7918
|
-
import { join as
|
|
7337
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync5, appendFileSync as appendFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
7338
|
+
import { join as join8 } from "path";
|
|
7919
7339
|
function getHistoryDir() {
|
|
7920
|
-
const dir =
|
|
7921
|
-
if (!
|
|
7340
|
+
const dir = join8(ensureAppDataDirectory(), "history");
|
|
7341
|
+
if (!existsSync15(dir)) mkdirSync5(dir, { recursive: true });
|
|
7922
7342
|
return dir;
|
|
7923
7343
|
}
|
|
7924
7344
|
function appendTurn(turn) {
|
|
7925
7345
|
try {
|
|
7926
7346
|
const dir = getHistoryDir();
|
|
7927
|
-
const path =
|
|
7347
|
+
const path = join8(dir, `${turn.sessionId}.jsonl`);
|
|
7928
7348
|
const line = JSON.stringify({
|
|
7929
7349
|
ts: turn.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
7930
7350
|
sessionId: turn.sessionId,
|
|
@@ -7953,7 +7373,7 @@ function flattenContent(content) {
|
|
|
7953
7373
|
}
|
|
7954
7374
|
function listSessionArchives() {
|
|
7955
7375
|
const dir = getHistoryDir();
|
|
7956
|
-
if (!
|
|
7376
|
+
if (!existsSync15(dir)) return [];
|
|
7957
7377
|
return readdirSync2(dir).filter((f) => f.endsWith(".jsonl"));
|
|
7958
7378
|
}
|
|
7959
7379
|
var init_conversation_archive = __esm({
|
|
@@ -8025,6 +7445,18 @@ function repairToolPairing(messages) {
|
|
|
8025
7445
|
}
|
|
8026
7446
|
return repaired;
|
|
8027
7447
|
}
|
|
7448
|
+
function ensureEndsWithUserOrTool(messages) {
|
|
7449
|
+
if (!Array.isArray(messages) || messages.length === 0) return messages;
|
|
7450
|
+
const last = messages[messages.length - 1];
|
|
7451
|
+
if (last?.role !== "assistant") return messages;
|
|
7452
|
+
console.warn(
|
|
7453
|
+
"[context] Trailing assistant message detected \u2014 appending synthetic user turn to satisfy prefill restrictions"
|
|
7454
|
+
);
|
|
7455
|
+
return [
|
|
7456
|
+
...messages,
|
|
7457
|
+
{ role: "user", content: [{ type: "text", text: "Please continue." }] }
|
|
7458
|
+
];
|
|
7459
|
+
}
|
|
8028
7460
|
var TOOL_OUTPUT_TRIM_CHARS, COMPACTABLE_TOOLS, ContextManager;
|
|
8029
7461
|
var init_context = __esm({
|
|
8030
7462
|
"src/agent/context.ts"() {
|
|
@@ -8082,6 +7514,7 @@ ${summaryContent}`
|
|
|
8082
7514
|
];
|
|
8083
7515
|
}
|
|
8084
7516
|
messages = repairToolPairing(messages);
|
|
7517
|
+
messages = ensureEndsWithUserOrTool(messages);
|
|
8085
7518
|
return messages;
|
|
8086
7519
|
}
|
|
8087
7520
|
// ---------------------------------------------------------------------------
|
|
@@ -8275,7 +7708,8 @@ ${summaryContent}`
|
|
|
8275
7708
|
}
|
|
8276
7709
|
}
|
|
8277
7710
|
async addResponseMessages(messages) {
|
|
8278
|
-
|
|
7711
|
+
const safe = repairToolPairing(messages);
|
|
7712
|
+
await messageQueries.addMany(this.sessionId, safe);
|
|
8279
7713
|
try {
|
|
8280
7714
|
const { appendTurn: appendTurn2, flattenContent: flattenContent2 } = await Promise.resolve().then(() => (init_conversation_archive(), conversation_archive_exports));
|
|
8281
7715
|
const { sessionQueries: sessionQueries2 } = await Promise.resolve().then(() => (init_db(), db_exports));
|
|
@@ -8716,7 +8150,7 @@ var init_messenger = __esm({
|
|
|
8716
8150
|
});
|
|
8717
8151
|
|
|
8718
8152
|
// src/orchestrator/schedules-store.ts
|
|
8719
|
-
import { nanoid as
|
|
8153
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
8720
8154
|
async function readOrch(orchestratorSessionId) {
|
|
8721
8155
|
const s = await sessionQueries.getById(orchestratorSessionId);
|
|
8722
8156
|
if (!s) return null;
|
|
@@ -8731,7 +8165,7 @@ async function createSchedule(orchestratorSessionId, input) {
|
|
|
8731
8165
|
const data = await readOrch(orchestratorSessionId);
|
|
8732
8166
|
if (!data) throw new Error("orchestrator session not found");
|
|
8733
8167
|
const row = {
|
|
8734
|
-
id: `sch_${
|
|
8168
|
+
id: `sch_${nanoid4(10)}`,
|
|
8735
8169
|
name: input.name,
|
|
8736
8170
|
cron: input.cron,
|
|
8737
8171
|
prompt: input.prompt,
|
|
@@ -8768,7 +8202,7 @@ var init_schedules_store = __esm({
|
|
|
8768
8202
|
|
|
8769
8203
|
// src/orchestrator/webhooks-store.ts
|
|
8770
8204
|
import { randomBytes } from "crypto";
|
|
8771
|
-
import { nanoid as
|
|
8205
|
+
import { nanoid as nanoid5 } from "nanoid";
|
|
8772
8206
|
function newToken() {
|
|
8773
8207
|
return randomBytes(24).toString("base64url");
|
|
8774
8208
|
}
|
|
@@ -8785,7 +8219,7 @@ async function createWebhook(orchestratorSessionId, input) {
|
|
|
8785
8219
|
const data = await readOrch2(orchestratorSessionId);
|
|
8786
8220
|
if (!data) throw new Error("orchestrator session not found");
|
|
8787
8221
|
const row = {
|
|
8788
|
-
id: `whk_${
|
|
8222
|
+
id: `whk_${nanoid5(10)}`,
|
|
8789
8223
|
name: input.name,
|
|
8790
8224
|
token: newToken(),
|
|
8791
8225
|
wake: input.wake ?? "now",
|
|
@@ -8841,8 +8275,8 @@ var init_webhooks_store = __esm({
|
|
|
8841
8275
|
});
|
|
8842
8276
|
|
|
8843
8277
|
// src/tools/orchestrator-actions.ts
|
|
8844
|
-
import { tool as
|
|
8845
|
-
import { z as
|
|
8278
|
+
import { tool as tool13 } from "ai";
|
|
8279
|
+
import { z as z14 } from "zod";
|
|
8846
8280
|
async function api2(baseUrl, path, init = {}) {
|
|
8847
8281
|
const res = await fetch(`${baseUrl}${path}`, {
|
|
8848
8282
|
method: init.method || "GET",
|
|
@@ -8868,7 +8302,7 @@ function previewMessageContent(content) {
|
|
|
8868
8302
|
return "";
|
|
8869
8303
|
}
|
|
8870
8304
|
function buildAgentTool(opts) {
|
|
8871
|
-
return
|
|
8305
|
+
return tool13({
|
|
8872
8306
|
description: "Manage worker agents. Actions: list (browse with optional status filter), get (deep dive: status, todos, pending question, recent messages, final result; required: id), spawn (start a new worker; required: name, goal), message (post a message to a running worker; force=true to soft-interrupt; required: id, text), answer_question (resolve a worker's ask_question_to_user prompt; required: id, questionId, answer), stop (hard-cancel a running worker; required: id).",
|
|
8873
8307
|
inputSchema: agentInputSchema,
|
|
8874
8308
|
execute: async (input) => {
|
|
@@ -8971,7 +8405,7 @@ function buildAgentTool(opts) {
|
|
|
8971
8405
|
});
|
|
8972
8406
|
}
|
|
8973
8407
|
function buildMessengerTool() {
|
|
8974
|
-
return
|
|
8408
|
+
return tool13({
|
|
8975
8409
|
description: "Send messages on configured external channels. Actions: list_channels (no args; returns which integrations are configured), post (required: channel, to, text). Use this to ping the user on Slack when a worker finishes, when a schedule fires, etc.",
|
|
8976
8410
|
inputSchema: messengerInputSchema,
|
|
8977
8411
|
execute: async (input) => {
|
|
@@ -8993,7 +8427,7 @@ function buildMessengerTool() {
|
|
|
8993
8427
|
});
|
|
8994
8428
|
}
|
|
8995
8429
|
function buildScheduleTool(opts) {
|
|
8996
|
-
return
|
|
8430
|
+
return tool13({
|
|
8997
8431
|
description: "Recurring prompts. Actions: create (required: name, cron, prompt), list, update (required: id; any of name/cron/prompt/enabled/replyChannel), delete (required: id), pause (required: id), resume (required: id). Cron is standard 5-field syntax.",
|
|
8998
8432
|
inputSchema: scheduleInputSchema,
|
|
8999
8433
|
execute: async (input) => {
|
|
@@ -9036,7 +8470,7 @@ function buildWebhookUrl(opts, token) {
|
|
|
9036
8470
|
return `${base}${webhookPrefix2}/inbox/${token}`;
|
|
9037
8471
|
}
|
|
9038
8472
|
function buildWebhookTool(opts) {
|
|
9039
|
-
return
|
|
8473
|
+
return tool13({
|
|
9040
8474
|
description: "Custom token-protected inbound URLs. Anyone POSTing JSON to the URL pings the orchestrator. Actions: create (required: name), list, update (required: id; optional: name, wake, template, rotateToken), delete (required: id). Use these to wire up GitHub, IFTTT, n8n, etc.",
|
|
9041
8475
|
inputSchema: webhookInputSchema,
|
|
9042
8476
|
execute: async (input) => {
|
|
@@ -9085,66 +8519,66 @@ var init_orchestrator_actions = __esm({
|
|
|
9085
8519
|
init_schedules_store();
|
|
9086
8520
|
init_config();
|
|
9087
8521
|
init_webhooks_store();
|
|
9088
|
-
AGENT_STATUS_ENUM =
|
|
9089
|
-
agentInputSchema =
|
|
9090
|
-
action:
|
|
8522
|
+
AGENT_STATUS_ENUM = z14.enum(["running", "needs_attention", "completed", "failed", "idle"]);
|
|
8523
|
+
agentInputSchema = z14.object({
|
|
8524
|
+
action: z14.enum(["list", "get", "spawn", "message", "answer_question", "stop"]).describe("Which agent operation to perform."),
|
|
9091
8525
|
// list
|
|
9092
8526
|
status: AGENT_STATUS_ENUM.optional().describe("list only: filter to one status."),
|
|
9093
|
-
limit:
|
|
8527
|
+
limit: z14.number().int().min(1).max(100).optional().describe("list only: max rows."),
|
|
9094
8528
|
// get / message / answer_question / stop
|
|
9095
|
-
id:
|
|
9096
|
-
recentMessages:
|
|
8529
|
+
id: z14.string().optional().describe("get | message | answer_question | stop: the agent (session) id."),
|
|
8530
|
+
recentMessages: z14.number().int().min(0).max(50).optional().describe("get only: how many recent messages to include."),
|
|
9097
8531
|
// spawn
|
|
9098
|
-
name:
|
|
9099
|
-
goal:
|
|
9100
|
-
outputSchema:
|
|
8532
|
+
name: z14.string().optional().describe("spawn only: short human-readable label."),
|
|
8533
|
+
goal: z14.string().optional().describe("spawn only: the worker's self-contained instruction."),
|
|
8534
|
+
outputSchema: z14.record(z14.string(), z14.unknown()).optional().describe(
|
|
9101
8535
|
'spawn only: JSON Schema for the worker result. Defaults to {type:"object", properties:{summary:{type:"string"}}, required:["summary"]}.'
|
|
9102
8536
|
),
|
|
9103
|
-
model:
|
|
9104
|
-
workingDirectory:
|
|
9105
|
-
maxIterations:
|
|
8537
|
+
model: z14.string().optional().describe("spawn only: model override."),
|
|
8538
|
+
workingDirectory: z14.string().optional().describe("spawn only: working directory override."),
|
|
8539
|
+
maxIterations: z14.number().int().min(1).max(500).optional().describe("spawn only."),
|
|
9106
8540
|
// message
|
|
9107
|
-
text:
|
|
9108
|
-
force:
|
|
8541
|
+
text: z14.string().optional().describe("message only: the text to deliver to the worker."),
|
|
8542
|
+
force: z14.boolean().optional().describe("message only: soft-interrupt the current step."),
|
|
9109
8543
|
// answer_question
|
|
9110
|
-
questionId:
|
|
9111
|
-
answer:
|
|
8544
|
+
questionId: z14.string().optional().describe("answer_question only: pending question id (e.g. q_abc123)."),
|
|
8545
|
+
answer: z14.string().optional().describe("answer_question only: your answer.")
|
|
9112
8546
|
});
|
|
9113
|
-
messengerInputSchema =
|
|
9114
|
-
action:
|
|
8547
|
+
messengerInputSchema = z14.object({
|
|
8548
|
+
action: z14.enum(["list_channels", "post"]),
|
|
9115
8549
|
// post
|
|
9116
|
-
channel:
|
|
9117
|
-
to:
|
|
9118
|
-
text:
|
|
9119
|
-
threadTs:
|
|
9120
|
-
subject:
|
|
8550
|
+
channel: z14.string().optional().describe('post only: channel id (e.g. "slack").'),
|
|
8551
|
+
to: z14.string().optional().describe('post only: destination. Slack: channel id (C0123), user id (U0123), or "#channel-name".'),
|
|
8552
|
+
text: z14.string().optional().describe("post only: message body."),
|
|
8553
|
+
threadTs: z14.string().optional().describe("post + slack: reply in this thread."),
|
|
8554
|
+
subject: z14.string().optional().describe("post + email: subject (future).")
|
|
9121
8555
|
});
|
|
9122
|
-
scheduleInputSchema =
|
|
9123
|
-
action:
|
|
8556
|
+
scheduleInputSchema = z14.object({
|
|
8557
|
+
action: z14.enum(["create", "list", "update", "delete", "pause", "resume"]),
|
|
9124
8558
|
// create / update
|
|
9125
|
-
name:
|
|
9126
|
-
cron:
|
|
9127
|
-
prompt:
|
|
9128
|
-
replyChannel:
|
|
8559
|
+
name: z14.string().optional().describe("create | update"),
|
|
8560
|
+
cron: z14.string().optional().describe('create | update: 5-field cron (e.g. "0 9 * * 1-5" = weekdays at 9am).'),
|
|
8561
|
+
prompt: z14.string().optional().describe("create | update: the prompt injected when the schedule fires."),
|
|
8562
|
+
replyChannel: z14.string().optional().describe("create | update: default channel id for orchestrator replies."),
|
|
9129
8563
|
// update / delete / pause / resume
|
|
9130
|
-
id:
|
|
9131
|
-
enabled:
|
|
8564
|
+
id: z14.string().optional().describe("update | delete | pause | resume: schedule id."),
|
|
8565
|
+
enabled: z14.boolean().optional().describe("update only.")
|
|
9132
8566
|
});
|
|
9133
|
-
webhookInputSchema =
|
|
9134
|
-
action:
|
|
9135
|
-
name:
|
|
9136
|
-
wake:
|
|
9137
|
-
template:
|
|
9138
|
-
id:
|
|
9139
|
-
rotateToken:
|
|
8567
|
+
webhookInputSchema = z14.object({
|
|
8568
|
+
action: z14.enum(["create", "list", "update", "delete"]),
|
|
8569
|
+
name: z14.string().optional().describe("create | update."),
|
|
8570
|
+
wake: z14.enum(["now", "next"]).optional().describe("create | update: now = wake orchestrator immediately; next = add as context."),
|
|
8571
|
+
template: z14.string().optional().describe("create | update: mustache-style template ({{path.to.field}}). Defaults to pretty-printed JSON."),
|
|
8572
|
+
id: z14.string().optional().describe("update | delete: webhook id."),
|
|
8573
|
+
rotateToken: z14.boolean().optional().describe("update only: regenerate the URL token.")
|
|
9140
8574
|
});
|
|
9141
8575
|
}
|
|
9142
8576
|
});
|
|
9143
8577
|
|
|
9144
8578
|
// src/integrations/mcp/store.ts
|
|
9145
|
-
import { nanoid as
|
|
9146
|
-
import { existsSync as
|
|
9147
|
-
import { resolve as resolve10, join as
|
|
8579
|
+
import { nanoid as nanoid6 } from "nanoid";
|
|
8580
|
+
import { existsSync as existsSync16, readFileSync as readFileSync7 } from "fs";
|
|
8581
|
+
import { resolve as resolve10, join as join9 } from "path";
|
|
9148
8582
|
function readServers() {
|
|
9149
8583
|
try {
|
|
9150
8584
|
const cfg = getConfig();
|
|
@@ -9156,12 +8590,12 @@ function readServers() {
|
|
|
9156
8590
|
function refreshMcpServersFromDisk() {
|
|
9157
8591
|
const candidates = [
|
|
9158
8592
|
resolve10(process.cwd(), "sparkecoder.config.json"),
|
|
9159
|
-
|
|
8593
|
+
join9(ensureAppDataDirectory(), "sparkecoder.config.json")
|
|
9160
8594
|
];
|
|
9161
8595
|
for (const path of candidates) {
|
|
9162
|
-
if (!
|
|
8596
|
+
if (!existsSync16(path)) continue;
|
|
9163
8597
|
try {
|
|
9164
|
-
const raw = JSON.parse(
|
|
8598
|
+
const raw = JSON.parse(readFileSync7(path, "utf-8"));
|
|
9165
8599
|
const servers2 = Array.isArray(raw?.mcp?.servers) ? raw.mcp.servers : [];
|
|
9166
8600
|
setMcpServers(servers2);
|
|
9167
8601
|
return servers2;
|
|
@@ -9180,7 +8614,7 @@ function createMcpServer(input) {
|
|
|
9180
8614
|
const all = readServers();
|
|
9181
8615
|
validateInput(input);
|
|
9182
8616
|
const row = {
|
|
9183
|
-
id: `mcp_${
|
|
8617
|
+
id: `mcp_${nanoid6(10)}`,
|
|
9184
8618
|
name: sanitizeName(input.name),
|
|
9185
8619
|
transport: input.transport,
|
|
9186
8620
|
url: input.url,
|
|
@@ -9771,15 +9205,15 @@ var recorder_exports = {};
|
|
|
9771
9205
|
__export(recorder_exports, {
|
|
9772
9206
|
FrameRecorder: () => FrameRecorder
|
|
9773
9207
|
});
|
|
9774
|
-
import { exec as
|
|
9775
|
-
import { promisify as
|
|
9208
|
+
import { exec as exec5 } from "child_process";
|
|
9209
|
+
import { promisify as promisify5 } from "util";
|
|
9776
9210
|
import { writeFile as writeFile5, mkdir as mkdir4, readFile as readFile11, unlink as unlink2, readdir as readdir5, rm } from "fs/promises";
|
|
9777
|
-
import { join as
|
|
9778
|
-
import { tmpdir
|
|
9779
|
-
import { nanoid as
|
|
9211
|
+
import { join as join10 } from "path";
|
|
9212
|
+
import { tmpdir } from "os";
|
|
9213
|
+
import { nanoid as nanoid7 } from "nanoid";
|
|
9780
9214
|
async function checkFfmpeg() {
|
|
9781
9215
|
try {
|
|
9782
|
-
await
|
|
9216
|
+
await execAsync5("ffmpeg -version", { timeout: 5e3 });
|
|
9783
9217
|
return true;
|
|
9784
9218
|
} catch {
|
|
9785
9219
|
return false;
|
|
@@ -9791,11 +9225,11 @@ async function cleanup(dir) {
|
|
|
9791
9225
|
} catch {
|
|
9792
9226
|
}
|
|
9793
9227
|
}
|
|
9794
|
-
var
|
|
9228
|
+
var execAsync5, FrameRecorder;
|
|
9795
9229
|
var init_recorder = __esm({
|
|
9796
9230
|
"src/browser/recorder.ts"() {
|
|
9797
9231
|
"use strict";
|
|
9798
|
-
|
|
9232
|
+
execAsync5 = promisify5(exec5);
|
|
9799
9233
|
FrameRecorder = class {
|
|
9800
9234
|
frames = [];
|
|
9801
9235
|
startTime = null;
|
|
@@ -9831,21 +9265,21 @@ var init_recorder = __esm({
|
|
|
9831
9265
|
*/
|
|
9832
9266
|
async encode() {
|
|
9833
9267
|
if (this.frames.length === 0) return null;
|
|
9834
|
-
const workDir =
|
|
9268
|
+
const workDir = join10(tmpdir(), `sparkecoder-recording-${nanoid7(8)}`);
|
|
9835
9269
|
await mkdir4(workDir, { recursive: true });
|
|
9836
9270
|
try {
|
|
9837
9271
|
for (let i = 0; i < this.frames.length; i++) {
|
|
9838
|
-
const framePath =
|
|
9272
|
+
const framePath = join10(workDir, `frame_${String(i).padStart(6, "0")}.jpg`);
|
|
9839
9273
|
await writeFile5(framePath, this.frames[i].data);
|
|
9840
9274
|
}
|
|
9841
9275
|
const duration = (this.frames[this.frames.length - 1].timestamp - this.frames[0].timestamp) / 1e3;
|
|
9842
9276
|
const fps = duration > 0 ? Math.round(this.frames.length / duration) : 10;
|
|
9843
9277
|
const clampedFps = Math.max(1, Math.min(fps, 30));
|
|
9844
|
-
const outputPath =
|
|
9278
|
+
const outputPath = join10(workDir, `recording_${this.sessionId}.mp4`);
|
|
9845
9279
|
const hasFfmpeg = await checkFfmpeg();
|
|
9846
9280
|
if (hasFfmpeg) {
|
|
9847
|
-
await
|
|
9848
|
-
`ffmpeg -y -framerate ${clampedFps} -i "${
|
|
9281
|
+
await execAsync5(
|
|
9282
|
+
`ffmpeg -y -framerate ${clampedFps} -i "${join10(workDir, "frame_%06d.jpg")}" -c:v libx264 -pix_fmt yuv420p -preset fast -crf 23 "${outputPath}"`,
|
|
9849
9283
|
{ timeout: 12e4 }
|
|
9850
9284
|
);
|
|
9851
9285
|
} else {
|
|
@@ -9857,7 +9291,7 @@ var init_recorder = __esm({
|
|
|
9857
9291
|
const files = await readdir5(workDir);
|
|
9858
9292
|
for (const f of files) {
|
|
9859
9293
|
if (f.startsWith("frame_")) {
|
|
9860
|
-
await unlink2(
|
|
9294
|
+
await unlink2(join10(workDir, f)).catch(() => {
|
|
9861
9295
|
});
|
|
9862
9296
|
}
|
|
9863
9297
|
}
|
|
@@ -9882,11 +9316,11 @@ var init_recorder = __esm({
|
|
|
9882
9316
|
import {
|
|
9883
9317
|
streamText as streamText2,
|
|
9884
9318
|
generateText as generateText3,
|
|
9885
|
-
tool as
|
|
9319
|
+
tool as tool14,
|
|
9886
9320
|
stepCountIs as stepCountIs2
|
|
9887
9321
|
} from "ai";
|
|
9888
|
-
import { z as
|
|
9889
|
-
import { nanoid as
|
|
9322
|
+
import { z as z15 } from "zod";
|
|
9323
|
+
import { nanoid as nanoid8 } from "nanoid";
|
|
9890
9324
|
function anySignal(signals) {
|
|
9891
9325
|
const ctrl = new AbortController();
|
|
9892
9326
|
for (const s of signals) {
|
|
@@ -9955,14 +9389,10 @@ var init_agent = __esm({
|
|
|
9955
9389
|
*/
|
|
9956
9390
|
async createToolsWithCallbacks(options) {
|
|
9957
9391
|
const config = getConfig();
|
|
9958
|
-
const sessionConfig = this.session.config || {};
|
|
9959
9392
|
const tools = await createTools({
|
|
9960
9393
|
sessionId: this.session.id,
|
|
9961
9394
|
workingDirectory: this.session.workingDirectory,
|
|
9962
9395
|
skillsDirectories: config.resolvedSkillsDirectories,
|
|
9963
|
-
enableComputerUse: sessionConfig.computerUseEnabled === true,
|
|
9964
|
-
computerUseDisplayWidth: sessionConfig.computerUseDisplayWidth,
|
|
9965
|
-
computerUseDisplayHeight: sessionConfig.computerUseDisplayHeight,
|
|
9966
9396
|
onBashProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "bash", data: progress }) : void 0,
|
|
9967
9397
|
onWriteFileProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "write_file", data: progress }) : void 0,
|
|
9968
9398
|
onSearchProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "explore_agent", data: progress }) : void 0
|
|
@@ -10012,14 +9442,10 @@ var init_agent = __esm({
|
|
|
10012
9442
|
keepRecentMessages: config.context?.keepRecentMessages || 10,
|
|
10013
9443
|
autoSummarize: config.context?.autoSummarize ?? true
|
|
10014
9444
|
});
|
|
10015
|
-
const sessionConfig = session.config || {};
|
|
10016
9445
|
const tools = await createTools({
|
|
10017
9446
|
sessionId: session.id,
|
|
10018
9447
|
workingDirectory: session.workingDirectory,
|
|
10019
|
-
skillsDirectories: config.resolvedSkillsDirectories
|
|
10020
|
-
enableComputerUse: sessionConfig.computerUseEnabled === true,
|
|
10021
|
-
computerUseDisplayWidth: sessionConfig.computerUseDisplayWidth,
|
|
10022
|
-
computerUseDisplayHeight: sessionConfig.computerUseDisplayHeight
|
|
9448
|
+
skillsDirectories: config.resolvedSkillsDirectories
|
|
10023
9449
|
});
|
|
10024
9450
|
if (session.config?.role === "orchestrator") {
|
|
10025
9451
|
const baseUrl = `http://127.0.0.1:${config.server?.port ?? 3141}`;
|
|
@@ -10257,14 +9683,10 @@ ${personality.trim()}`;
|
|
|
10257
9683
|
});
|
|
10258
9684
|
}
|
|
10259
9685
|
};
|
|
10260
|
-
const taskSessionConfig = this.session.config || {};
|
|
10261
9686
|
const taskTools = await createTools({
|
|
10262
9687
|
sessionId: this.session.id,
|
|
10263
9688
|
workingDirectory: this.session.workingDirectory,
|
|
10264
9689
|
skillsDirectories: config.resolvedSkillsDirectories,
|
|
10265
|
-
enableComputerUse: taskSessionConfig.computerUseEnabled === true,
|
|
10266
|
-
computerUseDisplayWidth: taskSessionConfig.computerUseDisplayWidth,
|
|
10267
|
-
computerUseDisplayHeight: taskSessionConfig.computerUseDisplayHeight,
|
|
10268
9690
|
onBashProgress: bashProgressHandler,
|
|
10269
9691
|
onWriteFileProgress: (progress) => {
|
|
10270
9692
|
options.onToolProgress?.({ toolName: "write_file", data: progress });
|
|
@@ -10612,11 +10034,11 @@ ${p.text}` : p.text;
|
|
|
10612
10034
|
const { isRemoteConfigured: isRemoteConfigured2, storageQueries: storageQueries2 } = await Promise.resolve().then(() => (init_remote(), remote_exports));
|
|
10613
10035
|
if (!isRemoteConfigured2()) return [];
|
|
10614
10036
|
const { readFile: readFile12 } = await import("fs/promises");
|
|
10615
|
-
const { join:
|
|
10037
|
+
const { join: join16, basename: basename6 } = await import("path");
|
|
10616
10038
|
const urls = [];
|
|
10617
10039
|
for (const filePath of filePaths) {
|
|
10618
10040
|
try {
|
|
10619
|
-
const fullPath = filePath.startsWith("/") ? filePath :
|
|
10041
|
+
const fullPath = filePath.startsWith("/") ? filePath : join16(this.session.workingDirectory, filePath);
|
|
10620
10042
|
const fileName = basename6(fullPath);
|
|
10621
10043
|
const ext = fileName.split(".").pop()?.toLowerCase() || "";
|
|
10622
10044
|
const mimeMap = {
|
|
@@ -10674,11 +10096,11 @@ ${p.text}` : p.text;
|
|
|
10674
10096
|
wrappedTools[name] = originalTool;
|
|
10675
10097
|
continue;
|
|
10676
10098
|
}
|
|
10677
|
-
wrappedTools[name] =
|
|
10099
|
+
wrappedTools[name] = tool14({
|
|
10678
10100
|
description: originalTool.description || "",
|
|
10679
|
-
inputSchema: originalTool.inputSchema ||
|
|
10101
|
+
inputSchema: originalTool.inputSchema || z15.object({}),
|
|
10680
10102
|
execute: async (input, toolOptions) => {
|
|
10681
|
-
const toolCallId = toolOptions.toolCallId ||
|
|
10103
|
+
const toolCallId = toolOptions.toolCallId || nanoid8();
|
|
10682
10104
|
const execution = toolExecutionQueries.create({
|
|
10683
10105
|
sessionId: this.session.id,
|
|
10684
10106
|
toolName: name,
|
|
@@ -10696,10 +10118,10 @@ ${p.text}` : p.text;
|
|
|
10696
10118
|
const resolverData = approvalResolvers.get(toolCallId);
|
|
10697
10119
|
approvalResolvers.delete(toolCallId);
|
|
10698
10120
|
this.pendingApprovals.delete(toolCallId);
|
|
10699
|
-
const
|
|
10121
|
+
const exec7 = await execution;
|
|
10700
10122
|
if (!approved) {
|
|
10701
10123
|
const reason = resolverData?.reason || "User rejected the tool execution";
|
|
10702
|
-
await toolExecutionQueries.reject(
|
|
10124
|
+
await toolExecutionQueries.reject(exec7.id);
|
|
10703
10125
|
await sessionQueries.updateStatus(this.session.id, "active");
|
|
10704
10126
|
return {
|
|
10705
10127
|
status: "rejected",
|
|
@@ -10709,14 +10131,14 @@ ${p.text}` : p.text;
|
|
|
10709
10131
|
message: `Tool "${name}" was rejected by the user. Reason: ${reason}`
|
|
10710
10132
|
};
|
|
10711
10133
|
}
|
|
10712
|
-
await toolExecutionQueries.approve(
|
|
10134
|
+
await toolExecutionQueries.approve(exec7.id);
|
|
10713
10135
|
await sessionQueries.updateStatus(this.session.id, "active");
|
|
10714
10136
|
try {
|
|
10715
10137
|
const result = await originalTool.execute(input, toolOptions);
|
|
10716
|
-
await toolExecutionQueries.complete(
|
|
10138
|
+
await toolExecutionQueries.complete(exec7.id, result);
|
|
10717
10139
|
return result;
|
|
10718
10140
|
} catch (error) {
|
|
10719
|
-
await toolExecutionQueries.complete(
|
|
10141
|
+
await toolExecutionQueries.complete(exec7.id, null, error.message);
|
|
10720
10142
|
throw error;
|
|
10721
10143
|
}
|
|
10722
10144
|
}
|
|
@@ -10821,19 +10243,19 @@ var init_session_lock = __esm({
|
|
|
10821
10243
|
});
|
|
10822
10244
|
|
|
10823
10245
|
// src/orchestrator/webhook-events.ts
|
|
10824
|
-
import { existsSync as
|
|
10825
|
-
import { dirname as dirname6, join as
|
|
10826
|
-
import { nanoid as
|
|
10246
|
+
import { existsSync as existsSync17, readFileSync as readFileSync8, appendFileSync as appendFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync6 } from "fs";
|
|
10247
|
+
import { dirname as dirname6, join as join11 } from "path";
|
|
10248
|
+
import { nanoid as nanoid9 } from "nanoid";
|
|
10827
10249
|
function logFilePath() {
|
|
10828
|
-
return
|
|
10250
|
+
return join11(getAppDataDirectory(), "webhook-events.jsonl");
|
|
10829
10251
|
}
|
|
10830
10252
|
function ensureLoaded() {
|
|
10831
10253
|
if (cache !== null) return cache;
|
|
10832
10254
|
cache = [];
|
|
10833
10255
|
try {
|
|
10834
10256
|
const p = logFilePath();
|
|
10835
|
-
if (!
|
|
10836
|
-
const lines =
|
|
10257
|
+
if (!existsSync17(p)) return cache;
|
|
10258
|
+
const lines = readFileSync8(p, "utf-8").split("\n").filter(Boolean);
|
|
10837
10259
|
for (const line of lines) {
|
|
10838
10260
|
try {
|
|
10839
10261
|
cache.push(JSON.parse(line));
|
|
@@ -10857,14 +10279,14 @@ function appendEvent(ev) {
|
|
|
10857
10279
|
if (list.length > MAX_EVENTS) list.shift();
|
|
10858
10280
|
try {
|
|
10859
10281
|
const p = logFilePath();
|
|
10860
|
-
|
|
10282
|
+
mkdirSync6(dirname6(p), { recursive: true });
|
|
10861
10283
|
appendFileSync3(p, JSON.stringify(ev) + "\n");
|
|
10862
10284
|
} catch {
|
|
10863
10285
|
}
|
|
10864
10286
|
}
|
|
10865
10287
|
function recordEvent(ev) {
|
|
10866
10288
|
const full = {
|
|
10867
|
-
id: ev.id ??
|
|
10289
|
+
id: ev.id ?? nanoid9(),
|
|
10868
10290
|
ts: ev.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
10869
10291
|
source: ev.source,
|
|
10870
10292
|
status: ev.status,
|
|
@@ -10888,7 +10310,7 @@ function updateEvent(id, patch) {
|
|
|
10888
10310
|
list[i] = { ...list[i], ...patch };
|
|
10889
10311
|
try {
|
|
10890
10312
|
const p = logFilePath();
|
|
10891
|
-
|
|
10313
|
+
mkdirSync6(dirname6(p), { recursive: true });
|
|
10892
10314
|
writeFileSync3(p, list.map((e) => JSON.stringify(e)).join("\n") + "\n");
|
|
10893
10315
|
} catch {
|
|
10894
10316
|
}
|
|
@@ -11192,8 +10614,8 @@ import { Hono as Hono9 } from "hono";
|
|
|
11192
10614
|
import { serve } from "@hono/node-server";
|
|
11193
10615
|
import { cors } from "hono/cors";
|
|
11194
10616
|
import { logger } from "hono/logger";
|
|
11195
|
-
import { existsSync as
|
|
11196
|
-
import { resolve as resolve11, dirname as dirname8, join as
|
|
10617
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync9, writeFileSync as writeFileSync6 } from "fs";
|
|
10618
|
+
import { resolve as resolve11, dirname as dirname8, join as join15 } from "path";
|
|
11197
10619
|
import { spawn as spawn2 } from "child_process";
|
|
11198
10620
|
import { createServer as createNetServer } from "net";
|
|
11199
10621
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -11206,11 +10628,11 @@ init_tmux();
|
|
|
11206
10628
|
init_checkpoints();
|
|
11207
10629
|
import { Hono } from "hono";
|
|
11208
10630
|
import { zValidator } from "@hono/zod-validator";
|
|
11209
|
-
import { z as
|
|
11210
|
-
import { existsSync as
|
|
10631
|
+
import { z as z16 } from "zod";
|
|
10632
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync7, writeFileSync as writeFileSync4, readdirSync as readdirSync3, statSync as statSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
11211
10633
|
import { readdir as readdir6 } from "fs/promises";
|
|
11212
|
-
import { join as
|
|
11213
|
-
import { nanoid as
|
|
10634
|
+
import { join as join12, basename as basename5, extname as extname8, relative as relative9 } from "path";
|
|
10635
|
+
import { nanoid as nanoid10 } from "nanoid";
|
|
11214
10636
|
|
|
11215
10637
|
// src/tasks/agent-status.ts
|
|
11216
10638
|
init_questions();
|
|
@@ -11268,22 +10690,22 @@ function cleanupPendingInputs() {
|
|
|
11268
10690
|
}
|
|
11269
10691
|
}
|
|
11270
10692
|
}
|
|
11271
|
-
var createSessionSchema =
|
|
11272
|
-
name:
|
|
11273
|
-
workingDirectory:
|
|
11274
|
-
model:
|
|
11275
|
-
toolApprovals:
|
|
11276
|
-
// Optional full session-config passthrough (
|
|
11277
|
-
config:
|
|
11278
|
-
role:
|
|
10693
|
+
var createSessionSchema = z16.object({
|
|
10694
|
+
name: z16.string().optional(),
|
|
10695
|
+
workingDirectory: z16.string().optional(),
|
|
10696
|
+
model: z16.string().optional(),
|
|
10697
|
+
toolApprovals: z16.record(z16.string(), z16.boolean()).optional(),
|
|
10698
|
+
// Optional full session-config passthrough (role, persona, etc.)
|
|
10699
|
+
config: z16.record(z16.string(), z16.unknown()).optional(),
|
|
10700
|
+
role: z16.enum(["orchestrator", "worker", "chat"]).optional()
|
|
11279
10701
|
});
|
|
11280
|
-
var paginationQuerySchema =
|
|
11281
|
-
limit:
|
|
11282
|
-
offset:
|
|
11283
|
-
role:
|
|
10702
|
+
var paginationQuerySchema = z16.object({
|
|
10703
|
+
limit: z16.string().optional(),
|
|
10704
|
+
offset: z16.string().optional(),
|
|
10705
|
+
role: z16.enum(["orchestrator", "worker", "chat", "all"]).optional()
|
|
11284
10706
|
});
|
|
11285
|
-
var messagesQuerySchema =
|
|
11286
|
-
limit:
|
|
10707
|
+
var messagesQuerySchema = z16.object({
|
|
10708
|
+
limit: z16.string().optional()
|
|
11287
10709
|
});
|
|
11288
10710
|
sessions2.get(
|
|
11289
10711
|
"/",
|
|
@@ -11352,15 +10774,11 @@ sessions2.post(
|
|
|
11352
10774
|
async (c) => {
|
|
11353
10775
|
const body = c.req.valid("json");
|
|
11354
10776
|
const config = getConfig();
|
|
11355
|
-
const cuDefault = process.env.SPARKECODER_COMPUTER_USE === "1";
|
|
11356
10777
|
const baseConfig = body.config || {};
|
|
11357
10778
|
const mergedConfig = {
|
|
11358
10779
|
...baseConfig,
|
|
11359
10780
|
...body.toolApprovals ? { toolApprovals: body.toolApprovals } : {},
|
|
11360
|
-
...body.role ? { role: body.role } : {}
|
|
11361
|
-
// Turn on computer use by default if the server was launched with --enable-computer-use,
|
|
11362
|
-
// unless the client explicitly provided a value.
|
|
11363
|
-
...cuDefault && baseConfig.computerUseEnabled === void 0 ? { computerUseEnabled: true } : {}
|
|
10781
|
+
...body.role ? { role: body.role } : {}
|
|
11364
10782
|
};
|
|
11365
10783
|
const agent = await Agent.create({
|
|
11366
10784
|
name: body.name,
|
|
@@ -11537,10 +10955,10 @@ sessions2.get("/:id/tools", async (c) => {
|
|
|
11537
10955
|
count: executions.length
|
|
11538
10956
|
});
|
|
11539
10957
|
});
|
|
11540
|
-
var updateSessionSchema =
|
|
11541
|
-
model:
|
|
11542
|
-
name:
|
|
11543
|
-
toolApprovals:
|
|
10958
|
+
var updateSessionSchema = z16.object({
|
|
10959
|
+
model: z16.string().optional(),
|
|
10960
|
+
name: z16.string().optional(),
|
|
10961
|
+
toolApprovals: z16.record(z16.string(), z16.boolean()).optional()
|
|
11544
10962
|
});
|
|
11545
10963
|
sessions2.patch(
|
|
11546
10964
|
"/:id",
|
|
@@ -11610,10 +11028,10 @@ sessions2.post("/:id/clear", async (c) => {
|
|
|
11610
11028
|
await agent.clearContext();
|
|
11611
11029
|
return c.json({ success: true, sessionId: id });
|
|
11612
11030
|
});
|
|
11613
|
-
var injectMessageSchema =
|
|
11614
|
-
text:
|
|
11615
|
-
source:
|
|
11616
|
-
force:
|
|
11031
|
+
var injectMessageSchema = z16.object({
|
|
11032
|
+
text: z16.string().min(1),
|
|
11033
|
+
source: z16.enum(["orchestrator", "user", "system"]).optional(),
|
|
11034
|
+
force: z16.boolean().optional()
|
|
11617
11035
|
});
|
|
11618
11036
|
sessions2.post(
|
|
11619
11037
|
"/:id/messages",
|
|
@@ -11627,8 +11045,8 @@ sessions2.post(
|
|
|
11627
11045
|
return c.json({ success: true, sessionId: id, queued: true, force });
|
|
11628
11046
|
}
|
|
11629
11047
|
);
|
|
11630
|
-
var pendingInputSchema =
|
|
11631
|
-
text:
|
|
11048
|
+
var pendingInputSchema = z16.object({
|
|
11049
|
+
text: z16.string()
|
|
11632
11050
|
});
|
|
11633
11051
|
sessions2.post(
|
|
11634
11052
|
"/:id/pending-input",
|
|
@@ -11659,13 +11077,13 @@ sessions2.get("/:id/pending-input", async (c) => {
|
|
|
11659
11077
|
createdAt: pending.createdAt.toISOString()
|
|
11660
11078
|
});
|
|
11661
11079
|
});
|
|
11662
|
-
var devtoolsContextSchema =
|
|
11663
|
-
url:
|
|
11664
|
-
path:
|
|
11665
|
-
pageName:
|
|
11666
|
-
screenWidth:
|
|
11667
|
-
screenHeight:
|
|
11668
|
-
devicePixelRatio:
|
|
11080
|
+
var devtoolsContextSchema = z16.object({
|
|
11081
|
+
url: z16.string(),
|
|
11082
|
+
path: z16.string(),
|
|
11083
|
+
pageName: z16.string().optional(),
|
|
11084
|
+
screenWidth: z16.number().optional(),
|
|
11085
|
+
screenHeight: z16.number().optional(),
|
|
11086
|
+
devicePixelRatio: z16.number().optional()
|
|
11669
11087
|
});
|
|
11670
11088
|
sessions2.post(
|
|
11671
11089
|
"/:id/devtools-context",
|
|
@@ -11851,12 +11269,12 @@ sessions2.get("/:id/diff/:filePath", async (c) => {
|
|
|
11851
11269
|
});
|
|
11852
11270
|
function getAttachmentsDir(sessionId) {
|
|
11853
11271
|
const appDataDir = getAppDataDirectory();
|
|
11854
|
-
return
|
|
11272
|
+
return join12(appDataDir, "attachments", sessionId);
|
|
11855
11273
|
}
|
|
11856
11274
|
function ensureAttachmentsDir(sessionId) {
|
|
11857
11275
|
const dir = getAttachmentsDir(sessionId);
|
|
11858
|
-
if (!
|
|
11859
|
-
|
|
11276
|
+
if (!existsSync18(dir)) {
|
|
11277
|
+
mkdirSync7(dir, { recursive: true });
|
|
11860
11278
|
}
|
|
11861
11279
|
return dir;
|
|
11862
11280
|
}
|
|
@@ -11867,12 +11285,12 @@ sessions2.get("/:id/attachments", async (c) => {
|
|
|
11867
11285
|
return c.json({ error: "Session not found" }, 404);
|
|
11868
11286
|
}
|
|
11869
11287
|
const dir = getAttachmentsDir(sessionId);
|
|
11870
|
-
if (!
|
|
11288
|
+
if (!existsSync18(dir)) {
|
|
11871
11289
|
return c.json({ sessionId, attachments: [], count: 0 });
|
|
11872
11290
|
}
|
|
11873
11291
|
const files = readdirSync3(dir);
|
|
11874
11292
|
const attachments = files.map((filename) => {
|
|
11875
|
-
const filePath =
|
|
11293
|
+
const filePath = join12(dir, filename);
|
|
11876
11294
|
const stats = statSync2(filePath);
|
|
11877
11295
|
return {
|
|
11878
11296
|
id: filename.split("_")[0],
|
|
@@ -11904,10 +11322,10 @@ sessions2.post("/:id/attachments", async (c) => {
|
|
|
11904
11322
|
return c.json({ error: "No file provided" }, 400);
|
|
11905
11323
|
}
|
|
11906
11324
|
const dir = ensureAttachmentsDir(sessionId);
|
|
11907
|
-
const id =
|
|
11325
|
+
const id = nanoid10(10);
|
|
11908
11326
|
const ext = extname8(file.name) || "";
|
|
11909
11327
|
const safeFilename = `${id}_${basename5(file.name).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
11910
|
-
const filePath =
|
|
11328
|
+
const filePath = join12(dir, safeFilename);
|
|
11911
11329
|
const arrayBuffer = await file.arrayBuffer();
|
|
11912
11330
|
writeFileSync4(filePath, Buffer.from(arrayBuffer));
|
|
11913
11331
|
return c.json({
|
|
@@ -11930,10 +11348,10 @@ sessions2.post("/:id/attachments", async (c) => {
|
|
|
11930
11348
|
return c.json({ error: "Missing filename or data" }, 400);
|
|
11931
11349
|
}
|
|
11932
11350
|
const dir = ensureAttachmentsDir(sessionId);
|
|
11933
|
-
const id =
|
|
11351
|
+
const id = nanoid10(10);
|
|
11934
11352
|
const ext = extname8(body.filename) || "";
|
|
11935
11353
|
const safeFilename = `${id}_${basename5(body.filename).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
11936
|
-
const filePath =
|
|
11354
|
+
const filePath = join12(dir, safeFilename);
|
|
11937
11355
|
let base64Data = body.data;
|
|
11938
11356
|
if (base64Data.includes(",")) {
|
|
11939
11357
|
base64Data = base64Data.split(",")[1];
|
|
@@ -11962,7 +11380,7 @@ sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
|
|
|
11962
11380
|
return c.json({ error: "Session not found" }, 404);
|
|
11963
11381
|
}
|
|
11964
11382
|
const dir = getAttachmentsDir(sessionId);
|
|
11965
|
-
if (!
|
|
11383
|
+
if (!existsSync18(dir)) {
|
|
11966
11384
|
return c.json({ error: "Attachment not found" }, 404);
|
|
11967
11385
|
}
|
|
11968
11386
|
const files = readdirSync3(dir);
|
|
@@ -11970,14 +11388,14 @@ sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
|
|
|
11970
11388
|
if (!file) {
|
|
11971
11389
|
return c.json({ error: "Attachment not found" }, 404);
|
|
11972
11390
|
}
|
|
11973
|
-
const filePath =
|
|
11974
|
-
|
|
11391
|
+
const filePath = join12(dir, file);
|
|
11392
|
+
unlinkSync2(filePath);
|
|
11975
11393
|
return c.json({ success: true, id: attachmentId });
|
|
11976
11394
|
});
|
|
11977
|
-
var filesQuerySchema =
|
|
11978
|
-
query:
|
|
11395
|
+
var filesQuerySchema = z16.object({
|
|
11396
|
+
query: z16.string().optional(),
|
|
11979
11397
|
// Filter query (e.g., "src/com" to match "src/components")
|
|
11980
|
-
limit:
|
|
11398
|
+
limit: z16.string().optional()
|
|
11981
11399
|
// Max results (default 50)
|
|
11982
11400
|
});
|
|
11983
11401
|
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
@@ -12053,7 +11471,7 @@ async function listWorkspaceFiles(baseDir, currentDir, query, limit, results = [
|
|
|
12053
11471
|
const entries = await readdir6(currentDir, { withFileTypes: true });
|
|
12054
11472
|
for (const entry2 of entries) {
|
|
12055
11473
|
if (results.length >= limit * 2) break;
|
|
12056
|
-
const fullPath =
|
|
11474
|
+
const fullPath = join12(currentDir, entry2.name);
|
|
12057
11475
|
const relativePath = relative9(baseDir, fullPath);
|
|
12058
11476
|
if (entry2.isDirectory() && IGNORED_DIRECTORIES.has(entry2.name)) {
|
|
12059
11477
|
continue;
|
|
@@ -12101,7 +11519,7 @@ sessions2.get(
|
|
|
12101
11519
|
return c.json({ error: "Session not found" }, 404);
|
|
12102
11520
|
}
|
|
12103
11521
|
const workingDirectory = session.workingDirectory;
|
|
12104
|
-
if (!
|
|
11522
|
+
if (!existsSync18(workingDirectory)) {
|
|
12105
11523
|
return c.json({
|
|
12106
11524
|
sessionId,
|
|
12107
11525
|
workingDirectory,
|
|
@@ -12214,9 +11632,9 @@ init_session_lock();
|
|
|
12214
11632
|
init_config();
|
|
12215
11633
|
import { Hono as Hono2 } from "hono";
|
|
12216
11634
|
import { zValidator as zValidator2 } from "@hono/zod-validator";
|
|
12217
|
-
import { z as
|
|
12218
|
-
import { existsSync as
|
|
12219
|
-
import { join as
|
|
11635
|
+
import { z as z17 } from "zod";
|
|
11636
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
11637
|
+
import { join as join13 } from "path";
|
|
12220
11638
|
|
|
12221
11639
|
// src/server/resumable-stream.ts
|
|
12222
11640
|
import { createResumableStreamContext } from "resumable-stream/generic";
|
|
@@ -12303,7 +11721,7 @@ var streamContext = createResumableStreamContext({
|
|
|
12303
11721
|
|
|
12304
11722
|
// src/server/routes/agents.ts
|
|
12305
11723
|
init_checkpoints();
|
|
12306
|
-
import { nanoid as
|
|
11724
|
+
import { nanoid as nanoid11 } from "nanoid";
|
|
12307
11725
|
init_stream_proxy();
|
|
12308
11726
|
init_recorder();
|
|
12309
11727
|
init_remote();
|
|
@@ -12395,40 +11813,40 @@ function enrichPromptWithDevtoolsContext(sessionId, prompt) {
|
|
|
12395
11813
|
${prompt}`;
|
|
12396
11814
|
}
|
|
12397
11815
|
var agents = new Hono2();
|
|
12398
|
-
var attachmentSchema =
|
|
12399
|
-
type:
|
|
12400
|
-
data:
|
|
11816
|
+
var attachmentSchema = z17.object({
|
|
11817
|
+
type: z17.enum(["image", "file"]),
|
|
11818
|
+
data: z17.string(),
|
|
12401
11819
|
// base64 data URL or raw base64
|
|
12402
|
-
mediaType:
|
|
12403
|
-
filename:
|
|
11820
|
+
mediaType: z17.string().optional(),
|
|
11821
|
+
filename: z17.string().optional()
|
|
12404
11822
|
});
|
|
12405
|
-
var runPromptSchema =
|
|
12406
|
-
prompt:
|
|
11823
|
+
var runPromptSchema = z17.object({
|
|
11824
|
+
prompt: z17.string(),
|
|
12407
11825
|
// Can be empty if attachments are provided
|
|
12408
|
-
attachments:
|
|
11826
|
+
attachments: z17.array(attachmentSchema).optional()
|
|
12409
11827
|
}).refine(
|
|
12410
11828
|
(data) => data.prompt.trim().length > 0 || data.attachments && data.attachments.length > 0,
|
|
12411
11829
|
{ message: "Either prompt or attachments must be provided" }
|
|
12412
11830
|
);
|
|
12413
|
-
var quickStartSchema =
|
|
12414
|
-
prompt:
|
|
12415
|
-
name:
|
|
12416
|
-
workingDirectory:
|
|
12417
|
-
model:
|
|
12418
|
-
toolApprovals:
|
|
12419
|
-
});
|
|
12420
|
-
var rejectSchema =
|
|
12421
|
-
reason:
|
|
11831
|
+
var quickStartSchema = z17.object({
|
|
11832
|
+
prompt: z17.string().min(1),
|
|
11833
|
+
name: z17.string().optional(),
|
|
11834
|
+
workingDirectory: z17.string().optional(),
|
|
11835
|
+
model: z17.string().optional(),
|
|
11836
|
+
toolApprovals: z17.record(z17.string(), z17.boolean()).optional()
|
|
11837
|
+
});
|
|
11838
|
+
var rejectSchema = z17.object({
|
|
11839
|
+
reason: z17.string().optional()
|
|
12422
11840
|
}).optional();
|
|
12423
11841
|
var streamAbortControllers = /* @__PURE__ */ new Map();
|
|
12424
11842
|
function getAttachmentsDirectory(sessionId) {
|
|
12425
11843
|
const appDataDir = getAppDataDirectory();
|
|
12426
|
-
return
|
|
11844
|
+
return join13(appDataDir, "attachments", sessionId);
|
|
12427
11845
|
}
|
|
12428
11846
|
async function saveAttachmentToDisk(sessionId, attachment, index) {
|
|
12429
11847
|
const attachmentsDir = getAttachmentsDirectory(sessionId);
|
|
12430
|
-
if (!
|
|
12431
|
-
|
|
11848
|
+
if (!existsSync19(attachmentsDir)) {
|
|
11849
|
+
mkdirSync8(attachmentsDir, { recursive: true });
|
|
12432
11850
|
}
|
|
12433
11851
|
let filename = attachment.filename;
|
|
12434
11852
|
if (!filename) {
|
|
@@ -12446,7 +11864,7 @@ async function saveAttachmentToDisk(sessionId, attachment, index) {
|
|
|
12446
11864
|
attachment.mediaType = resized.mediaType;
|
|
12447
11865
|
attachment.data = buffer.toString("base64");
|
|
12448
11866
|
}
|
|
12449
|
-
const filePath =
|
|
11867
|
+
const filePath = join13(attachmentsDir, filename);
|
|
12450
11868
|
writeFileSync5(filePath, buffer);
|
|
12451
11869
|
return filePath;
|
|
12452
11870
|
}
|
|
@@ -12883,7 +12301,7 @@ ${prompt}` });
|
|
|
12883
12301
|
});
|
|
12884
12302
|
} catch {
|
|
12885
12303
|
}
|
|
12886
|
-
const streamId = `stream_${id}_${
|
|
12304
|
+
const streamId = `stream_${id}_${nanoid11(10)}`;
|
|
12887
12305
|
console.log(`[STREAM] Creating stream ${streamId} for session ${id}`);
|
|
12888
12306
|
await activeStreamQueries.create(id, streamId);
|
|
12889
12307
|
const stream = await streamContext.resumableStream(
|
|
@@ -13088,7 +12506,7 @@ agents.post(
|
|
|
13088
12506
|
});
|
|
13089
12507
|
const session = agent.getSession();
|
|
13090
12508
|
const enrichedPrompt = enrichPromptWithDevtoolsContext(session.id, body.prompt);
|
|
13091
|
-
const streamId = `stream_${session.id}_${
|
|
12509
|
+
const streamId = `stream_${session.id}_${nanoid11(10)}`;
|
|
13092
12510
|
await createCheckpoint(session.id, session.workingDirectory, 0);
|
|
13093
12511
|
await activeStreamQueries.create(session.id, streamId);
|
|
13094
12512
|
const createQuickStreamProducer = () => {
|
|
@@ -13355,23 +12773,23 @@ agents.post(
|
|
|
13355
12773
|
});
|
|
13356
12774
|
}
|
|
13357
12775
|
);
|
|
13358
|
-
var browserInputSchema =
|
|
13359
|
-
type:
|
|
13360
|
-
eventType:
|
|
13361
|
-
x:
|
|
13362
|
-
y:
|
|
13363
|
-
button:
|
|
13364
|
-
clickCount:
|
|
13365
|
-
deltaX:
|
|
13366
|
-
deltaY:
|
|
13367
|
-
key:
|
|
13368
|
-
code:
|
|
13369
|
-
text:
|
|
13370
|
-
modifiers:
|
|
13371
|
-
touchPoints:
|
|
13372
|
-
x:
|
|
13373
|
-
y:
|
|
13374
|
-
id:
|
|
12776
|
+
var browserInputSchema = z17.object({
|
|
12777
|
+
type: z17.enum(["input_mouse", "input_keyboard", "input_touch"]),
|
|
12778
|
+
eventType: z17.string(),
|
|
12779
|
+
x: z17.number().optional(),
|
|
12780
|
+
y: z17.number().optional(),
|
|
12781
|
+
button: z17.string().optional(),
|
|
12782
|
+
clickCount: z17.number().optional(),
|
|
12783
|
+
deltaX: z17.number().optional(),
|
|
12784
|
+
deltaY: z17.number().optional(),
|
|
12785
|
+
key: z17.string().optional(),
|
|
12786
|
+
code: z17.string().optional(),
|
|
12787
|
+
text: z17.string().optional(),
|
|
12788
|
+
modifiers: z17.number().optional(),
|
|
12789
|
+
touchPoints: z17.array(z17.object({
|
|
12790
|
+
x: z17.number(),
|
|
12791
|
+
y: z17.number(),
|
|
12792
|
+
id: z17.number().optional()
|
|
13375
12793
|
})).optional()
|
|
13376
12794
|
});
|
|
13377
12795
|
agents.post(
|
|
@@ -13406,27 +12824,27 @@ agents.get("/:id/browser-stream", async (c) => {
|
|
|
13406
12824
|
init_config();
|
|
13407
12825
|
import { Hono as Hono3 } from "hono";
|
|
13408
12826
|
import { zValidator as zValidator3 } from "@hono/zod-validator";
|
|
13409
|
-
import { z as
|
|
13410
|
-
import { readFileSync as
|
|
12827
|
+
import { z as z18 } from "zod";
|
|
12828
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
13411
12829
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
13412
|
-
import { dirname as dirname7, join as
|
|
12830
|
+
import { dirname as dirname7, join as join14 } from "path";
|
|
13413
12831
|
var __filename = fileURLToPath3(import.meta.url);
|
|
13414
12832
|
var __dirname = dirname7(__filename);
|
|
13415
12833
|
var possiblePaths = [
|
|
13416
|
-
|
|
12834
|
+
join14(__dirname, "../package.json"),
|
|
13417
12835
|
// From dist/server -> dist/../package.json
|
|
13418
|
-
|
|
12836
|
+
join14(__dirname, "../../package.json"),
|
|
13419
12837
|
// From dist/server (if nested differently)
|
|
13420
|
-
|
|
12838
|
+
join14(__dirname, "../../../package.json"),
|
|
13421
12839
|
// From src/server/routes (development)
|
|
13422
|
-
|
|
12840
|
+
join14(process.cwd(), "package.json")
|
|
13423
12841
|
// From current working directory
|
|
13424
12842
|
];
|
|
13425
12843
|
var currentVersion = "0.0.0";
|
|
13426
12844
|
var packageName = "sparkecoder";
|
|
13427
12845
|
for (const packageJsonPath of possiblePaths) {
|
|
13428
12846
|
try {
|
|
13429
|
-
const packageJson = JSON.parse(
|
|
12847
|
+
const packageJson = JSON.parse(readFileSync9(packageJsonPath, "utf-8"));
|
|
13430
12848
|
if (packageJson.name === "sparkecoder") {
|
|
13431
12849
|
currentVersion = packageJson.version || "0.0.0";
|
|
13432
12850
|
packageName = packageJson.name || "sparkecoder";
|
|
@@ -13518,9 +12936,9 @@ health.get("/api-keys", async (c) => {
|
|
|
13518
12936
|
supportedProviders: SUPPORTED_PROVIDERS
|
|
13519
12937
|
});
|
|
13520
12938
|
});
|
|
13521
|
-
var setApiKeySchema =
|
|
13522
|
-
provider:
|
|
13523
|
-
apiKey:
|
|
12939
|
+
var setApiKeySchema = z18.object({
|
|
12940
|
+
provider: z18.string(),
|
|
12941
|
+
apiKey: z18.string().min(1)
|
|
13524
12942
|
});
|
|
13525
12943
|
health.post(
|
|
13526
12944
|
"/api-keys",
|
|
@@ -13561,12 +12979,12 @@ init_tmux();
|
|
|
13561
12979
|
init_db();
|
|
13562
12980
|
import { Hono as Hono4 } from "hono";
|
|
13563
12981
|
import { zValidator as zValidator4 } from "@hono/zod-validator";
|
|
13564
|
-
import { z as
|
|
12982
|
+
import { z as z19 } from "zod";
|
|
13565
12983
|
var terminals = new Hono4();
|
|
13566
|
-
var spawnSchema =
|
|
13567
|
-
command:
|
|
13568
|
-
cwd:
|
|
13569
|
-
name:
|
|
12984
|
+
var spawnSchema = z19.object({
|
|
12985
|
+
command: z19.string(),
|
|
12986
|
+
cwd: z19.string().optional(),
|
|
12987
|
+
name: z19.string().optional()
|
|
13570
12988
|
});
|
|
13571
12989
|
terminals.post(
|
|
13572
12990
|
"/:sessionId/terminals",
|
|
@@ -13647,8 +13065,8 @@ terminals.get("/:sessionId/terminals/:terminalId", async (c) => {
|
|
|
13647
13065
|
// We don't track exit codes in tmux mode
|
|
13648
13066
|
});
|
|
13649
13067
|
});
|
|
13650
|
-
var logsQuerySchema =
|
|
13651
|
-
tail:
|
|
13068
|
+
var logsQuerySchema = z19.object({
|
|
13069
|
+
tail: z19.string().optional().transform((v) => v ? parseInt(v, 10) : void 0)
|
|
13652
13070
|
});
|
|
13653
13071
|
terminals.get(
|
|
13654
13072
|
"/:sessionId/terminals/:terminalId/logs",
|
|
@@ -13672,8 +13090,8 @@ terminals.get(
|
|
|
13672
13090
|
});
|
|
13673
13091
|
}
|
|
13674
13092
|
);
|
|
13675
|
-
var killSchema =
|
|
13676
|
-
signal:
|
|
13093
|
+
var killSchema = z19.object({
|
|
13094
|
+
signal: z19.enum(["SIGTERM", "SIGKILL"]).optional()
|
|
13677
13095
|
});
|
|
13678
13096
|
terminals.post(
|
|
13679
13097
|
"/:sessionId/terminals/:terminalId/kill",
|
|
@@ -13687,8 +13105,8 @@ terminals.post(
|
|
|
13687
13105
|
return c.json({ success: true, message: "Terminal killed" });
|
|
13688
13106
|
}
|
|
13689
13107
|
);
|
|
13690
|
-
var writeSchema =
|
|
13691
|
-
input:
|
|
13108
|
+
var writeSchema = z19.object({
|
|
13109
|
+
input: z19.string()
|
|
13692
13110
|
});
|
|
13693
13111
|
terminals.post(
|
|
13694
13112
|
"/:sessionId/terminals/:terminalId/write",
|
|
@@ -13875,23 +13293,23 @@ init_agent();
|
|
|
13875
13293
|
init_config();
|
|
13876
13294
|
import { Hono as Hono5 } from "hono";
|
|
13877
13295
|
import { zValidator as zValidator5 } from "@hono/zod-validator";
|
|
13878
|
-
import { z as
|
|
13879
|
-
import { nanoid as
|
|
13296
|
+
import { z as z20 } from "zod";
|
|
13297
|
+
import { nanoid as nanoid12 } from "nanoid";
|
|
13880
13298
|
init_questions();
|
|
13881
13299
|
var tasks = new Hono5();
|
|
13882
13300
|
var taskAbortControllers = /* @__PURE__ */ new Map();
|
|
13883
|
-
var createTaskSchema =
|
|
13884
|
-
prompt:
|
|
13885
|
-
outputSchema:
|
|
13886
|
-
webhookUrl:
|
|
13887
|
-
model:
|
|
13888
|
-
workingDirectory:
|
|
13889
|
-
name:
|
|
13890
|
-
maxIterations:
|
|
13891
|
-
parentTaskId:
|
|
13301
|
+
var createTaskSchema = z20.object({
|
|
13302
|
+
prompt: z20.string().min(1),
|
|
13303
|
+
outputSchema: z20.record(z20.string(), z20.unknown()),
|
|
13304
|
+
webhookUrl: z20.string().url().optional(),
|
|
13305
|
+
model: z20.string().optional(),
|
|
13306
|
+
workingDirectory: z20.string().optional(),
|
|
13307
|
+
name: z20.string().optional(),
|
|
13308
|
+
maxIterations: z20.number().int().min(1).max(500).optional(),
|
|
13309
|
+
parentTaskId: z20.string().optional(),
|
|
13892
13310
|
/** When set, the spawning orchestrator's session id. Stamped on the
|
|
13893
13311
|
* worker's config so terminal events can wake the orchestrator. */
|
|
13894
|
-
orchestratorSessionId:
|
|
13312
|
+
orchestratorSessionId: z20.string().optional()
|
|
13895
13313
|
});
|
|
13896
13314
|
tasks.post(
|
|
13897
13315
|
"/",
|
|
@@ -13957,7 +13375,7 @@ tasks.post(
|
|
|
13957
13375
|
const taskId = agent.sessionId;
|
|
13958
13376
|
const abortController = new AbortController();
|
|
13959
13377
|
taskAbortControllers.set(taskId, abortController);
|
|
13960
|
-
const streamId = `stream_${taskId}_${
|
|
13378
|
+
const streamId = `stream_${taskId}_${nanoid12(10)}`;
|
|
13961
13379
|
await activeStreamQueries.create(taskId, streamId);
|
|
13962
13380
|
const taskStreamProducer = () => {
|
|
13963
13381
|
const { readable, writable } = new TransformStream();
|
|
@@ -14106,9 +13524,9 @@ tasks.post("/:id/cancel", async (c) => {
|
|
|
14106
13524
|
}
|
|
14107
13525
|
return c.json({ taskId: id, status: "failed", error: "Task cancelled by user" });
|
|
14108
13526
|
});
|
|
14109
|
-
var answerQuestionSchema =
|
|
14110
|
-
answer:
|
|
14111
|
-
answeredBy:
|
|
13527
|
+
var answerQuestionSchema = z20.object({
|
|
13528
|
+
answer: z20.string().min(1),
|
|
13529
|
+
answeredBy: z20.enum(["orchestrator", "user", "system"]).optional()
|
|
14112
13530
|
});
|
|
14113
13531
|
tasks.post(
|
|
14114
13532
|
"/:id/questions/:questionId/answer",
|
|
@@ -14392,7 +13810,7 @@ init_pool();
|
|
|
14392
13810
|
init_webhook_events();
|
|
14393
13811
|
import { Hono as Hono8 } from "hono";
|
|
14394
13812
|
import { zValidator as zValidator6 } from "@hono/zod-validator";
|
|
14395
|
-
import { z as
|
|
13813
|
+
import { z as z21 } from "zod";
|
|
14396
13814
|
var integrations = new Hono8();
|
|
14397
13815
|
var orchestratorRouter = new Hono8();
|
|
14398
13816
|
async function getOrchestratorId() {
|
|
@@ -14415,9 +13833,9 @@ orchestratorRouter.get("/", async (c) => {
|
|
|
14415
13833
|
});
|
|
14416
13834
|
orchestratorRouter.patch(
|
|
14417
13835
|
"/",
|
|
14418
|
-
zValidator6("json",
|
|
14419
|
-
name:
|
|
14420
|
-
personality:
|
|
13836
|
+
zValidator6("json", z21.object({
|
|
13837
|
+
name: z21.string().min(1).optional(),
|
|
13838
|
+
personality: z21.string().optional()
|
|
14421
13839
|
})),
|
|
14422
13840
|
async (c) => {
|
|
14423
13841
|
const id = await getOrchestratorId();
|
|
@@ -14493,15 +13911,15 @@ integrations.get("/", async (c) => {
|
|
|
14493
13911
|
}
|
|
14494
13912
|
});
|
|
14495
13913
|
});
|
|
14496
|
-
var slackConfigSchema =
|
|
14497
|
-
botToken:
|
|
14498
|
-
signingSecret:
|
|
14499
|
-
defaultOrchestratorName:
|
|
14500
|
-
allowedUsers:
|
|
14501
|
-
allowedChannels:
|
|
14502
|
-
allowDmsFromAnyone:
|
|
14503
|
-
deniedReplyEnabled:
|
|
14504
|
-
deniedReplyTemplate:
|
|
13914
|
+
var slackConfigSchema = z21.object({
|
|
13915
|
+
botToken: z21.string().optional(),
|
|
13916
|
+
signingSecret: z21.string().optional(),
|
|
13917
|
+
defaultOrchestratorName: z21.string().optional(),
|
|
13918
|
+
allowedUsers: z21.array(z21.string()).optional(),
|
|
13919
|
+
allowedChannels: z21.array(z21.string()).optional(),
|
|
13920
|
+
allowDmsFromAnyone: z21.boolean().optional(),
|
|
13921
|
+
deniedReplyEnabled: z21.boolean().optional(),
|
|
13922
|
+
deniedReplyTemplate: z21.string().optional()
|
|
14505
13923
|
});
|
|
14506
13924
|
integrations.post("/slack", zValidator6("json", slackConfigSchema), async (c) => {
|
|
14507
13925
|
const body = c.req.valid("json");
|
|
@@ -14530,11 +13948,11 @@ schedulesRouter.get("/", async (c) => {
|
|
|
14530
13948
|
});
|
|
14531
13949
|
schedulesRouter.post(
|
|
14532
13950
|
"/",
|
|
14533
|
-
zValidator6("json",
|
|
14534
|
-
name:
|
|
14535
|
-
cron:
|
|
14536
|
-
prompt:
|
|
14537
|
-
replyChannel:
|
|
13951
|
+
zValidator6("json", z21.object({
|
|
13952
|
+
name: z21.string().min(1),
|
|
13953
|
+
cron: z21.string().min(1),
|
|
13954
|
+
prompt: z21.string().min(1),
|
|
13955
|
+
replyChannel: z21.string().optional()
|
|
14538
13956
|
})),
|
|
14539
13957
|
async (c) => {
|
|
14540
13958
|
const orcId = await getOrchestratorId();
|
|
@@ -14545,12 +13963,12 @@ schedulesRouter.post(
|
|
|
14545
13963
|
);
|
|
14546
13964
|
schedulesRouter.patch(
|
|
14547
13965
|
"/:id",
|
|
14548
|
-
zValidator6("json",
|
|
14549
|
-
name:
|
|
14550
|
-
cron:
|
|
14551
|
-
prompt:
|
|
14552
|
-
enabled:
|
|
14553
|
-
replyChannel:
|
|
13966
|
+
zValidator6("json", z21.object({
|
|
13967
|
+
name: z21.string().optional(),
|
|
13968
|
+
cron: z21.string().optional(),
|
|
13969
|
+
prompt: z21.string().optional(),
|
|
13970
|
+
enabled: z21.boolean().optional(),
|
|
13971
|
+
replyChannel: z21.string().optional()
|
|
14554
13972
|
})),
|
|
14555
13973
|
async (c) => {
|
|
14556
13974
|
const orcId = await getOrchestratorId();
|
|
@@ -14578,10 +13996,10 @@ webhooksRouter.get("/", async (c) => {
|
|
|
14578
13996
|
});
|
|
14579
13997
|
webhooksRouter.post(
|
|
14580
13998
|
"/",
|
|
14581
|
-
zValidator6("json",
|
|
14582
|
-
name:
|
|
14583
|
-
wake:
|
|
14584
|
-
template:
|
|
13999
|
+
zValidator6("json", z21.object({
|
|
14000
|
+
name: z21.string().min(1),
|
|
14001
|
+
wake: z21.enum(["now", "next"]).optional(),
|
|
14002
|
+
template: z21.string().optional()
|
|
14585
14003
|
})),
|
|
14586
14004
|
async (c) => {
|
|
14587
14005
|
const orcId = await getOrchestratorId();
|
|
@@ -14592,11 +14010,11 @@ webhooksRouter.post(
|
|
|
14592
14010
|
);
|
|
14593
14011
|
webhooksRouter.patch(
|
|
14594
14012
|
"/:id",
|
|
14595
|
-
zValidator6("json",
|
|
14596
|
-
name:
|
|
14597
|
-
wake:
|
|
14598
|
-
template:
|
|
14599
|
-
rotateToken:
|
|
14013
|
+
zValidator6("json", z21.object({
|
|
14014
|
+
name: z21.string().optional(),
|
|
14015
|
+
wake: z21.enum(["now", "next"]).optional(),
|
|
14016
|
+
template: z21.string().optional(),
|
|
14017
|
+
rotateToken: z21.boolean().optional()
|
|
14600
14018
|
})),
|
|
14601
14019
|
async (c) => {
|
|
14602
14020
|
const orcId = await getOrchestratorId();
|
|
@@ -14613,22 +14031,22 @@ webhooksRouter.delete("/:id", async (c) => {
|
|
|
14613
14031
|
return c.json({ deleted: ok });
|
|
14614
14032
|
});
|
|
14615
14033
|
var mcpRouter = new Hono8();
|
|
14616
|
-
var mcpServerSchema =
|
|
14617
|
-
name:
|
|
14618
|
-
transport:
|
|
14619
|
-
url:
|
|
14620
|
-
headers:
|
|
14621
|
-
command:
|
|
14622
|
-
args:
|
|
14623
|
-
enabled:
|
|
14624
|
-
});
|
|
14625
|
-
var mcpPatchSchema =
|
|
14626
|
-
name:
|
|
14627
|
-
url:
|
|
14628
|
-
headers:
|
|
14629
|
-
command:
|
|
14630
|
-
args:
|
|
14631
|
-
enabled:
|
|
14034
|
+
var mcpServerSchema = z21.object({
|
|
14035
|
+
name: z21.string().min(1),
|
|
14036
|
+
transport: z21.enum(["http", "sse", "stdio"]),
|
|
14037
|
+
url: z21.string().optional(),
|
|
14038
|
+
headers: z21.record(z21.string(), z21.string()).optional(),
|
|
14039
|
+
command: z21.string().optional(),
|
|
14040
|
+
args: z21.array(z21.string()).optional(),
|
|
14041
|
+
enabled: z21.boolean().optional()
|
|
14042
|
+
});
|
|
14043
|
+
var mcpPatchSchema = z21.object({
|
|
14044
|
+
name: z21.string().optional(),
|
|
14045
|
+
url: z21.string().optional(),
|
|
14046
|
+
headers: z21.record(z21.string(), z21.string()).optional(),
|
|
14047
|
+
command: z21.string().optional(),
|
|
14048
|
+
args: z21.array(z21.string()).optional(),
|
|
14049
|
+
enabled: z21.boolean().optional()
|
|
14632
14050
|
});
|
|
14633
14051
|
mcpRouter.get("/", async (c) => {
|
|
14634
14052
|
const rows = listMcpServers().map((s) => ({
|
|
@@ -14745,10 +14163,10 @@ init_config();
|
|
|
14745
14163
|
init_db();
|
|
14746
14164
|
|
|
14747
14165
|
// src/utils/dependencies.ts
|
|
14748
|
-
import { exec as
|
|
14749
|
-
import { promisify as
|
|
14166
|
+
import { exec as exec6 } from "child_process";
|
|
14167
|
+
import { promisify as promisify6 } from "util";
|
|
14750
14168
|
import { platform as platform2 } from "os";
|
|
14751
|
-
var
|
|
14169
|
+
var execAsync6 = promisify6(exec6);
|
|
14752
14170
|
function getInstallInstructions() {
|
|
14753
14171
|
const os2 = platform2();
|
|
14754
14172
|
if (os2 === "darwin") {
|
|
@@ -14781,7 +14199,7 @@ Install tmux:
|
|
|
14781
14199
|
}
|
|
14782
14200
|
async function checkTmux() {
|
|
14783
14201
|
try {
|
|
14784
|
-
const { stdout } = await
|
|
14202
|
+
const { stdout } = await execAsync6("tmux -V", { timeout: 5e3 });
|
|
14785
14203
|
const version = stdout.trim();
|
|
14786
14204
|
return {
|
|
14787
14205
|
available: true,
|
|
@@ -14830,11 +14248,11 @@ function getWebDirectory() {
|
|
|
14830
14248
|
try {
|
|
14831
14249
|
const currentDir = dirname8(fileURLToPath4(import.meta.url));
|
|
14832
14250
|
const webDir = resolve11(currentDir, "..", "web");
|
|
14833
|
-
if (
|
|
14251
|
+
if (existsSync20(webDir) && existsSync20(join15(webDir, "package.json"))) {
|
|
14834
14252
|
return webDir;
|
|
14835
14253
|
}
|
|
14836
14254
|
const altWebDir = resolve11(currentDir, "..", "..", "web");
|
|
14837
|
-
if (
|
|
14255
|
+
if (existsSync20(altWebDir) && existsSync20(join15(altWebDir, "package.json"))) {
|
|
14838
14256
|
return altWebDir;
|
|
14839
14257
|
}
|
|
14840
14258
|
return null;
|
|
@@ -14892,23 +14310,23 @@ async function findWebPort(preferredPort) {
|
|
|
14892
14310
|
return { port: preferredPort, alreadyRunning: false };
|
|
14893
14311
|
}
|
|
14894
14312
|
function hasProductionBuild(webDir) {
|
|
14895
|
-
const buildIdPath =
|
|
14896
|
-
return
|
|
14313
|
+
const buildIdPath = join15(webDir, ".next", "BUILD_ID");
|
|
14314
|
+
return existsSync20(buildIdPath);
|
|
14897
14315
|
}
|
|
14898
14316
|
function hasSourceFiles(webDir) {
|
|
14899
|
-
const appDir =
|
|
14900
|
-
const pagesDir =
|
|
14901
|
-
const rootAppDir =
|
|
14902
|
-
const rootPagesDir =
|
|
14903
|
-
return
|
|
14317
|
+
const appDir = join15(webDir, "src", "app");
|
|
14318
|
+
const pagesDir = join15(webDir, "src", "pages");
|
|
14319
|
+
const rootAppDir = join15(webDir, "app");
|
|
14320
|
+
const rootPagesDir = join15(webDir, "pages");
|
|
14321
|
+
return existsSync20(appDir) || existsSync20(pagesDir) || existsSync20(rootAppDir) || existsSync20(rootPagesDir);
|
|
14904
14322
|
}
|
|
14905
14323
|
function getStandaloneServerPath(webDir) {
|
|
14906
14324
|
const possiblePaths2 = [
|
|
14907
|
-
|
|
14908
|
-
|
|
14325
|
+
join15(webDir, ".next", "standalone", "server.js"),
|
|
14326
|
+
join15(webDir, ".next", "standalone", "web", "server.js")
|
|
14909
14327
|
];
|
|
14910
14328
|
for (const serverPath of possiblePaths2) {
|
|
14911
|
-
if (
|
|
14329
|
+
if (existsSync20(serverPath)) {
|
|
14912
14330
|
return serverPath;
|
|
14913
14331
|
}
|
|
14914
14332
|
}
|
|
@@ -14948,13 +14366,13 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
14948
14366
|
if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
|
|
14949
14367
|
return { process: null, port: actualPort };
|
|
14950
14368
|
}
|
|
14951
|
-
const usePnpm =
|
|
14952
|
-
const useNpm = !usePnpm &&
|
|
14369
|
+
const usePnpm = existsSync20(join15(webDir, "pnpm-lock.yaml"));
|
|
14370
|
+
const useNpm = !usePnpm && existsSync20(join15(webDir, "package-lock.json"));
|
|
14953
14371
|
const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
|
|
14954
14372
|
const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
|
|
14955
14373
|
const apiUrl = publicUrl || `http://127.0.0.1:${apiPort}`;
|
|
14956
14374
|
const runtimeConfig = { apiBaseUrl: apiUrl };
|
|
14957
|
-
const runtimeConfigPath =
|
|
14375
|
+
const runtimeConfigPath = join15(webDir, "runtime-config.json");
|
|
14958
14376
|
try {
|
|
14959
14377
|
writeFileSync6(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2));
|
|
14960
14378
|
if (!quiet) console.log(` \u{1F4DD} Runtime config written to ${runtimeConfigPath}`);
|
|
@@ -15169,8 +14587,8 @@ async function startServer(options = {}) {
|
|
|
15169
14587
|
if (options.workingDirectory) {
|
|
15170
14588
|
config.resolvedWorkingDirectory = options.workingDirectory;
|
|
15171
14589
|
}
|
|
15172
|
-
if (!
|
|
15173
|
-
|
|
14590
|
+
if (!existsSync20(config.resolvedWorkingDirectory)) {
|
|
14591
|
+
mkdirSync9(config.resolvedWorkingDirectory, { recursive: true });
|
|
15174
14592
|
if (!options.quiet) console.log(`\u{1F4C1} Created agent workspace: ${config.resolvedWorkingDirectory}`);
|
|
15175
14593
|
}
|
|
15176
14594
|
if (!config.resolvedRemoteServer.url) {
|