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/index.js
CHANGED
|
@@ -211,12 +211,6 @@ var init_types = __esm({
|
|
|
211
211
|
skillsDirectory: z.string().optional(),
|
|
212
212
|
maxContextChars: z.number().optional().default(2e5),
|
|
213
213
|
task: TaskConfigSchema.optional(),
|
|
214
|
-
// Anthropic computer use tool — opt-in. When true, the `computer` tool is
|
|
215
|
-
// included in the toolset for Anthropic models. Default false.
|
|
216
|
-
computerUseEnabled: z.boolean().optional(),
|
|
217
|
-
// Display dimensions for the computer use tool (defaults: 1280x800).
|
|
218
|
-
computerUseDisplayWidth: z.number().int().positive().optional(),
|
|
219
|
-
computerUseDisplayHeight: z.number().int().positive().optional(),
|
|
220
214
|
// 'orchestrator' = supervisor session; 'worker' = task spawned by an orchestrator.
|
|
221
215
|
role: z.enum(["orchestrator", "worker", "chat"]).optional(),
|
|
222
216
|
// Optional persona / extra system-prompt text appended to the orchestrator's
|
|
@@ -2731,12 +2725,12 @@ function findNearestRoot(startDir, markers) {
|
|
|
2731
2725
|
}
|
|
2732
2726
|
async function commandExists(cmd) {
|
|
2733
2727
|
try {
|
|
2734
|
-
const { exec:
|
|
2735
|
-
const { promisify:
|
|
2736
|
-
const
|
|
2728
|
+
const { exec: exec7 } = await import("child_process");
|
|
2729
|
+
const { promisify: promisify7 } = await import("util");
|
|
2730
|
+
const execAsync7 = promisify7(exec7);
|
|
2737
2731
|
const isWindows = process.platform === "win32";
|
|
2738
2732
|
const checkCmd = isWindows ? `where ${cmd}` : `which ${cmd}`;
|
|
2739
|
-
await
|
|
2733
|
+
await execAsync7(checkCmd);
|
|
2740
2734
|
return true;
|
|
2741
2735
|
} catch {
|
|
2742
2736
|
return false;
|
|
@@ -6381,581 +6375,6 @@ var init_upload_file = __esm({
|
|
|
6381
6375
|
}
|
|
6382
6376
|
});
|
|
6383
6377
|
|
|
6384
|
-
// src/tools/computer-use.ts
|
|
6385
|
-
import { anthropic } from "@ai-sdk/anthropic";
|
|
6386
|
-
import { exec as exec5 } from "child_process";
|
|
6387
|
-
import { promisify as promisify5 } from "util";
|
|
6388
|
-
import { mkdirSync as mkdirSync5, existsSync as existsSync15, readFileSync as readFileSync7, unlinkSync as unlinkSync2 } from "fs";
|
|
6389
|
-
import { join as join8 } from "path";
|
|
6390
|
-
import { tmpdir } from "os";
|
|
6391
|
-
import { nanoid as nanoid4 } from "nanoid";
|
|
6392
|
-
function isMacOs() {
|
|
6393
|
-
return process.platform === "darwin";
|
|
6394
|
-
}
|
|
6395
|
-
async function isCliclickInstalled() {
|
|
6396
|
-
try {
|
|
6397
|
-
await execAsync5("command -v cliclick", { timeout: 2e3 });
|
|
6398
|
-
return true;
|
|
6399
|
-
} catch {
|
|
6400
|
-
return false;
|
|
6401
|
-
}
|
|
6402
|
-
}
|
|
6403
|
-
async function runJxa(script) {
|
|
6404
|
-
try {
|
|
6405
|
-
const escaped = script.replace(/'/g, `'\\''`);
|
|
6406
|
-
const { stdout } = await execAsync5(`osascript -l JavaScript -e '${escaped}'`, {
|
|
6407
|
-
timeout: 5e3
|
|
6408
|
-
});
|
|
6409
|
-
return JSON.parse(stdout.trim());
|
|
6410
|
-
} catch {
|
|
6411
|
-
return null;
|
|
6412
|
-
}
|
|
6413
|
-
}
|
|
6414
|
-
async function hasAccessibilityPermissions() {
|
|
6415
|
-
try {
|
|
6416
|
-
const { stderr } = await execAsync5("cliclick p:.", { timeout: 3e3 });
|
|
6417
|
-
if (/accessibility privileges not enabled/i.test(stderr)) {
|
|
6418
|
-
return { ok: false, error: stderr.trim().split("\n")[0] };
|
|
6419
|
-
}
|
|
6420
|
-
return { ok: true };
|
|
6421
|
-
} catch (err) {
|
|
6422
|
-
return { ok: false, error: err?.message || String(err) };
|
|
6423
|
-
}
|
|
6424
|
-
}
|
|
6425
|
-
async function hasScreenRecordingPermissions() {
|
|
6426
|
-
const result = await runJxa(
|
|
6427
|
-
`ObjC.import("Cocoa");
|
|
6428
|
-
ObjC.import("CoreGraphics");
|
|
6429
|
-
ObjC.bindFunction("CGPreflightScreenCaptureAccess", ["bool", []]);
|
|
6430
|
-
JSON.stringify({ hasAccess: !!$.CGPreflightScreenCaptureAccess() });`
|
|
6431
|
-
);
|
|
6432
|
-
return result?.hasAccess ?? false;
|
|
6433
|
-
}
|
|
6434
|
-
async function requestAccessibilityPrompt() {
|
|
6435
|
-
const result = await runJxa(
|
|
6436
|
-
`ObjC.import("ApplicationServices");
|
|
6437
|
-
var key = $.kAXTrustedCheckOptionPrompt;
|
|
6438
|
-
var dict = $.NSDictionary.dictionaryWithObjectForKey($.kCFBooleanTrue, key);
|
|
6439
|
-
var trusted = $.AXIsProcessTrustedWithOptions(dict);
|
|
6440
|
-
JSON.stringify({ trusted: !!trusted });`
|
|
6441
|
-
);
|
|
6442
|
-
return result?.trusted ?? false;
|
|
6443
|
-
}
|
|
6444
|
-
async function requestScreenRecordingPrompt() {
|
|
6445
|
-
const result = await runJxa(
|
|
6446
|
-
`ObjC.import("Cocoa");
|
|
6447
|
-
ObjC.import("CoreGraphics");
|
|
6448
|
-
ObjC.bindFunction("CGRequestScreenCaptureAccess", ["bool", []]);
|
|
6449
|
-
JSON.stringify({ granted: !!$.CGRequestScreenCaptureAccess() });`
|
|
6450
|
-
);
|
|
6451
|
-
return result?.granted ?? false;
|
|
6452
|
-
}
|
|
6453
|
-
async function openSystemSettings(pane) {
|
|
6454
|
-
const url = pane === "accessibility" ? "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility" : "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
|
|
6455
|
-
try {
|
|
6456
|
-
await execAsync5(`open '${url}'`, { timeout: 3e3 });
|
|
6457
|
-
} catch {
|
|
6458
|
-
}
|
|
6459
|
-
}
|
|
6460
|
-
async function detectScreenSize() {
|
|
6461
|
-
try {
|
|
6462
|
-
const { stdout } = await execAsync5(
|
|
6463
|
-
`osascript -e 'tell application "Finder" to get bounds of window of desktop'`,
|
|
6464
|
-
{ timeout: 3e3 }
|
|
6465
|
-
);
|
|
6466
|
-
const parts = stdout.trim().split(",").map((s) => parseInt(s.trim(), 10));
|
|
6467
|
-
if (parts.length >= 4 && parts.every((n) => Number.isFinite(n))) {
|
|
6468
|
-
const [x1, y1, x2, y2] = parts;
|
|
6469
|
-
return { width: x2 - x1, height: y2 - y1 };
|
|
6470
|
-
}
|
|
6471
|
-
} catch {
|
|
6472
|
-
}
|
|
6473
|
-
return null;
|
|
6474
|
-
}
|
|
6475
|
-
async function runCliclick(args) {
|
|
6476
|
-
const quoted = args.map((a) => `'${a.replace(/'/g, `'\\''`)}'`).join(" ");
|
|
6477
|
-
const { stdout, stderr } = await execAsync5(`cliclick ${quoted}`, {
|
|
6478
|
-
timeout: 15e3,
|
|
6479
|
-
maxBuffer: 1024 * 1024
|
|
6480
|
-
});
|
|
6481
|
-
if (/accessibility privileges not enabled/i.test(stderr)) {
|
|
6482
|
-
throw new Error(
|
|
6483
|
-
"Accessibility permissions not granted to cliclick. Open System Settings \u2192 Privacy & Security \u2192 Accessibility, add cliclick (or the agent runtime), and toggle it on."
|
|
6484
|
-
);
|
|
6485
|
-
}
|
|
6486
|
-
if (stderr && !stdout) throw new Error(stderr.trim());
|
|
6487
|
-
return (stdout || "").trim();
|
|
6488
|
-
}
|
|
6489
|
-
async function runScreencapture(path) {
|
|
6490
|
-
await execAsync5(`screencapture -x -t png '${path.replace(/'/g, `'\\''`)}'`, {
|
|
6491
|
-
timeout: 5e3
|
|
6492
|
-
});
|
|
6493
|
-
}
|
|
6494
|
-
async function resizeScreenshotToPoints(path, targetWidth, targetHeight) {
|
|
6495
|
-
const sharpModule = await import("sharp");
|
|
6496
|
-
const sharp2 = sharpModule.default || sharpModule;
|
|
6497
|
-
const meta = await sharp2(path).metadata();
|
|
6498
|
-
if (meta.width === targetWidth && meta.height === targetHeight) {
|
|
6499
|
-
return readFileSync7(path);
|
|
6500
|
-
}
|
|
6501
|
-
return await sharp2(path).resize(targetWidth, targetHeight, { fit: "fill" }).png().toBuffer();
|
|
6502
|
-
}
|
|
6503
|
-
async function runScroll(dx, dy) {
|
|
6504
|
-
const wheelY = -Math.round(dy);
|
|
6505
|
-
const wheelX = -Math.round(dx);
|
|
6506
|
-
const script = `ObjC.import('CoreGraphics');var ev = $.CGEventCreateScrollWheelEvent(null, 0, 2, ${wheelY}, ${wheelX});$.CGEventPost(0, ev);`;
|
|
6507
|
-
await execAsync5(
|
|
6508
|
-
`osascript -l JavaScript -e '${script.replace(/'/g, `'\\''`)}'`,
|
|
6509
|
-
{ timeout: 5e3 }
|
|
6510
|
-
);
|
|
6511
|
-
}
|
|
6512
|
-
function translateKeyForCliclick(key2) {
|
|
6513
|
-
if (!key2) return [];
|
|
6514
|
-
const parts = key2.split("+").map((p) => p.trim()).filter(Boolean);
|
|
6515
|
-
if (parts.length === 0) return [];
|
|
6516
|
-
const modMap = {
|
|
6517
|
-
ctrl: "ctrl",
|
|
6518
|
-
control: "ctrl",
|
|
6519
|
-
alt: "alt",
|
|
6520
|
-
option: "alt",
|
|
6521
|
-
shift: "shift",
|
|
6522
|
-
cmd: "cmd",
|
|
6523
|
-
super: "cmd",
|
|
6524
|
-
meta: "cmd",
|
|
6525
|
-
win: "cmd",
|
|
6526
|
-
fn: "fn"
|
|
6527
|
-
};
|
|
6528
|
-
const keyMap = {
|
|
6529
|
-
return: "enter",
|
|
6530
|
-
enter: "enter",
|
|
6531
|
-
esc: "esc",
|
|
6532
|
-
escape: "esc",
|
|
6533
|
-
backspace: "delete",
|
|
6534
|
-
back_space: "delete",
|
|
6535
|
-
delete: "fwd-delete",
|
|
6536
|
-
fwd_delete: "fwd-delete",
|
|
6537
|
-
forward_delete: "fwd-delete",
|
|
6538
|
-
tab: "tab",
|
|
6539
|
-
space: "space",
|
|
6540
|
-
up: "arrow-up",
|
|
6541
|
-
arrow_up: "arrow-up",
|
|
6542
|
-
down: "arrow-down",
|
|
6543
|
-
arrow_down: "arrow-down",
|
|
6544
|
-
left: "arrow-left",
|
|
6545
|
-
arrow_left: "arrow-left",
|
|
6546
|
-
right: "arrow-right",
|
|
6547
|
-
arrow_right: "arrow-right",
|
|
6548
|
-
page_up: "page-up",
|
|
6549
|
-
pageup: "page-up",
|
|
6550
|
-
page_down: "page-down",
|
|
6551
|
-
pagedown: "page-down",
|
|
6552
|
-
home: "home",
|
|
6553
|
-
end: "end",
|
|
6554
|
-
f1: "f1",
|
|
6555
|
-
f2: "f2",
|
|
6556
|
-
f3: "f3",
|
|
6557
|
-
f4: "f4",
|
|
6558
|
-
f5: "f5",
|
|
6559
|
-
f6: "f6",
|
|
6560
|
-
f7: "f7",
|
|
6561
|
-
f8: "f8",
|
|
6562
|
-
f9: "f9",
|
|
6563
|
-
f10: "f10",
|
|
6564
|
-
f11: "f11",
|
|
6565
|
-
f12: "f12"
|
|
6566
|
-
};
|
|
6567
|
-
const modifiers = [];
|
|
6568
|
-
let mainKey = null;
|
|
6569
|
-
for (let i = 0; i < parts.length; i++) {
|
|
6570
|
-
const lower = parts[i].toLowerCase().replace(/-/g, "_");
|
|
6571
|
-
if (i < parts.length - 1 && modMap[lower]) {
|
|
6572
|
-
modifiers.push(modMap[lower]);
|
|
6573
|
-
} else {
|
|
6574
|
-
mainKey = keyMap[lower] || lower;
|
|
6575
|
-
}
|
|
6576
|
-
}
|
|
6577
|
-
const args = [];
|
|
6578
|
-
if (modifiers.length > 0) args.push(`kd:${modifiers.join(",")}`);
|
|
6579
|
-
if (mainKey) {
|
|
6580
|
-
const isNamedKey = Object.values(keyMap).includes(mainKey) || /^f([1-9]|1[0-9]|20)$/.test(mainKey) || /^num-/.test(mainKey);
|
|
6581
|
-
if (isNamedKey) {
|
|
6582
|
-
args.push(`kp:${mainKey}`);
|
|
6583
|
-
} else {
|
|
6584
|
-
args.push(`t:${mainKey}`);
|
|
6585
|
-
}
|
|
6586
|
-
}
|
|
6587
|
-
if (modifiers.length > 0) args.push(`ku:${modifiers.join(",")}`);
|
|
6588
|
-
return args;
|
|
6589
|
-
}
|
|
6590
|
-
function modifierStringToCliclick(text) {
|
|
6591
|
-
return text.split("+").map((p) => p.trim().toLowerCase()).map((p) => {
|
|
6592
|
-
if (p === "ctrl" || p === "control") return "ctrl";
|
|
6593
|
-
if (p === "alt" || p === "option") return "alt";
|
|
6594
|
-
if (p === "shift") return "shift";
|
|
6595
|
-
if (p === "super" || p === "meta" || p === "cmd") return "cmd";
|
|
6596
|
-
return "";
|
|
6597
|
-
}).filter(Boolean);
|
|
6598
|
-
}
|
|
6599
|
-
function createComputerUseTool(options) {
|
|
6600
|
-
const displayWidth = options.displayWidth ?? DEFAULT_WIDTH;
|
|
6601
|
-
const displayHeight = options.displayHeight ?? DEFAULT_HEIGHT;
|
|
6602
|
-
return anthropic.tools.computer_20251124({
|
|
6603
|
-
displayWidthPx: displayWidth,
|
|
6604
|
-
displayHeightPx: displayHeight,
|
|
6605
|
-
enableZoom: true,
|
|
6606
|
-
execute: async (input) => {
|
|
6607
|
-
try {
|
|
6608
|
-
switch (input.action) {
|
|
6609
|
-
case "screenshot": {
|
|
6610
|
-
const path = join8(tmpdir(), `cu-${nanoid4(8)}.png`);
|
|
6611
|
-
await runScreencapture(path);
|
|
6612
|
-
const resized = await resizeScreenshotToPoints(path, displayWidth, displayHeight);
|
|
6613
|
-
try {
|
|
6614
|
-
unlinkSync2(path);
|
|
6615
|
-
} catch {
|
|
6616
|
-
}
|
|
6617
|
-
return { type: "image", data: resized.toString("base64") };
|
|
6618
|
-
}
|
|
6619
|
-
case "left_click": {
|
|
6620
|
-
const [x, y] = input.coordinate ?? [0, 0];
|
|
6621
|
-
if (input.text) {
|
|
6622
|
-
const mods = modifierStringToCliclick(input.text);
|
|
6623
|
-
if (mods.length > 0) {
|
|
6624
|
-
await runCliclick([`kd:${mods.join(",")}`, `c:${x},${y}`, `ku:${mods.join(",")}`]);
|
|
6625
|
-
} else {
|
|
6626
|
-
await runCliclick([`c:${x},${y}`]);
|
|
6627
|
-
}
|
|
6628
|
-
} else {
|
|
6629
|
-
await runCliclick([`c:${x},${y}`]);
|
|
6630
|
-
}
|
|
6631
|
-
return `clicked at (${x}, ${y})${input.text ? ` with ${input.text}` : ""}`;
|
|
6632
|
-
}
|
|
6633
|
-
case "right_click": {
|
|
6634
|
-
const [x, y] = input.coordinate ?? [0, 0];
|
|
6635
|
-
await runCliclick([`rc:${x},${y}`]);
|
|
6636
|
-
return `right-clicked at (${x}, ${y})`;
|
|
6637
|
-
}
|
|
6638
|
-
case "middle_click": {
|
|
6639
|
-
const [x, y] = input.coordinate ?? [0, 0];
|
|
6640
|
-
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);`;
|
|
6641
|
-
await execAsync5(
|
|
6642
|
-
`osascript -l JavaScript -e '${script.replace(/'/g, `'\\''`)}'`,
|
|
6643
|
-
{ timeout: 3e3 }
|
|
6644
|
-
);
|
|
6645
|
-
return `middle-clicked at (${x}, ${y})`;
|
|
6646
|
-
}
|
|
6647
|
-
case "double_click": {
|
|
6648
|
-
const [x, y] = input.coordinate ?? [0, 0];
|
|
6649
|
-
await runCliclick([`dc:${x},${y}`]);
|
|
6650
|
-
return `double-clicked at (${x}, ${y})`;
|
|
6651
|
-
}
|
|
6652
|
-
case "triple_click": {
|
|
6653
|
-
const [x, y] = input.coordinate ?? [0, 0];
|
|
6654
|
-
await runCliclick([`tc:${x},${y}`]);
|
|
6655
|
-
return `triple-clicked at (${x}, ${y})`;
|
|
6656
|
-
}
|
|
6657
|
-
case "mouse_move": {
|
|
6658
|
-
const [x, y] = input.coordinate ?? [0, 0];
|
|
6659
|
-
await runCliclick([`m:${x},${y}`]);
|
|
6660
|
-
return `moved cursor to (${x}, ${y})`;
|
|
6661
|
-
}
|
|
6662
|
-
case "left_mouse_down": {
|
|
6663
|
-
const [x, y] = input.coordinate ?? [0, 0];
|
|
6664
|
-
await runCliclick([`dd:${x},${y}`]);
|
|
6665
|
-
return `left mouse button pressed at (${x}, ${y})`;
|
|
6666
|
-
}
|
|
6667
|
-
case "left_mouse_up": {
|
|
6668
|
-
const [x, y] = input.coordinate ?? [0, 0];
|
|
6669
|
-
await runCliclick([`du:${x},${y}`]);
|
|
6670
|
-
return `left mouse button released at (${x}, ${y})`;
|
|
6671
|
-
}
|
|
6672
|
-
case "left_click_drag": {
|
|
6673
|
-
const [sx, sy] = input.start_coordinate ?? [0, 0];
|
|
6674
|
-
const [ex, ey] = input.coordinate ?? [0, 0];
|
|
6675
|
-
await runCliclick([`dd:${sx},${sy}`, `m:${ex},${ey}`, `du:${ex},${ey}`]);
|
|
6676
|
-
return `dragged from (${sx}, ${sy}) to (${ex}, ${ey})`;
|
|
6677
|
-
}
|
|
6678
|
-
case "type": {
|
|
6679
|
-
const text = input.text ?? "";
|
|
6680
|
-
await runCliclick([`t:${text}`]);
|
|
6681
|
-
return `typed ${text.length} character(s)`;
|
|
6682
|
-
}
|
|
6683
|
-
case "key": {
|
|
6684
|
-
const args = translateKeyForCliclick(input.text ?? "");
|
|
6685
|
-
if (args.length === 0) return "no key specified";
|
|
6686
|
-
await runCliclick(args);
|
|
6687
|
-
return `pressed ${input.text}`;
|
|
6688
|
-
}
|
|
6689
|
-
case "hold_key": {
|
|
6690
|
-
const text = (input.text ?? "").toLowerCase();
|
|
6691
|
-
const duration = input.duration ?? 1;
|
|
6692
|
-
const modMap = {
|
|
6693
|
-
ctrl: "ctrl",
|
|
6694
|
-
control: "ctrl",
|
|
6695
|
-
alt: "alt",
|
|
6696
|
-
option: "alt",
|
|
6697
|
-
shift: "shift",
|
|
6698
|
-
cmd: "cmd",
|
|
6699
|
-
super: "cmd",
|
|
6700
|
-
meta: "cmd",
|
|
6701
|
-
fn: "fn"
|
|
6702
|
-
};
|
|
6703
|
-
const cliName = modMap[text] || text;
|
|
6704
|
-
await runCliclick([`kd:${cliName}`]);
|
|
6705
|
-
await new Promise((r) => setTimeout(r, duration * 1e3));
|
|
6706
|
-
await runCliclick([`ku:${cliName}`]);
|
|
6707
|
-
return `held ${text} for ${duration}s`;
|
|
6708
|
-
}
|
|
6709
|
-
case "scroll": {
|
|
6710
|
-
const direction = input.scroll_direction ?? "down";
|
|
6711
|
-
const amount = input.scroll_amount ?? 3;
|
|
6712
|
-
const px = amount * 100;
|
|
6713
|
-
const dx = direction === "left" ? -px : direction === "right" ? px : 0;
|
|
6714
|
-
const dy = direction === "up" ? -px : direction === "down" ? px : 0;
|
|
6715
|
-
if (input.coordinate) {
|
|
6716
|
-
const [x, y] = input.coordinate;
|
|
6717
|
-
await runCliclick([`m:${x},${y}`]);
|
|
6718
|
-
}
|
|
6719
|
-
const mods = input.text ? modifierStringToCliclick(input.text) : [];
|
|
6720
|
-
if (mods.length > 0) {
|
|
6721
|
-
await runCliclick([`kd:${mods.join(",")}`]);
|
|
6722
|
-
}
|
|
6723
|
-
await runScroll(dx, dy);
|
|
6724
|
-
if (mods.length > 0) {
|
|
6725
|
-
await runCliclick([`ku:${mods.join(",")}`]);
|
|
6726
|
-
}
|
|
6727
|
-
return `scrolled ${direction} by ${amount}`;
|
|
6728
|
-
}
|
|
6729
|
-
case "wait": {
|
|
6730
|
-
const duration = input.duration ?? 1;
|
|
6731
|
-
await new Promise((r) => setTimeout(r, duration * 1e3));
|
|
6732
|
-
return `waited ${duration}s`;
|
|
6733
|
-
}
|
|
6734
|
-
case "cursor_position": {
|
|
6735
|
-
const out = await runCliclick(["p:."]);
|
|
6736
|
-
return `cursor at ${out}`;
|
|
6737
|
-
}
|
|
6738
|
-
case "zoom": {
|
|
6739
|
-
const region = input.region ?? [0, 0, displayWidth, displayHeight];
|
|
6740
|
-
const [x1, y1, x2, y2] = region;
|
|
6741
|
-
const tmpPath = join8(tmpdir(), `cu-zoom-${nanoid4(8)}.png`);
|
|
6742
|
-
await runScreencapture(tmpPath);
|
|
6743
|
-
const sharpModule = await import("sharp");
|
|
6744
|
-
const sharp2 = sharpModule.default || sharpModule;
|
|
6745
|
-
const meta = await sharp2(tmpPath).metadata();
|
|
6746
|
-
const scaleX = (meta.width || displayWidth) / displayWidth;
|
|
6747
|
-
const scaleY = (meta.height || displayHeight) / displayHeight;
|
|
6748
|
-
const px = {
|
|
6749
|
-
left: Math.max(0, Math.round(x1 * scaleX)),
|
|
6750
|
-
top: Math.max(0, Math.round(y1 * scaleY)),
|
|
6751
|
-
width: Math.max(1, Math.round((x2 - x1) * scaleX)),
|
|
6752
|
-
height: Math.max(1, Math.round((y2 - y1) * scaleY))
|
|
6753
|
-
};
|
|
6754
|
-
const buf = await sharp2(tmpPath).extract(px).png().toBuffer();
|
|
6755
|
-
try {
|
|
6756
|
-
unlinkSync2(tmpPath);
|
|
6757
|
-
} catch {
|
|
6758
|
-
}
|
|
6759
|
-
return { type: "image", data: buf.toString("base64") };
|
|
6760
|
-
}
|
|
6761
|
-
default: {
|
|
6762
|
-
const exhaustive = input.action;
|
|
6763
|
-
return `unsupported action: ${String(exhaustive)}`;
|
|
6764
|
-
}
|
|
6765
|
-
}
|
|
6766
|
-
} catch (err) {
|
|
6767
|
-
const msg = err?.message || String(err);
|
|
6768
|
-
let hint = "";
|
|
6769
|
-
if (/accessibility|not authorized|tcc|operation not permitted/i.test(msg)) {
|
|
6770
|
-
hint = " (Hint: call enable_computer_use to (re-)check permissions and open System Settings)";
|
|
6771
|
-
} else if (/command not found/i.test(msg)) {
|
|
6772
|
-
hint = " (Hint: install cliclick with `brew install cliclick`)";
|
|
6773
|
-
}
|
|
6774
|
-
return `Error: ${msg}${hint}`;
|
|
6775
|
-
}
|
|
6776
|
-
},
|
|
6777
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6778
|
-
toModelOutput({ output }) {
|
|
6779
|
-
if (typeof output === "string") {
|
|
6780
|
-
return { type: "content", value: [{ type: "text", text: output }] };
|
|
6781
|
-
}
|
|
6782
|
-
return {
|
|
6783
|
-
type: "content",
|
|
6784
|
-
value: [{ type: "media", data: output.data, mediaType: "image/png" }]
|
|
6785
|
-
};
|
|
6786
|
-
}
|
|
6787
|
-
});
|
|
6788
|
-
}
|
|
6789
|
-
var execAsync5, DEFAULT_WIDTH, DEFAULT_HEIGHT;
|
|
6790
|
-
var init_computer_use = __esm({
|
|
6791
|
-
"src/tools/computer-use.ts"() {
|
|
6792
|
-
"use strict";
|
|
6793
|
-
execAsync5 = promisify5(exec5);
|
|
6794
|
-
DEFAULT_WIDTH = 1280;
|
|
6795
|
-
DEFAULT_HEIGHT = 800;
|
|
6796
|
-
}
|
|
6797
|
-
});
|
|
6798
|
-
|
|
6799
|
-
// src/tools/enable-computer-use.ts
|
|
6800
|
-
import { tool as tool13 } from "ai";
|
|
6801
|
-
import { z as z14 } from "zod";
|
|
6802
|
-
function createEnableComputerUseTool(options) {
|
|
6803
|
-
return tool13({
|
|
6804
|
-
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.",
|
|
6805
|
-
inputSchema,
|
|
6806
|
-
execute: async ({ display_width, display_height, request_permissions }) => {
|
|
6807
|
-
try {
|
|
6808
|
-
if (!isMacOs()) {
|
|
6809
|
-
return {
|
|
6810
|
-
success: false,
|
|
6811
|
-
error: "Computer use is currently only supported on macOS.",
|
|
6812
|
-
platform: process.platform
|
|
6813
|
-
};
|
|
6814
|
-
}
|
|
6815
|
-
if (!await isCliclickInstalled()) {
|
|
6816
|
-
return {
|
|
6817
|
-
success: false,
|
|
6818
|
-
error: "`cliclick` is not installed. It is required for mouse/keyboard control on macOS.",
|
|
6819
|
-
installCommand: "brew install cliclick",
|
|
6820
|
-
fixSteps: [
|
|
6821
|
-
"In a terminal on this Mac, run: brew install cliclick",
|
|
6822
|
-
"(If Homebrew is not installed, install it first from https://brew.sh)",
|
|
6823
|
-
"Then call enable_computer_use again"
|
|
6824
|
-
]
|
|
6825
|
-
};
|
|
6826
|
-
}
|
|
6827
|
-
const acc = await hasAccessibilityPermissions();
|
|
6828
|
-
const screen = await hasScreenRecordingPermissions();
|
|
6829
|
-
const missing = [];
|
|
6830
|
-
if (!acc.ok) {
|
|
6831
|
-
let prompted = false;
|
|
6832
|
-
let panelOpened = false;
|
|
6833
|
-
if (request_permissions) {
|
|
6834
|
-
prompted = await requestAccessibilityPrompt().then(() => true).catch(() => false);
|
|
6835
|
-
await openSystemSettings("accessibility").then(() => {
|
|
6836
|
-
panelOpened = true;
|
|
6837
|
-
}).catch(() => void 0);
|
|
6838
|
-
}
|
|
6839
|
-
missing.push({
|
|
6840
|
-
name: "Accessibility",
|
|
6841
|
-
reason: "cliclick failed: " + (acc.error?.split("\n")[0] || "no permission"),
|
|
6842
|
-
pane: "accessibility",
|
|
6843
|
-
settingsUrl: ACCESSIBILITY_URL,
|
|
6844
|
-
fixSteps: [
|
|
6845
|
-
"In the System Settings \u2192 Privacy & Security \u2192 Accessibility pane that opened",
|
|
6846
|
-
"Click the + button",
|
|
6847
|
-
"Add the application running the agent (Terminal, iTerm, your IDE, or `node`)",
|
|
6848
|
-
"Toggle the switch ON",
|
|
6849
|
-
"Restart the agent process so the new permission takes effect",
|
|
6850
|
-
"Then call enable_computer_use again"
|
|
6851
|
-
],
|
|
6852
|
-
prompted,
|
|
6853
|
-
panelOpened
|
|
6854
|
-
});
|
|
6855
|
-
}
|
|
6856
|
-
if (!screen) {
|
|
6857
|
-
let prompted = false;
|
|
6858
|
-
let panelOpened = false;
|
|
6859
|
-
if (request_permissions) {
|
|
6860
|
-
prompted = await requestScreenRecordingPrompt().then(() => true).catch(() => false);
|
|
6861
|
-
await openSystemSettings("screen-recording").then(() => {
|
|
6862
|
-
panelOpened = true;
|
|
6863
|
-
}).catch(() => void 0);
|
|
6864
|
-
}
|
|
6865
|
-
missing.push({
|
|
6866
|
-
name: "Screen Recording",
|
|
6867
|
-
reason: "CGPreflightScreenCaptureAccess returned false",
|
|
6868
|
-
pane: "screen-recording",
|
|
6869
|
-
settingsUrl: SCREEN_RECORDING_URL,
|
|
6870
|
-
fixSteps: [
|
|
6871
|
-
"In the System Settings \u2192 Privacy & Security \u2192 Screen Recording pane that opened",
|
|
6872
|
-
"Click the + button",
|
|
6873
|
-
"Add the application running the agent (Terminal, iTerm, your IDE, or `node`)",
|
|
6874
|
-
"Toggle the switch ON",
|
|
6875
|
-
"Restart the agent process so the new permission takes effect",
|
|
6876
|
-
"Then call enable_computer_use again"
|
|
6877
|
-
],
|
|
6878
|
-
prompted,
|
|
6879
|
-
panelOpened
|
|
6880
|
-
});
|
|
6881
|
-
}
|
|
6882
|
-
if (missing.length > 0) {
|
|
6883
|
-
return {
|
|
6884
|
-
success: false,
|
|
6885
|
-
error: `Missing permission${missing.length > 1 ? "s" : ""}: ` + missing.map((m) => m.name).join(" and ") + ".",
|
|
6886
|
-
missingPermissions: missing,
|
|
6887
|
-
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."
|
|
6888
|
-
};
|
|
6889
|
-
}
|
|
6890
|
-
let width = display_width;
|
|
6891
|
-
let height = display_height;
|
|
6892
|
-
let detected = null;
|
|
6893
|
-
if (width === void 0 || height === void 0) {
|
|
6894
|
-
detected = await detectScreenSize();
|
|
6895
|
-
width = width ?? detected?.width ?? 1280;
|
|
6896
|
-
height = height ?? detected?.height ?? 800;
|
|
6897
|
-
}
|
|
6898
|
-
const session = await sessionQueries.getById(options.sessionId);
|
|
6899
|
-
if (!session) {
|
|
6900
|
-
return { success: false, error: "Session not found" };
|
|
6901
|
-
}
|
|
6902
|
-
const config = session.config || {};
|
|
6903
|
-
if (config.computerUseEnabled === true && config.computerUseDisplayWidth === width && config.computerUseDisplayHeight === height) {
|
|
6904
|
-
return {
|
|
6905
|
-
success: true,
|
|
6906
|
-
alreadyEnabled: true,
|
|
6907
|
-
message: "Computer use was already enabled for this session.",
|
|
6908
|
-
displayWidth: width,
|
|
6909
|
-
displayHeight: height
|
|
6910
|
-
};
|
|
6911
|
-
}
|
|
6912
|
-
const updated = {
|
|
6913
|
-
...config,
|
|
6914
|
-
computerUseEnabled: true,
|
|
6915
|
-
computerUseDisplayWidth: width,
|
|
6916
|
-
computerUseDisplayHeight: height
|
|
6917
|
-
};
|
|
6918
|
-
await sessionQueries.update(options.sessionId, { config: updated });
|
|
6919
|
-
return {
|
|
6920
|
-
success: true,
|
|
6921
|
-
enabled: true,
|
|
6922
|
-
platform: "darwin",
|
|
6923
|
-
displayWidth: width,
|
|
6924
|
-
displayHeight: height,
|
|
6925
|
-
detectedScreenSize: detected || void 0,
|
|
6926
|
-
permissions: {
|
|
6927
|
-
accessibility: "granted",
|
|
6928
|
-
screenRecording: "granted"
|
|
6929
|
-
},
|
|
6930
|
-
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.`
|
|
6931
|
-
};
|
|
6932
|
-
} catch (err) {
|
|
6933
|
-
return {
|
|
6934
|
-
success: false,
|
|
6935
|
-
error: err?.message || String(err)
|
|
6936
|
-
};
|
|
6937
|
-
}
|
|
6938
|
-
}
|
|
6939
|
-
});
|
|
6940
|
-
}
|
|
6941
|
-
var inputSchema, ACCESSIBILITY_URL, SCREEN_RECORDING_URL;
|
|
6942
|
-
var init_enable_computer_use = __esm({
|
|
6943
|
-
"src/tools/enable-computer-use.ts"() {
|
|
6944
|
-
"use strict";
|
|
6945
|
-
init_db();
|
|
6946
|
-
init_computer_use();
|
|
6947
|
-
inputSchema = z14.object({
|
|
6948
|
-
display_width: z14.number().int().positive().optional().describe("Display width in pixels (defaults to detected primary display, fallback 1280)"),
|
|
6949
|
-
display_height: z14.number().int().positive().optional().describe("Display height in pixels (defaults to detected primary display, fallback 800)"),
|
|
6950
|
-
request_permissions: z14.boolean().optional().default(true).describe(
|
|
6951
|
-
"When true (default), proactively trigger macOS permission prompts and open System Settings panes for any missing permissions."
|
|
6952
|
-
)
|
|
6953
|
-
});
|
|
6954
|
-
ACCESSIBILITY_URL = "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility";
|
|
6955
|
-
SCREEN_RECORDING_URL = "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
|
|
6956
|
-
}
|
|
6957
|
-
});
|
|
6958
|
-
|
|
6959
6378
|
// src/tools/index.ts
|
|
6960
6379
|
async function createTools(options) {
|
|
6961
6380
|
const tools = {
|
|
@@ -7000,20 +6419,6 @@ async function createTools(options) {
|
|
|
7000
6419
|
sessionId: options.sessionId
|
|
7001
6420
|
});
|
|
7002
6421
|
}
|
|
7003
|
-
if (process.platform === "darwin") {
|
|
7004
|
-
if (options.enableComputerUse) {
|
|
7005
|
-
tools.computer = createComputerUseTool({
|
|
7006
|
-
workingDirectory: options.workingDirectory,
|
|
7007
|
-
sessionId: options.sessionId,
|
|
7008
|
-
displayWidth: options.computerUseDisplayWidth,
|
|
7009
|
-
displayHeight: options.computerUseDisplayHeight
|
|
7010
|
-
});
|
|
7011
|
-
} else {
|
|
7012
|
-
tools.enable_computer_use = createEnableComputerUseTool({
|
|
7013
|
-
sessionId: options.sessionId
|
|
7014
|
-
});
|
|
7015
|
-
}
|
|
7016
|
-
}
|
|
7017
6422
|
if (options.enableSemanticSearch !== false) {
|
|
7018
6423
|
try {
|
|
7019
6424
|
if (isVectorGatewayConfigured()) {
|
|
@@ -7048,8 +6453,6 @@ var init_tools = __esm({
|
|
|
7048
6453
|
init_code_graph();
|
|
7049
6454
|
init_task();
|
|
7050
6455
|
init_upload_file();
|
|
7051
|
-
init_computer_use();
|
|
7052
|
-
init_enable_computer_use();
|
|
7053
6456
|
init_semantic();
|
|
7054
6457
|
init_remote();
|
|
7055
6458
|
init_bash();
|
|
@@ -7063,8 +6466,6 @@ var init_tools = __esm({
|
|
|
7063
6466
|
init_code_graph();
|
|
7064
6467
|
init_task();
|
|
7065
6468
|
init_upload_file();
|
|
7066
|
-
init_computer_use();
|
|
7067
|
-
init_enable_computer_use();
|
|
7068
6469
|
}
|
|
7069
6470
|
});
|
|
7070
6471
|
|
|
@@ -7540,8 +6941,7 @@ ${JSON.stringify(outputSchema, null, 2)}
|
|
|
7540
6941
|
`;
|
|
7541
6942
|
}
|
|
7542
6943
|
function buildOrchestratorPromptAddendum() {
|
|
7543
|
-
const
|
|
7544
|
-
const computerUseAvailable = platform3 === "darwin";
|
|
6944
|
+
const desktopAvailable = process.platform === "darwin";
|
|
7545
6945
|
return `
|
|
7546
6946
|
## Orchestrator Mode
|
|
7547
6947
|
|
|
@@ -7640,14 +7040,14 @@ When NOT to split (keep as one worker):
|
|
|
7640
7040
|
When spawning a worker, push it toward the *cheapest tool that gets the job done*:
|
|
7641
7041
|
|
|
7642
7042
|
1. **Bash / file tools** for anything with a CLI (git, npm, brew, builds, tests, file editing, HTTP via curl, scripting).
|
|
7643
|
-
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.${
|
|
7644
|
-
3. **
|
|
7043
|
+
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 ? `
|
|
7044
|
+
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.
|
|
7645
7045
|
|
|
7646
|
-
A common anti-pattern: a worker reaches for
|
|
7046
|
+
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."*
|
|
7647
7047
|
|
|
7648
|
-
### Serialize desktop
|
|
7048
|
+
### Serialize desktop-automation tasks
|
|
7649
7049
|
|
|
7650
|
-
There is exactly **one** desktop, mouse, and keyboard on the host. If two or more workers both
|
|
7050
|
+
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.
|
|
7651
7051
|
|
|
7652
7052
|
**Rule**: when spawning workers, look at each one's goal:
|
|
7653
7053
|
|
|
@@ -7668,10 +7068,30 @@ Example: *"Take a screenshot of Calculator AND run the test suite AND open Syste
|
|
|
7668
7068
|
|
|
7669
7069
|
Headless workers never interfere with desktop workers (they don't touch the screen), so they always run in parallel.
|
|
7670
7070
|
|
|
7671
|
-
When you spawn a **desktop worker**,
|
|
7071
|
+
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.` : ""}
|
|
7672
7072
|
|
|
7673
7073
|
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.
|
|
7674
7074
|
|
|
7075
|
+
### How to TALK to the user (versus how you reason internally)
|
|
7076
|
+
|
|
7077
|
+
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.
|
|
7078
|
+
|
|
7079
|
+
When replying to the user (Slack, web, or any channel), be a normal helpful assistant:
|
|
7080
|
+
|
|
7081
|
+
- Tell them *what you're doing*, not *how you're doing it internally*.
|
|
7082
|
+
- 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.
|
|
7083
|
+
- 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).
|
|
7084
|
+
- 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.
|
|
7085
|
+
|
|
7086
|
+
| What you might be tempted to say | What you should say instead |
|
|
7087
|
+
|---|---|
|
|
7088
|
+
| *"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."* |
|
|
7089
|
+
| *"Decomposing into two parallel workers: one headless, one desktop."* | *"Running the tests and pulling the diff at the same time. Back in ~30s."* |
|
|
7090
|
+
| *"Spawning worker \`screenshot-calc\` with goal: \u2026"* | *"Taking a screenshot of Calculator now."* |
|
|
7091
|
+
| *"I'll relay the user's instructions to the worker verbatim."* | *(say nothing \u2014 just do it)* |
|
|
7092
|
+
|
|
7093
|
+
If the user explicitly asks how you work, *then* you can explain the orchestrator/worker split. Otherwise: less is more.
|
|
7094
|
+
|
|
7675
7095
|
### How to write a worker goal (and what NOT to put in it)
|
|
7676
7096
|
|
|
7677
7097
|
You delegate; the worker executes. Stay at the **what** level, not the **how**.
|
|
@@ -7679,7 +7099,7 @@ You delegate; the worker executes. Stay at the **what** level, not the **how**.
|
|
|
7679
7099
|
**DO** put in the goal:
|
|
7680
7100
|
|
|
7681
7101
|
- The end objective ("open the macOS Weather app and capture the forecast for Anchorage as a screen recording").
|
|
7682
|
-
- Which skills the worker should load up-front (\`load_skill recording\`, \`load_skill
|
|
7102
|
+
- Which skills the worker should load up-front (\`load_skill recording\`, \`load_skill desktop-automation\`).
|
|
7683
7103
|
- Acceptance criteria ("verify the city name is visible in the screenshot before reporting done").
|
|
7684
7104
|
- Routing back ("post the recording URL to Slack channel C0123 thread 1700.001").
|
|
7685
7105
|
|
|
@@ -7712,7 +7132,7 @@ Bad goal (don't do this):
|
|
|
7712
7132
|
> "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'..."
|
|
7713
7133
|
|
|
7714
7134
|
Good goal (do this):
|
|
7715
|
-
> "Capture a 30\u201360s screen recording of opening the macOS Weather app and viewing the Anchorage, AK forecast. \`load_skill recording\` and \`load_skill
|
|
7135
|
+
> "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."
|
|
7716
7136
|
`;
|
|
7717
7137
|
}
|
|
7718
7138
|
function createSummaryPrompt(conversationHistory) {
|
|
@@ -7931,17 +7351,17 @@ __export(conversation_archive_exports, {
|
|
|
7931
7351
|
getHistoryDir: () => getHistoryDir,
|
|
7932
7352
|
listSessionArchives: () => listSessionArchives
|
|
7933
7353
|
});
|
|
7934
|
-
import { existsSync as
|
|
7935
|
-
import { join as
|
|
7354
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync5, appendFileSync as appendFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
7355
|
+
import { join as join8 } from "path";
|
|
7936
7356
|
function getHistoryDir() {
|
|
7937
|
-
const dir =
|
|
7938
|
-
if (!
|
|
7357
|
+
const dir = join8(ensureAppDataDirectory(), "history");
|
|
7358
|
+
if (!existsSync15(dir)) mkdirSync5(dir, { recursive: true });
|
|
7939
7359
|
return dir;
|
|
7940
7360
|
}
|
|
7941
7361
|
function appendTurn(turn) {
|
|
7942
7362
|
try {
|
|
7943
7363
|
const dir = getHistoryDir();
|
|
7944
|
-
const path =
|
|
7364
|
+
const path = join8(dir, `${turn.sessionId}.jsonl`);
|
|
7945
7365
|
const line = JSON.stringify({
|
|
7946
7366
|
ts: turn.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
7947
7367
|
sessionId: turn.sessionId,
|
|
@@ -7970,7 +7390,7 @@ function flattenContent(content) {
|
|
|
7970
7390
|
}
|
|
7971
7391
|
function listSessionArchives() {
|
|
7972
7392
|
const dir = getHistoryDir();
|
|
7973
|
-
if (!
|
|
7393
|
+
if (!existsSync15(dir)) return [];
|
|
7974
7394
|
return readdirSync2(dir).filter((f) => f.endsWith(".jsonl"));
|
|
7975
7395
|
}
|
|
7976
7396
|
var init_conversation_archive = __esm({
|
|
@@ -8042,6 +7462,18 @@ function repairToolPairing(messages) {
|
|
|
8042
7462
|
}
|
|
8043
7463
|
return repaired;
|
|
8044
7464
|
}
|
|
7465
|
+
function ensureEndsWithUserOrTool(messages) {
|
|
7466
|
+
if (!Array.isArray(messages) || messages.length === 0) return messages;
|
|
7467
|
+
const last = messages[messages.length - 1];
|
|
7468
|
+
if (last?.role !== "assistant") return messages;
|
|
7469
|
+
console.warn(
|
|
7470
|
+
"[context] Trailing assistant message detected \u2014 appending synthetic user turn to satisfy prefill restrictions"
|
|
7471
|
+
);
|
|
7472
|
+
return [
|
|
7473
|
+
...messages,
|
|
7474
|
+
{ role: "user", content: [{ type: "text", text: "Please continue." }] }
|
|
7475
|
+
];
|
|
7476
|
+
}
|
|
8045
7477
|
var TOOL_OUTPUT_TRIM_CHARS, COMPACTABLE_TOOLS, ContextManager;
|
|
8046
7478
|
var init_context = __esm({
|
|
8047
7479
|
"src/agent/context.ts"() {
|
|
@@ -8099,6 +7531,7 @@ ${summaryContent}`
|
|
|
8099
7531
|
];
|
|
8100
7532
|
}
|
|
8101
7533
|
messages = repairToolPairing(messages);
|
|
7534
|
+
messages = ensureEndsWithUserOrTool(messages);
|
|
8102
7535
|
return messages;
|
|
8103
7536
|
}
|
|
8104
7537
|
// ---------------------------------------------------------------------------
|
|
@@ -8292,7 +7725,8 @@ ${summaryContent}`
|
|
|
8292
7725
|
}
|
|
8293
7726
|
}
|
|
8294
7727
|
async addResponseMessages(messages) {
|
|
8295
|
-
|
|
7728
|
+
const safe = repairToolPairing(messages);
|
|
7729
|
+
await messageQueries.addMany(this.sessionId, safe);
|
|
8296
7730
|
try {
|
|
8297
7731
|
const { appendTurn: appendTurn2, flattenContent: flattenContent2 } = await Promise.resolve().then(() => (init_conversation_archive(), conversation_archive_exports));
|
|
8298
7732
|
const { sessionQueries: sessionQueries2 } = await Promise.resolve().then(() => (init_db(), db_exports));
|
|
@@ -8733,7 +8167,7 @@ var init_messenger = __esm({
|
|
|
8733
8167
|
});
|
|
8734
8168
|
|
|
8735
8169
|
// src/orchestrator/schedules-store.ts
|
|
8736
|
-
import { nanoid as
|
|
8170
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
8737
8171
|
async function readOrch(orchestratorSessionId) {
|
|
8738
8172
|
const s = await sessionQueries.getById(orchestratorSessionId);
|
|
8739
8173
|
if (!s) return null;
|
|
@@ -8748,7 +8182,7 @@ async function createSchedule(orchestratorSessionId, input) {
|
|
|
8748
8182
|
const data = await readOrch(orchestratorSessionId);
|
|
8749
8183
|
if (!data) throw new Error("orchestrator session not found");
|
|
8750
8184
|
const row = {
|
|
8751
|
-
id: `sch_${
|
|
8185
|
+
id: `sch_${nanoid4(10)}`,
|
|
8752
8186
|
name: input.name,
|
|
8753
8187
|
cron: input.cron,
|
|
8754
8188
|
prompt: input.prompt,
|
|
@@ -8785,7 +8219,7 @@ var init_schedules_store = __esm({
|
|
|
8785
8219
|
|
|
8786
8220
|
// src/orchestrator/webhooks-store.ts
|
|
8787
8221
|
import { randomBytes } from "crypto";
|
|
8788
|
-
import { nanoid as
|
|
8222
|
+
import { nanoid as nanoid5 } from "nanoid";
|
|
8789
8223
|
function newToken() {
|
|
8790
8224
|
return randomBytes(24).toString("base64url");
|
|
8791
8225
|
}
|
|
@@ -8802,7 +8236,7 @@ async function createWebhook(orchestratorSessionId, input) {
|
|
|
8802
8236
|
const data = await readOrch2(orchestratorSessionId);
|
|
8803
8237
|
if (!data) throw new Error("orchestrator session not found");
|
|
8804
8238
|
const row = {
|
|
8805
|
-
id: `whk_${
|
|
8239
|
+
id: `whk_${nanoid5(10)}`,
|
|
8806
8240
|
name: input.name,
|
|
8807
8241
|
token: newToken(),
|
|
8808
8242
|
wake: input.wake ?? "now",
|
|
@@ -8858,8 +8292,8 @@ var init_webhooks_store = __esm({
|
|
|
8858
8292
|
});
|
|
8859
8293
|
|
|
8860
8294
|
// src/tools/orchestrator-actions.ts
|
|
8861
|
-
import { tool as
|
|
8862
|
-
import { z as
|
|
8295
|
+
import { tool as tool13 } from "ai";
|
|
8296
|
+
import { z as z14 } from "zod";
|
|
8863
8297
|
async function api2(baseUrl, path, init = {}) {
|
|
8864
8298
|
const res = await fetch(`${baseUrl}${path}`, {
|
|
8865
8299
|
method: init.method || "GET",
|
|
@@ -8885,7 +8319,7 @@ function previewMessageContent(content) {
|
|
|
8885
8319
|
return "";
|
|
8886
8320
|
}
|
|
8887
8321
|
function buildAgentTool(opts) {
|
|
8888
|
-
return
|
|
8322
|
+
return tool13({
|
|
8889
8323
|
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).",
|
|
8890
8324
|
inputSchema: agentInputSchema,
|
|
8891
8325
|
execute: async (input) => {
|
|
@@ -8988,7 +8422,7 @@ function buildAgentTool(opts) {
|
|
|
8988
8422
|
});
|
|
8989
8423
|
}
|
|
8990
8424
|
function buildMessengerTool() {
|
|
8991
|
-
return
|
|
8425
|
+
return tool13({
|
|
8992
8426
|
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.",
|
|
8993
8427
|
inputSchema: messengerInputSchema,
|
|
8994
8428
|
execute: async (input) => {
|
|
@@ -9010,7 +8444,7 @@ function buildMessengerTool() {
|
|
|
9010
8444
|
});
|
|
9011
8445
|
}
|
|
9012
8446
|
function buildScheduleTool(opts) {
|
|
9013
|
-
return
|
|
8447
|
+
return tool13({
|
|
9014
8448
|
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.",
|
|
9015
8449
|
inputSchema: scheduleInputSchema,
|
|
9016
8450
|
execute: async (input) => {
|
|
@@ -9053,7 +8487,7 @@ function buildWebhookUrl(opts, token) {
|
|
|
9053
8487
|
return `${base}${webhookPrefix2}/inbox/${token}`;
|
|
9054
8488
|
}
|
|
9055
8489
|
function buildWebhookTool(opts) {
|
|
9056
|
-
return
|
|
8490
|
+
return tool13({
|
|
9057
8491
|
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.",
|
|
9058
8492
|
inputSchema: webhookInputSchema,
|
|
9059
8493
|
execute: async (input) => {
|
|
@@ -9102,66 +8536,66 @@ var init_orchestrator_actions = __esm({
|
|
|
9102
8536
|
init_schedules_store();
|
|
9103
8537
|
init_config();
|
|
9104
8538
|
init_webhooks_store();
|
|
9105
|
-
AGENT_STATUS_ENUM =
|
|
9106
|
-
agentInputSchema =
|
|
9107
|
-
action:
|
|
8539
|
+
AGENT_STATUS_ENUM = z14.enum(["running", "needs_attention", "completed", "failed", "idle"]);
|
|
8540
|
+
agentInputSchema = z14.object({
|
|
8541
|
+
action: z14.enum(["list", "get", "spawn", "message", "answer_question", "stop"]).describe("Which agent operation to perform."),
|
|
9108
8542
|
// list
|
|
9109
8543
|
status: AGENT_STATUS_ENUM.optional().describe("list only: filter to one status."),
|
|
9110
|
-
limit:
|
|
8544
|
+
limit: z14.number().int().min(1).max(100).optional().describe("list only: max rows."),
|
|
9111
8545
|
// get / message / answer_question / stop
|
|
9112
|
-
id:
|
|
9113
|
-
recentMessages:
|
|
8546
|
+
id: z14.string().optional().describe("get | message | answer_question | stop: the agent (session) id."),
|
|
8547
|
+
recentMessages: z14.number().int().min(0).max(50).optional().describe("get only: how many recent messages to include."),
|
|
9114
8548
|
// spawn
|
|
9115
|
-
name:
|
|
9116
|
-
goal:
|
|
9117
|
-
outputSchema:
|
|
8549
|
+
name: z14.string().optional().describe("spawn only: short human-readable label."),
|
|
8550
|
+
goal: z14.string().optional().describe("spawn only: the worker's self-contained instruction."),
|
|
8551
|
+
outputSchema: z14.record(z14.string(), z14.unknown()).optional().describe(
|
|
9118
8552
|
'spawn only: JSON Schema for the worker result. Defaults to {type:"object", properties:{summary:{type:"string"}}, required:["summary"]}.'
|
|
9119
8553
|
),
|
|
9120
|
-
model:
|
|
9121
|
-
workingDirectory:
|
|
9122
|
-
maxIterations:
|
|
8554
|
+
model: z14.string().optional().describe("spawn only: model override."),
|
|
8555
|
+
workingDirectory: z14.string().optional().describe("spawn only: working directory override."),
|
|
8556
|
+
maxIterations: z14.number().int().min(1).max(500).optional().describe("spawn only."),
|
|
9123
8557
|
// message
|
|
9124
|
-
text:
|
|
9125
|
-
force:
|
|
8558
|
+
text: z14.string().optional().describe("message only: the text to deliver to the worker."),
|
|
8559
|
+
force: z14.boolean().optional().describe("message only: soft-interrupt the current step."),
|
|
9126
8560
|
// answer_question
|
|
9127
|
-
questionId:
|
|
9128
|
-
answer:
|
|
8561
|
+
questionId: z14.string().optional().describe("answer_question only: pending question id (e.g. q_abc123)."),
|
|
8562
|
+
answer: z14.string().optional().describe("answer_question only: your answer.")
|
|
9129
8563
|
});
|
|
9130
|
-
messengerInputSchema =
|
|
9131
|
-
action:
|
|
8564
|
+
messengerInputSchema = z14.object({
|
|
8565
|
+
action: z14.enum(["list_channels", "post"]),
|
|
9132
8566
|
// post
|
|
9133
|
-
channel:
|
|
9134
|
-
to:
|
|
9135
|
-
text:
|
|
9136
|
-
threadTs:
|
|
9137
|
-
subject:
|
|
8567
|
+
channel: z14.string().optional().describe('post only: channel id (e.g. "slack").'),
|
|
8568
|
+
to: z14.string().optional().describe('post only: destination. Slack: channel id (C0123), user id (U0123), or "#channel-name".'),
|
|
8569
|
+
text: z14.string().optional().describe("post only: message body."),
|
|
8570
|
+
threadTs: z14.string().optional().describe("post + slack: reply in this thread."),
|
|
8571
|
+
subject: z14.string().optional().describe("post + email: subject (future).")
|
|
9138
8572
|
});
|
|
9139
|
-
scheduleInputSchema =
|
|
9140
|
-
action:
|
|
8573
|
+
scheduleInputSchema = z14.object({
|
|
8574
|
+
action: z14.enum(["create", "list", "update", "delete", "pause", "resume"]),
|
|
9141
8575
|
// create / update
|
|
9142
|
-
name:
|
|
9143
|
-
cron:
|
|
9144
|
-
prompt:
|
|
9145
|
-
replyChannel:
|
|
8576
|
+
name: z14.string().optional().describe("create | update"),
|
|
8577
|
+
cron: z14.string().optional().describe('create | update: 5-field cron (e.g. "0 9 * * 1-5" = weekdays at 9am).'),
|
|
8578
|
+
prompt: z14.string().optional().describe("create | update: the prompt injected when the schedule fires."),
|
|
8579
|
+
replyChannel: z14.string().optional().describe("create | update: default channel id for orchestrator replies."),
|
|
9146
8580
|
// update / delete / pause / resume
|
|
9147
|
-
id:
|
|
9148
|
-
enabled:
|
|
8581
|
+
id: z14.string().optional().describe("update | delete | pause | resume: schedule id."),
|
|
8582
|
+
enabled: z14.boolean().optional().describe("update only.")
|
|
9149
8583
|
});
|
|
9150
|
-
webhookInputSchema =
|
|
9151
|
-
action:
|
|
9152
|
-
name:
|
|
9153
|
-
wake:
|
|
9154
|
-
template:
|
|
9155
|
-
id:
|
|
9156
|
-
rotateToken:
|
|
8584
|
+
webhookInputSchema = z14.object({
|
|
8585
|
+
action: z14.enum(["create", "list", "update", "delete"]),
|
|
8586
|
+
name: z14.string().optional().describe("create | update."),
|
|
8587
|
+
wake: z14.enum(["now", "next"]).optional().describe("create | update: now = wake orchestrator immediately; next = add as context."),
|
|
8588
|
+
template: z14.string().optional().describe("create | update: mustache-style template ({{path.to.field}}). Defaults to pretty-printed JSON."),
|
|
8589
|
+
id: z14.string().optional().describe("update | delete: webhook id."),
|
|
8590
|
+
rotateToken: z14.boolean().optional().describe("update only: regenerate the URL token.")
|
|
9157
8591
|
});
|
|
9158
8592
|
}
|
|
9159
8593
|
});
|
|
9160
8594
|
|
|
9161
8595
|
// src/integrations/mcp/store.ts
|
|
9162
|
-
import { nanoid as
|
|
9163
|
-
import { existsSync as
|
|
9164
|
-
import { resolve as resolve10, join as
|
|
8596
|
+
import { nanoid as nanoid6 } from "nanoid";
|
|
8597
|
+
import { existsSync as existsSync16, readFileSync as readFileSync7 } from "fs";
|
|
8598
|
+
import { resolve as resolve10, join as join9 } from "path";
|
|
9165
8599
|
function readServers() {
|
|
9166
8600
|
try {
|
|
9167
8601
|
const cfg = getConfig();
|
|
@@ -9173,12 +8607,12 @@ function readServers() {
|
|
|
9173
8607
|
function refreshMcpServersFromDisk() {
|
|
9174
8608
|
const candidates = [
|
|
9175
8609
|
resolve10(process.cwd(), "sparkecoder.config.json"),
|
|
9176
|
-
|
|
8610
|
+
join9(ensureAppDataDirectory(), "sparkecoder.config.json")
|
|
9177
8611
|
];
|
|
9178
8612
|
for (const path of candidates) {
|
|
9179
|
-
if (!
|
|
8613
|
+
if (!existsSync16(path)) continue;
|
|
9180
8614
|
try {
|
|
9181
|
-
const raw = JSON.parse(
|
|
8615
|
+
const raw = JSON.parse(readFileSync7(path, "utf-8"));
|
|
9182
8616
|
const servers2 = Array.isArray(raw?.mcp?.servers) ? raw.mcp.servers : [];
|
|
9183
8617
|
setMcpServers(servers2);
|
|
9184
8618
|
return servers2;
|
|
@@ -9197,7 +8631,7 @@ function createMcpServer(input) {
|
|
|
9197
8631
|
const all = readServers();
|
|
9198
8632
|
validateInput(input);
|
|
9199
8633
|
const row = {
|
|
9200
|
-
id: `mcp_${
|
|
8634
|
+
id: `mcp_${nanoid6(10)}`,
|
|
9201
8635
|
name: sanitizeName(input.name),
|
|
9202
8636
|
transport: input.transport,
|
|
9203
8637
|
url: input.url,
|
|
@@ -9788,15 +9222,15 @@ var recorder_exports = {};
|
|
|
9788
9222
|
__export(recorder_exports, {
|
|
9789
9223
|
FrameRecorder: () => FrameRecorder
|
|
9790
9224
|
});
|
|
9791
|
-
import { exec as
|
|
9792
|
-
import { promisify as
|
|
9225
|
+
import { exec as exec5 } from "child_process";
|
|
9226
|
+
import { promisify as promisify5 } from "util";
|
|
9793
9227
|
import { writeFile as writeFile5, mkdir as mkdir4, readFile as readFile11, unlink as unlink2, readdir as readdir5, rm } from "fs/promises";
|
|
9794
|
-
import { join as
|
|
9795
|
-
import { tmpdir
|
|
9796
|
-
import { nanoid as
|
|
9228
|
+
import { join as join10 } from "path";
|
|
9229
|
+
import { tmpdir } from "os";
|
|
9230
|
+
import { nanoid as nanoid7 } from "nanoid";
|
|
9797
9231
|
async function checkFfmpeg() {
|
|
9798
9232
|
try {
|
|
9799
|
-
await
|
|
9233
|
+
await execAsync5("ffmpeg -version", { timeout: 5e3 });
|
|
9800
9234
|
return true;
|
|
9801
9235
|
} catch {
|
|
9802
9236
|
return false;
|
|
@@ -9808,11 +9242,11 @@ async function cleanup(dir) {
|
|
|
9808
9242
|
} catch {
|
|
9809
9243
|
}
|
|
9810
9244
|
}
|
|
9811
|
-
var
|
|
9245
|
+
var execAsync5, FrameRecorder;
|
|
9812
9246
|
var init_recorder = __esm({
|
|
9813
9247
|
"src/browser/recorder.ts"() {
|
|
9814
9248
|
"use strict";
|
|
9815
|
-
|
|
9249
|
+
execAsync5 = promisify5(exec5);
|
|
9816
9250
|
FrameRecorder = class {
|
|
9817
9251
|
frames = [];
|
|
9818
9252
|
startTime = null;
|
|
@@ -9848,21 +9282,21 @@ var init_recorder = __esm({
|
|
|
9848
9282
|
*/
|
|
9849
9283
|
async encode() {
|
|
9850
9284
|
if (this.frames.length === 0) return null;
|
|
9851
|
-
const workDir =
|
|
9285
|
+
const workDir = join10(tmpdir(), `sparkecoder-recording-${nanoid7(8)}`);
|
|
9852
9286
|
await mkdir4(workDir, { recursive: true });
|
|
9853
9287
|
try {
|
|
9854
9288
|
for (let i = 0; i < this.frames.length; i++) {
|
|
9855
|
-
const framePath =
|
|
9289
|
+
const framePath = join10(workDir, `frame_${String(i).padStart(6, "0")}.jpg`);
|
|
9856
9290
|
await writeFile5(framePath, this.frames[i].data);
|
|
9857
9291
|
}
|
|
9858
9292
|
const duration = (this.frames[this.frames.length - 1].timestamp - this.frames[0].timestamp) / 1e3;
|
|
9859
9293
|
const fps = duration > 0 ? Math.round(this.frames.length / duration) : 10;
|
|
9860
9294
|
const clampedFps = Math.max(1, Math.min(fps, 30));
|
|
9861
|
-
const outputPath =
|
|
9295
|
+
const outputPath = join10(workDir, `recording_${this.sessionId}.mp4`);
|
|
9862
9296
|
const hasFfmpeg = await checkFfmpeg();
|
|
9863
9297
|
if (hasFfmpeg) {
|
|
9864
|
-
await
|
|
9865
|
-
`ffmpeg -y -framerate ${clampedFps} -i "${
|
|
9298
|
+
await execAsync5(
|
|
9299
|
+
`ffmpeg -y -framerate ${clampedFps} -i "${join10(workDir, "frame_%06d.jpg")}" -c:v libx264 -pix_fmt yuv420p -preset fast -crf 23 "${outputPath}"`,
|
|
9866
9300
|
{ timeout: 12e4 }
|
|
9867
9301
|
);
|
|
9868
9302
|
} else {
|
|
@@ -9874,7 +9308,7 @@ var init_recorder = __esm({
|
|
|
9874
9308
|
const files = await readdir5(workDir);
|
|
9875
9309
|
for (const f of files) {
|
|
9876
9310
|
if (f.startsWith("frame_")) {
|
|
9877
|
-
await unlink2(
|
|
9311
|
+
await unlink2(join10(workDir, f)).catch(() => {
|
|
9878
9312
|
});
|
|
9879
9313
|
}
|
|
9880
9314
|
}
|
|
@@ -9899,11 +9333,11 @@ var init_recorder = __esm({
|
|
|
9899
9333
|
import {
|
|
9900
9334
|
streamText as streamText2,
|
|
9901
9335
|
generateText as generateText3,
|
|
9902
|
-
tool as
|
|
9336
|
+
tool as tool14,
|
|
9903
9337
|
stepCountIs as stepCountIs2
|
|
9904
9338
|
} from "ai";
|
|
9905
|
-
import { z as
|
|
9906
|
-
import { nanoid as
|
|
9339
|
+
import { z as z15 } from "zod";
|
|
9340
|
+
import { nanoid as nanoid8 } from "nanoid";
|
|
9907
9341
|
function anySignal(signals) {
|
|
9908
9342
|
const ctrl = new AbortController();
|
|
9909
9343
|
for (const s of signals) {
|
|
@@ -9972,14 +9406,10 @@ var init_agent = __esm({
|
|
|
9972
9406
|
*/
|
|
9973
9407
|
async createToolsWithCallbacks(options) {
|
|
9974
9408
|
const config = getConfig();
|
|
9975
|
-
const sessionConfig = this.session.config || {};
|
|
9976
9409
|
const tools = await createTools({
|
|
9977
9410
|
sessionId: this.session.id,
|
|
9978
9411
|
workingDirectory: this.session.workingDirectory,
|
|
9979
9412
|
skillsDirectories: config.resolvedSkillsDirectories,
|
|
9980
|
-
enableComputerUse: sessionConfig.computerUseEnabled === true,
|
|
9981
|
-
computerUseDisplayWidth: sessionConfig.computerUseDisplayWidth,
|
|
9982
|
-
computerUseDisplayHeight: sessionConfig.computerUseDisplayHeight,
|
|
9983
9413
|
onBashProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "bash", data: progress }) : void 0,
|
|
9984
9414
|
onWriteFileProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "write_file", data: progress }) : void 0,
|
|
9985
9415
|
onSearchProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "explore_agent", data: progress }) : void 0
|
|
@@ -10029,14 +9459,10 @@ var init_agent = __esm({
|
|
|
10029
9459
|
keepRecentMessages: config.context?.keepRecentMessages || 10,
|
|
10030
9460
|
autoSummarize: config.context?.autoSummarize ?? true
|
|
10031
9461
|
});
|
|
10032
|
-
const sessionConfig = session.config || {};
|
|
10033
9462
|
const tools = await createTools({
|
|
10034
9463
|
sessionId: session.id,
|
|
10035
9464
|
workingDirectory: session.workingDirectory,
|
|
10036
|
-
skillsDirectories: config.resolvedSkillsDirectories
|
|
10037
|
-
enableComputerUse: sessionConfig.computerUseEnabled === true,
|
|
10038
|
-
computerUseDisplayWidth: sessionConfig.computerUseDisplayWidth,
|
|
10039
|
-
computerUseDisplayHeight: sessionConfig.computerUseDisplayHeight
|
|
9465
|
+
skillsDirectories: config.resolvedSkillsDirectories
|
|
10040
9466
|
});
|
|
10041
9467
|
if (session.config?.role === "orchestrator") {
|
|
10042
9468
|
const baseUrl = `http://127.0.0.1:${config.server?.port ?? 3141}`;
|
|
@@ -10274,14 +9700,10 @@ ${personality.trim()}`;
|
|
|
10274
9700
|
});
|
|
10275
9701
|
}
|
|
10276
9702
|
};
|
|
10277
|
-
const taskSessionConfig = this.session.config || {};
|
|
10278
9703
|
const taskTools = await createTools({
|
|
10279
9704
|
sessionId: this.session.id,
|
|
10280
9705
|
workingDirectory: this.session.workingDirectory,
|
|
10281
9706
|
skillsDirectories: config.resolvedSkillsDirectories,
|
|
10282
|
-
enableComputerUse: taskSessionConfig.computerUseEnabled === true,
|
|
10283
|
-
computerUseDisplayWidth: taskSessionConfig.computerUseDisplayWidth,
|
|
10284
|
-
computerUseDisplayHeight: taskSessionConfig.computerUseDisplayHeight,
|
|
10285
9707
|
onBashProgress: bashProgressHandler,
|
|
10286
9708
|
onWriteFileProgress: (progress) => {
|
|
10287
9709
|
options.onToolProgress?.({ toolName: "write_file", data: progress });
|
|
@@ -10629,11 +10051,11 @@ ${p.text}` : p.text;
|
|
|
10629
10051
|
const { isRemoteConfigured: isRemoteConfigured2, storageQueries: storageQueries2 } = await Promise.resolve().then(() => (init_remote(), remote_exports));
|
|
10630
10052
|
if (!isRemoteConfigured2()) return [];
|
|
10631
10053
|
const { readFile: readFile12 } = await import("fs/promises");
|
|
10632
|
-
const { join:
|
|
10054
|
+
const { join: join16, basename: basename6 } = await import("path");
|
|
10633
10055
|
const urls = [];
|
|
10634
10056
|
for (const filePath of filePaths) {
|
|
10635
10057
|
try {
|
|
10636
|
-
const fullPath = filePath.startsWith("/") ? filePath :
|
|
10058
|
+
const fullPath = filePath.startsWith("/") ? filePath : join16(this.session.workingDirectory, filePath);
|
|
10637
10059
|
const fileName = basename6(fullPath);
|
|
10638
10060
|
const ext = fileName.split(".").pop()?.toLowerCase() || "";
|
|
10639
10061
|
const mimeMap = {
|
|
@@ -10691,11 +10113,11 @@ ${p.text}` : p.text;
|
|
|
10691
10113
|
wrappedTools[name] = originalTool;
|
|
10692
10114
|
continue;
|
|
10693
10115
|
}
|
|
10694
|
-
wrappedTools[name] =
|
|
10116
|
+
wrappedTools[name] = tool14({
|
|
10695
10117
|
description: originalTool.description || "",
|
|
10696
|
-
inputSchema: originalTool.inputSchema ||
|
|
10118
|
+
inputSchema: originalTool.inputSchema || z15.object({}),
|
|
10697
10119
|
execute: async (input, toolOptions) => {
|
|
10698
|
-
const toolCallId = toolOptions.toolCallId ||
|
|
10120
|
+
const toolCallId = toolOptions.toolCallId || nanoid8();
|
|
10699
10121
|
const execution = toolExecutionQueries.create({
|
|
10700
10122
|
sessionId: this.session.id,
|
|
10701
10123
|
toolName: name,
|
|
@@ -10713,10 +10135,10 @@ ${p.text}` : p.text;
|
|
|
10713
10135
|
const resolverData = approvalResolvers.get(toolCallId);
|
|
10714
10136
|
approvalResolvers.delete(toolCallId);
|
|
10715
10137
|
this.pendingApprovals.delete(toolCallId);
|
|
10716
|
-
const
|
|
10138
|
+
const exec7 = await execution;
|
|
10717
10139
|
if (!approved) {
|
|
10718
10140
|
const reason = resolverData?.reason || "User rejected the tool execution";
|
|
10719
|
-
await toolExecutionQueries.reject(
|
|
10141
|
+
await toolExecutionQueries.reject(exec7.id);
|
|
10720
10142
|
await sessionQueries.updateStatus(this.session.id, "active");
|
|
10721
10143
|
return {
|
|
10722
10144
|
status: "rejected",
|
|
@@ -10726,14 +10148,14 @@ ${p.text}` : p.text;
|
|
|
10726
10148
|
message: `Tool "${name}" was rejected by the user. Reason: ${reason}`
|
|
10727
10149
|
};
|
|
10728
10150
|
}
|
|
10729
|
-
await toolExecutionQueries.approve(
|
|
10151
|
+
await toolExecutionQueries.approve(exec7.id);
|
|
10730
10152
|
await sessionQueries.updateStatus(this.session.id, "active");
|
|
10731
10153
|
try {
|
|
10732
10154
|
const result = await originalTool.execute(input, toolOptions);
|
|
10733
|
-
await toolExecutionQueries.complete(
|
|
10155
|
+
await toolExecutionQueries.complete(exec7.id, result);
|
|
10734
10156
|
return result;
|
|
10735
10157
|
} catch (error) {
|
|
10736
|
-
await toolExecutionQueries.complete(
|
|
10158
|
+
await toolExecutionQueries.complete(exec7.id, null, error.message);
|
|
10737
10159
|
throw error;
|
|
10738
10160
|
}
|
|
10739
10161
|
}
|
|
@@ -10838,19 +10260,19 @@ var init_session_lock = __esm({
|
|
|
10838
10260
|
});
|
|
10839
10261
|
|
|
10840
10262
|
// src/orchestrator/webhook-events.ts
|
|
10841
|
-
import { existsSync as
|
|
10842
|
-
import { dirname as dirname6, join as
|
|
10843
|
-
import { nanoid as
|
|
10263
|
+
import { existsSync as existsSync17, readFileSync as readFileSync8, appendFileSync as appendFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync6 } from "fs";
|
|
10264
|
+
import { dirname as dirname6, join as join11 } from "path";
|
|
10265
|
+
import { nanoid as nanoid9 } from "nanoid";
|
|
10844
10266
|
function logFilePath() {
|
|
10845
|
-
return
|
|
10267
|
+
return join11(getAppDataDirectory(), "webhook-events.jsonl");
|
|
10846
10268
|
}
|
|
10847
10269
|
function ensureLoaded() {
|
|
10848
10270
|
if (cache !== null) return cache;
|
|
10849
10271
|
cache = [];
|
|
10850
10272
|
try {
|
|
10851
10273
|
const p = logFilePath();
|
|
10852
|
-
if (!
|
|
10853
|
-
const lines =
|
|
10274
|
+
if (!existsSync17(p)) return cache;
|
|
10275
|
+
const lines = readFileSync8(p, "utf-8").split("\n").filter(Boolean);
|
|
10854
10276
|
for (const line of lines) {
|
|
10855
10277
|
try {
|
|
10856
10278
|
cache.push(JSON.parse(line));
|
|
@@ -10874,14 +10296,14 @@ function appendEvent(ev) {
|
|
|
10874
10296
|
if (list.length > MAX_EVENTS) list.shift();
|
|
10875
10297
|
try {
|
|
10876
10298
|
const p = logFilePath();
|
|
10877
|
-
|
|
10299
|
+
mkdirSync6(dirname6(p), { recursive: true });
|
|
10878
10300
|
appendFileSync3(p, JSON.stringify(ev) + "\n");
|
|
10879
10301
|
} catch {
|
|
10880
10302
|
}
|
|
10881
10303
|
}
|
|
10882
10304
|
function recordEvent(ev) {
|
|
10883
10305
|
const full = {
|
|
10884
|
-
id: ev.id ??
|
|
10306
|
+
id: ev.id ?? nanoid9(),
|
|
10885
10307
|
ts: ev.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
10886
10308
|
source: ev.source,
|
|
10887
10309
|
status: ev.status,
|
|
@@ -10905,7 +10327,7 @@ function updateEvent(id, patch) {
|
|
|
10905
10327
|
list[i] = { ...list[i], ...patch };
|
|
10906
10328
|
try {
|
|
10907
10329
|
const p = logFilePath();
|
|
10908
|
-
|
|
10330
|
+
mkdirSync6(dirname6(p), { recursive: true });
|
|
10909
10331
|
writeFileSync3(p, list.map((e) => JSON.stringify(e)).join("\n") + "\n");
|
|
10910
10332
|
} catch {
|
|
10911
10333
|
}
|
|
@@ -11212,8 +10634,8 @@ import { Hono as Hono9 } from "hono";
|
|
|
11212
10634
|
import { serve } from "@hono/node-server";
|
|
11213
10635
|
import { cors } from "hono/cors";
|
|
11214
10636
|
import { logger } from "hono/logger";
|
|
11215
|
-
import { existsSync as
|
|
11216
|
-
import { resolve as resolve11, dirname as dirname8, join as
|
|
10637
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync9, writeFileSync as writeFileSync6 } from "fs";
|
|
10638
|
+
import { resolve as resolve11, dirname as dirname8, join as join15 } from "path";
|
|
11217
10639
|
import { spawn as spawn2 } from "child_process";
|
|
11218
10640
|
import { createServer as createNetServer } from "net";
|
|
11219
10641
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -11226,11 +10648,11 @@ init_tmux();
|
|
|
11226
10648
|
init_checkpoints();
|
|
11227
10649
|
import { Hono } from "hono";
|
|
11228
10650
|
import { zValidator } from "@hono/zod-validator";
|
|
11229
|
-
import { z as
|
|
11230
|
-
import { existsSync as
|
|
10651
|
+
import { z as z16 } from "zod";
|
|
10652
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync7, writeFileSync as writeFileSync4, readdirSync as readdirSync3, statSync as statSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
11231
10653
|
import { readdir as readdir6 } from "fs/promises";
|
|
11232
|
-
import { join as
|
|
11233
|
-
import { nanoid as
|
|
10654
|
+
import { join as join12, basename as basename5, extname as extname8, relative as relative9 } from "path";
|
|
10655
|
+
import { nanoid as nanoid10 } from "nanoid";
|
|
11234
10656
|
|
|
11235
10657
|
// src/tasks/agent-status.ts
|
|
11236
10658
|
init_questions();
|
|
@@ -11288,22 +10710,22 @@ function cleanupPendingInputs() {
|
|
|
11288
10710
|
}
|
|
11289
10711
|
}
|
|
11290
10712
|
}
|
|
11291
|
-
var createSessionSchema =
|
|
11292
|
-
name:
|
|
11293
|
-
workingDirectory:
|
|
11294
|
-
model:
|
|
11295
|
-
toolApprovals:
|
|
11296
|
-
// Optional full session-config passthrough (
|
|
11297
|
-
config:
|
|
11298
|
-
role:
|
|
10713
|
+
var createSessionSchema = z16.object({
|
|
10714
|
+
name: z16.string().optional(),
|
|
10715
|
+
workingDirectory: z16.string().optional(),
|
|
10716
|
+
model: z16.string().optional(),
|
|
10717
|
+
toolApprovals: z16.record(z16.string(), z16.boolean()).optional(),
|
|
10718
|
+
// Optional full session-config passthrough (role, persona, etc.)
|
|
10719
|
+
config: z16.record(z16.string(), z16.unknown()).optional(),
|
|
10720
|
+
role: z16.enum(["orchestrator", "worker", "chat"]).optional()
|
|
11299
10721
|
});
|
|
11300
|
-
var paginationQuerySchema =
|
|
11301
|
-
limit:
|
|
11302
|
-
offset:
|
|
11303
|
-
role:
|
|
10722
|
+
var paginationQuerySchema = z16.object({
|
|
10723
|
+
limit: z16.string().optional(),
|
|
10724
|
+
offset: z16.string().optional(),
|
|
10725
|
+
role: z16.enum(["orchestrator", "worker", "chat", "all"]).optional()
|
|
11304
10726
|
});
|
|
11305
|
-
var messagesQuerySchema =
|
|
11306
|
-
limit:
|
|
10727
|
+
var messagesQuerySchema = z16.object({
|
|
10728
|
+
limit: z16.string().optional()
|
|
11307
10729
|
});
|
|
11308
10730
|
sessions2.get(
|
|
11309
10731
|
"/",
|
|
@@ -11372,15 +10794,11 @@ sessions2.post(
|
|
|
11372
10794
|
async (c) => {
|
|
11373
10795
|
const body = c.req.valid("json");
|
|
11374
10796
|
const config = getConfig();
|
|
11375
|
-
const cuDefault = process.env.SPARKECODER_COMPUTER_USE === "1";
|
|
11376
10797
|
const baseConfig = body.config || {};
|
|
11377
10798
|
const mergedConfig = {
|
|
11378
10799
|
...baseConfig,
|
|
11379
10800
|
...body.toolApprovals ? { toolApprovals: body.toolApprovals } : {},
|
|
11380
|
-
...body.role ? { role: body.role } : {}
|
|
11381
|
-
// Turn on computer use by default if the server was launched with --enable-computer-use,
|
|
11382
|
-
// unless the client explicitly provided a value.
|
|
11383
|
-
...cuDefault && baseConfig.computerUseEnabled === void 0 ? { computerUseEnabled: true } : {}
|
|
10801
|
+
...body.role ? { role: body.role } : {}
|
|
11384
10802
|
};
|
|
11385
10803
|
const agent = await Agent.create({
|
|
11386
10804
|
name: body.name,
|
|
@@ -11557,10 +10975,10 @@ sessions2.get("/:id/tools", async (c) => {
|
|
|
11557
10975
|
count: executions.length
|
|
11558
10976
|
});
|
|
11559
10977
|
});
|
|
11560
|
-
var updateSessionSchema =
|
|
11561
|
-
model:
|
|
11562
|
-
name:
|
|
11563
|
-
toolApprovals:
|
|
10978
|
+
var updateSessionSchema = z16.object({
|
|
10979
|
+
model: z16.string().optional(),
|
|
10980
|
+
name: z16.string().optional(),
|
|
10981
|
+
toolApprovals: z16.record(z16.string(), z16.boolean()).optional()
|
|
11564
10982
|
});
|
|
11565
10983
|
sessions2.patch(
|
|
11566
10984
|
"/:id",
|
|
@@ -11630,10 +11048,10 @@ sessions2.post("/:id/clear", async (c) => {
|
|
|
11630
11048
|
await agent.clearContext();
|
|
11631
11049
|
return c.json({ success: true, sessionId: id });
|
|
11632
11050
|
});
|
|
11633
|
-
var injectMessageSchema =
|
|
11634
|
-
text:
|
|
11635
|
-
source:
|
|
11636
|
-
force:
|
|
11051
|
+
var injectMessageSchema = z16.object({
|
|
11052
|
+
text: z16.string().min(1),
|
|
11053
|
+
source: z16.enum(["orchestrator", "user", "system"]).optional(),
|
|
11054
|
+
force: z16.boolean().optional()
|
|
11637
11055
|
});
|
|
11638
11056
|
sessions2.post(
|
|
11639
11057
|
"/:id/messages",
|
|
@@ -11647,8 +11065,8 @@ sessions2.post(
|
|
|
11647
11065
|
return c.json({ success: true, sessionId: id, queued: true, force });
|
|
11648
11066
|
}
|
|
11649
11067
|
);
|
|
11650
|
-
var pendingInputSchema =
|
|
11651
|
-
text:
|
|
11068
|
+
var pendingInputSchema = z16.object({
|
|
11069
|
+
text: z16.string()
|
|
11652
11070
|
});
|
|
11653
11071
|
sessions2.post(
|
|
11654
11072
|
"/:id/pending-input",
|
|
@@ -11679,13 +11097,13 @@ sessions2.get("/:id/pending-input", async (c) => {
|
|
|
11679
11097
|
createdAt: pending.createdAt.toISOString()
|
|
11680
11098
|
});
|
|
11681
11099
|
});
|
|
11682
|
-
var devtoolsContextSchema =
|
|
11683
|
-
url:
|
|
11684
|
-
path:
|
|
11685
|
-
pageName:
|
|
11686
|
-
screenWidth:
|
|
11687
|
-
screenHeight:
|
|
11688
|
-
devicePixelRatio:
|
|
11100
|
+
var devtoolsContextSchema = z16.object({
|
|
11101
|
+
url: z16.string(),
|
|
11102
|
+
path: z16.string(),
|
|
11103
|
+
pageName: z16.string().optional(),
|
|
11104
|
+
screenWidth: z16.number().optional(),
|
|
11105
|
+
screenHeight: z16.number().optional(),
|
|
11106
|
+
devicePixelRatio: z16.number().optional()
|
|
11689
11107
|
});
|
|
11690
11108
|
sessions2.post(
|
|
11691
11109
|
"/:id/devtools-context",
|
|
@@ -11871,12 +11289,12 @@ sessions2.get("/:id/diff/:filePath", async (c) => {
|
|
|
11871
11289
|
});
|
|
11872
11290
|
function getAttachmentsDir(sessionId) {
|
|
11873
11291
|
const appDataDir = getAppDataDirectory();
|
|
11874
|
-
return
|
|
11292
|
+
return join12(appDataDir, "attachments", sessionId);
|
|
11875
11293
|
}
|
|
11876
11294
|
function ensureAttachmentsDir(sessionId) {
|
|
11877
11295
|
const dir = getAttachmentsDir(sessionId);
|
|
11878
|
-
if (!
|
|
11879
|
-
|
|
11296
|
+
if (!existsSync18(dir)) {
|
|
11297
|
+
mkdirSync7(dir, { recursive: true });
|
|
11880
11298
|
}
|
|
11881
11299
|
return dir;
|
|
11882
11300
|
}
|
|
@@ -11887,12 +11305,12 @@ sessions2.get("/:id/attachments", async (c) => {
|
|
|
11887
11305
|
return c.json({ error: "Session not found" }, 404);
|
|
11888
11306
|
}
|
|
11889
11307
|
const dir = getAttachmentsDir(sessionId);
|
|
11890
|
-
if (!
|
|
11308
|
+
if (!existsSync18(dir)) {
|
|
11891
11309
|
return c.json({ sessionId, attachments: [], count: 0 });
|
|
11892
11310
|
}
|
|
11893
11311
|
const files = readdirSync3(dir);
|
|
11894
11312
|
const attachments = files.map((filename) => {
|
|
11895
|
-
const filePath =
|
|
11313
|
+
const filePath = join12(dir, filename);
|
|
11896
11314
|
const stats = statSync2(filePath);
|
|
11897
11315
|
return {
|
|
11898
11316
|
id: filename.split("_")[0],
|
|
@@ -11924,10 +11342,10 @@ sessions2.post("/:id/attachments", async (c) => {
|
|
|
11924
11342
|
return c.json({ error: "No file provided" }, 400);
|
|
11925
11343
|
}
|
|
11926
11344
|
const dir = ensureAttachmentsDir(sessionId);
|
|
11927
|
-
const id =
|
|
11345
|
+
const id = nanoid10(10);
|
|
11928
11346
|
const ext = extname8(file.name) || "";
|
|
11929
11347
|
const safeFilename = `${id}_${basename5(file.name).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
11930
|
-
const filePath =
|
|
11348
|
+
const filePath = join12(dir, safeFilename);
|
|
11931
11349
|
const arrayBuffer = await file.arrayBuffer();
|
|
11932
11350
|
writeFileSync4(filePath, Buffer.from(arrayBuffer));
|
|
11933
11351
|
return c.json({
|
|
@@ -11950,10 +11368,10 @@ sessions2.post("/:id/attachments", async (c) => {
|
|
|
11950
11368
|
return c.json({ error: "Missing filename or data" }, 400);
|
|
11951
11369
|
}
|
|
11952
11370
|
const dir = ensureAttachmentsDir(sessionId);
|
|
11953
|
-
const id =
|
|
11371
|
+
const id = nanoid10(10);
|
|
11954
11372
|
const ext = extname8(body.filename) || "";
|
|
11955
11373
|
const safeFilename = `${id}_${basename5(body.filename).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
11956
|
-
const filePath =
|
|
11374
|
+
const filePath = join12(dir, safeFilename);
|
|
11957
11375
|
let base64Data = body.data;
|
|
11958
11376
|
if (base64Data.includes(",")) {
|
|
11959
11377
|
base64Data = base64Data.split(",")[1];
|
|
@@ -11982,7 +11400,7 @@ sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
|
|
|
11982
11400
|
return c.json({ error: "Session not found" }, 404);
|
|
11983
11401
|
}
|
|
11984
11402
|
const dir = getAttachmentsDir(sessionId);
|
|
11985
|
-
if (!
|
|
11403
|
+
if (!existsSync18(dir)) {
|
|
11986
11404
|
return c.json({ error: "Attachment not found" }, 404);
|
|
11987
11405
|
}
|
|
11988
11406
|
const files = readdirSync3(dir);
|
|
@@ -11990,14 +11408,14 @@ sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
|
|
|
11990
11408
|
if (!file) {
|
|
11991
11409
|
return c.json({ error: "Attachment not found" }, 404);
|
|
11992
11410
|
}
|
|
11993
|
-
const filePath =
|
|
11994
|
-
|
|
11411
|
+
const filePath = join12(dir, file);
|
|
11412
|
+
unlinkSync2(filePath);
|
|
11995
11413
|
return c.json({ success: true, id: attachmentId });
|
|
11996
11414
|
});
|
|
11997
|
-
var filesQuerySchema =
|
|
11998
|
-
query:
|
|
11415
|
+
var filesQuerySchema = z16.object({
|
|
11416
|
+
query: z16.string().optional(),
|
|
11999
11417
|
// Filter query (e.g., "src/com" to match "src/components")
|
|
12000
|
-
limit:
|
|
11418
|
+
limit: z16.string().optional()
|
|
12001
11419
|
// Max results (default 50)
|
|
12002
11420
|
});
|
|
12003
11421
|
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
@@ -12073,7 +11491,7 @@ async function listWorkspaceFiles(baseDir, currentDir, query, limit, results = [
|
|
|
12073
11491
|
const entries = await readdir6(currentDir, { withFileTypes: true });
|
|
12074
11492
|
for (const entry2 of entries) {
|
|
12075
11493
|
if (results.length >= limit * 2) break;
|
|
12076
|
-
const fullPath =
|
|
11494
|
+
const fullPath = join12(currentDir, entry2.name);
|
|
12077
11495
|
const relativePath = relative9(baseDir, fullPath);
|
|
12078
11496
|
if (entry2.isDirectory() && IGNORED_DIRECTORIES.has(entry2.name)) {
|
|
12079
11497
|
continue;
|
|
@@ -12121,7 +11539,7 @@ sessions2.get(
|
|
|
12121
11539
|
return c.json({ error: "Session not found" }, 404);
|
|
12122
11540
|
}
|
|
12123
11541
|
const workingDirectory = session.workingDirectory;
|
|
12124
|
-
if (!
|
|
11542
|
+
if (!existsSync18(workingDirectory)) {
|
|
12125
11543
|
return c.json({
|
|
12126
11544
|
sessionId,
|
|
12127
11545
|
workingDirectory,
|
|
@@ -12234,9 +11652,9 @@ init_session_lock();
|
|
|
12234
11652
|
init_config();
|
|
12235
11653
|
import { Hono as Hono2 } from "hono";
|
|
12236
11654
|
import { zValidator as zValidator2 } from "@hono/zod-validator";
|
|
12237
|
-
import { z as
|
|
12238
|
-
import { existsSync as
|
|
12239
|
-
import { join as
|
|
11655
|
+
import { z as z17 } from "zod";
|
|
11656
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
11657
|
+
import { join as join13 } from "path";
|
|
12240
11658
|
|
|
12241
11659
|
// src/server/resumable-stream.ts
|
|
12242
11660
|
import { createResumableStreamContext } from "resumable-stream/generic";
|
|
@@ -12323,7 +11741,7 @@ var streamContext = createResumableStreamContext({
|
|
|
12323
11741
|
|
|
12324
11742
|
// src/server/routes/agents.ts
|
|
12325
11743
|
init_checkpoints();
|
|
12326
|
-
import { nanoid as
|
|
11744
|
+
import { nanoid as nanoid11 } from "nanoid";
|
|
12327
11745
|
init_stream_proxy();
|
|
12328
11746
|
init_recorder();
|
|
12329
11747
|
init_remote();
|
|
@@ -12415,40 +11833,40 @@ function enrichPromptWithDevtoolsContext(sessionId, prompt) {
|
|
|
12415
11833
|
${prompt}`;
|
|
12416
11834
|
}
|
|
12417
11835
|
var agents = new Hono2();
|
|
12418
|
-
var attachmentSchema =
|
|
12419
|
-
type:
|
|
12420
|
-
data:
|
|
11836
|
+
var attachmentSchema = z17.object({
|
|
11837
|
+
type: z17.enum(["image", "file"]),
|
|
11838
|
+
data: z17.string(),
|
|
12421
11839
|
// base64 data URL or raw base64
|
|
12422
|
-
mediaType:
|
|
12423
|
-
filename:
|
|
11840
|
+
mediaType: z17.string().optional(),
|
|
11841
|
+
filename: z17.string().optional()
|
|
12424
11842
|
});
|
|
12425
|
-
var runPromptSchema =
|
|
12426
|
-
prompt:
|
|
11843
|
+
var runPromptSchema = z17.object({
|
|
11844
|
+
prompt: z17.string(),
|
|
12427
11845
|
// Can be empty if attachments are provided
|
|
12428
|
-
attachments:
|
|
11846
|
+
attachments: z17.array(attachmentSchema).optional()
|
|
12429
11847
|
}).refine(
|
|
12430
11848
|
(data) => data.prompt.trim().length > 0 || data.attachments && data.attachments.length > 0,
|
|
12431
11849
|
{ message: "Either prompt or attachments must be provided" }
|
|
12432
11850
|
);
|
|
12433
|
-
var quickStartSchema =
|
|
12434
|
-
prompt:
|
|
12435
|
-
name:
|
|
12436
|
-
workingDirectory:
|
|
12437
|
-
model:
|
|
12438
|
-
toolApprovals:
|
|
12439
|
-
});
|
|
12440
|
-
var rejectSchema =
|
|
12441
|
-
reason:
|
|
11851
|
+
var quickStartSchema = z17.object({
|
|
11852
|
+
prompt: z17.string().min(1),
|
|
11853
|
+
name: z17.string().optional(),
|
|
11854
|
+
workingDirectory: z17.string().optional(),
|
|
11855
|
+
model: z17.string().optional(),
|
|
11856
|
+
toolApprovals: z17.record(z17.string(), z17.boolean()).optional()
|
|
11857
|
+
});
|
|
11858
|
+
var rejectSchema = z17.object({
|
|
11859
|
+
reason: z17.string().optional()
|
|
12442
11860
|
}).optional();
|
|
12443
11861
|
var streamAbortControllers = /* @__PURE__ */ new Map();
|
|
12444
11862
|
function getAttachmentsDirectory(sessionId) {
|
|
12445
11863
|
const appDataDir = getAppDataDirectory();
|
|
12446
|
-
return
|
|
11864
|
+
return join13(appDataDir, "attachments", sessionId);
|
|
12447
11865
|
}
|
|
12448
11866
|
async function saveAttachmentToDisk(sessionId, attachment, index) {
|
|
12449
11867
|
const attachmentsDir = getAttachmentsDirectory(sessionId);
|
|
12450
|
-
if (!
|
|
12451
|
-
|
|
11868
|
+
if (!existsSync19(attachmentsDir)) {
|
|
11869
|
+
mkdirSync8(attachmentsDir, { recursive: true });
|
|
12452
11870
|
}
|
|
12453
11871
|
let filename = attachment.filename;
|
|
12454
11872
|
if (!filename) {
|
|
@@ -12466,7 +11884,7 @@ async function saveAttachmentToDisk(sessionId, attachment, index) {
|
|
|
12466
11884
|
attachment.mediaType = resized.mediaType;
|
|
12467
11885
|
attachment.data = buffer.toString("base64");
|
|
12468
11886
|
}
|
|
12469
|
-
const filePath =
|
|
11887
|
+
const filePath = join13(attachmentsDir, filename);
|
|
12470
11888
|
writeFileSync5(filePath, buffer);
|
|
12471
11889
|
return filePath;
|
|
12472
11890
|
}
|
|
@@ -12903,7 +12321,7 @@ ${prompt}` });
|
|
|
12903
12321
|
});
|
|
12904
12322
|
} catch {
|
|
12905
12323
|
}
|
|
12906
|
-
const streamId = `stream_${id}_${
|
|
12324
|
+
const streamId = `stream_${id}_${nanoid11(10)}`;
|
|
12907
12325
|
console.log(`[STREAM] Creating stream ${streamId} for session ${id}`);
|
|
12908
12326
|
await activeStreamQueries.create(id, streamId);
|
|
12909
12327
|
const stream = await streamContext.resumableStream(
|
|
@@ -13108,7 +12526,7 @@ agents.post(
|
|
|
13108
12526
|
});
|
|
13109
12527
|
const session = agent.getSession();
|
|
13110
12528
|
const enrichedPrompt = enrichPromptWithDevtoolsContext(session.id, body.prompt);
|
|
13111
|
-
const streamId = `stream_${session.id}_${
|
|
12529
|
+
const streamId = `stream_${session.id}_${nanoid11(10)}`;
|
|
13112
12530
|
await createCheckpoint(session.id, session.workingDirectory, 0);
|
|
13113
12531
|
await activeStreamQueries.create(session.id, streamId);
|
|
13114
12532
|
const createQuickStreamProducer = () => {
|
|
@@ -13375,23 +12793,23 @@ agents.post(
|
|
|
13375
12793
|
});
|
|
13376
12794
|
}
|
|
13377
12795
|
);
|
|
13378
|
-
var browserInputSchema =
|
|
13379
|
-
type:
|
|
13380
|
-
eventType:
|
|
13381
|
-
x:
|
|
13382
|
-
y:
|
|
13383
|
-
button:
|
|
13384
|
-
clickCount:
|
|
13385
|
-
deltaX:
|
|
13386
|
-
deltaY:
|
|
13387
|
-
key:
|
|
13388
|
-
code:
|
|
13389
|
-
text:
|
|
13390
|
-
modifiers:
|
|
13391
|
-
touchPoints:
|
|
13392
|
-
x:
|
|
13393
|
-
y:
|
|
13394
|
-
id:
|
|
12796
|
+
var browserInputSchema = z17.object({
|
|
12797
|
+
type: z17.enum(["input_mouse", "input_keyboard", "input_touch"]),
|
|
12798
|
+
eventType: z17.string(),
|
|
12799
|
+
x: z17.number().optional(),
|
|
12800
|
+
y: z17.number().optional(),
|
|
12801
|
+
button: z17.string().optional(),
|
|
12802
|
+
clickCount: z17.number().optional(),
|
|
12803
|
+
deltaX: z17.number().optional(),
|
|
12804
|
+
deltaY: z17.number().optional(),
|
|
12805
|
+
key: z17.string().optional(),
|
|
12806
|
+
code: z17.string().optional(),
|
|
12807
|
+
text: z17.string().optional(),
|
|
12808
|
+
modifiers: z17.number().optional(),
|
|
12809
|
+
touchPoints: z17.array(z17.object({
|
|
12810
|
+
x: z17.number(),
|
|
12811
|
+
y: z17.number(),
|
|
12812
|
+
id: z17.number().optional()
|
|
13395
12813
|
})).optional()
|
|
13396
12814
|
});
|
|
13397
12815
|
agents.post(
|
|
@@ -13426,27 +12844,27 @@ agents.get("/:id/browser-stream", async (c) => {
|
|
|
13426
12844
|
init_config();
|
|
13427
12845
|
import { Hono as Hono3 } from "hono";
|
|
13428
12846
|
import { zValidator as zValidator3 } from "@hono/zod-validator";
|
|
13429
|
-
import { z as
|
|
13430
|
-
import { readFileSync as
|
|
12847
|
+
import { z as z18 } from "zod";
|
|
12848
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
13431
12849
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
13432
|
-
import { dirname as dirname7, join as
|
|
12850
|
+
import { dirname as dirname7, join as join14 } from "path";
|
|
13433
12851
|
var __filename = fileURLToPath3(import.meta.url);
|
|
13434
12852
|
var __dirname = dirname7(__filename);
|
|
13435
12853
|
var possiblePaths = [
|
|
13436
|
-
|
|
12854
|
+
join14(__dirname, "../package.json"),
|
|
13437
12855
|
// From dist/server -> dist/../package.json
|
|
13438
|
-
|
|
12856
|
+
join14(__dirname, "../../package.json"),
|
|
13439
12857
|
// From dist/server (if nested differently)
|
|
13440
|
-
|
|
12858
|
+
join14(__dirname, "../../../package.json"),
|
|
13441
12859
|
// From src/server/routes (development)
|
|
13442
|
-
|
|
12860
|
+
join14(process.cwd(), "package.json")
|
|
13443
12861
|
// From current working directory
|
|
13444
12862
|
];
|
|
13445
12863
|
var currentVersion = "0.0.0";
|
|
13446
12864
|
var packageName = "sparkecoder";
|
|
13447
12865
|
for (const packageJsonPath of possiblePaths) {
|
|
13448
12866
|
try {
|
|
13449
|
-
const packageJson = JSON.parse(
|
|
12867
|
+
const packageJson = JSON.parse(readFileSync9(packageJsonPath, "utf-8"));
|
|
13450
12868
|
if (packageJson.name === "sparkecoder") {
|
|
13451
12869
|
currentVersion = packageJson.version || "0.0.0";
|
|
13452
12870
|
packageName = packageJson.name || "sparkecoder";
|
|
@@ -13538,9 +12956,9 @@ health.get("/api-keys", async (c) => {
|
|
|
13538
12956
|
supportedProviders: SUPPORTED_PROVIDERS
|
|
13539
12957
|
});
|
|
13540
12958
|
});
|
|
13541
|
-
var setApiKeySchema =
|
|
13542
|
-
provider:
|
|
13543
|
-
apiKey:
|
|
12959
|
+
var setApiKeySchema = z18.object({
|
|
12960
|
+
provider: z18.string(),
|
|
12961
|
+
apiKey: z18.string().min(1)
|
|
13544
12962
|
});
|
|
13545
12963
|
health.post(
|
|
13546
12964
|
"/api-keys",
|
|
@@ -13581,12 +12999,12 @@ init_tmux();
|
|
|
13581
12999
|
init_db();
|
|
13582
13000
|
import { Hono as Hono4 } from "hono";
|
|
13583
13001
|
import { zValidator as zValidator4 } from "@hono/zod-validator";
|
|
13584
|
-
import { z as
|
|
13002
|
+
import { z as z19 } from "zod";
|
|
13585
13003
|
var terminals = new Hono4();
|
|
13586
|
-
var spawnSchema =
|
|
13587
|
-
command:
|
|
13588
|
-
cwd:
|
|
13589
|
-
name:
|
|
13004
|
+
var spawnSchema = z19.object({
|
|
13005
|
+
command: z19.string(),
|
|
13006
|
+
cwd: z19.string().optional(),
|
|
13007
|
+
name: z19.string().optional()
|
|
13590
13008
|
});
|
|
13591
13009
|
terminals.post(
|
|
13592
13010
|
"/:sessionId/terminals",
|
|
@@ -13667,8 +13085,8 @@ terminals.get("/:sessionId/terminals/:terminalId", async (c) => {
|
|
|
13667
13085
|
// We don't track exit codes in tmux mode
|
|
13668
13086
|
});
|
|
13669
13087
|
});
|
|
13670
|
-
var logsQuerySchema =
|
|
13671
|
-
tail:
|
|
13088
|
+
var logsQuerySchema = z19.object({
|
|
13089
|
+
tail: z19.string().optional().transform((v) => v ? parseInt(v, 10) : void 0)
|
|
13672
13090
|
});
|
|
13673
13091
|
terminals.get(
|
|
13674
13092
|
"/:sessionId/terminals/:terminalId/logs",
|
|
@@ -13692,8 +13110,8 @@ terminals.get(
|
|
|
13692
13110
|
});
|
|
13693
13111
|
}
|
|
13694
13112
|
);
|
|
13695
|
-
var killSchema =
|
|
13696
|
-
signal:
|
|
13113
|
+
var killSchema = z19.object({
|
|
13114
|
+
signal: z19.enum(["SIGTERM", "SIGKILL"]).optional()
|
|
13697
13115
|
});
|
|
13698
13116
|
terminals.post(
|
|
13699
13117
|
"/:sessionId/terminals/:terminalId/kill",
|
|
@@ -13707,8 +13125,8 @@ terminals.post(
|
|
|
13707
13125
|
return c.json({ success: true, message: "Terminal killed" });
|
|
13708
13126
|
}
|
|
13709
13127
|
);
|
|
13710
|
-
var writeSchema =
|
|
13711
|
-
input:
|
|
13128
|
+
var writeSchema = z19.object({
|
|
13129
|
+
input: z19.string()
|
|
13712
13130
|
});
|
|
13713
13131
|
terminals.post(
|
|
13714
13132
|
"/:sessionId/terminals/:terminalId/write",
|
|
@@ -13895,23 +13313,23 @@ init_agent();
|
|
|
13895
13313
|
init_config();
|
|
13896
13314
|
import { Hono as Hono5 } from "hono";
|
|
13897
13315
|
import { zValidator as zValidator5 } from "@hono/zod-validator";
|
|
13898
|
-
import { z as
|
|
13899
|
-
import { nanoid as
|
|
13316
|
+
import { z as z20 } from "zod";
|
|
13317
|
+
import { nanoid as nanoid12 } from "nanoid";
|
|
13900
13318
|
init_questions();
|
|
13901
13319
|
var tasks = new Hono5();
|
|
13902
13320
|
var taskAbortControllers = /* @__PURE__ */ new Map();
|
|
13903
|
-
var createTaskSchema =
|
|
13904
|
-
prompt:
|
|
13905
|
-
outputSchema:
|
|
13906
|
-
webhookUrl:
|
|
13907
|
-
model:
|
|
13908
|
-
workingDirectory:
|
|
13909
|
-
name:
|
|
13910
|
-
maxIterations:
|
|
13911
|
-
parentTaskId:
|
|
13321
|
+
var createTaskSchema = z20.object({
|
|
13322
|
+
prompt: z20.string().min(1),
|
|
13323
|
+
outputSchema: z20.record(z20.string(), z20.unknown()),
|
|
13324
|
+
webhookUrl: z20.string().url().optional(),
|
|
13325
|
+
model: z20.string().optional(),
|
|
13326
|
+
workingDirectory: z20.string().optional(),
|
|
13327
|
+
name: z20.string().optional(),
|
|
13328
|
+
maxIterations: z20.number().int().min(1).max(500).optional(),
|
|
13329
|
+
parentTaskId: z20.string().optional(),
|
|
13912
13330
|
/** When set, the spawning orchestrator's session id. Stamped on the
|
|
13913
13331
|
* worker's config so terminal events can wake the orchestrator. */
|
|
13914
|
-
orchestratorSessionId:
|
|
13332
|
+
orchestratorSessionId: z20.string().optional()
|
|
13915
13333
|
});
|
|
13916
13334
|
tasks.post(
|
|
13917
13335
|
"/",
|
|
@@ -13977,7 +13395,7 @@ tasks.post(
|
|
|
13977
13395
|
const taskId = agent.sessionId;
|
|
13978
13396
|
const abortController = new AbortController();
|
|
13979
13397
|
taskAbortControllers.set(taskId, abortController);
|
|
13980
|
-
const streamId = `stream_${taskId}_${
|
|
13398
|
+
const streamId = `stream_${taskId}_${nanoid12(10)}`;
|
|
13981
13399
|
await activeStreamQueries.create(taskId, streamId);
|
|
13982
13400
|
const taskStreamProducer = () => {
|
|
13983
13401
|
const { readable, writable } = new TransformStream();
|
|
@@ -14126,9 +13544,9 @@ tasks.post("/:id/cancel", async (c) => {
|
|
|
14126
13544
|
}
|
|
14127
13545
|
return c.json({ taskId: id, status: "failed", error: "Task cancelled by user" });
|
|
14128
13546
|
});
|
|
14129
|
-
var answerQuestionSchema =
|
|
14130
|
-
answer:
|
|
14131
|
-
answeredBy:
|
|
13547
|
+
var answerQuestionSchema = z20.object({
|
|
13548
|
+
answer: z20.string().min(1),
|
|
13549
|
+
answeredBy: z20.enum(["orchestrator", "user", "system"]).optional()
|
|
14132
13550
|
});
|
|
14133
13551
|
tasks.post(
|
|
14134
13552
|
"/:id/questions/:questionId/answer",
|
|
@@ -14412,7 +13830,7 @@ init_pool();
|
|
|
14412
13830
|
init_webhook_events();
|
|
14413
13831
|
import { Hono as Hono8 } from "hono";
|
|
14414
13832
|
import { zValidator as zValidator6 } from "@hono/zod-validator";
|
|
14415
|
-
import { z as
|
|
13833
|
+
import { z as z21 } from "zod";
|
|
14416
13834
|
var integrations = new Hono8();
|
|
14417
13835
|
var orchestratorRouter = new Hono8();
|
|
14418
13836
|
async function getOrchestratorId() {
|
|
@@ -14435,9 +13853,9 @@ orchestratorRouter.get("/", async (c) => {
|
|
|
14435
13853
|
});
|
|
14436
13854
|
orchestratorRouter.patch(
|
|
14437
13855
|
"/",
|
|
14438
|
-
zValidator6("json",
|
|
14439
|
-
name:
|
|
14440
|
-
personality:
|
|
13856
|
+
zValidator6("json", z21.object({
|
|
13857
|
+
name: z21.string().min(1).optional(),
|
|
13858
|
+
personality: z21.string().optional()
|
|
14441
13859
|
})),
|
|
14442
13860
|
async (c) => {
|
|
14443
13861
|
const id = await getOrchestratorId();
|
|
@@ -14513,15 +13931,15 @@ integrations.get("/", async (c) => {
|
|
|
14513
13931
|
}
|
|
14514
13932
|
});
|
|
14515
13933
|
});
|
|
14516
|
-
var slackConfigSchema =
|
|
14517
|
-
botToken:
|
|
14518
|
-
signingSecret:
|
|
14519
|
-
defaultOrchestratorName:
|
|
14520
|
-
allowedUsers:
|
|
14521
|
-
allowedChannels:
|
|
14522
|
-
allowDmsFromAnyone:
|
|
14523
|
-
deniedReplyEnabled:
|
|
14524
|
-
deniedReplyTemplate:
|
|
13934
|
+
var slackConfigSchema = z21.object({
|
|
13935
|
+
botToken: z21.string().optional(),
|
|
13936
|
+
signingSecret: z21.string().optional(),
|
|
13937
|
+
defaultOrchestratorName: z21.string().optional(),
|
|
13938
|
+
allowedUsers: z21.array(z21.string()).optional(),
|
|
13939
|
+
allowedChannels: z21.array(z21.string()).optional(),
|
|
13940
|
+
allowDmsFromAnyone: z21.boolean().optional(),
|
|
13941
|
+
deniedReplyEnabled: z21.boolean().optional(),
|
|
13942
|
+
deniedReplyTemplate: z21.string().optional()
|
|
14525
13943
|
});
|
|
14526
13944
|
integrations.post("/slack", zValidator6("json", slackConfigSchema), async (c) => {
|
|
14527
13945
|
const body = c.req.valid("json");
|
|
@@ -14550,11 +13968,11 @@ schedulesRouter.get("/", async (c) => {
|
|
|
14550
13968
|
});
|
|
14551
13969
|
schedulesRouter.post(
|
|
14552
13970
|
"/",
|
|
14553
|
-
zValidator6("json",
|
|
14554
|
-
name:
|
|
14555
|
-
cron:
|
|
14556
|
-
prompt:
|
|
14557
|
-
replyChannel:
|
|
13971
|
+
zValidator6("json", z21.object({
|
|
13972
|
+
name: z21.string().min(1),
|
|
13973
|
+
cron: z21.string().min(1),
|
|
13974
|
+
prompt: z21.string().min(1),
|
|
13975
|
+
replyChannel: z21.string().optional()
|
|
14558
13976
|
})),
|
|
14559
13977
|
async (c) => {
|
|
14560
13978
|
const orcId = await getOrchestratorId();
|
|
@@ -14565,12 +13983,12 @@ schedulesRouter.post(
|
|
|
14565
13983
|
);
|
|
14566
13984
|
schedulesRouter.patch(
|
|
14567
13985
|
"/:id",
|
|
14568
|
-
zValidator6("json",
|
|
14569
|
-
name:
|
|
14570
|
-
cron:
|
|
14571
|
-
prompt:
|
|
14572
|
-
enabled:
|
|
14573
|
-
replyChannel:
|
|
13986
|
+
zValidator6("json", z21.object({
|
|
13987
|
+
name: z21.string().optional(),
|
|
13988
|
+
cron: z21.string().optional(),
|
|
13989
|
+
prompt: z21.string().optional(),
|
|
13990
|
+
enabled: z21.boolean().optional(),
|
|
13991
|
+
replyChannel: z21.string().optional()
|
|
14574
13992
|
})),
|
|
14575
13993
|
async (c) => {
|
|
14576
13994
|
const orcId = await getOrchestratorId();
|
|
@@ -14598,10 +14016,10 @@ webhooksRouter.get("/", async (c) => {
|
|
|
14598
14016
|
});
|
|
14599
14017
|
webhooksRouter.post(
|
|
14600
14018
|
"/",
|
|
14601
|
-
zValidator6("json",
|
|
14602
|
-
name:
|
|
14603
|
-
wake:
|
|
14604
|
-
template:
|
|
14019
|
+
zValidator6("json", z21.object({
|
|
14020
|
+
name: z21.string().min(1),
|
|
14021
|
+
wake: z21.enum(["now", "next"]).optional(),
|
|
14022
|
+
template: z21.string().optional()
|
|
14605
14023
|
})),
|
|
14606
14024
|
async (c) => {
|
|
14607
14025
|
const orcId = await getOrchestratorId();
|
|
@@ -14612,11 +14030,11 @@ webhooksRouter.post(
|
|
|
14612
14030
|
);
|
|
14613
14031
|
webhooksRouter.patch(
|
|
14614
14032
|
"/:id",
|
|
14615
|
-
zValidator6("json",
|
|
14616
|
-
name:
|
|
14617
|
-
wake:
|
|
14618
|
-
template:
|
|
14619
|
-
rotateToken:
|
|
14033
|
+
zValidator6("json", z21.object({
|
|
14034
|
+
name: z21.string().optional(),
|
|
14035
|
+
wake: z21.enum(["now", "next"]).optional(),
|
|
14036
|
+
template: z21.string().optional(),
|
|
14037
|
+
rotateToken: z21.boolean().optional()
|
|
14620
14038
|
})),
|
|
14621
14039
|
async (c) => {
|
|
14622
14040
|
const orcId = await getOrchestratorId();
|
|
@@ -14633,22 +14051,22 @@ webhooksRouter.delete("/:id", async (c) => {
|
|
|
14633
14051
|
return c.json({ deleted: ok });
|
|
14634
14052
|
});
|
|
14635
14053
|
var mcpRouter = new Hono8();
|
|
14636
|
-
var mcpServerSchema =
|
|
14637
|
-
name:
|
|
14638
|
-
transport:
|
|
14639
|
-
url:
|
|
14640
|
-
headers:
|
|
14641
|
-
command:
|
|
14642
|
-
args:
|
|
14643
|
-
enabled:
|
|
14644
|
-
});
|
|
14645
|
-
var mcpPatchSchema =
|
|
14646
|
-
name:
|
|
14647
|
-
url:
|
|
14648
|
-
headers:
|
|
14649
|
-
command:
|
|
14650
|
-
args:
|
|
14651
|
-
enabled:
|
|
14054
|
+
var mcpServerSchema = z21.object({
|
|
14055
|
+
name: z21.string().min(1),
|
|
14056
|
+
transport: z21.enum(["http", "sse", "stdio"]),
|
|
14057
|
+
url: z21.string().optional(),
|
|
14058
|
+
headers: z21.record(z21.string(), z21.string()).optional(),
|
|
14059
|
+
command: z21.string().optional(),
|
|
14060
|
+
args: z21.array(z21.string()).optional(),
|
|
14061
|
+
enabled: z21.boolean().optional()
|
|
14062
|
+
});
|
|
14063
|
+
var mcpPatchSchema = z21.object({
|
|
14064
|
+
name: z21.string().optional(),
|
|
14065
|
+
url: z21.string().optional(),
|
|
14066
|
+
headers: z21.record(z21.string(), z21.string()).optional(),
|
|
14067
|
+
command: z21.string().optional(),
|
|
14068
|
+
args: z21.array(z21.string()).optional(),
|
|
14069
|
+
enabled: z21.boolean().optional()
|
|
14652
14070
|
});
|
|
14653
14071
|
mcpRouter.get("/", async (c) => {
|
|
14654
14072
|
const rows = listMcpServers().map((s) => ({
|
|
@@ -14765,10 +14183,10 @@ init_config();
|
|
|
14765
14183
|
init_db();
|
|
14766
14184
|
|
|
14767
14185
|
// src/utils/dependencies.ts
|
|
14768
|
-
import { exec as
|
|
14769
|
-
import { promisify as
|
|
14186
|
+
import { exec as exec6 } from "child_process";
|
|
14187
|
+
import { promisify as promisify6 } from "util";
|
|
14770
14188
|
import { platform as platform2 } from "os";
|
|
14771
|
-
var
|
|
14189
|
+
var execAsync6 = promisify6(exec6);
|
|
14772
14190
|
function getInstallInstructions() {
|
|
14773
14191
|
const os2 = platform2();
|
|
14774
14192
|
if (os2 === "darwin") {
|
|
@@ -14801,7 +14219,7 @@ Install tmux:
|
|
|
14801
14219
|
}
|
|
14802
14220
|
async function checkTmux() {
|
|
14803
14221
|
try {
|
|
14804
|
-
const { stdout } = await
|
|
14222
|
+
const { stdout } = await execAsync6("tmux -V", { timeout: 5e3 });
|
|
14805
14223
|
const version = stdout.trim();
|
|
14806
14224
|
return {
|
|
14807
14225
|
available: true,
|
|
@@ -14850,11 +14268,11 @@ function getWebDirectory() {
|
|
|
14850
14268
|
try {
|
|
14851
14269
|
const currentDir = dirname8(fileURLToPath4(import.meta.url));
|
|
14852
14270
|
const webDir = resolve11(currentDir, "..", "web");
|
|
14853
|
-
if (
|
|
14271
|
+
if (existsSync20(webDir) && existsSync20(join15(webDir, "package.json"))) {
|
|
14854
14272
|
return webDir;
|
|
14855
14273
|
}
|
|
14856
14274
|
const altWebDir = resolve11(currentDir, "..", "..", "web");
|
|
14857
|
-
if (
|
|
14275
|
+
if (existsSync20(altWebDir) && existsSync20(join15(altWebDir, "package.json"))) {
|
|
14858
14276
|
return altWebDir;
|
|
14859
14277
|
}
|
|
14860
14278
|
return null;
|
|
@@ -14912,23 +14330,23 @@ async function findWebPort(preferredPort) {
|
|
|
14912
14330
|
return { port: preferredPort, alreadyRunning: false };
|
|
14913
14331
|
}
|
|
14914
14332
|
function hasProductionBuild(webDir) {
|
|
14915
|
-
const buildIdPath =
|
|
14916
|
-
return
|
|
14333
|
+
const buildIdPath = join15(webDir, ".next", "BUILD_ID");
|
|
14334
|
+
return existsSync20(buildIdPath);
|
|
14917
14335
|
}
|
|
14918
14336
|
function hasSourceFiles(webDir) {
|
|
14919
|
-
const appDir =
|
|
14920
|
-
const pagesDir =
|
|
14921
|
-
const rootAppDir =
|
|
14922
|
-
const rootPagesDir =
|
|
14923
|
-
return
|
|
14337
|
+
const appDir = join15(webDir, "src", "app");
|
|
14338
|
+
const pagesDir = join15(webDir, "src", "pages");
|
|
14339
|
+
const rootAppDir = join15(webDir, "app");
|
|
14340
|
+
const rootPagesDir = join15(webDir, "pages");
|
|
14341
|
+
return existsSync20(appDir) || existsSync20(pagesDir) || existsSync20(rootAppDir) || existsSync20(rootPagesDir);
|
|
14924
14342
|
}
|
|
14925
14343
|
function getStandaloneServerPath(webDir) {
|
|
14926
14344
|
const possiblePaths2 = [
|
|
14927
|
-
|
|
14928
|
-
|
|
14345
|
+
join15(webDir, ".next", "standalone", "server.js"),
|
|
14346
|
+
join15(webDir, ".next", "standalone", "web", "server.js")
|
|
14929
14347
|
];
|
|
14930
14348
|
for (const serverPath of possiblePaths2) {
|
|
14931
|
-
if (
|
|
14349
|
+
if (existsSync20(serverPath)) {
|
|
14932
14350
|
return serverPath;
|
|
14933
14351
|
}
|
|
14934
14352
|
}
|
|
@@ -14968,13 +14386,13 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
14968
14386
|
if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
|
|
14969
14387
|
return { process: null, port: actualPort };
|
|
14970
14388
|
}
|
|
14971
|
-
const usePnpm =
|
|
14972
|
-
const useNpm = !usePnpm &&
|
|
14389
|
+
const usePnpm = existsSync20(join15(webDir, "pnpm-lock.yaml"));
|
|
14390
|
+
const useNpm = !usePnpm && existsSync20(join15(webDir, "package-lock.json"));
|
|
14973
14391
|
const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
|
|
14974
14392
|
const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
|
|
14975
14393
|
const apiUrl = publicUrl || `http://127.0.0.1:${apiPort}`;
|
|
14976
14394
|
const runtimeConfig = { apiBaseUrl: apiUrl };
|
|
14977
|
-
const runtimeConfigPath =
|
|
14395
|
+
const runtimeConfigPath = join15(webDir, "runtime-config.json");
|
|
14978
14396
|
try {
|
|
14979
14397
|
writeFileSync6(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2));
|
|
14980
14398
|
if (!quiet) console.log(` \u{1F4DD} Runtime config written to ${runtimeConfigPath}`);
|
|
@@ -15189,8 +14607,8 @@ async function startServer(options = {}) {
|
|
|
15189
14607
|
if (options.workingDirectory) {
|
|
15190
14608
|
config.resolvedWorkingDirectory = options.workingDirectory;
|
|
15191
14609
|
}
|
|
15192
|
-
if (!
|
|
15193
|
-
|
|
14610
|
+
if (!existsSync20(config.resolvedWorkingDirectory)) {
|
|
14611
|
+
mkdirSync9(config.resolvedWorkingDirectory, { recursive: true });
|
|
15194
14612
|
if (!options.quiet) console.log(`\u{1F4C1} Created agent workspace: ${config.resolvedWorkingDirectory}`);
|
|
15195
14613
|
}
|
|
15196
14614
|
if (!config.resolvedRemoteServer.url) {
|