sparkecoder 0.1.117 → 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 +2 -2
- package/dist/agent/index.js +116 -697
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +566 -1033
- package/dist/cli.js.map +1 -1
- package/dist/db/index.d.ts +2 -2
- package/dist/{index-Bi8Ek02A.d.ts → index-Bcz0aCAR.d.ts} +1 -10
- package/dist/index.d.ts +4 -4
- package/dist/index.js +333 -935
- package/dist/index.js.map +1 -1
- package/dist/{schema-ecQSnCMz.d.ts → schema-BWbWmfDQ.d.ts} +0 -2
- package/dist/server/index.js +333 -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 +1 -167
- 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/{static/vLqK4jK7EKdLCpQ-D6-qL → T8x1J_CS0n9FaWBr5GhLe}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{static/vLqK4jK7EKdLCpQ-D6-qL → T8x1J_CS0n9FaWBr5GhLe}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{static/vLqK4jK7EKdLCpQ-D6-qL → T8x1J_CS0n9FaWBr5GhLe}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{vLqK4jK7EKdLCpQ-D6-qL → static/T8x1J_CS0n9FaWBr5GhLe}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{vLqK4jK7EKdLCpQ-D6-qL → static/T8x1J_CS0n9FaWBr5GhLe}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{vLqK4jK7EKdLCpQ-D6-qL → static/T8x1J_CS0n9FaWBr5GhLe}/_ssgManifest.js +0 -0
- /package/web/.next/static/{vLqK4jK7EKdLCpQ-D6-qL → T8x1J_CS0n9FaWBr5GhLe}/_buildManifest.js +0 -0
- /package/web/.next/static/{vLqK4jK7EKdLCpQ-D6-qL → T8x1J_CS0n9FaWBr5GhLe}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{vLqK4jK7EKdLCpQ-D6-qL → 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,7 +7068,7 @@ 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
|
|
|
@@ -7699,7 +7099,7 @@ You delegate; the worker executes. Stay at the **what** level, not the **how**.
|
|
|
7699
7099
|
**DO** put in the goal:
|
|
7700
7100
|
|
|
7701
7101
|
- The end objective ("open the macOS Weather app and capture the forecast for Anchorage as a screen recording").
|
|
7702
|
-
- 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\`).
|
|
7703
7103
|
- Acceptance criteria ("verify the city name is visible in the screenshot before reporting done").
|
|
7704
7104
|
- Routing back ("post the recording URL to Slack channel C0123 thread 1700.001").
|
|
7705
7105
|
|
|
@@ -7732,7 +7132,7 @@ Bad goal (don't do this):
|
|
|
7732
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'..."
|
|
7733
7133
|
|
|
7734
7134
|
Good goal (do this):
|
|
7735
|
-
> "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."
|
|
7736
7136
|
`;
|
|
7737
7137
|
}
|
|
7738
7138
|
function createSummaryPrompt(conversationHistory) {
|
|
@@ -7951,17 +7351,17 @@ __export(conversation_archive_exports, {
|
|
|
7951
7351
|
getHistoryDir: () => getHistoryDir,
|
|
7952
7352
|
listSessionArchives: () => listSessionArchives
|
|
7953
7353
|
});
|
|
7954
|
-
import { existsSync as
|
|
7955
|
-
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";
|
|
7956
7356
|
function getHistoryDir() {
|
|
7957
|
-
const dir =
|
|
7958
|
-
if (!
|
|
7357
|
+
const dir = join8(ensureAppDataDirectory(), "history");
|
|
7358
|
+
if (!existsSync15(dir)) mkdirSync5(dir, { recursive: true });
|
|
7959
7359
|
return dir;
|
|
7960
7360
|
}
|
|
7961
7361
|
function appendTurn(turn) {
|
|
7962
7362
|
try {
|
|
7963
7363
|
const dir = getHistoryDir();
|
|
7964
|
-
const path =
|
|
7364
|
+
const path = join8(dir, `${turn.sessionId}.jsonl`);
|
|
7965
7365
|
const line = JSON.stringify({
|
|
7966
7366
|
ts: turn.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
7967
7367
|
sessionId: turn.sessionId,
|
|
@@ -7990,7 +7390,7 @@ function flattenContent(content) {
|
|
|
7990
7390
|
}
|
|
7991
7391
|
function listSessionArchives() {
|
|
7992
7392
|
const dir = getHistoryDir();
|
|
7993
|
-
if (!
|
|
7393
|
+
if (!existsSync15(dir)) return [];
|
|
7994
7394
|
return readdirSync2(dir).filter((f) => f.endsWith(".jsonl"));
|
|
7995
7395
|
}
|
|
7996
7396
|
var init_conversation_archive = __esm({
|
|
@@ -8062,6 +7462,18 @@ function repairToolPairing(messages) {
|
|
|
8062
7462
|
}
|
|
8063
7463
|
return repaired;
|
|
8064
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
|
+
}
|
|
8065
7477
|
var TOOL_OUTPUT_TRIM_CHARS, COMPACTABLE_TOOLS, ContextManager;
|
|
8066
7478
|
var init_context = __esm({
|
|
8067
7479
|
"src/agent/context.ts"() {
|
|
@@ -8119,6 +7531,7 @@ ${summaryContent}`
|
|
|
8119
7531
|
];
|
|
8120
7532
|
}
|
|
8121
7533
|
messages = repairToolPairing(messages);
|
|
7534
|
+
messages = ensureEndsWithUserOrTool(messages);
|
|
8122
7535
|
return messages;
|
|
8123
7536
|
}
|
|
8124
7537
|
// ---------------------------------------------------------------------------
|
|
@@ -8312,7 +7725,8 @@ ${summaryContent}`
|
|
|
8312
7725
|
}
|
|
8313
7726
|
}
|
|
8314
7727
|
async addResponseMessages(messages) {
|
|
8315
|
-
|
|
7728
|
+
const safe = repairToolPairing(messages);
|
|
7729
|
+
await messageQueries.addMany(this.sessionId, safe);
|
|
8316
7730
|
try {
|
|
8317
7731
|
const { appendTurn: appendTurn2, flattenContent: flattenContent2 } = await Promise.resolve().then(() => (init_conversation_archive(), conversation_archive_exports));
|
|
8318
7732
|
const { sessionQueries: sessionQueries2 } = await Promise.resolve().then(() => (init_db(), db_exports));
|
|
@@ -8753,7 +8167,7 @@ var init_messenger = __esm({
|
|
|
8753
8167
|
});
|
|
8754
8168
|
|
|
8755
8169
|
// src/orchestrator/schedules-store.ts
|
|
8756
|
-
import { nanoid as
|
|
8170
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
8757
8171
|
async function readOrch(orchestratorSessionId) {
|
|
8758
8172
|
const s = await sessionQueries.getById(orchestratorSessionId);
|
|
8759
8173
|
if (!s) return null;
|
|
@@ -8768,7 +8182,7 @@ async function createSchedule(orchestratorSessionId, input) {
|
|
|
8768
8182
|
const data = await readOrch(orchestratorSessionId);
|
|
8769
8183
|
if (!data) throw new Error("orchestrator session not found");
|
|
8770
8184
|
const row = {
|
|
8771
|
-
id: `sch_${
|
|
8185
|
+
id: `sch_${nanoid4(10)}`,
|
|
8772
8186
|
name: input.name,
|
|
8773
8187
|
cron: input.cron,
|
|
8774
8188
|
prompt: input.prompt,
|
|
@@ -8805,7 +8219,7 @@ var init_schedules_store = __esm({
|
|
|
8805
8219
|
|
|
8806
8220
|
// src/orchestrator/webhooks-store.ts
|
|
8807
8221
|
import { randomBytes } from "crypto";
|
|
8808
|
-
import { nanoid as
|
|
8222
|
+
import { nanoid as nanoid5 } from "nanoid";
|
|
8809
8223
|
function newToken() {
|
|
8810
8224
|
return randomBytes(24).toString("base64url");
|
|
8811
8225
|
}
|
|
@@ -8822,7 +8236,7 @@ async function createWebhook(orchestratorSessionId, input) {
|
|
|
8822
8236
|
const data = await readOrch2(orchestratorSessionId);
|
|
8823
8237
|
if (!data) throw new Error("orchestrator session not found");
|
|
8824
8238
|
const row = {
|
|
8825
|
-
id: `whk_${
|
|
8239
|
+
id: `whk_${nanoid5(10)}`,
|
|
8826
8240
|
name: input.name,
|
|
8827
8241
|
token: newToken(),
|
|
8828
8242
|
wake: input.wake ?? "now",
|
|
@@ -8878,8 +8292,8 @@ var init_webhooks_store = __esm({
|
|
|
8878
8292
|
});
|
|
8879
8293
|
|
|
8880
8294
|
// src/tools/orchestrator-actions.ts
|
|
8881
|
-
import { tool as
|
|
8882
|
-
import { z as
|
|
8295
|
+
import { tool as tool13 } from "ai";
|
|
8296
|
+
import { z as z14 } from "zod";
|
|
8883
8297
|
async function api2(baseUrl, path, init = {}) {
|
|
8884
8298
|
const res = await fetch(`${baseUrl}${path}`, {
|
|
8885
8299
|
method: init.method || "GET",
|
|
@@ -8905,7 +8319,7 @@ function previewMessageContent(content) {
|
|
|
8905
8319
|
return "";
|
|
8906
8320
|
}
|
|
8907
8321
|
function buildAgentTool(opts) {
|
|
8908
|
-
return
|
|
8322
|
+
return tool13({
|
|
8909
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).",
|
|
8910
8324
|
inputSchema: agentInputSchema,
|
|
8911
8325
|
execute: async (input) => {
|
|
@@ -9008,7 +8422,7 @@ function buildAgentTool(opts) {
|
|
|
9008
8422
|
});
|
|
9009
8423
|
}
|
|
9010
8424
|
function buildMessengerTool() {
|
|
9011
|
-
return
|
|
8425
|
+
return tool13({
|
|
9012
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.",
|
|
9013
8427
|
inputSchema: messengerInputSchema,
|
|
9014
8428
|
execute: async (input) => {
|
|
@@ -9030,7 +8444,7 @@ function buildMessengerTool() {
|
|
|
9030
8444
|
});
|
|
9031
8445
|
}
|
|
9032
8446
|
function buildScheduleTool(opts) {
|
|
9033
|
-
return
|
|
8447
|
+
return tool13({
|
|
9034
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.",
|
|
9035
8449
|
inputSchema: scheduleInputSchema,
|
|
9036
8450
|
execute: async (input) => {
|
|
@@ -9073,7 +8487,7 @@ function buildWebhookUrl(opts, token) {
|
|
|
9073
8487
|
return `${base}${webhookPrefix2}/inbox/${token}`;
|
|
9074
8488
|
}
|
|
9075
8489
|
function buildWebhookTool(opts) {
|
|
9076
|
-
return
|
|
8490
|
+
return tool13({
|
|
9077
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.",
|
|
9078
8492
|
inputSchema: webhookInputSchema,
|
|
9079
8493
|
execute: async (input) => {
|
|
@@ -9122,66 +8536,66 @@ var init_orchestrator_actions = __esm({
|
|
|
9122
8536
|
init_schedules_store();
|
|
9123
8537
|
init_config();
|
|
9124
8538
|
init_webhooks_store();
|
|
9125
|
-
AGENT_STATUS_ENUM =
|
|
9126
|
-
agentInputSchema =
|
|
9127
|
-
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."),
|
|
9128
8542
|
// list
|
|
9129
8543
|
status: AGENT_STATUS_ENUM.optional().describe("list only: filter to one status."),
|
|
9130
|
-
limit:
|
|
8544
|
+
limit: z14.number().int().min(1).max(100).optional().describe("list only: max rows."),
|
|
9131
8545
|
// get / message / answer_question / stop
|
|
9132
|
-
id:
|
|
9133
|
-
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."),
|
|
9134
8548
|
// spawn
|
|
9135
|
-
name:
|
|
9136
|
-
goal:
|
|
9137
|
-
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(
|
|
9138
8552
|
'spawn only: JSON Schema for the worker result. Defaults to {type:"object", properties:{summary:{type:"string"}}, required:["summary"]}.'
|
|
9139
8553
|
),
|
|
9140
|
-
model:
|
|
9141
|
-
workingDirectory:
|
|
9142
|
-
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."),
|
|
9143
8557
|
// message
|
|
9144
|
-
text:
|
|
9145
|
-
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."),
|
|
9146
8560
|
// answer_question
|
|
9147
|
-
questionId:
|
|
9148
|
-
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.")
|
|
9149
8563
|
});
|
|
9150
|
-
messengerInputSchema =
|
|
9151
|
-
action:
|
|
8564
|
+
messengerInputSchema = z14.object({
|
|
8565
|
+
action: z14.enum(["list_channels", "post"]),
|
|
9152
8566
|
// post
|
|
9153
|
-
channel:
|
|
9154
|
-
to:
|
|
9155
|
-
text:
|
|
9156
|
-
threadTs:
|
|
9157
|
-
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).")
|
|
9158
8572
|
});
|
|
9159
|
-
scheduleInputSchema =
|
|
9160
|
-
action:
|
|
8573
|
+
scheduleInputSchema = z14.object({
|
|
8574
|
+
action: z14.enum(["create", "list", "update", "delete", "pause", "resume"]),
|
|
9161
8575
|
// create / update
|
|
9162
|
-
name:
|
|
9163
|
-
cron:
|
|
9164
|
-
prompt:
|
|
9165
|
-
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."),
|
|
9166
8580
|
// update / delete / pause / resume
|
|
9167
|
-
id:
|
|
9168
|
-
enabled:
|
|
8581
|
+
id: z14.string().optional().describe("update | delete | pause | resume: schedule id."),
|
|
8582
|
+
enabled: z14.boolean().optional().describe("update only.")
|
|
9169
8583
|
});
|
|
9170
|
-
webhookInputSchema =
|
|
9171
|
-
action:
|
|
9172
|
-
name:
|
|
9173
|
-
wake:
|
|
9174
|
-
template:
|
|
9175
|
-
id:
|
|
9176
|
-
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.")
|
|
9177
8591
|
});
|
|
9178
8592
|
}
|
|
9179
8593
|
});
|
|
9180
8594
|
|
|
9181
8595
|
// src/integrations/mcp/store.ts
|
|
9182
|
-
import { nanoid as
|
|
9183
|
-
import { existsSync as
|
|
9184
|
-
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";
|
|
9185
8599
|
function readServers() {
|
|
9186
8600
|
try {
|
|
9187
8601
|
const cfg = getConfig();
|
|
@@ -9193,12 +8607,12 @@ function readServers() {
|
|
|
9193
8607
|
function refreshMcpServersFromDisk() {
|
|
9194
8608
|
const candidates = [
|
|
9195
8609
|
resolve10(process.cwd(), "sparkecoder.config.json"),
|
|
9196
|
-
|
|
8610
|
+
join9(ensureAppDataDirectory(), "sparkecoder.config.json")
|
|
9197
8611
|
];
|
|
9198
8612
|
for (const path of candidates) {
|
|
9199
|
-
if (!
|
|
8613
|
+
if (!existsSync16(path)) continue;
|
|
9200
8614
|
try {
|
|
9201
|
-
const raw = JSON.parse(
|
|
8615
|
+
const raw = JSON.parse(readFileSync7(path, "utf-8"));
|
|
9202
8616
|
const servers2 = Array.isArray(raw?.mcp?.servers) ? raw.mcp.servers : [];
|
|
9203
8617
|
setMcpServers(servers2);
|
|
9204
8618
|
return servers2;
|
|
@@ -9217,7 +8631,7 @@ function createMcpServer(input) {
|
|
|
9217
8631
|
const all = readServers();
|
|
9218
8632
|
validateInput(input);
|
|
9219
8633
|
const row = {
|
|
9220
|
-
id: `mcp_${
|
|
8634
|
+
id: `mcp_${nanoid6(10)}`,
|
|
9221
8635
|
name: sanitizeName(input.name),
|
|
9222
8636
|
transport: input.transport,
|
|
9223
8637
|
url: input.url,
|
|
@@ -9808,15 +9222,15 @@ var recorder_exports = {};
|
|
|
9808
9222
|
__export(recorder_exports, {
|
|
9809
9223
|
FrameRecorder: () => FrameRecorder
|
|
9810
9224
|
});
|
|
9811
|
-
import { exec as
|
|
9812
|
-
import { promisify as
|
|
9225
|
+
import { exec as exec5 } from "child_process";
|
|
9226
|
+
import { promisify as promisify5 } from "util";
|
|
9813
9227
|
import { writeFile as writeFile5, mkdir as mkdir4, readFile as readFile11, unlink as unlink2, readdir as readdir5, rm } from "fs/promises";
|
|
9814
|
-
import { join as
|
|
9815
|
-
import { tmpdir
|
|
9816
|
-
import { nanoid as
|
|
9228
|
+
import { join as join10 } from "path";
|
|
9229
|
+
import { tmpdir } from "os";
|
|
9230
|
+
import { nanoid as nanoid7 } from "nanoid";
|
|
9817
9231
|
async function checkFfmpeg() {
|
|
9818
9232
|
try {
|
|
9819
|
-
await
|
|
9233
|
+
await execAsync5("ffmpeg -version", { timeout: 5e3 });
|
|
9820
9234
|
return true;
|
|
9821
9235
|
} catch {
|
|
9822
9236
|
return false;
|
|
@@ -9828,11 +9242,11 @@ async function cleanup(dir) {
|
|
|
9828
9242
|
} catch {
|
|
9829
9243
|
}
|
|
9830
9244
|
}
|
|
9831
|
-
var
|
|
9245
|
+
var execAsync5, FrameRecorder;
|
|
9832
9246
|
var init_recorder = __esm({
|
|
9833
9247
|
"src/browser/recorder.ts"() {
|
|
9834
9248
|
"use strict";
|
|
9835
|
-
|
|
9249
|
+
execAsync5 = promisify5(exec5);
|
|
9836
9250
|
FrameRecorder = class {
|
|
9837
9251
|
frames = [];
|
|
9838
9252
|
startTime = null;
|
|
@@ -9868,21 +9282,21 @@ var init_recorder = __esm({
|
|
|
9868
9282
|
*/
|
|
9869
9283
|
async encode() {
|
|
9870
9284
|
if (this.frames.length === 0) return null;
|
|
9871
|
-
const workDir =
|
|
9285
|
+
const workDir = join10(tmpdir(), `sparkecoder-recording-${nanoid7(8)}`);
|
|
9872
9286
|
await mkdir4(workDir, { recursive: true });
|
|
9873
9287
|
try {
|
|
9874
9288
|
for (let i = 0; i < this.frames.length; i++) {
|
|
9875
|
-
const framePath =
|
|
9289
|
+
const framePath = join10(workDir, `frame_${String(i).padStart(6, "0")}.jpg`);
|
|
9876
9290
|
await writeFile5(framePath, this.frames[i].data);
|
|
9877
9291
|
}
|
|
9878
9292
|
const duration = (this.frames[this.frames.length - 1].timestamp - this.frames[0].timestamp) / 1e3;
|
|
9879
9293
|
const fps = duration > 0 ? Math.round(this.frames.length / duration) : 10;
|
|
9880
9294
|
const clampedFps = Math.max(1, Math.min(fps, 30));
|
|
9881
|
-
const outputPath =
|
|
9295
|
+
const outputPath = join10(workDir, `recording_${this.sessionId}.mp4`);
|
|
9882
9296
|
const hasFfmpeg = await checkFfmpeg();
|
|
9883
9297
|
if (hasFfmpeg) {
|
|
9884
|
-
await
|
|
9885
|
-
`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}"`,
|
|
9886
9300
|
{ timeout: 12e4 }
|
|
9887
9301
|
);
|
|
9888
9302
|
} else {
|
|
@@ -9894,7 +9308,7 @@ var init_recorder = __esm({
|
|
|
9894
9308
|
const files = await readdir5(workDir);
|
|
9895
9309
|
for (const f of files) {
|
|
9896
9310
|
if (f.startsWith("frame_")) {
|
|
9897
|
-
await unlink2(
|
|
9311
|
+
await unlink2(join10(workDir, f)).catch(() => {
|
|
9898
9312
|
});
|
|
9899
9313
|
}
|
|
9900
9314
|
}
|
|
@@ -9919,11 +9333,11 @@ var init_recorder = __esm({
|
|
|
9919
9333
|
import {
|
|
9920
9334
|
streamText as streamText2,
|
|
9921
9335
|
generateText as generateText3,
|
|
9922
|
-
tool as
|
|
9336
|
+
tool as tool14,
|
|
9923
9337
|
stepCountIs as stepCountIs2
|
|
9924
9338
|
} from "ai";
|
|
9925
|
-
import { z as
|
|
9926
|
-
import { nanoid as
|
|
9339
|
+
import { z as z15 } from "zod";
|
|
9340
|
+
import { nanoid as nanoid8 } from "nanoid";
|
|
9927
9341
|
function anySignal(signals) {
|
|
9928
9342
|
const ctrl = new AbortController();
|
|
9929
9343
|
for (const s of signals) {
|
|
@@ -9992,14 +9406,10 @@ var init_agent = __esm({
|
|
|
9992
9406
|
*/
|
|
9993
9407
|
async createToolsWithCallbacks(options) {
|
|
9994
9408
|
const config = getConfig();
|
|
9995
|
-
const sessionConfig = this.session.config || {};
|
|
9996
9409
|
const tools = await createTools({
|
|
9997
9410
|
sessionId: this.session.id,
|
|
9998
9411
|
workingDirectory: this.session.workingDirectory,
|
|
9999
9412
|
skillsDirectories: config.resolvedSkillsDirectories,
|
|
10000
|
-
enableComputerUse: sessionConfig.computerUseEnabled === true,
|
|
10001
|
-
computerUseDisplayWidth: sessionConfig.computerUseDisplayWidth,
|
|
10002
|
-
computerUseDisplayHeight: sessionConfig.computerUseDisplayHeight,
|
|
10003
9413
|
onBashProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "bash", data: progress }) : void 0,
|
|
10004
9414
|
onWriteFileProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "write_file", data: progress }) : void 0,
|
|
10005
9415
|
onSearchProgress: options.onToolProgress ? (progress) => options.onToolProgress({ toolName: "explore_agent", data: progress }) : void 0
|
|
@@ -10049,14 +9459,10 @@ var init_agent = __esm({
|
|
|
10049
9459
|
keepRecentMessages: config.context?.keepRecentMessages || 10,
|
|
10050
9460
|
autoSummarize: config.context?.autoSummarize ?? true
|
|
10051
9461
|
});
|
|
10052
|
-
const sessionConfig = session.config || {};
|
|
10053
9462
|
const tools = await createTools({
|
|
10054
9463
|
sessionId: session.id,
|
|
10055
9464
|
workingDirectory: session.workingDirectory,
|
|
10056
|
-
skillsDirectories: config.resolvedSkillsDirectories
|
|
10057
|
-
enableComputerUse: sessionConfig.computerUseEnabled === true,
|
|
10058
|
-
computerUseDisplayWidth: sessionConfig.computerUseDisplayWidth,
|
|
10059
|
-
computerUseDisplayHeight: sessionConfig.computerUseDisplayHeight
|
|
9465
|
+
skillsDirectories: config.resolvedSkillsDirectories
|
|
10060
9466
|
});
|
|
10061
9467
|
if (session.config?.role === "orchestrator") {
|
|
10062
9468
|
const baseUrl = `http://127.0.0.1:${config.server?.port ?? 3141}`;
|
|
@@ -10294,14 +9700,10 @@ ${personality.trim()}`;
|
|
|
10294
9700
|
});
|
|
10295
9701
|
}
|
|
10296
9702
|
};
|
|
10297
|
-
const taskSessionConfig = this.session.config || {};
|
|
10298
9703
|
const taskTools = await createTools({
|
|
10299
9704
|
sessionId: this.session.id,
|
|
10300
9705
|
workingDirectory: this.session.workingDirectory,
|
|
10301
9706
|
skillsDirectories: config.resolvedSkillsDirectories,
|
|
10302
|
-
enableComputerUse: taskSessionConfig.computerUseEnabled === true,
|
|
10303
|
-
computerUseDisplayWidth: taskSessionConfig.computerUseDisplayWidth,
|
|
10304
|
-
computerUseDisplayHeight: taskSessionConfig.computerUseDisplayHeight,
|
|
10305
9707
|
onBashProgress: bashProgressHandler,
|
|
10306
9708
|
onWriteFileProgress: (progress) => {
|
|
10307
9709
|
options.onToolProgress?.({ toolName: "write_file", data: progress });
|
|
@@ -10649,11 +10051,11 @@ ${p.text}` : p.text;
|
|
|
10649
10051
|
const { isRemoteConfigured: isRemoteConfigured2, storageQueries: storageQueries2 } = await Promise.resolve().then(() => (init_remote(), remote_exports));
|
|
10650
10052
|
if (!isRemoteConfigured2()) return [];
|
|
10651
10053
|
const { readFile: readFile12 } = await import("fs/promises");
|
|
10652
|
-
const { join:
|
|
10054
|
+
const { join: join16, basename: basename6 } = await import("path");
|
|
10653
10055
|
const urls = [];
|
|
10654
10056
|
for (const filePath of filePaths) {
|
|
10655
10057
|
try {
|
|
10656
|
-
const fullPath = filePath.startsWith("/") ? filePath :
|
|
10058
|
+
const fullPath = filePath.startsWith("/") ? filePath : join16(this.session.workingDirectory, filePath);
|
|
10657
10059
|
const fileName = basename6(fullPath);
|
|
10658
10060
|
const ext = fileName.split(".").pop()?.toLowerCase() || "";
|
|
10659
10061
|
const mimeMap = {
|
|
@@ -10711,11 +10113,11 @@ ${p.text}` : p.text;
|
|
|
10711
10113
|
wrappedTools[name] = originalTool;
|
|
10712
10114
|
continue;
|
|
10713
10115
|
}
|
|
10714
|
-
wrappedTools[name] =
|
|
10116
|
+
wrappedTools[name] = tool14({
|
|
10715
10117
|
description: originalTool.description || "",
|
|
10716
|
-
inputSchema: originalTool.inputSchema ||
|
|
10118
|
+
inputSchema: originalTool.inputSchema || z15.object({}),
|
|
10717
10119
|
execute: async (input, toolOptions) => {
|
|
10718
|
-
const toolCallId = toolOptions.toolCallId ||
|
|
10120
|
+
const toolCallId = toolOptions.toolCallId || nanoid8();
|
|
10719
10121
|
const execution = toolExecutionQueries.create({
|
|
10720
10122
|
sessionId: this.session.id,
|
|
10721
10123
|
toolName: name,
|
|
@@ -10733,10 +10135,10 @@ ${p.text}` : p.text;
|
|
|
10733
10135
|
const resolverData = approvalResolvers.get(toolCallId);
|
|
10734
10136
|
approvalResolvers.delete(toolCallId);
|
|
10735
10137
|
this.pendingApprovals.delete(toolCallId);
|
|
10736
|
-
const
|
|
10138
|
+
const exec7 = await execution;
|
|
10737
10139
|
if (!approved) {
|
|
10738
10140
|
const reason = resolverData?.reason || "User rejected the tool execution";
|
|
10739
|
-
await toolExecutionQueries.reject(
|
|
10141
|
+
await toolExecutionQueries.reject(exec7.id);
|
|
10740
10142
|
await sessionQueries.updateStatus(this.session.id, "active");
|
|
10741
10143
|
return {
|
|
10742
10144
|
status: "rejected",
|
|
@@ -10746,14 +10148,14 @@ ${p.text}` : p.text;
|
|
|
10746
10148
|
message: `Tool "${name}" was rejected by the user. Reason: ${reason}`
|
|
10747
10149
|
};
|
|
10748
10150
|
}
|
|
10749
|
-
await toolExecutionQueries.approve(
|
|
10151
|
+
await toolExecutionQueries.approve(exec7.id);
|
|
10750
10152
|
await sessionQueries.updateStatus(this.session.id, "active");
|
|
10751
10153
|
try {
|
|
10752
10154
|
const result = await originalTool.execute(input, toolOptions);
|
|
10753
|
-
await toolExecutionQueries.complete(
|
|
10155
|
+
await toolExecutionQueries.complete(exec7.id, result);
|
|
10754
10156
|
return result;
|
|
10755
10157
|
} catch (error) {
|
|
10756
|
-
await toolExecutionQueries.complete(
|
|
10158
|
+
await toolExecutionQueries.complete(exec7.id, null, error.message);
|
|
10757
10159
|
throw error;
|
|
10758
10160
|
}
|
|
10759
10161
|
}
|
|
@@ -10858,19 +10260,19 @@ var init_session_lock = __esm({
|
|
|
10858
10260
|
});
|
|
10859
10261
|
|
|
10860
10262
|
// src/orchestrator/webhook-events.ts
|
|
10861
|
-
import { existsSync as
|
|
10862
|
-
import { dirname as dirname6, join as
|
|
10863
|
-
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";
|
|
10864
10266
|
function logFilePath() {
|
|
10865
|
-
return
|
|
10267
|
+
return join11(getAppDataDirectory(), "webhook-events.jsonl");
|
|
10866
10268
|
}
|
|
10867
10269
|
function ensureLoaded() {
|
|
10868
10270
|
if (cache !== null) return cache;
|
|
10869
10271
|
cache = [];
|
|
10870
10272
|
try {
|
|
10871
10273
|
const p = logFilePath();
|
|
10872
|
-
if (!
|
|
10873
|
-
const lines =
|
|
10274
|
+
if (!existsSync17(p)) return cache;
|
|
10275
|
+
const lines = readFileSync8(p, "utf-8").split("\n").filter(Boolean);
|
|
10874
10276
|
for (const line of lines) {
|
|
10875
10277
|
try {
|
|
10876
10278
|
cache.push(JSON.parse(line));
|
|
@@ -10894,14 +10296,14 @@ function appendEvent(ev) {
|
|
|
10894
10296
|
if (list.length > MAX_EVENTS) list.shift();
|
|
10895
10297
|
try {
|
|
10896
10298
|
const p = logFilePath();
|
|
10897
|
-
|
|
10299
|
+
mkdirSync6(dirname6(p), { recursive: true });
|
|
10898
10300
|
appendFileSync3(p, JSON.stringify(ev) + "\n");
|
|
10899
10301
|
} catch {
|
|
10900
10302
|
}
|
|
10901
10303
|
}
|
|
10902
10304
|
function recordEvent(ev) {
|
|
10903
10305
|
const full = {
|
|
10904
|
-
id: ev.id ??
|
|
10306
|
+
id: ev.id ?? nanoid9(),
|
|
10905
10307
|
ts: ev.ts ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
10906
10308
|
source: ev.source,
|
|
10907
10309
|
status: ev.status,
|
|
@@ -10925,7 +10327,7 @@ function updateEvent(id, patch) {
|
|
|
10925
10327
|
list[i] = { ...list[i], ...patch };
|
|
10926
10328
|
try {
|
|
10927
10329
|
const p = logFilePath();
|
|
10928
|
-
|
|
10330
|
+
mkdirSync6(dirname6(p), { recursive: true });
|
|
10929
10331
|
writeFileSync3(p, list.map((e) => JSON.stringify(e)).join("\n") + "\n");
|
|
10930
10332
|
} catch {
|
|
10931
10333
|
}
|
|
@@ -11232,8 +10634,8 @@ import { Hono as Hono9 } from "hono";
|
|
|
11232
10634
|
import { serve } from "@hono/node-server";
|
|
11233
10635
|
import { cors } from "hono/cors";
|
|
11234
10636
|
import { logger } from "hono/logger";
|
|
11235
|
-
import { existsSync as
|
|
11236
|
-
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";
|
|
11237
10639
|
import { spawn as spawn2 } from "child_process";
|
|
11238
10640
|
import { createServer as createNetServer } from "net";
|
|
11239
10641
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -11246,11 +10648,11 @@ init_tmux();
|
|
|
11246
10648
|
init_checkpoints();
|
|
11247
10649
|
import { Hono } from "hono";
|
|
11248
10650
|
import { zValidator } from "@hono/zod-validator";
|
|
11249
|
-
import { z as
|
|
11250
|
-
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";
|
|
11251
10653
|
import { readdir as readdir6 } from "fs/promises";
|
|
11252
|
-
import { join as
|
|
11253
|
-
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";
|
|
11254
10656
|
|
|
11255
10657
|
// src/tasks/agent-status.ts
|
|
11256
10658
|
init_questions();
|
|
@@ -11308,22 +10710,22 @@ function cleanupPendingInputs() {
|
|
|
11308
10710
|
}
|
|
11309
10711
|
}
|
|
11310
10712
|
}
|
|
11311
|
-
var createSessionSchema =
|
|
11312
|
-
name:
|
|
11313
|
-
workingDirectory:
|
|
11314
|
-
model:
|
|
11315
|
-
toolApprovals:
|
|
11316
|
-
// Optional full session-config passthrough (
|
|
11317
|
-
config:
|
|
11318
|
-
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()
|
|
11319
10721
|
});
|
|
11320
|
-
var paginationQuerySchema =
|
|
11321
|
-
limit:
|
|
11322
|
-
offset:
|
|
11323
|
-
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()
|
|
11324
10726
|
});
|
|
11325
|
-
var messagesQuerySchema =
|
|
11326
|
-
limit:
|
|
10727
|
+
var messagesQuerySchema = z16.object({
|
|
10728
|
+
limit: z16.string().optional()
|
|
11327
10729
|
});
|
|
11328
10730
|
sessions2.get(
|
|
11329
10731
|
"/",
|
|
@@ -11392,15 +10794,11 @@ sessions2.post(
|
|
|
11392
10794
|
async (c) => {
|
|
11393
10795
|
const body = c.req.valid("json");
|
|
11394
10796
|
const config = getConfig();
|
|
11395
|
-
const cuDefault = process.env.SPARKECODER_COMPUTER_USE === "1";
|
|
11396
10797
|
const baseConfig = body.config || {};
|
|
11397
10798
|
const mergedConfig = {
|
|
11398
10799
|
...baseConfig,
|
|
11399
10800
|
...body.toolApprovals ? { toolApprovals: body.toolApprovals } : {},
|
|
11400
|
-
...body.role ? { role: body.role } : {}
|
|
11401
|
-
// Turn on computer use by default if the server was launched with --enable-computer-use,
|
|
11402
|
-
// unless the client explicitly provided a value.
|
|
11403
|
-
...cuDefault && baseConfig.computerUseEnabled === void 0 ? { computerUseEnabled: true } : {}
|
|
10801
|
+
...body.role ? { role: body.role } : {}
|
|
11404
10802
|
};
|
|
11405
10803
|
const agent = await Agent.create({
|
|
11406
10804
|
name: body.name,
|
|
@@ -11577,10 +10975,10 @@ sessions2.get("/:id/tools", async (c) => {
|
|
|
11577
10975
|
count: executions.length
|
|
11578
10976
|
});
|
|
11579
10977
|
});
|
|
11580
|
-
var updateSessionSchema =
|
|
11581
|
-
model:
|
|
11582
|
-
name:
|
|
11583
|
-
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()
|
|
11584
10982
|
});
|
|
11585
10983
|
sessions2.patch(
|
|
11586
10984
|
"/:id",
|
|
@@ -11650,10 +11048,10 @@ sessions2.post("/:id/clear", async (c) => {
|
|
|
11650
11048
|
await agent.clearContext();
|
|
11651
11049
|
return c.json({ success: true, sessionId: id });
|
|
11652
11050
|
});
|
|
11653
|
-
var injectMessageSchema =
|
|
11654
|
-
text:
|
|
11655
|
-
source:
|
|
11656
|
-
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()
|
|
11657
11055
|
});
|
|
11658
11056
|
sessions2.post(
|
|
11659
11057
|
"/:id/messages",
|
|
@@ -11667,8 +11065,8 @@ sessions2.post(
|
|
|
11667
11065
|
return c.json({ success: true, sessionId: id, queued: true, force });
|
|
11668
11066
|
}
|
|
11669
11067
|
);
|
|
11670
|
-
var pendingInputSchema =
|
|
11671
|
-
text:
|
|
11068
|
+
var pendingInputSchema = z16.object({
|
|
11069
|
+
text: z16.string()
|
|
11672
11070
|
});
|
|
11673
11071
|
sessions2.post(
|
|
11674
11072
|
"/:id/pending-input",
|
|
@@ -11699,13 +11097,13 @@ sessions2.get("/:id/pending-input", async (c) => {
|
|
|
11699
11097
|
createdAt: pending.createdAt.toISOString()
|
|
11700
11098
|
});
|
|
11701
11099
|
});
|
|
11702
|
-
var devtoolsContextSchema =
|
|
11703
|
-
url:
|
|
11704
|
-
path:
|
|
11705
|
-
pageName:
|
|
11706
|
-
screenWidth:
|
|
11707
|
-
screenHeight:
|
|
11708
|
-
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()
|
|
11709
11107
|
});
|
|
11710
11108
|
sessions2.post(
|
|
11711
11109
|
"/:id/devtools-context",
|
|
@@ -11891,12 +11289,12 @@ sessions2.get("/:id/diff/:filePath", async (c) => {
|
|
|
11891
11289
|
});
|
|
11892
11290
|
function getAttachmentsDir(sessionId) {
|
|
11893
11291
|
const appDataDir = getAppDataDirectory();
|
|
11894
|
-
return
|
|
11292
|
+
return join12(appDataDir, "attachments", sessionId);
|
|
11895
11293
|
}
|
|
11896
11294
|
function ensureAttachmentsDir(sessionId) {
|
|
11897
11295
|
const dir = getAttachmentsDir(sessionId);
|
|
11898
|
-
if (!
|
|
11899
|
-
|
|
11296
|
+
if (!existsSync18(dir)) {
|
|
11297
|
+
mkdirSync7(dir, { recursive: true });
|
|
11900
11298
|
}
|
|
11901
11299
|
return dir;
|
|
11902
11300
|
}
|
|
@@ -11907,12 +11305,12 @@ sessions2.get("/:id/attachments", async (c) => {
|
|
|
11907
11305
|
return c.json({ error: "Session not found" }, 404);
|
|
11908
11306
|
}
|
|
11909
11307
|
const dir = getAttachmentsDir(sessionId);
|
|
11910
|
-
if (!
|
|
11308
|
+
if (!existsSync18(dir)) {
|
|
11911
11309
|
return c.json({ sessionId, attachments: [], count: 0 });
|
|
11912
11310
|
}
|
|
11913
11311
|
const files = readdirSync3(dir);
|
|
11914
11312
|
const attachments = files.map((filename) => {
|
|
11915
|
-
const filePath =
|
|
11313
|
+
const filePath = join12(dir, filename);
|
|
11916
11314
|
const stats = statSync2(filePath);
|
|
11917
11315
|
return {
|
|
11918
11316
|
id: filename.split("_")[0],
|
|
@@ -11944,10 +11342,10 @@ sessions2.post("/:id/attachments", async (c) => {
|
|
|
11944
11342
|
return c.json({ error: "No file provided" }, 400);
|
|
11945
11343
|
}
|
|
11946
11344
|
const dir = ensureAttachmentsDir(sessionId);
|
|
11947
|
-
const id =
|
|
11345
|
+
const id = nanoid10(10);
|
|
11948
11346
|
const ext = extname8(file.name) || "";
|
|
11949
11347
|
const safeFilename = `${id}_${basename5(file.name).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
11950
|
-
const filePath =
|
|
11348
|
+
const filePath = join12(dir, safeFilename);
|
|
11951
11349
|
const arrayBuffer = await file.arrayBuffer();
|
|
11952
11350
|
writeFileSync4(filePath, Buffer.from(arrayBuffer));
|
|
11953
11351
|
return c.json({
|
|
@@ -11970,10 +11368,10 @@ sessions2.post("/:id/attachments", async (c) => {
|
|
|
11970
11368
|
return c.json({ error: "Missing filename or data" }, 400);
|
|
11971
11369
|
}
|
|
11972
11370
|
const dir = ensureAttachmentsDir(sessionId);
|
|
11973
|
-
const id =
|
|
11371
|
+
const id = nanoid10(10);
|
|
11974
11372
|
const ext = extname8(body.filename) || "";
|
|
11975
11373
|
const safeFilename = `${id}_${basename5(body.filename).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
11976
|
-
const filePath =
|
|
11374
|
+
const filePath = join12(dir, safeFilename);
|
|
11977
11375
|
let base64Data = body.data;
|
|
11978
11376
|
if (base64Data.includes(",")) {
|
|
11979
11377
|
base64Data = base64Data.split(",")[1];
|
|
@@ -12002,7 +11400,7 @@ sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
|
|
|
12002
11400
|
return c.json({ error: "Session not found" }, 404);
|
|
12003
11401
|
}
|
|
12004
11402
|
const dir = getAttachmentsDir(sessionId);
|
|
12005
|
-
if (!
|
|
11403
|
+
if (!existsSync18(dir)) {
|
|
12006
11404
|
return c.json({ error: "Attachment not found" }, 404);
|
|
12007
11405
|
}
|
|
12008
11406
|
const files = readdirSync3(dir);
|
|
@@ -12010,14 +11408,14 @@ sessions2.delete("/:id/attachments/:attachmentId", async (c) => {
|
|
|
12010
11408
|
if (!file) {
|
|
12011
11409
|
return c.json({ error: "Attachment not found" }, 404);
|
|
12012
11410
|
}
|
|
12013
|
-
const filePath =
|
|
12014
|
-
|
|
11411
|
+
const filePath = join12(dir, file);
|
|
11412
|
+
unlinkSync2(filePath);
|
|
12015
11413
|
return c.json({ success: true, id: attachmentId });
|
|
12016
11414
|
});
|
|
12017
|
-
var filesQuerySchema =
|
|
12018
|
-
query:
|
|
11415
|
+
var filesQuerySchema = z16.object({
|
|
11416
|
+
query: z16.string().optional(),
|
|
12019
11417
|
// Filter query (e.g., "src/com" to match "src/components")
|
|
12020
|
-
limit:
|
|
11418
|
+
limit: z16.string().optional()
|
|
12021
11419
|
// Max results (default 50)
|
|
12022
11420
|
});
|
|
12023
11421
|
var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
@@ -12093,7 +11491,7 @@ async function listWorkspaceFiles(baseDir, currentDir, query, limit, results = [
|
|
|
12093
11491
|
const entries = await readdir6(currentDir, { withFileTypes: true });
|
|
12094
11492
|
for (const entry2 of entries) {
|
|
12095
11493
|
if (results.length >= limit * 2) break;
|
|
12096
|
-
const fullPath =
|
|
11494
|
+
const fullPath = join12(currentDir, entry2.name);
|
|
12097
11495
|
const relativePath = relative9(baseDir, fullPath);
|
|
12098
11496
|
if (entry2.isDirectory() && IGNORED_DIRECTORIES.has(entry2.name)) {
|
|
12099
11497
|
continue;
|
|
@@ -12141,7 +11539,7 @@ sessions2.get(
|
|
|
12141
11539
|
return c.json({ error: "Session not found" }, 404);
|
|
12142
11540
|
}
|
|
12143
11541
|
const workingDirectory = session.workingDirectory;
|
|
12144
|
-
if (!
|
|
11542
|
+
if (!existsSync18(workingDirectory)) {
|
|
12145
11543
|
return c.json({
|
|
12146
11544
|
sessionId,
|
|
12147
11545
|
workingDirectory,
|
|
@@ -12254,9 +11652,9 @@ init_session_lock();
|
|
|
12254
11652
|
init_config();
|
|
12255
11653
|
import { Hono as Hono2 } from "hono";
|
|
12256
11654
|
import { zValidator as zValidator2 } from "@hono/zod-validator";
|
|
12257
|
-
import { z as
|
|
12258
|
-
import { existsSync as
|
|
12259
|
-
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";
|
|
12260
11658
|
|
|
12261
11659
|
// src/server/resumable-stream.ts
|
|
12262
11660
|
import { createResumableStreamContext } from "resumable-stream/generic";
|
|
@@ -12343,7 +11741,7 @@ var streamContext = createResumableStreamContext({
|
|
|
12343
11741
|
|
|
12344
11742
|
// src/server/routes/agents.ts
|
|
12345
11743
|
init_checkpoints();
|
|
12346
|
-
import { nanoid as
|
|
11744
|
+
import { nanoid as nanoid11 } from "nanoid";
|
|
12347
11745
|
init_stream_proxy();
|
|
12348
11746
|
init_recorder();
|
|
12349
11747
|
init_remote();
|
|
@@ -12435,40 +11833,40 @@ function enrichPromptWithDevtoolsContext(sessionId, prompt) {
|
|
|
12435
11833
|
${prompt}`;
|
|
12436
11834
|
}
|
|
12437
11835
|
var agents = new Hono2();
|
|
12438
|
-
var attachmentSchema =
|
|
12439
|
-
type:
|
|
12440
|
-
data:
|
|
11836
|
+
var attachmentSchema = z17.object({
|
|
11837
|
+
type: z17.enum(["image", "file"]),
|
|
11838
|
+
data: z17.string(),
|
|
12441
11839
|
// base64 data URL or raw base64
|
|
12442
|
-
mediaType:
|
|
12443
|
-
filename:
|
|
11840
|
+
mediaType: z17.string().optional(),
|
|
11841
|
+
filename: z17.string().optional()
|
|
12444
11842
|
});
|
|
12445
|
-
var runPromptSchema =
|
|
12446
|
-
prompt:
|
|
11843
|
+
var runPromptSchema = z17.object({
|
|
11844
|
+
prompt: z17.string(),
|
|
12447
11845
|
// Can be empty if attachments are provided
|
|
12448
|
-
attachments:
|
|
11846
|
+
attachments: z17.array(attachmentSchema).optional()
|
|
12449
11847
|
}).refine(
|
|
12450
11848
|
(data) => data.prompt.trim().length > 0 || data.attachments && data.attachments.length > 0,
|
|
12451
11849
|
{ message: "Either prompt or attachments must be provided" }
|
|
12452
11850
|
);
|
|
12453
|
-
var quickStartSchema =
|
|
12454
|
-
prompt:
|
|
12455
|
-
name:
|
|
12456
|
-
workingDirectory:
|
|
12457
|
-
model:
|
|
12458
|
-
toolApprovals:
|
|
12459
|
-
});
|
|
12460
|
-
var rejectSchema =
|
|
12461
|
-
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()
|
|
12462
11860
|
}).optional();
|
|
12463
11861
|
var streamAbortControllers = /* @__PURE__ */ new Map();
|
|
12464
11862
|
function getAttachmentsDirectory(sessionId) {
|
|
12465
11863
|
const appDataDir = getAppDataDirectory();
|
|
12466
|
-
return
|
|
11864
|
+
return join13(appDataDir, "attachments", sessionId);
|
|
12467
11865
|
}
|
|
12468
11866
|
async function saveAttachmentToDisk(sessionId, attachment, index) {
|
|
12469
11867
|
const attachmentsDir = getAttachmentsDirectory(sessionId);
|
|
12470
|
-
if (!
|
|
12471
|
-
|
|
11868
|
+
if (!existsSync19(attachmentsDir)) {
|
|
11869
|
+
mkdirSync8(attachmentsDir, { recursive: true });
|
|
12472
11870
|
}
|
|
12473
11871
|
let filename = attachment.filename;
|
|
12474
11872
|
if (!filename) {
|
|
@@ -12486,7 +11884,7 @@ async function saveAttachmentToDisk(sessionId, attachment, index) {
|
|
|
12486
11884
|
attachment.mediaType = resized.mediaType;
|
|
12487
11885
|
attachment.data = buffer.toString("base64");
|
|
12488
11886
|
}
|
|
12489
|
-
const filePath =
|
|
11887
|
+
const filePath = join13(attachmentsDir, filename);
|
|
12490
11888
|
writeFileSync5(filePath, buffer);
|
|
12491
11889
|
return filePath;
|
|
12492
11890
|
}
|
|
@@ -12923,7 +12321,7 @@ ${prompt}` });
|
|
|
12923
12321
|
});
|
|
12924
12322
|
} catch {
|
|
12925
12323
|
}
|
|
12926
|
-
const streamId = `stream_${id}_${
|
|
12324
|
+
const streamId = `stream_${id}_${nanoid11(10)}`;
|
|
12927
12325
|
console.log(`[STREAM] Creating stream ${streamId} for session ${id}`);
|
|
12928
12326
|
await activeStreamQueries.create(id, streamId);
|
|
12929
12327
|
const stream = await streamContext.resumableStream(
|
|
@@ -13128,7 +12526,7 @@ agents.post(
|
|
|
13128
12526
|
});
|
|
13129
12527
|
const session = agent.getSession();
|
|
13130
12528
|
const enrichedPrompt = enrichPromptWithDevtoolsContext(session.id, body.prompt);
|
|
13131
|
-
const streamId = `stream_${session.id}_${
|
|
12529
|
+
const streamId = `stream_${session.id}_${nanoid11(10)}`;
|
|
13132
12530
|
await createCheckpoint(session.id, session.workingDirectory, 0);
|
|
13133
12531
|
await activeStreamQueries.create(session.id, streamId);
|
|
13134
12532
|
const createQuickStreamProducer = () => {
|
|
@@ -13395,23 +12793,23 @@ agents.post(
|
|
|
13395
12793
|
});
|
|
13396
12794
|
}
|
|
13397
12795
|
);
|
|
13398
|
-
var browserInputSchema =
|
|
13399
|
-
type:
|
|
13400
|
-
eventType:
|
|
13401
|
-
x:
|
|
13402
|
-
y:
|
|
13403
|
-
button:
|
|
13404
|
-
clickCount:
|
|
13405
|
-
deltaX:
|
|
13406
|
-
deltaY:
|
|
13407
|
-
key:
|
|
13408
|
-
code:
|
|
13409
|
-
text:
|
|
13410
|
-
modifiers:
|
|
13411
|
-
touchPoints:
|
|
13412
|
-
x:
|
|
13413
|
-
y:
|
|
13414
|
-
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()
|
|
13415
12813
|
})).optional()
|
|
13416
12814
|
});
|
|
13417
12815
|
agents.post(
|
|
@@ -13446,27 +12844,27 @@ agents.get("/:id/browser-stream", async (c) => {
|
|
|
13446
12844
|
init_config();
|
|
13447
12845
|
import { Hono as Hono3 } from "hono";
|
|
13448
12846
|
import { zValidator as zValidator3 } from "@hono/zod-validator";
|
|
13449
|
-
import { z as
|
|
13450
|
-
import { readFileSync as
|
|
12847
|
+
import { z as z18 } from "zod";
|
|
12848
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
13451
12849
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
13452
|
-
import { dirname as dirname7, join as
|
|
12850
|
+
import { dirname as dirname7, join as join14 } from "path";
|
|
13453
12851
|
var __filename = fileURLToPath3(import.meta.url);
|
|
13454
12852
|
var __dirname = dirname7(__filename);
|
|
13455
12853
|
var possiblePaths = [
|
|
13456
|
-
|
|
12854
|
+
join14(__dirname, "../package.json"),
|
|
13457
12855
|
// From dist/server -> dist/../package.json
|
|
13458
|
-
|
|
12856
|
+
join14(__dirname, "../../package.json"),
|
|
13459
12857
|
// From dist/server (if nested differently)
|
|
13460
|
-
|
|
12858
|
+
join14(__dirname, "../../../package.json"),
|
|
13461
12859
|
// From src/server/routes (development)
|
|
13462
|
-
|
|
12860
|
+
join14(process.cwd(), "package.json")
|
|
13463
12861
|
// From current working directory
|
|
13464
12862
|
];
|
|
13465
12863
|
var currentVersion = "0.0.0";
|
|
13466
12864
|
var packageName = "sparkecoder";
|
|
13467
12865
|
for (const packageJsonPath of possiblePaths) {
|
|
13468
12866
|
try {
|
|
13469
|
-
const packageJson = JSON.parse(
|
|
12867
|
+
const packageJson = JSON.parse(readFileSync9(packageJsonPath, "utf-8"));
|
|
13470
12868
|
if (packageJson.name === "sparkecoder") {
|
|
13471
12869
|
currentVersion = packageJson.version || "0.0.0";
|
|
13472
12870
|
packageName = packageJson.name || "sparkecoder";
|
|
@@ -13558,9 +12956,9 @@ health.get("/api-keys", async (c) => {
|
|
|
13558
12956
|
supportedProviders: SUPPORTED_PROVIDERS
|
|
13559
12957
|
});
|
|
13560
12958
|
});
|
|
13561
|
-
var setApiKeySchema =
|
|
13562
|
-
provider:
|
|
13563
|
-
apiKey:
|
|
12959
|
+
var setApiKeySchema = z18.object({
|
|
12960
|
+
provider: z18.string(),
|
|
12961
|
+
apiKey: z18.string().min(1)
|
|
13564
12962
|
});
|
|
13565
12963
|
health.post(
|
|
13566
12964
|
"/api-keys",
|
|
@@ -13601,12 +12999,12 @@ init_tmux();
|
|
|
13601
12999
|
init_db();
|
|
13602
13000
|
import { Hono as Hono4 } from "hono";
|
|
13603
13001
|
import { zValidator as zValidator4 } from "@hono/zod-validator";
|
|
13604
|
-
import { z as
|
|
13002
|
+
import { z as z19 } from "zod";
|
|
13605
13003
|
var terminals = new Hono4();
|
|
13606
|
-
var spawnSchema =
|
|
13607
|
-
command:
|
|
13608
|
-
cwd:
|
|
13609
|
-
name:
|
|
13004
|
+
var spawnSchema = z19.object({
|
|
13005
|
+
command: z19.string(),
|
|
13006
|
+
cwd: z19.string().optional(),
|
|
13007
|
+
name: z19.string().optional()
|
|
13610
13008
|
});
|
|
13611
13009
|
terminals.post(
|
|
13612
13010
|
"/:sessionId/terminals",
|
|
@@ -13687,8 +13085,8 @@ terminals.get("/:sessionId/terminals/:terminalId", async (c) => {
|
|
|
13687
13085
|
// We don't track exit codes in tmux mode
|
|
13688
13086
|
});
|
|
13689
13087
|
});
|
|
13690
|
-
var logsQuerySchema =
|
|
13691
|
-
tail:
|
|
13088
|
+
var logsQuerySchema = z19.object({
|
|
13089
|
+
tail: z19.string().optional().transform((v) => v ? parseInt(v, 10) : void 0)
|
|
13692
13090
|
});
|
|
13693
13091
|
terminals.get(
|
|
13694
13092
|
"/:sessionId/terminals/:terminalId/logs",
|
|
@@ -13712,8 +13110,8 @@ terminals.get(
|
|
|
13712
13110
|
});
|
|
13713
13111
|
}
|
|
13714
13112
|
);
|
|
13715
|
-
var killSchema =
|
|
13716
|
-
signal:
|
|
13113
|
+
var killSchema = z19.object({
|
|
13114
|
+
signal: z19.enum(["SIGTERM", "SIGKILL"]).optional()
|
|
13717
13115
|
});
|
|
13718
13116
|
terminals.post(
|
|
13719
13117
|
"/:sessionId/terminals/:terminalId/kill",
|
|
@@ -13727,8 +13125,8 @@ terminals.post(
|
|
|
13727
13125
|
return c.json({ success: true, message: "Terminal killed" });
|
|
13728
13126
|
}
|
|
13729
13127
|
);
|
|
13730
|
-
var writeSchema =
|
|
13731
|
-
input:
|
|
13128
|
+
var writeSchema = z19.object({
|
|
13129
|
+
input: z19.string()
|
|
13732
13130
|
});
|
|
13733
13131
|
terminals.post(
|
|
13734
13132
|
"/:sessionId/terminals/:terminalId/write",
|
|
@@ -13915,23 +13313,23 @@ init_agent();
|
|
|
13915
13313
|
init_config();
|
|
13916
13314
|
import { Hono as Hono5 } from "hono";
|
|
13917
13315
|
import { zValidator as zValidator5 } from "@hono/zod-validator";
|
|
13918
|
-
import { z as
|
|
13919
|
-
import { nanoid as
|
|
13316
|
+
import { z as z20 } from "zod";
|
|
13317
|
+
import { nanoid as nanoid12 } from "nanoid";
|
|
13920
13318
|
init_questions();
|
|
13921
13319
|
var tasks = new Hono5();
|
|
13922
13320
|
var taskAbortControllers = /* @__PURE__ */ new Map();
|
|
13923
|
-
var createTaskSchema =
|
|
13924
|
-
prompt:
|
|
13925
|
-
outputSchema:
|
|
13926
|
-
webhookUrl:
|
|
13927
|
-
model:
|
|
13928
|
-
workingDirectory:
|
|
13929
|
-
name:
|
|
13930
|
-
maxIterations:
|
|
13931
|
-
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(),
|
|
13932
13330
|
/** When set, the spawning orchestrator's session id. Stamped on the
|
|
13933
13331
|
* worker's config so terminal events can wake the orchestrator. */
|
|
13934
|
-
orchestratorSessionId:
|
|
13332
|
+
orchestratorSessionId: z20.string().optional()
|
|
13935
13333
|
});
|
|
13936
13334
|
tasks.post(
|
|
13937
13335
|
"/",
|
|
@@ -13997,7 +13395,7 @@ tasks.post(
|
|
|
13997
13395
|
const taskId = agent.sessionId;
|
|
13998
13396
|
const abortController = new AbortController();
|
|
13999
13397
|
taskAbortControllers.set(taskId, abortController);
|
|
14000
|
-
const streamId = `stream_${taskId}_${
|
|
13398
|
+
const streamId = `stream_${taskId}_${nanoid12(10)}`;
|
|
14001
13399
|
await activeStreamQueries.create(taskId, streamId);
|
|
14002
13400
|
const taskStreamProducer = () => {
|
|
14003
13401
|
const { readable, writable } = new TransformStream();
|
|
@@ -14146,9 +13544,9 @@ tasks.post("/:id/cancel", async (c) => {
|
|
|
14146
13544
|
}
|
|
14147
13545
|
return c.json({ taskId: id, status: "failed", error: "Task cancelled by user" });
|
|
14148
13546
|
});
|
|
14149
|
-
var answerQuestionSchema =
|
|
14150
|
-
answer:
|
|
14151
|
-
answeredBy:
|
|
13547
|
+
var answerQuestionSchema = z20.object({
|
|
13548
|
+
answer: z20.string().min(1),
|
|
13549
|
+
answeredBy: z20.enum(["orchestrator", "user", "system"]).optional()
|
|
14152
13550
|
});
|
|
14153
13551
|
tasks.post(
|
|
14154
13552
|
"/:id/questions/:questionId/answer",
|
|
@@ -14432,7 +13830,7 @@ init_pool();
|
|
|
14432
13830
|
init_webhook_events();
|
|
14433
13831
|
import { Hono as Hono8 } from "hono";
|
|
14434
13832
|
import { zValidator as zValidator6 } from "@hono/zod-validator";
|
|
14435
|
-
import { z as
|
|
13833
|
+
import { z as z21 } from "zod";
|
|
14436
13834
|
var integrations = new Hono8();
|
|
14437
13835
|
var orchestratorRouter = new Hono8();
|
|
14438
13836
|
async function getOrchestratorId() {
|
|
@@ -14455,9 +13853,9 @@ orchestratorRouter.get("/", async (c) => {
|
|
|
14455
13853
|
});
|
|
14456
13854
|
orchestratorRouter.patch(
|
|
14457
13855
|
"/",
|
|
14458
|
-
zValidator6("json",
|
|
14459
|
-
name:
|
|
14460
|
-
personality:
|
|
13856
|
+
zValidator6("json", z21.object({
|
|
13857
|
+
name: z21.string().min(1).optional(),
|
|
13858
|
+
personality: z21.string().optional()
|
|
14461
13859
|
})),
|
|
14462
13860
|
async (c) => {
|
|
14463
13861
|
const id = await getOrchestratorId();
|
|
@@ -14533,15 +13931,15 @@ integrations.get("/", async (c) => {
|
|
|
14533
13931
|
}
|
|
14534
13932
|
});
|
|
14535
13933
|
});
|
|
14536
|
-
var slackConfigSchema =
|
|
14537
|
-
botToken:
|
|
14538
|
-
signingSecret:
|
|
14539
|
-
defaultOrchestratorName:
|
|
14540
|
-
allowedUsers:
|
|
14541
|
-
allowedChannels:
|
|
14542
|
-
allowDmsFromAnyone:
|
|
14543
|
-
deniedReplyEnabled:
|
|
14544
|
-
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()
|
|
14545
13943
|
});
|
|
14546
13944
|
integrations.post("/slack", zValidator6("json", slackConfigSchema), async (c) => {
|
|
14547
13945
|
const body = c.req.valid("json");
|
|
@@ -14570,11 +13968,11 @@ schedulesRouter.get("/", async (c) => {
|
|
|
14570
13968
|
});
|
|
14571
13969
|
schedulesRouter.post(
|
|
14572
13970
|
"/",
|
|
14573
|
-
zValidator6("json",
|
|
14574
|
-
name:
|
|
14575
|
-
cron:
|
|
14576
|
-
prompt:
|
|
14577
|
-
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()
|
|
14578
13976
|
})),
|
|
14579
13977
|
async (c) => {
|
|
14580
13978
|
const orcId = await getOrchestratorId();
|
|
@@ -14585,12 +13983,12 @@ schedulesRouter.post(
|
|
|
14585
13983
|
);
|
|
14586
13984
|
schedulesRouter.patch(
|
|
14587
13985
|
"/:id",
|
|
14588
|
-
zValidator6("json",
|
|
14589
|
-
name:
|
|
14590
|
-
cron:
|
|
14591
|
-
prompt:
|
|
14592
|
-
enabled:
|
|
14593
|
-
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()
|
|
14594
13992
|
})),
|
|
14595
13993
|
async (c) => {
|
|
14596
13994
|
const orcId = await getOrchestratorId();
|
|
@@ -14618,10 +14016,10 @@ webhooksRouter.get("/", async (c) => {
|
|
|
14618
14016
|
});
|
|
14619
14017
|
webhooksRouter.post(
|
|
14620
14018
|
"/",
|
|
14621
|
-
zValidator6("json",
|
|
14622
|
-
name:
|
|
14623
|
-
wake:
|
|
14624
|
-
template:
|
|
14019
|
+
zValidator6("json", z21.object({
|
|
14020
|
+
name: z21.string().min(1),
|
|
14021
|
+
wake: z21.enum(["now", "next"]).optional(),
|
|
14022
|
+
template: z21.string().optional()
|
|
14625
14023
|
})),
|
|
14626
14024
|
async (c) => {
|
|
14627
14025
|
const orcId = await getOrchestratorId();
|
|
@@ -14632,11 +14030,11 @@ webhooksRouter.post(
|
|
|
14632
14030
|
);
|
|
14633
14031
|
webhooksRouter.patch(
|
|
14634
14032
|
"/:id",
|
|
14635
|
-
zValidator6("json",
|
|
14636
|
-
name:
|
|
14637
|
-
wake:
|
|
14638
|
-
template:
|
|
14639
|
-
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()
|
|
14640
14038
|
})),
|
|
14641
14039
|
async (c) => {
|
|
14642
14040
|
const orcId = await getOrchestratorId();
|
|
@@ -14653,22 +14051,22 @@ webhooksRouter.delete("/:id", async (c) => {
|
|
|
14653
14051
|
return c.json({ deleted: ok });
|
|
14654
14052
|
});
|
|
14655
14053
|
var mcpRouter = new Hono8();
|
|
14656
|
-
var mcpServerSchema =
|
|
14657
|
-
name:
|
|
14658
|
-
transport:
|
|
14659
|
-
url:
|
|
14660
|
-
headers:
|
|
14661
|
-
command:
|
|
14662
|
-
args:
|
|
14663
|
-
enabled:
|
|
14664
|
-
});
|
|
14665
|
-
var mcpPatchSchema =
|
|
14666
|
-
name:
|
|
14667
|
-
url:
|
|
14668
|
-
headers:
|
|
14669
|
-
command:
|
|
14670
|
-
args:
|
|
14671
|
-
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()
|
|
14672
14070
|
});
|
|
14673
14071
|
mcpRouter.get("/", async (c) => {
|
|
14674
14072
|
const rows = listMcpServers().map((s) => ({
|
|
@@ -14785,10 +14183,10 @@ init_config();
|
|
|
14785
14183
|
init_db();
|
|
14786
14184
|
|
|
14787
14185
|
// src/utils/dependencies.ts
|
|
14788
|
-
import { exec as
|
|
14789
|
-
import { promisify as
|
|
14186
|
+
import { exec as exec6 } from "child_process";
|
|
14187
|
+
import { promisify as promisify6 } from "util";
|
|
14790
14188
|
import { platform as platform2 } from "os";
|
|
14791
|
-
var
|
|
14189
|
+
var execAsync6 = promisify6(exec6);
|
|
14792
14190
|
function getInstallInstructions() {
|
|
14793
14191
|
const os2 = platform2();
|
|
14794
14192
|
if (os2 === "darwin") {
|
|
@@ -14821,7 +14219,7 @@ Install tmux:
|
|
|
14821
14219
|
}
|
|
14822
14220
|
async function checkTmux() {
|
|
14823
14221
|
try {
|
|
14824
|
-
const { stdout } = await
|
|
14222
|
+
const { stdout } = await execAsync6("tmux -V", { timeout: 5e3 });
|
|
14825
14223
|
const version = stdout.trim();
|
|
14826
14224
|
return {
|
|
14827
14225
|
available: true,
|
|
@@ -14870,11 +14268,11 @@ function getWebDirectory() {
|
|
|
14870
14268
|
try {
|
|
14871
14269
|
const currentDir = dirname8(fileURLToPath4(import.meta.url));
|
|
14872
14270
|
const webDir = resolve11(currentDir, "..", "web");
|
|
14873
|
-
if (
|
|
14271
|
+
if (existsSync20(webDir) && existsSync20(join15(webDir, "package.json"))) {
|
|
14874
14272
|
return webDir;
|
|
14875
14273
|
}
|
|
14876
14274
|
const altWebDir = resolve11(currentDir, "..", "..", "web");
|
|
14877
|
-
if (
|
|
14275
|
+
if (existsSync20(altWebDir) && existsSync20(join15(altWebDir, "package.json"))) {
|
|
14878
14276
|
return altWebDir;
|
|
14879
14277
|
}
|
|
14880
14278
|
return null;
|
|
@@ -14932,23 +14330,23 @@ async function findWebPort(preferredPort) {
|
|
|
14932
14330
|
return { port: preferredPort, alreadyRunning: false };
|
|
14933
14331
|
}
|
|
14934
14332
|
function hasProductionBuild(webDir) {
|
|
14935
|
-
const buildIdPath =
|
|
14936
|
-
return
|
|
14333
|
+
const buildIdPath = join15(webDir, ".next", "BUILD_ID");
|
|
14334
|
+
return existsSync20(buildIdPath);
|
|
14937
14335
|
}
|
|
14938
14336
|
function hasSourceFiles(webDir) {
|
|
14939
|
-
const appDir =
|
|
14940
|
-
const pagesDir =
|
|
14941
|
-
const rootAppDir =
|
|
14942
|
-
const rootPagesDir =
|
|
14943
|
-
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);
|
|
14944
14342
|
}
|
|
14945
14343
|
function getStandaloneServerPath(webDir) {
|
|
14946
14344
|
const possiblePaths2 = [
|
|
14947
|
-
|
|
14948
|
-
|
|
14345
|
+
join15(webDir, ".next", "standalone", "server.js"),
|
|
14346
|
+
join15(webDir, ".next", "standalone", "web", "server.js")
|
|
14949
14347
|
];
|
|
14950
14348
|
for (const serverPath of possiblePaths2) {
|
|
14951
|
-
if (
|
|
14349
|
+
if (existsSync20(serverPath)) {
|
|
14952
14350
|
return serverPath;
|
|
14953
14351
|
}
|
|
14954
14352
|
}
|
|
@@ -14988,13 +14386,13 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
14988
14386
|
if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
|
|
14989
14387
|
return { process: null, port: actualPort };
|
|
14990
14388
|
}
|
|
14991
|
-
const usePnpm =
|
|
14992
|
-
const useNpm = !usePnpm &&
|
|
14389
|
+
const usePnpm = existsSync20(join15(webDir, "pnpm-lock.yaml"));
|
|
14390
|
+
const useNpm = !usePnpm && existsSync20(join15(webDir, "package-lock.json"));
|
|
14993
14391
|
const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
|
|
14994
14392
|
const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
|
|
14995
14393
|
const apiUrl = publicUrl || `http://127.0.0.1:${apiPort}`;
|
|
14996
14394
|
const runtimeConfig = { apiBaseUrl: apiUrl };
|
|
14997
|
-
const runtimeConfigPath =
|
|
14395
|
+
const runtimeConfigPath = join15(webDir, "runtime-config.json");
|
|
14998
14396
|
try {
|
|
14999
14397
|
writeFileSync6(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2));
|
|
15000
14398
|
if (!quiet) console.log(` \u{1F4DD} Runtime config written to ${runtimeConfigPath}`);
|
|
@@ -15209,8 +14607,8 @@ async function startServer(options = {}) {
|
|
|
15209
14607
|
if (options.workingDirectory) {
|
|
15210
14608
|
config.resolvedWorkingDirectory = options.workingDirectory;
|
|
15211
14609
|
}
|
|
15212
|
-
if (!
|
|
15213
|
-
|
|
14610
|
+
if (!existsSync20(config.resolvedWorkingDirectory)) {
|
|
14611
|
+
mkdirSync9(config.resolvedWorkingDirectory, { recursive: true });
|
|
15214
14612
|
if (!options.quiet) console.log(`\u{1F4C1} Created agent workspace: ${config.resolvedWorkingDirectory}`);
|
|
15215
14613
|
}
|
|
15216
14614
|
if (!config.resolvedRemoteServer.url) {
|