@docyrus/docyrus 0.0.59 → 0.0.62
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/README.md +46 -0
- package/agent-loader.js +1 -1
- package/agent-loader.js.map +2 -2
- package/main.js +321 -25
- package/main.js.map +2 -2
- package/package.json +1 -1
- package/resources/browser-tools/browser-click.js +74 -0
- package/resources/browser-tools/browser-client.js +236 -0
- package/resources/browser-tools/browser-close.js +19 -0
- package/resources/browser-tools/browser-console.js +73 -0
- package/resources/browser-tools/browser-content.js +36 -75
- package/resources/browser-tools/browser-cookies.js +19 -14
- package/resources/browser-tools/browser-daemon.js +452 -0
- package/resources/browser-tools/browser-devtools.js +62 -0
- package/resources/browser-tools/browser-eval.js +16 -22
- package/resources/browser-tools/browser-fill.js +70 -0
- package/resources/browser-tools/browser-info.js +13 -0
- package/resources/browser-tools/browser-nav.js +21 -22
- package/resources/browser-tools/browser-network.js +91 -0
- package/resources/browser-tools/browser-run-script.js +12 -30
- package/resources/browser-tools/browser-screenshot.js +22 -22
- package/resources/browser-tools/browser-select.js +59 -0
- package/resources/browser-tools/browser-snapshot.js +100 -0
- package/resources/browser-tools/browser-start.js +101 -85
- package/resources/browser-tools/browser-tabs.js +38 -0
- package/resources/browser-tools/browser-wait.js +50 -0
- package/resources/pi-agent/extensions/browser-tools.ts +229 -0
- package/resources/pi-agent/skills/docyrus-chrome-devtools-cli/SKILL.md +157 -46
- package/server-loader.js +580 -232
- package/server-loader.js.map +4 -4
- package/resources/browser-tools/browser-connect.js +0 -172
- package/resources/browser-tools/browser-pick.js +0 -143
- package/resources/pi-agent/extensions/docyrus-web-browser.ts +0 -31
- package/resources/pi-agent/shared/docyrusWebBrowserProtocol.ts +0 -169
- package/resources/pi-agent/skills/agent-browser/SKILL.md +0 -779
- package/resources/pi-agent/skills/agent-browser/references/authentication.md +0 -303
- package/resources/pi-agent/skills/agent-browser/references/commands.md +0 -295
- package/resources/pi-agent/skills/agent-browser/references/profiling.md +0 -120
- package/resources/pi-agent/skills/agent-browser/references/proxy-support.md +0 -194
- package/resources/pi-agent/skills/agent-browser/references/session-management.md +0 -193
- package/resources/pi-agent/skills/agent-browser/references/snapshot-refs.md +0 -219
- package/resources/pi-agent/skills/agent-browser/references/video-recording.md +0 -173
- package/resources/pi-agent/skills/agent-browser/templates/authenticated-session.sh +0 -105
- package/resources/pi-agent/skills/agent-browser/templates/capture-workflow.sh +0 -69
- package/resources/pi-agent/skills/agent-browser/templates/form-automation.sh +0 -62
package/server-loader.js
CHANGED
|
@@ -13610,7 +13610,7 @@ __export(graph_exports2, {
|
|
|
13610
13610
|
upsertProjectPlanTask: () => upsertProjectPlanTask,
|
|
13611
13611
|
validateProjectPlanGraph: () => validateProjectPlanGraph
|
|
13612
13612
|
});
|
|
13613
|
-
function
|
|
13613
|
+
function isRecord2(value2) {
|
|
13614
13614
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
13615
13615
|
}
|
|
13616
13616
|
function isNonEmptyString2(value2) {
|
|
@@ -13721,14 +13721,14 @@ function migrateV1ToV2(v1) {
|
|
|
13721
13721
|
return { version: 2, projectVersion: "0.1.0", phases, features, tasks };
|
|
13722
13722
|
}
|
|
13723
13723
|
function parseV1Graph(rawValue) {
|
|
13724
|
-
const sections = Array.isArray(rawValue.sections) ? rawValue.sections.filter((value2) =>
|
|
13724
|
+
const sections = Array.isArray(rawValue.sections) ? rawValue.sections.filter((value2) => isRecord2(value2)).map((value2) => ({
|
|
13725
13725
|
id: isNonEmptyString2(value2.id) ? value2.id.trim() : "",
|
|
13726
13726
|
title: isNonEmptyString2(value2.title) ? value2.title.trim() : "",
|
|
13727
13727
|
slug: isNonEmptyString2(value2.slug) ? value2.slug.trim() : "",
|
|
13728
13728
|
summary: typeof value2.summary === "string" ? value2.summary.trim() : "",
|
|
13729
13729
|
...typeof value2.order === "number" ? { order: value2.order } : {}
|
|
13730
13730
|
})).filter((value2) => value2.id.length > 0) : [];
|
|
13731
|
-
const features = Array.isArray(rawValue.features) ? rawValue.features.filter((value2) =>
|
|
13731
|
+
const features = Array.isArray(rawValue.features) ? rawValue.features.filter((value2) => isRecord2(value2)).map((value2) => ({
|
|
13732
13732
|
id: isNonEmptyString2(value2.id) ? value2.id.trim() : "",
|
|
13733
13733
|
title: isNonEmptyString2(value2.title) ? value2.title.trim() : "",
|
|
13734
13734
|
slug: isNonEmptyString2(value2.slug) ? value2.slug.trim() : "",
|
|
@@ -13736,7 +13736,7 @@ function parseV1Graph(rawValue) {
|
|
|
13736
13736
|
sectionId: isNonEmptyString2(value2.sectionId) ? value2.sectionId.trim() : "",
|
|
13737
13737
|
...typeof value2.order === "number" ? { order: value2.order } : {}
|
|
13738
13738
|
})).filter((value2) => value2.id.length > 0) : [];
|
|
13739
|
-
const tasks = Array.isArray(rawValue.tasks) ? rawValue.tasks.filter((value2) =>
|
|
13739
|
+
const tasks = Array.isArray(rawValue.tasks) ? rawValue.tasks.filter((value2) => isRecord2(value2)).map((value2) => ({
|
|
13740
13740
|
id: isNonEmptyString2(value2.id) ? value2.id.trim() : "",
|
|
13741
13741
|
title: isNonEmptyString2(value2.title) ? value2.title.trim() : "",
|
|
13742
13742
|
summary: typeof value2.summary === "string" ? value2.summary.trim() : "",
|
|
@@ -13752,7 +13752,7 @@ function parseV1Graph(rawValue) {
|
|
|
13752
13752
|
}
|
|
13753
13753
|
function parseV2Graph(rawValue) {
|
|
13754
13754
|
const projectVersion = isNonEmptyString2(rawValue.projectVersion) ? rawValue.projectVersion.trim() : "0.1.0";
|
|
13755
|
-
const phases = Array.isArray(rawValue.phases) ? rawValue.phases.filter((value2) =>
|
|
13755
|
+
const phases = Array.isArray(rawValue.phases) ? rawValue.phases.filter((value2) => isRecord2(value2)).map((value2) => ({
|
|
13756
13756
|
id: isNonEmptyString2(value2.id) ? value2.id.trim() : "",
|
|
13757
13757
|
title: isNonEmptyString2(value2.title) ? value2.title.trim() : "",
|
|
13758
13758
|
slug: isNonEmptyString2(value2.slug) ? value2.slug.trim() : "",
|
|
@@ -13760,7 +13760,7 @@ function parseV2Graph(rawValue) {
|
|
|
13760
13760
|
...isNonEmptyString2(value2.projectVersion) ? { projectVersion: value2.projectVersion.trim() } : {},
|
|
13761
13761
|
...typeof value2.order === "number" ? { order: value2.order } : {}
|
|
13762
13762
|
})).filter((value2) => value2.id.length > 0) : [];
|
|
13763
|
-
const features = Array.isArray(rawValue.features) ? rawValue.features.filter((value2) =>
|
|
13763
|
+
const features = Array.isArray(rawValue.features) ? rawValue.features.filter((value2) => isRecord2(value2)).map((value2) => ({
|
|
13764
13764
|
id: isNonEmptyString2(value2.id) ? value2.id.trim() : "",
|
|
13765
13765
|
title: isNonEmptyString2(value2.title) ? value2.title.trim() : "",
|
|
13766
13766
|
slug: isNonEmptyString2(value2.slug) ? value2.slug.trim() : "",
|
|
@@ -13770,7 +13770,7 @@ function parseV2Graph(rawValue) {
|
|
|
13770
13770
|
...isNonEmptyString2(value2.projectVersion) ? { projectVersion: value2.projectVersion.trim() } : {},
|
|
13771
13771
|
...typeof value2.order === "number" ? { order: value2.order } : {}
|
|
13772
13772
|
})).filter((value2) => value2.id.length > 0) : [];
|
|
13773
|
-
const tasks = Array.isArray(rawValue.tasks) ? rawValue.tasks.filter((value2) =>
|
|
13773
|
+
const tasks = Array.isArray(rawValue.tasks) ? rawValue.tasks.filter((value2) => isRecord2(value2)).map((value2) => ({
|
|
13774
13774
|
id: isNonEmptyString2(value2.id) ? value2.id.trim() : "",
|
|
13775
13775
|
title: isNonEmptyString2(value2.title) ? value2.title.trim() : "",
|
|
13776
13776
|
summary: typeof value2.summary === "string" ? value2.summary.trim() : "",
|
|
@@ -13785,7 +13785,7 @@ function parseV2Graph(rawValue) {
|
|
|
13785
13785
|
return sortGraph({ version: 2, projectVersion, phases, features, tasks });
|
|
13786
13786
|
}
|
|
13787
13787
|
function parseProjectPlanGraph(rawValue) {
|
|
13788
|
-
if (!
|
|
13788
|
+
if (!isRecord2(rawValue)) {
|
|
13789
13789
|
return createEmptyProjectPlanGraph();
|
|
13790
13790
|
}
|
|
13791
13791
|
if (rawValue.version === 2) {
|
|
@@ -18724,7 +18724,7 @@ var AgentEnvStore = class {
|
|
|
18724
18724
|
// src/agent/packagedExtensions.ts
|
|
18725
18725
|
var import_node_fs = require("node:fs");
|
|
18726
18726
|
var import_node_path2 = require("node:path");
|
|
18727
|
-
var SERVER_ONLY_PACKAGED_EXTENSION_NAMES = /* @__PURE__ */ new Set(["
|
|
18727
|
+
var SERVER_ONLY_PACKAGED_EXTENSION_NAMES = /* @__PURE__ */ new Set(["server-auto-commit"]);
|
|
18728
18728
|
function isPackagedExtensionEntry(entryName, isDirectory) {
|
|
18729
18729
|
return isDirectory || entryName.endsWith(".ts") || entryName.endsWith(".js");
|
|
18730
18730
|
}
|
|
@@ -35486,122 +35486,9 @@ function parseAskUserResponseFromToolOutput(output) {
|
|
|
35486
35486
|
return normalizeAskUserResponse(output);
|
|
35487
35487
|
}
|
|
35488
35488
|
|
|
35489
|
-
// resources/pi-agent/shared/docyrusWebBrowserProtocol.ts
|
|
35490
|
-
var DOCYRUS_WEB_BROWSER_TAG = "docyrus_web_browser";
|
|
35491
|
-
var DOCYRUS_WEB_BROWSER_OPEN = `<${DOCYRUS_WEB_BROWSER_TAG}>`;
|
|
35492
|
-
var DOCYRUS_WEB_BROWSER_CLOSE = `</${DOCYRUS_WEB_BROWSER_TAG}>`;
|
|
35493
|
-
var DOCYRUS_WEB_BROWSER_RESULT_TAG = "docyrus_web_browser_result";
|
|
35494
|
-
var DOCYRUS_WEB_BROWSER_RESULT_OPEN = `<${DOCYRUS_WEB_BROWSER_RESULT_TAG}>`;
|
|
35495
|
-
var DOCYRUS_WEB_BROWSER_RESULT_CLOSE = `</${DOCYRUS_WEB_BROWSER_RESULT_TAG}>`;
|
|
35496
|
-
var WEB_PREVIEW_CONTEXT_TOOL = "web_preview_context";
|
|
35497
|
-
var WEB_PREVIEW_PLAYWRIGHT_TOOL = "web_preview_playwright";
|
|
35498
|
-
var DOCYRUS_WEB_BROWSER_TOOL_NAMES = [
|
|
35499
|
-
WEB_PREVIEW_CONTEXT_TOOL,
|
|
35500
|
-
WEB_PREVIEW_PLAYWRIGHT_TOOL
|
|
35501
|
-
];
|
|
35502
|
-
var DOCYRUS_WEB_BROWSER_CLIENT_TOOLS = [
|
|
35503
|
-
{
|
|
35504
|
-
name: WEB_PREVIEW_CONTEXT_TOOL,
|
|
35505
|
-
description: "Inspect the current Docyrus web preview state before automation. Prefer this first when preview availability or bridge state is unknown.",
|
|
35506
|
-
inputSchema: {
|
|
35507
|
-
type: "object",
|
|
35508
|
-
properties: {
|
|
35509
|
-
includeSnapshot: { type: "boolean" }
|
|
35510
|
-
},
|
|
35511
|
-
additionalProperties: false
|
|
35512
|
-
}
|
|
35513
|
-
},
|
|
35514
|
-
{
|
|
35515
|
-
name: WEB_PREVIEW_PLAYWRIGHT_TOOL,
|
|
35516
|
-
description: "Run Playwright-style actions against the Docyrus web preview. Prefer structured steps over raw scripts.",
|
|
35517
|
-
inputSchema: {
|
|
35518
|
-
type: "object",
|
|
35519
|
-
properties: {
|
|
35520
|
-
script: { type: "string" },
|
|
35521
|
-
steps: {
|
|
35522
|
-
type: "array",
|
|
35523
|
-
items: {
|
|
35524
|
-
type: "object",
|
|
35525
|
-
properties: {
|
|
35526
|
-
action: { type: "string" },
|
|
35527
|
-
selector: { type: "string" },
|
|
35528
|
-
url: { type: "string" },
|
|
35529
|
-
value: { type: "string" },
|
|
35530
|
-
values: {
|
|
35531
|
-
type: "array",
|
|
35532
|
-
items: { type: "string" }
|
|
35533
|
-
},
|
|
35534
|
-
key: { type: "string" },
|
|
35535
|
-
attribute: { type: "string" },
|
|
35536
|
-
timeoutMs: { type: "number" },
|
|
35537
|
-
state: { type: "string" }
|
|
35538
|
-
},
|
|
35539
|
-
additionalProperties: true
|
|
35540
|
-
}
|
|
35541
|
-
},
|
|
35542
|
-
timeoutMs: { type: "number" },
|
|
35543
|
-
stopOnError: { type: "boolean" }
|
|
35544
|
-
},
|
|
35545
|
-
additionalProperties: true
|
|
35546
|
-
}
|
|
35547
|
-
}
|
|
35548
|
-
];
|
|
35549
|
-
function hashString2(value2) {
|
|
35550
|
-
let hash2 = 0;
|
|
35551
|
-
for (let index2 = 0; index2 < value2.length; index2 += 1) {
|
|
35552
|
-
hash2 = (hash2 << 5) - hash2 + value2.charCodeAt(index2);
|
|
35553
|
-
hash2 |= 0;
|
|
35554
|
-
}
|
|
35555
|
-
return Math.abs(hash2).toString(36);
|
|
35556
|
-
}
|
|
35557
|
-
function isRecord2(value2) {
|
|
35558
|
-
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
35559
|
-
}
|
|
35560
|
-
function isDocyrusWebBrowserToolName(value2) {
|
|
35561
|
-
return DOCYRUS_WEB_BROWSER_TOOL_NAMES.some((toolName) => toolName === value2);
|
|
35562
|
-
}
|
|
35563
|
-
function normalizeDocyrusWebBrowserToolRequest(value2) {
|
|
35564
|
-
if (!isRecord2(value2) || !isDocyrusWebBrowserToolName(value2.tool)) {
|
|
35565
|
-
return void 0;
|
|
35566
|
-
}
|
|
35567
|
-
return {
|
|
35568
|
-
tool: value2.tool,
|
|
35569
|
-
input: isRecord2(value2.input) ? value2.input : void 0
|
|
35570
|
-
};
|
|
35571
|
-
}
|
|
35572
|
-
function createDocyrusWebBrowserToolCallId(request) {
|
|
35573
|
-
return `docyrus_web_browser_${hashString2(JSON.stringify(request))}`;
|
|
35574
|
-
}
|
|
35575
|
-
function parseDocyrusWebBrowserRequestFromText(text3) {
|
|
35576
|
-
const trimmed = text3.trim();
|
|
35577
|
-
if (!trimmed.startsWith(DOCYRUS_WEB_BROWSER_OPEN) || !trimmed.endsWith(DOCYRUS_WEB_BROWSER_CLOSE)) {
|
|
35578
|
-
return void 0;
|
|
35579
|
-
}
|
|
35580
|
-
const body2 = trimmed.slice(DOCYRUS_WEB_BROWSER_OPEN.length, trimmed.length - DOCYRUS_WEB_BROWSER_CLOSE.length).trim();
|
|
35581
|
-
if (!body2) {
|
|
35582
|
-
return void 0;
|
|
35583
|
-
}
|
|
35584
|
-
try {
|
|
35585
|
-
return normalizeDocyrusWebBrowserToolRequest(JSON.parse(body2));
|
|
35586
|
-
} catch {
|
|
35587
|
-
return void 0;
|
|
35588
|
-
}
|
|
35589
|
-
}
|
|
35590
|
-
function formatDocyrusWebBrowserToolResponsePrompt(response) {
|
|
35591
|
-
return [
|
|
35592
|
-
"The docyrus-web-browser client tool returned a result.",
|
|
35593
|
-
"",
|
|
35594
|
-
`${DOCYRUS_WEB_BROWSER_RESULT_OPEN}`,
|
|
35595
|
-
JSON.stringify(response, null, 2),
|
|
35596
|
-
`${DOCYRUS_WEB_BROWSER_RESULT_CLOSE}`,
|
|
35597
|
-
"",
|
|
35598
|
-
"Continue the task using this result. If the tool reported an availability or bridge blocker, explain that blocker exactly and do not claim success."
|
|
35599
|
-
].join("\n");
|
|
35600
|
-
}
|
|
35601
|
-
|
|
35602
35489
|
// src/server/eventBridge.ts
|
|
35603
35490
|
function createEventBridge(params) {
|
|
35604
|
-
const { messageId, onChunk, onDone, onError, onAskUser
|
|
35491
|
+
const { messageId, onChunk, onDone, onError, onAskUser } = params;
|
|
35605
35492
|
const activeToolCalls = /* @__PURE__ */ new Map();
|
|
35606
35493
|
let activeTextBuffer = null;
|
|
35607
35494
|
function flushBufferedTextToStream(close2) {
|
|
@@ -35637,25 +35524,8 @@ function createEventBridge(params) {
|
|
|
35637
35524
|
});
|
|
35638
35525
|
return true;
|
|
35639
35526
|
}
|
|
35640
|
-
function maybeEmitDocyrusWebBrowserTool(text3) {
|
|
35641
|
-
const request = parseDocyrusWebBrowserRequestFromText(text3);
|
|
35642
|
-
if (!request) {
|
|
35643
|
-
return false;
|
|
35644
|
-
}
|
|
35645
|
-
const toolCallId = createDocyrusWebBrowserToolCallId(request);
|
|
35646
|
-
onDocyrusWebBrowserTool?.({ toolCallId, request });
|
|
35647
|
-
onChunk({ type: "tool-input-start", toolCallId, toolName: request.tool, dynamic: true });
|
|
35648
|
-
onChunk({
|
|
35649
|
-
type: "tool-input-available",
|
|
35650
|
-
toolCallId,
|
|
35651
|
-
toolName: request.tool,
|
|
35652
|
-
input: request.input ?? {},
|
|
35653
|
-
dynamic: true
|
|
35654
|
-
});
|
|
35655
|
-
return true;
|
|
35656
|
-
}
|
|
35657
35527
|
function shouldKeepBufferingDynamicToolText(text3) {
|
|
35658
|
-
const dynamicToolTags = ["<ask_user>"
|
|
35528
|
+
const dynamicToolTags = ["<ask_user>"];
|
|
35659
35529
|
return dynamicToolTags.some((tag) => tag.startsWith(text3) || text3.startsWith(tag));
|
|
35660
35530
|
}
|
|
35661
35531
|
function handleEvent(event) {
|
|
@@ -35712,7 +35582,7 @@ function createEventBridge(params) {
|
|
|
35712
35582
|
activeTextBuffer = null;
|
|
35713
35583
|
break;
|
|
35714
35584
|
}
|
|
35715
|
-
if (!maybeEmitAskUser(activeTextBuffer.text)
|
|
35585
|
+
if (!maybeEmitAskUser(activeTextBuffer.text)) {
|
|
35716
35586
|
flushBufferedTextToStream(true);
|
|
35717
35587
|
} else {
|
|
35718
35588
|
activeTextBuffer = null;
|
|
@@ -44061,7 +43931,7 @@ var BUILT_IN_TOOLS = {
|
|
|
44061
43931
|
]
|
|
44062
43932
|
};
|
|
44063
43933
|
async function listAllTools(params) {
|
|
44064
|
-
const { profile, agentDir, cwd
|
|
43934
|
+
const { profile, agentDir, cwd } = params;
|
|
44065
43935
|
const tools = [];
|
|
44066
43936
|
const builtIn = BUILT_IN_TOOLS[profile] ?? BUILT_IN_TOOLS.coder;
|
|
44067
43937
|
for (const tool of builtIn) {
|
|
@@ -44071,16 +43941,6 @@ async function listAllTools(params) {
|
|
|
44071
43941
|
source: "built-in"
|
|
44072
43942
|
});
|
|
44073
43943
|
}
|
|
44074
|
-
if (includeDocyrusWebBrowser) {
|
|
44075
|
-
for (const tool of DOCYRUS_WEB_BROWSER_CLIENT_TOOLS) {
|
|
44076
|
-
tools.push({
|
|
44077
|
-
name: tool.name,
|
|
44078
|
-
description: tool.description,
|
|
44079
|
-
source: "built-in",
|
|
44080
|
-
inputSchema: tool.inputSchema
|
|
44081
|
-
});
|
|
44082
|
-
}
|
|
44083
|
-
}
|
|
44084
43944
|
let config2;
|
|
44085
43945
|
let cache;
|
|
44086
43946
|
try {
|
|
@@ -44126,6 +43986,199 @@ async function listAllTools(params) {
|
|
|
44126
43986
|
return tools;
|
|
44127
43987
|
}
|
|
44128
43988
|
|
|
43989
|
+
// src/server/browserToolSchemas.ts
|
|
43990
|
+
var BROWSER_TOOL_PREFIX = "docyrus_browser_";
|
|
43991
|
+
var BROWSER_TOOL_SCHEMAS = [
|
|
43992
|
+
{
|
|
43993
|
+
name: "docyrus_browser_navigate",
|
|
43994
|
+
description: "Navigate the preview browser to a URL. Use --reload to force reload after navigation.",
|
|
43995
|
+
source: "built-in",
|
|
43996
|
+
inputSchema: {
|
|
43997
|
+
type: "object",
|
|
43998
|
+
properties: {
|
|
43999
|
+
url: { type: "string", description: "URL to navigate to" },
|
|
44000
|
+
reload: { type: "boolean", description: "Force reload after navigation" }
|
|
44001
|
+
},
|
|
44002
|
+
required: ["url"]
|
|
44003
|
+
}
|
|
44004
|
+
},
|
|
44005
|
+
{
|
|
44006
|
+
name: "docyrus_browser_wait",
|
|
44007
|
+
description: "Wait for a condition before proceeding: network idle, CSS selector appearance, URL pattern match, or fixed delay.",
|
|
44008
|
+
source: "built-in",
|
|
44009
|
+
inputSchema: {
|
|
44010
|
+
type: "object",
|
|
44011
|
+
properties: {
|
|
44012
|
+
idle: { type: "boolean", description: "Wait for network idle" },
|
|
44013
|
+
selector: { type: "string", description: "Wait for CSS selector to appear" },
|
|
44014
|
+
url: { type: "string", description: "Wait for URL to match glob pattern" },
|
|
44015
|
+
ms: { type: "number", description: "Wait for fixed milliseconds" },
|
|
44016
|
+
timeout: { type: "number", description: "Maximum wait time in ms (default: 15000)" }
|
|
44017
|
+
}
|
|
44018
|
+
}
|
|
44019
|
+
},
|
|
44020
|
+
{
|
|
44021
|
+
name: "docyrus_browser_snapshot",
|
|
44022
|
+
description: "Get a compact snapshot of interactive page elements with refs (@e1, @e2, ...) for use in click/fill/select. Returns tag, text, type, name, value, placeholder, href for each element.",
|
|
44023
|
+
source: "built-in",
|
|
44024
|
+
inputSchema: {
|
|
44025
|
+
type: "object",
|
|
44026
|
+
properties: {
|
|
44027
|
+
all: { type: "boolean", description: "Include all elements, not just interactive ones" },
|
|
44028
|
+
selector: { type: "string", description: "Scope snapshot to a CSS selector subtree" }
|
|
44029
|
+
}
|
|
44030
|
+
}
|
|
44031
|
+
},
|
|
44032
|
+
{
|
|
44033
|
+
name: "docyrus_browser_click",
|
|
44034
|
+
description: "Click an element by snapshot ref (@e1), CSS selector, or x,y coordinates. Coordinate clicks pass through iframes and shadow DOM at the compositor level.",
|
|
44035
|
+
source: "built-in",
|
|
44036
|
+
inputSchema: {
|
|
44037
|
+
type: "object",
|
|
44038
|
+
properties: {
|
|
44039
|
+
target: { type: "string", description: "Snapshot ref (@e1), CSS selector, or x coordinate" },
|
|
44040
|
+
x: { type: "number", description: "X coordinate (when using coordinate mode)" },
|
|
44041
|
+
y: { type: "number", description: "Y coordinate (when using coordinate mode)" },
|
|
44042
|
+
timeout: { type: "number", description: "Timeout in ms (default: 5000)" }
|
|
44043
|
+
},
|
|
44044
|
+
required: ["target"]
|
|
44045
|
+
}
|
|
44046
|
+
},
|
|
44047
|
+
{
|
|
44048
|
+
name: "docyrus_browser_fill",
|
|
44049
|
+
description: "Clear and type a value into an input or textarea by snapshot ref or CSS selector.",
|
|
44050
|
+
source: "built-in",
|
|
44051
|
+
inputSchema: {
|
|
44052
|
+
type: "object",
|
|
44053
|
+
properties: {
|
|
44054
|
+
target: { type: "string", description: "Snapshot ref (@e1) or CSS selector" },
|
|
44055
|
+
value: { type: "string", description: "Value to type into the element" },
|
|
44056
|
+
timeout: { type: "number", description: "Timeout in ms (default: 5000)" }
|
|
44057
|
+
},
|
|
44058
|
+
required: ["target", "value"]
|
|
44059
|
+
}
|
|
44060
|
+
},
|
|
44061
|
+
{
|
|
44062
|
+
name: "docyrus_browser_select",
|
|
44063
|
+
description: "Select a dropdown option by snapshot ref or CSS selector. Matches by option text or value.",
|
|
44064
|
+
source: "built-in",
|
|
44065
|
+
inputSchema: {
|
|
44066
|
+
type: "object",
|
|
44067
|
+
properties: {
|
|
44068
|
+
target: { type: "string", description: "Snapshot ref (@e1) or CSS selector" },
|
|
44069
|
+
value: { type: "string", description: "Option text or value to select" }
|
|
44070
|
+
},
|
|
44071
|
+
required: ["target", "value"]
|
|
44072
|
+
}
|
|
44073
|
+
},
|
|
44074
|
+
{
|
|
44075
|
+
name: "docyrus_browser_eval",
|
|
44076
|
+
description: "Evaluate JavaScript in the preview page and return the result. Supports expressions and multi-statement code with async/await.",
|
|
44077
|
+
source: "built-in",
|
|
44078
|
+
inputSchema: {
|
|
44079
|
+
type: "object",
|
|
44080
|
+
properties: {
|
|
44081
|
+
code: { type: "string", description: "JavaScript code to evaluate" }
|
|
44082
|
+
},
|
|
44083
|
+
required: ["code"]
|
|
44084
|
+
}
|
|
44085
|
+
},
|
|
44086
|
+
{
|
|
44087
|
+
name: "docyrus_browser_screenshot",
|
|
44088
|
+
description: "Capture a screenshot of the preview browser. Returns base64-encoded PNG image.",
|
|
44089
|
+
source: "built-in",
|
|
44090
|
+
inputSchema: {
|
|
44091
|
+
type: "object",
|
|
44092
|
+
properties: {
|
|
44093
|
+
full: { type: "boolean", description: "Capture full page instead of just the viewport" }
|
|
44094
|
+
}
|
|
44095
|
+
}
|
|
44096
|
+
},
|
|
44097
|
+
{
|
|
44098
|
+
name: "docyrus_browser_console",
|
|
44099
|
+
description: "Read captured console messages (log, warn, error) from the preview page. Run early in the session to start capturing.",
|
|
44100
|
+
source: "built-in",
|
|
44101
|
+
inputSchema: {
|
|
44102
|
+
type: "object",
|
|
44103
|
+
properties: {
|
|
44104
|
+
level: { type: "string", description: "Filter by level: log, warn, error, info, debug" }
|
|
44105
|
+
}
|
|
44106
|
+
}
|
|
44107
|
+
},
|
|
44108
|
+
{
|
|
44109
|
+
name: "docyrus_browser_network",
|
|
44110
|
+
description: "Inspect captured network requests from the preview page. Filter by HTTP method, status code, or URL.",
|
|
44111
|
+
source: "built-in",
|
|
44112
|
+
inputSchema: {
|
|
44113
|
+
type: "object",
|
|
44114
|
+
properties: {
|
|
44115
|
+
method: { type: "string", description: "Filter by HTTP method (GET, POST, etc.)" },
|
|
44116
|
+
status: { type: "string", description: "Filter by status code or pattern (200, 4xx, 5xx)" },
|
|
44117
|
+
url: { type: "string", description: "Filter by URL substring" }
|
|
44118
|
+
}
|
|
44119
|
+
}
|
|
44120
|
+
},
|
|
44121
|
+
{
|
|
44122
|
+
name: "docyrus_browser_cookies",
|
|
44123
|
+
description: "List cookies for the preview page, optionally filtered by name or domain.",
|
|
44124
|
+
source: "built-in",
|
|
44125
|
+
inputSchema: {
|
|
44126
|
+
type: "object",
|
|
44127
|
+
properties: {
|
|
44128
|
+
name: { type: "string", description: "Filter cookies by exact name" },
|
|
44129
|
+
domain: { type: "string", description: "Filter cookies by domain (substring match)" }
|
|
44130
|
+
}
|
|
44131
|
+
}
|
|
44132
|
+
},
|
|
44133
|
+
{
|
|
44134
|
+
name: "docyrus_browser_content",
|
|
44135
|
+
description: "Extract readable markdown content from the current preview page using Readability.",
|
|
44136
|
+
source: "built-in",
|
|
44137
|
+
inputSchema: {
|
|
44138
|
+
type: "object",
|
|
44139
|
+
properties: {}
|
|
44140
|
+
}
|
|
44141
|
+
},
|
|
44142
|
+
{
|
|
44143
|
+
name: "docyrus_browser_info",
|
|
44144
|
+
description: "Get current preview page info: URL, title, viewport dimensions, scroll position, page size, and ready state.",
|
|
44145
|
+
source: "built-in",
|
|
44146
|
+
inputSchema: {
|
|
44147
|
+
type: "object",
|
|
44148
|
+
properties: {}
|
|
44149
|
+
}
|
|
44150
|
+
},
|
|
44151
|
+
{
|
|
44152
|
+
name: "docyrus_browser_devtools",
|
|
44153
|
+
description: "Read @docyrus/devtools runtime diagnostics from the preview page: API errors, usage issues, and console entries.",
|
|
44154
|
+
source: "built-in",
|
|
44155
|
+
inputSchema: {
|
|
44156
|
+
type: "object",
|
|
44157
|
+
properties: {
|
|
44158
|
+
subcommand: {
|
|
44159
|
+
type: "string",
|
|
44160
|
+
enum: ["state", "errors", "issues", "console"],
|
|
44161
|
+
description: "What to read: state (full), errors (API errors), issues (usage issues), console (entries)"
|
|
44162
|
+
},
|
|
44163
|
+
level: { type: "string", description: "Filter console entries by level (for console subcommand)" }
|
|
44164
|
+
},
|
|
44165
|
+
required: ["subcommand"]
|
|
44166
|
+
}
|
|
44167
|
+
},
|
|
44168
|
+
{
|
|
44169
|
+
name: "docyrus_browser_open",
|
|
44170
|
+
description: "Open an external website in a new browser window for automation. Returns a webviewId for targeting subsequent commands.",
|
|
44171
|
+
source: "built-in",
|
|
44172
|
+
inputSchema: {
|
|
44173
|
+
type: "object",
|
|
44174
|
+
properties: {
|
|
44175
|
+
url: { type: "string", description: "URL to open in the new browser window" }
|
|
44176
|
+
},
|
|
44177
|
+
required: ["url"]
|
|
44178
|
+
}
|
|
44179
|
+
}
|
|
44180
|
+
];
|
|
44181
|
+
|
|
44129
44182
|
// src/server/sessionConfig.ts
|
|
44130
44183
|
var import_node_fs12 = require("node:fs");
|
|
44131
44184
|
var path5 = __toESM(require("node:path"));
|
|
@@ -44201,45 +44254,6 @@ function extractAskUserToolResponse(messages) {
|
|
|
44201
44254
|
}
|
|
44202
44255
|
return void 0;
|
|
44203
44256
|
}
|
|
44204
|
-
function extractDocyrusWebBrowserToolResponse(messages) {
|
|
44205
|
-
const message = getLastAssistantMessage(messages);
|
|
44206
|
-
if (!message) {
|
|
44207
|
-
return void 0;
|
|
44208
|
-
}
|
|
44209
|
-
for (let partIndex = message.parts.length - 1; partIndex >= 0; partIndex -= 1) {
|
|
44210
|
-
const part = message.parts[partIndex];
|
|
44211
|
-
if (part.type === "dynamic-tool" && isDocyrusWebBrowserToolName(part.toolName) && typeof part.toolCallId === "string" && (part.state === "output-available" || part.state === "output-error")) {
|
|
44212
|
-
const errorText = typeof part.errorText === "string" ? part.errorText : typeof part.output === "string" && part.state === "output-error" ? part.output : void 0;
|
|
44213
|
-
return {
|
|
44214
|
-
toolCallId: part.toolCallId,
|
|
44215
|
-
toolName: part.toolName,
|
|
44216
|
-
output: part.state === "output-available" ? part.output : void 0,
|
|
44217
|
-
errorText,
|
|
44218
|
-
isError: part.state === "output-error"
|
|
44219
|
-
};
|
|
44220
|
-
}
|
|
44221
|
-
}
|
|
44222
|
-
return void 0;
|
|
44223
|
-
}
|
|
44224
|
-
function extractDocyrusWebBrowserToolRequest(messages, toolCallId) {
|
|
44225
|
-
const message = getLastAssistantMessage(messages);
|
|
44226
|
-
if (!message) {
|
|
44227
|
-
return void 0;
|
|
44228
|
-
}
|
|
44229
|
-
for (let partIndex = message.parts.length - 1; partIndex >= 0; partIndex -= 1) {
|
|
44230
|
-
const part = message.parts[partIndex];
|
|
44231
|
-
if (part.type === "dynamic-tool" && isDocyrusWebBrowserToolName(part.toolName) && part.toolCallId === toolCallId) {
|
|
44232
|
-
const request = normalizeDocyrusWebBrowserToolRequest({
|
|
44233
|
-
tool: part.toolName,
|
|
44234
|
-
input: part.input
|
|
44235
|
-
});
|
|
44236
|
-
if (request) {
|
|
44237
|
-
return request;
|
|
44238
|
-
}
|
|
44239
|
-
}
|
|
44240
|
-
}
|
|
44241
|
-
return void 0;
|
|
44242
|
-
}
|
|
44243
44257
|
function normalizeSlashCommands(commands) {
|
|
44244
44258
|
const seen = /* @__PURE__ */ new Set();
|
|
44245
44259
|
return commands.filter((command) => typeof command.name === "string" && command.name.trim().length > 0).sort((left, right) => left.name.localeCompare(right.name)).filter((command) => {
|
|
@@ -44473,17 +44487,6 @@ function convertSessionEntriesToUIMessages(entries) {
|
|
|
44473
44487
|
});
|
|
44474
44488
|
continue;
|
|
44475
44489
|
}
|
|
44476
|
-
const docyrusWebBrowserRequest = parseDocyrusWebBrowserRequestFromText(block.text);
|
|
44477
|
-
if (docyrusWebBrowserRequest) {
|
|
44478
|
-
parts2.push({
|
|
44479
|
-
type: "dynamic-tool",
|
|
44480
|
-
toolCallId: createDocyrusWebBrowserToolCallId(docyrusWebBrowserRequest),
|
|
44481
|
-
toolName: docyrusWebBrowserRequest.tool,
|
|
44482
|
-
state: "input-available",
|
|
44483
|
-
input: docyrusWebBrowserRequest.input ?? {}
|
|
44484
|
-
});
|
|
44485
|
-
continue;
|
|
44486
|
-
}
|
|
44487
44490
|
parts2.push({ type: "text", text: block.text });
|
|
44488
44491
|
} else if (block.type === "thinking" && typeof block.thinking === "string") {
|
|
44489
44492
|
parts2.push({ type: "reasoning", text: block.thinking, state: "complete" });
|
|
@@ -44535,6 +44538,107 @@ function resolveSafePath(cwd, requestPath) {
|
|
|
44535
44538
|
}
|
|
44536
44539
|
return resolved;
|
|
44537
44540
|
}
|
|
44541
|
+
var ALLOWED_UPLOAD_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
44542
|
+
// Text / code
|
|
44543
|
+
".txt",
|
|
44544
|
+
".md",
|
|
44545
|
+
".markdown",
|
|
44546
|
+
".json",
|
|
44547
|
+
".jsonl",
|
|
44548
|
+
".csv",
|
|
44549
|
+
".tsv",
|
|
44550
|
+
".xml",
|
|
44551
|
+
".yaml",
|
|
44552
|
+
".yml",
|
|
44553
|
+
".toml",
|
|
44554
|
+
".ini",
|
|
44555
|
+
".cfg",
|
|
44556
|
+
".conf",
|
|
44557
|
+
".log",
|
|
44558
|
+
".rtf",
|
|
44559
|
+
// Documents
|
|
44560
|
+
".pdf",
|
|
44561
|
+
".doc",
|
|
44562
|
+
".docx",
|
|
44563
|
+
".xls",
|
|
44564
|
+
".xlsx",
|
|
44565
|
+
".ppt",
|
|
44566
|
+
".pptx",
|
|
44567
|
+
".odt",
|
|
44568
|
+
".ods",
|
|
44569
|
+
".odp",
|
|
44570
|
+
".pages",
|
|
44571
|
+
".numbers",
|
|
44572
|
+
".key",
|
|
44573
|
+
// Images
|
|
44574
|
+
".png",
|
|
44575
|
+
".jpg",
|
|
44576
|
+
".jpeg",
|
|
44577
|
+
".gif",
|
|
44578
|
+
".bmp",
|
|
44579
|
+
".webp",
|
|
44580
|
+
".svg",
|
|
44581
|
+
".ico",
|
|
44582
|
+
".tiff",
|
|
44583
|
+
".tif",
|
|
44584
|
+
".heic",
|
|
44585
|
+
".heif",
|
|
44586
|
+
".avif",
|
|
44587
|
+
// Audio
|
|
44588
|
+
".mp3",
|
|
44589
|
+
".wav",
|
|
44590
|
+
".ogg",
|
|
44591
|
+
".flac",
|
|
44592
|
+
".aac",
|
|
44593
|
+
".m4a",
|
|
44594
|
+
".wma",
|
|
44595
|
+
".opus",
|
|
44596
|
+
// Video
|
|
44597
|
+
".mp4",
|
|
44598
|
+
".webm",
|
|
44599
|
+
".mov",
|
|
44600
|
+
".avi",
|
|
44601
|
+
".mkv",
|
|
44602
|
+
".m4v",
|
|
44603
|
+
".wmv",
|
|
44604
|
+
// Archives
|
|
44605
|
+
".zip",
|
|
44606
|
+
".tar",
|
|
44607
|
+
".gz",
|
|
44608
|
+
".7z",
|
|
44609
|
+
".rar",
|
|
44610
|
+
// Fonts
|
|
44611
|
+
".woff",
|
|
44612
|
+
".woff2",
|
|
44613
|
+
".ttf",
|
|
44614
|
+
".otf",
|
|
44615
|
+
".eot"
|
|
44616
|
+
]);
|
|
44617
|
+
function sanitizeUploadPath(filePath) {
|
|
44618
|
+
const cleaned = filePath.replace(/[\x00-\x1f\x7f]/g, "");
|
|
44619
|
+
const normalized = cleaned.replace(/\\/g, "/").replace(/\/{2,}/g, "/");
|
|
44620
|
+
if (!normalized || normalized === "/") {
|
|
44621
|
+
throw new Error("Invalid upload path");
|
|
44622
|
+
}
|
|
44623
|
+
const ext = normalized.includes(".") ? `.${normalized.split(".").pop().toLowerCase()}` : "";
|
|
44624
|
+
if (!ext || !ALLOWED_UPLOAD_EXTENSIONS.has(ext)) {
|
|
44625
|
+
throw new Error(`File type '${ext || "(none)"}' is not allowed. Allowed: text, images, audio, video, and document files.`);
|
|
44626
|
+
}
|
|
44627
|
+
return normalized;
|
|
44628
|
+
}
|
|
44629
|
+
async function assertNotSymlink(filePath) {
|
|
44630
|
+
try {
|
|
44631
|
+
const stats = await (0, import_promises18.lstat)(filePath);
|
|
44632
|
+
if (stats.isSymbolicLink()) {
|
|
44633
|
+
throw new Error("Cannot write to symbolic link");
|
|
44634
|
+
}
|
|
44635
|
+
} catch (error48) {
|
|
44636
|
+
if (error48 instanceof Error && "code" in error48 && error48.code === "ENOENT") {
|
|
44637
|
+
return;
|
|
44638
|
+
}
|
|
44639
|
+
throw error48;
|
|
44640
|
+
}
|
|
44641
|
+
}
|
|
44538
44642
|
function globToRegex(pattern) {
|
|
44539
44643
|
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "\0").replace(/\*/g, "[^/]*").replace(/\0/g, ".*").replace(/\?/g, "[^/]");
|
|
44540
44644
|
return new RegExp(`^${escaped}$`);
|
|
@@ -44615,10 +44719,10 @@ async function walkFiles(params) {
|
|
|
44615
44719
|
}
|
|
44616
44720
|
async function createAgentServer(params) {
|
|
44617
44721
|
const { port, sessionManager, modelRegistry, authRuntime, context, onCreateSession, onResumeSession, authToken } = params;
|
|
44722
|
+
const extensionUIBridge = params.extensionUIBridge;
|
|
44618
44723
|
let activeSession = params.session;
|
|
44619
44724
|
let sessionMode = "normal";
|
|
44620
44725
|
const pendingAskUserRequests = /* @__PURE__ */ new Map();
|
|
44621
|
-
const pendingDocyrusWebBrowserRequests = /* @__PURE__ */ new Map();
|
|
44622
44726
|
const oauthFlowManager = new OAuthFlowManager();
|
|
44623
44727
|
const app = new Hono2();
|
|
44624
44728
|
const settingsRootPath = resolveServerSettingsRootPath(context.agentDir);
|
|
@@ -44679,33 +44783,18 @@ async function createAgentServer(params) {
|
|
|
44679
44783
|
}
|
|
44680
44784
|
const sessionId = body2.sessionId?.trim() || activeSession.id?.trim() || "active";
|
|
44681
44785
|
const askUserResponse = extractAskUserToolResponse(messages);
|
|
44682
|
-
const docyrusWebBrowserResponse = extractDocyrusWebBrowserToolResponse(messages);
|
|
44683
44786
|
const pendingAskUser = pendingAskUserRequests.get(sessionId);
|
|
44684
|
-
const
|
|
44685
|
-
const pendingDocyrusWebBrowserRequest = docyrusWebBrowserResponse ? pendingDocyrusWebBrowser?.get(docyrusWebBrowserResponse.toolCallId) ?? extractDocyrusWebBrowserToolRequest(messages, docyrusWebBrowserResponse.toolCallId) : void 0;
|
|
44686
|
-
const userMessage = askUserResponse ? pendingAskUser && pendingAskUser.toolCallId === askUserResponse.toolCallId ? formatAskUserResponsePrompt(askUserResponse.response) : void 0 : docyrusWebBrowserResponse ? pendingDocyrusWebBrowserRequest ? formatDocyrusWebBrowserToolResponsePrompt({
|
|
44687
|
-
tool: docyrusWebBrowserResponse.toolName,
|
|
44688
|
-
request: pendingDocyrusWebBrowserRequest.input,
|
|
44689
|
-
status: docyrusWebBrowserResponse.isError ? "error" : "success",
|
|
44690
|
-
output: docyrusWebBrowserResponse.output,
|
|
44691
|
-
errorText: docyrusWebBrowserResponse.errorText
|
|
44692
|
-
}) : void 0 : extractLastUserText(messages);
|
|
44787
|
+
const userMessage = askUserResponse ? pendingAskUser && pendingAskUser.toolCallId === askUserResponse.toolCallId ? formatAskUserResponsePrompt(askUserResponse.response) : void 0 : extractLastUserText(messages);
|
|
44693
44788
|
if (!userMessage) {
|
|
44694
44789
|
return c.json({
|
|
44695
|
-
error: askUserResponse ? "No matching pending ask_user request found" :
|
|
44790
|
+
error: askUserResponse ? "No matching pending ask_user request found" : "No user message found"
|
|
44696
44791
|
}, 400);
|
|
44697
44792
|
}
|
|
44698
44793
|
if (askUserResponse) {
|
|
44699
44794
|
pendingAskUserRequests.delete(sessionId);
|
|
44700
44795
|
}
|
|
44701
|
-
if (docyrusWebBrowserResponse && pendingDocyrusWebBrowser) {
|
|
44702
|
-
pendingDocyrusWebBrowser.delete(docyrusWebBrowserResponse.toolCallId);
|
|
44703
|
-
if (pendingDocyrusWebBrowser.size === 0) {
|
|
44704
|
-
pendingDocyrusWebBrowserRequests.delete(sessionId);
|
|
44705
|
-
}
|
|
44706
|
-
}
|
|
44707
44796
|
let promptText = userMessage;
|
|
44708
|
-
if (!askUserResponse
|
|
44797
|
+
if (!askUserResponse) {
|
|
44709
44798
|
const modeCmd = parseModeSlashCommand(userMessage);
|
|
44710
44799
|
if (modeCmd) {
|
|
44711
44800
|
if (activeSession.isStreaming) {
|
|
@@ -44747,23 +44836,30 @@ async function createAgentServer(params) {
|
|
|
44747
44836
|
}
|
|
44748
44837
|
writeChunk({ type: "start" });
|
|
44749
44838
|
writeChunk({ type: "start-step" });
|
|
44839
|
+
let extensionUICleanup;
|
|
44840
|
+
if (extensionUIBridge) {
|
|
44841
|
+
extensionUIBridge.setRequestHandler((request) => {
|
|
44842
|
+
writeChunk(request);
|
|
44843
|
+
});
|
|
44844
|
+
extensionUICleanup = () => {
|
|
44845
|
+
extensionUIBridge.setRequestHandler(() => {
|
|
44846
|
+
});
|
|
44847
|
+
};
|
|
44848
|
+
}
|
|
44750
44849
|
const bridge = createEventBridge({
|
|
44751
44850
|
messageId,
|
|
44752
44851
|
onChunk: writeChunk,
|
|
44753
44852
|
onAskUser: ({ toolCallId, request }) => {
|
|
44754
44853
|
pendingAskUserRequests.set(sessionId, { toolCallId, request });
|
|
44755
44854
|
},
|
|
44756
|
-
onDocyrusWebBrowserTool: ({ toolCallId, request }) => {
|
|
44757
|
-
const pendingForSession = pendingDocyrusWebBrowserRequests.get(sessionId) ?? /* @__PURE__ */ new Map();
|
|
44758
|
-
pendingForSession.set(toolCallId, request);
|
|
44759
|
-
pendingDocyrusWebBrowserRequests.set(sessionId, pendingForSession);
|
|
44760
|
-
},
|
|
44761
44855
|
onDone: () => {
|
|
44856
|
+
extensionUICleanup?.();
|
|
44762
44857
|
writeChunk({ type: "finish-step" });
|
|
44763
44858
|
writeChunk({ type: "finish" });
|
|
44764
44859
|
controller.close();
|
|
44765
44860
|
},
|
|
44766
44861
|
onError: (errorText) => {
|
|
44862
|
+
extensionUICleanup?.();
|
|
44767
44863
|
writeChunk({ type: "error", errorText });
|
|
44768
44864
|
writeChunk({ type: "finish-step" });
|
|
44769
44865
|
writeChunk({ type: "finish" });
|
|
@@ -44934,7 +45030,8 @@ async function createAgentServer(params) {
|
|
|
44934
45030
|
agentDir: context.agentDir,
|
|
44935
45031
|
version: context.version,
|
|
44936
45032
|
sessionDir: context.sessionDir,
|
|
44937
|
-
thinkingLevel: context.thinkingLevel
|
|
45033
|
+
thinkingLevel: context.thinkingLevel,
|
|
45034
|
+
desktop: context.desktop
|
|
44938
45035
|
});
|
|
44939
45036
|
});
|
|
44940
45037
|
app.get("/api/models", (c) => {
|
|
@@ -45623,6 +45720,59 @@ async function createAgentServer(params) {
|
|
|
45623
45720
|
return c.json({ error: message }, 400);
|
|
45624
45721
|
}
|
|
45625
45722
|
});
|
|
45723
|
+
app.post("/api/fs/upload", async (c) => {
|
|
45724
|
+
const MAX_UPLOAD_SIZE = 50 * 1024 * 1024;
|
|
45725
|
+
const contentLength = parseInt(c.req.header("content-length") || "0", 10);
|
|
45726
|
+
if (contentLength > MAX_UPLOAD_SIZE) {
|
|
45727
|
+
return c.json({ error: `File too large. Maximum upload size is ${MAX_UPLOAD_SIZE / 1024 / 1024} MB` }, 413);
|
|
45728
|
+
}
|
|
45729
|
+
const contentType = c.req.header("content-type") || "";
|
|
45730
|
+
if (contentType.includes("multipart/form-data")) {
|
|
45731
|
+
const formData = await c.req.formData();
|
|
45732
|
+
const file2 = formData.get("file");
|
|
45733
|
+
const targetPath2 = formData.get("path");
|
|
45734
|
+
if (!file2 || !(file2 instanceof File)) {
|
|
45735
|
+
return c.json({ error: "Missing required form field: file" }, 400);
|
|
45736
|
+
}
|
|
45737
|
+
if (file2.size > MAX_UPLOAD_SIZE) {
|
|
45738
|
+
return c.json({ error: `File too large (${(file2.size / 1024 / 1024).toFixed(1)} MB). Maximum is ${MAX_UPLOAD_SIZE / 1024 / 1024} MB` }, 413);
|
|
45739
|
+
}
|
|
45740
|
+
const destPath = typeof targetPath2 === "string" && targetPath2 ? targetPath2 : file2.name;
|
|
45741
|
+
try {
|
|
45742
|
+
const sanitized = sanitizeUploadPath(destPath);
|
|
45743
|
+
const resolved = resolveSafePath(context.cwd, sanitized);
|
|
45744
|
+
await assertNotSymlink(resolved);
|
|
45745
|
+
await (0, import_promises18.mkdir)((0, import_node_path22.join)(resolved, ".."), { recursive: true });
|
|
45746
|
+
const buffer = Buffer.from(await file2.arrayBuffer());
|
|
45747
|
+
await (0, import_promises18.writeFile)(resolved, buffer);
|
|
45748
|
+
const fileStat = await (0, import_promises18.stat)(resolved);
|
|
45749
|
+
return c.json({ ok: true, path: sanitized, name: file2.name, size: fileStat.size, type: file2.type });
|
|
45750
|
+
} catch (error48) {
|
|
45751
|
+
const message = error48 instanceof Error ? error48.message : String(error48);
|
|
45752
|
+
return c.json({ error: message }, 400);
|
|
45753
|
+
}
|
|
45754
|
+
}
|
|
45755
|
+
const targetPath = c.req.query("path") || c.req.header("x-file-path");
|
|
45756
|
+
if (!targetPath) {
|
|
45757
|
+
return c.json({ error: "Missing target path. Use multipart form field 'path', query param 'path', or header 'X-File-Path'" }, 400);
|
|
45758
|
+
}
|
|
45759
|
+
try {
|
|
45760
|
+
const sanitized = sanitizeUploadPath(targetPath);
|
|
45761
|
+
const resolved = resolveSafePath(context.cwd, sanitized);
|
|
45762
|
+
await assertNotSymlink(resolved);
|
|
45763
|
+
await (0, import_promises18.mkdir)((0, import_node_path22.join)(resolved, ".."), { recursive: true });
|
|
45764
|
+
const buffer = Buffer.from(await c.req.arrayBuffer());
|
|
45765
|
+
if (buffer.length > MAX_UPLOAD_SIZE) {
|
|
45766
|
+
return c.json({ error: `File too large (${(buffer.length / 1024 / 1024).toFixed(1)} MB). Maximum is ${MAX_UPLOAD_SIZE / 1024 / 1024} MB` }, 413);
|
|
45767
|
+
}
|
|
45768
|
+
await (0, import_promises18.writeFile)(resolved, buffer);
|
|
45769
|
+
const fileStat = await (0, import_promises18.stat)(resolved);
|
|
45770
|
+
return c.json({ ok: true, path: sanitized, size: fileStat.size });
|
|
45771
|
+
} catch (error48) {
|
|
45772
|
+
const message = error48 instanceof Error ? error48.message : String(error48);
|
|
45773
|
+
return c.json({ error: message }, 400);
|
|
45774
|
+
}
|
|
45775
|
+
});
|
|
45626
45776
|
app.post("/api/fs/mkdir", async (c) => {
|
|
45627
45777
|
const body2 = await c.req.json();
|
|
45628
45778
|
if (!body2.path) {
|
|
@@ -46275,19 +46425,36 @@ async function createAgentServer(params) {
|
|
|
46275
46425
|
return c.json({ error: message }, 500);
|
|
46276
46426
|
}
|
|
46277
46427
|
});
|
|
46428
|
+
app.post("/api/extension-ui-response", async (c) => {
|
|
46429
|
+
if (!extensionUIBridge) {
|
|
46430
|
+
return c.json({ error: "Extension UI bridge not available" }, 404);
|
|
46431
|
+
}
|
|
46432
|
+
const body2 = await c.req.json();
|
|
46433
|
+
if (!body2.id) {
|
|
46434
|
+
return c.json({ error: "Missing required field: id" }, 400);
|
|
46435
|
+
}
|
|
46436
|
+
const handled = extensionUIBridge.handleResponse(body2);
|
|
46437
|
+
if (!handled) {
|
|
46438
|
+
return c.json({ error: `No pending extension UI request for id: ${body2.id}` }, 404);
|
|
46439
|
+
}
|
|
46440
|
+
return c.json({ ok: true });
|
|
46441
|
+
});
|
|
46278
46442
|
app.get("/api/tools", async (c) => {
|
|
46279
46443
|
try {
|
|
46280
46444
|
const tools = await listAllTools({
|
|
46281
46445
|
profile: context.profile,
|
|
46282
46446
|
agentDir: context.agentDir,
|
|
46283
|
-
cwd: context.cwd
|
|
46284
|
-
includeDocyrusWebBrowser: true
|
|
46447
|
+
cwd: context.cwd
|
|
46285
46448
|
});
|
|
46449
|
+
if (context.desktop) {
|
|
46450
|
+
tools.push(...BROWSER_TOOL_SCHEMAS);
|
|
46451
|
+
}
|
|
46286
46452
|
const builtInCount = tools.filter((t) => t.source === "built-in").length;
|
|
46287
46453
|
const mcpCount = tools.filter((t) => t.source !== "built-in").length;
|
|
46288
46454
|
return c.json({
|
|
46289
46455
|
tools,
|
|
46290
|
-
summary: { builtIn: builtInCount, mcp: mcpCount, total: tools.length }
|
|
46456
|
+
summary: { builtIn: builtInCount, mcp: mcpCount, total: tools.length },
|
|
46457
|
+
clientSideToolPrefixes: context.desktop ? [BROWSER_TOOL_PREFIX] : []
|
|
46291
46458
|
});
|
|
46292
46459
|
} catch (error48) {
|
|
46293
46460
|
const message = error48 instanceof Error ? error48.message : String(error48);
|
|
@@ -46479,6 +46646,8 @@ async function createAgentServer(params) {
|
|
|
46479
46646
|
process.stderr.write(` GET /api/fs/read \u2014 read file contents
|
|
46480
46647
|
`);
|
|
46481
46648
|
process.stderr.write(` POST /api/fs/write \u2014 write file
|
|
46649
|
+
`);
|
|
46650
|
+
process.stderr.write(` POST /api/fs/upload \u2014 upload file (multipart or raw)
|
|
46482
46651
|
`);
|
|
46483
46652
|
process.stderr.write(` POST /api/fs/mkdir \u2014 create directory
|
|
46484
46653
|
`);
|
|
@@ -46510,6 +46679,12 @@ async function createAgentServer(params) {
|
|
|
46510
46679
|
`);
|
|
46511
46680
|
process.stderr.write(` GET /api/tools \u2014 list all tools
|
|
46512
46681
|
`);
|
|
46682
|
+
if (context.desktop) {
|
|
46683
|
+
process.stderr.write(` POST /api/extension-ui-response \u2014 submit extension UI response
|
|
46684
|
+
`);
|
|
46685
|
+
process.stderr.write(` Browser tools: docyrus_browser_* (client-side via extension_ui)
|
|
46686
|
+
`);
|
|
46687
|
+
}
|
|
46513
46688
|
process.stderr.write(` * /api/cli/** \u2014 proxy any docyrus CLI command
|
|
46514
46689
|
`);
|
|
46515
46690
|
process.stderr.write(` WS /api/terminal \u2014 PTY terminal (WebSocket)
|
|
@@ -46705,6 +46880,163 @@ function createServerSessionAdapter(params) {
|
|
|
46705
46880
|
};
|
|
46706
46881
|
}
|
|
46707
46882
|
|
|
46883
|
+
// src/server/extensionUIBridge.ts
|
|
46884
|
+
var import_node_crypto7 = require("node:crypto");
|
|
46885
|
+
var ExtensionUIBridge = class {
|
|
46886
|
+
pending = /* @__PURE__ */ new Map();
|
|
46887
|
+
requestHandler;
|
|
46888
|
+
defaultTimeout;
|
|
46889
|
+
constructor(options) {
|
|
46890
|
+
this.requestHandler = options.onRequest;
|
|
46891
|
+
this.defaultTimeout = options.defaultTimeout ?? 12e4;
|
|
46892
|
+
}
|
|
46893
|
+
/**
|
|
46894
|
+
* Update the request handler. Used to wire into the active chat stream.
|
|
46895
|
+
*/
|
|
46896
|
+
setRequestHandler(handler2) {
|
|
46897
|
+
this.requestHandler = handler2;
|
|
46898
|
+
}
|
|
46899
|
+
/**
|
|
46900
|
+
* Handle an incoming extension_ui_response from the client.
|
|
46901
|
+
* Returns true if the response matched a pending request.
|
|
46902
|
+
*/
|
|
46903
|
+
handleResponse(response) {
|
|
46904
|
+
const entry = this.pending.get(response.id);
|
|
46905
|
+
if (!entry) {
|
|
46906
|
+
return false;
|
|
46907
|
+
}
|
|
46908
|
+
if (entry.timer) {
|
|
46909
|
+
clearTimeout(entry.timer);
|
|
46910
|
+
}
|
|
46911
|
+
this.pending.delete(response.id);
|
|
46912
|
+
entry.resolve(response);
|
|
46913
|
+
return true;
|
|
46914
|
+
}
|
|
46915
|
+
/**
|
|
46916
|
+
* Check if there are any pending requests.
|
|
46917
|
+
*/
|
|
46918
|
+
hasPending() {
|
|
46919
|
+
return this.pending.size > 0;
|
|
46920
|
+
}
|
|
46921
|
+
/**
|
|
46922
|
+
* Create a dialog promise that sends a request and waits for a response.
|
|
46923
|
+
*/
|
|
46924
|
+
createDialogPromise(request, defaultValue, parseResponse, opts) {
|
|
46925
|
+
if (opts?.signal?.aborted) {
|
|
46926
|
+
return Promise.resolve(defaultValue);
|
|
46927
|
+
}
|
|
46928
|
+
const id = (0, import_node_crypto7.randomUUID)();
|
|
46929
|
+
const timeout = opts?.timeout ?? this.defaultTimeout;
|
|
46930
|
+
return new Promise((resolve4, reject) => {
|
|
46931
|
+
const cleanup = () => {
|
|
46932
|
+
if (timer) {
|
|
46933
|
+
clearTimeout(timer);
|
|
46934
|
+
}
|
|
46935
|
+
opts?.signal?.removeEventListener("abort", onAbort);
|
|
46936
|
+
this.pending.delete(id);
|
|
46937
|
+
};
|
|
46938
|
+
const onAbort = () => {
|
|
46939
|
+
cleanup();
|
|
46940
|
+
resolve4(defaultValue);
|
|
46941
|
+
};
|
|
46942
|
+
opts?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
46943
|
+
const timer = timeout > 0 ? setTimeout(() => {
|
|
46944
|
+
cleanup();
|
|
46945
|
+
resolve4(defaultValue);
|
|
46946
|
+
}, timeout) : void 0;
|
|
46947
|
+
this.pending.set(id, {
|
|
46948
|
+
resolve: (response) => {
|
|
46949
|
+
cleanup();
|
|
46950
|
+
resolve4(parseResponse(response));
|
|
46951
|
+
},
|
|
46952
|
+
reject: (error48) => {
|
|
46953
|
+
cleanup();
|
|
46954
|
+
reject(error48);
|
|
46955
|
+
},
|
|
46956
|
+
timer
|
|
46957
|
+
});
|
|
46958
|
+
this.requestHandler({ type: "extension_ui_request", id, ...request });
|
|
46959
|
+
});
|
|
46960
|
+
}
|
|
46961
|
+
/**
|
|
46962
|
+
* Build an ExtensionUIContext compatible with pi agent's extension system.
|
|
46963
|
+
*/
|
|
46964
|
+
createUIContext() {
|
|
46965
|
+
return {
|
|
46966
|
+
select: (title, options, opts) => this.createDialogPromise(
|
|
46967
|
+
{ method: "select", title, options, timeout: opts?.timeout },
|
|
46968
|
+
void 0,
|
|
46969
|
+
(r) => "cancelled" in r && r.cancelled ? void 0 : r.value,
|
|
46970
|
+
opts
|
|
46971
|
+
),
|
|
46972
|
+
confirm: (title, message, opts) => this.createDialogPromise(
|
|
46973
|
+
{ method: "confirm", title, message, timeout: opts?.timeout },
|
|
46974
|
+
false,
|
|
46975
|
+
(r) => "cancelled" in r && r.cancelled ? false : r.confirmed ?? false,
|
|
46976
|
+
opts
|
|
46977
|
+
),
|
|
46978
|
+
input: (title, placeholder, opts) => this.createDialogPromise(
|
|
46979
|
+
{ method: "input", title, placeholder, timeout: opts?.timeout },
|
|
46980
|
+
void 0,
|
|
46981
|
+
(r) => "cancelled" in r && r.cancelled ? void 0 : r.value,
|
|
46982
|
+
opts
|
|
46983
|
+
),
|
|
46984
|
+
editor: (title, prefill, opts) => this.createDialogPromise(
|
|
46985
|
+
{ method: "editor", title, placeholder: prefill },
|
|
46986
|
+
void 0,
|
|
46987
|
+
(r) => "cancelled" in r && r.cancelled ? void 0 : r.value,
|
|
46988
|
+
opts
|
|
46989
|
+
),
|
|
46990
|
+
notify: (message, notifyType) => {
|
|
46991
|
+
const id = (0, import_node_crypto7.randomUUID)();
|
|
46992
|
+
this.requestHandler({ type: "extension_ui_request", id, method: "notify", message, placeholder: notifyType });
|
|
46993
|
+
},
|
|
46994
|
+
setStatus: (statusKey, statusText) => {
|
|
46995
|
+
const id = (0, import_node_crypto7.randomUUID)();
|
|
46996
|
+
this.requestHandler({ type: "extension_ui_request", id, method: "setStatus", title: statusKey, message: statusText });
|
|
46997
|
+
},
|
|
46998
|
+
setWidget: (widgetKey, widgetLines) => {
|
|
46999
|
+
const id = (0, import_node_crypto7.randomUUID)();
|
|
47000
|
+
this.requestHandler({ type: "extension_ui_request", id, method: "setWidget", title: widgetKey, options: widgetLines });
|
|
47001
|
+
},
|
|
47002
|
+
setTitle: (title) => {
|
|
47003
|
+
const id = (0, import_node_crypto7.randomUUID)();
|
|
47004
|
+
this.requestHandler({ type: "extension_ui_request", id, method: "setTitle", title });
|
|
47005
|
+
},
|
|
47006
|
+
setEditorText: (text3) => {
|
|
47007
|
+
const id = (0, import_node_crypto7.randomUUID)();
|
|
47008
|
+
this.requestHandler({ type: "extension_ui_request", id, method: "set_editor_text", message: text3 });
|
|
47009
|
+
},
|
|
47010
|
+
// No-op methods for server mode (not applicable without TUI)
|
|
47011
|
+
setWorkingMessage: () => {
|
|
47012
|
+
},
|
|
47013
|
+
setHiddenThinkingLabel: () => {
|
|
47014
|
+
},
|
|
47015
|
+
setFooter: () => {
|
|
47016
|
+
},
|
|
47017
|
+
setHeader: () => {
|
|
47018
|
+
},
|
|
47019
|
+
onTerminalInput: () => () => {
|
|
47020
|
+
},
|
|
47021
|
+
custom: async () => void 0,
|
|
47022
|
+
pasteToEditor: () => {
|
|
47023
|
+
},
|
|
47024
|
+
getEditorText: () => "",
|
|
47025
|
+
setEditorComponent: () => {
|
|
47026
|
+
},
|
|
47027
|
+
get theme() {
|
|
47028
|
+
return { name: "default" };
|
|
47029
|
+
},
|
|
47030
|
+
getAllThemes: () => [],
|
|
47031
|
+
getTheme: () => void 0,
|
|
47032
|
+
setTheme: () => ({ success: false, error: "UI not available in server mode" }),
|
|
47033
|
+
getToolsExpanded: () => false,
|
|
47034
|
+
setToolsExpanded: () => {
|
|
47035
|
+
}
|
|
47036
|
+
};
|
|
47037
|
+
}
|
|
47038
|
+
};
|
|
47039
|
+
|
|
46708
47040
|
// src/services/spinner.ts
|
|
46709
47041
|
var import_picocolors = __toESM(require_picocolors());
|
|
46710
47042
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
@@ -46919,8 +47251,17 @@ async function main() {
|
|
|
46919
47251
|
`
|
|
46920
47252
|
);
|
|
46921
47253
|
}
|
|
47254
|
+
const extensionUIBridge = new ExtensionUIBridge({
|
|
47255
|
+
onRequest: () => {
|
|
47256
|
+
}
|
|
47257
|
+
// Will be wired up by agentServer after the chat stream is created
|
|
47258
|
+
});
|
|
47259
|
+
await session.bindExtensions({
|
|
47260
|
+
uiContext: extensionUIBridge.createUIContext()
|
|
47261
|
+
});
|
|
46922
47262
|
await createAgentServer({
|
|
46923
47263
|
session: createServerSessionAdapter({ session, extensionsResult }),
|
|
47264
|
+
extensionUIBridge,
|
|
46924
47265
|
authToken: request.auth,
|
|
46925
47266
|
port: request.port,
|
|
46926
47267
|
sessionManager: {
|
|
@@ -46940,7 +47281,8 @@ async function main() {
|
|
|
46940
47281
|
agentDir,
|
|
46941
47282
|
version: version2,
|
|
46942
47283
|
sessionDir: request.sessionDir ?? null,
|
|
46943
|
-
thinkingLevel: request.thinking ?? null
|
|
47284
|
+
thinkingLevel: request.thinking ?? null,
|
|
47285
|
+
desktop: request.desktop === true
|
|
46944
47286
|
},
|
|
46945
47287
|
onCreateSession: async () => {
|
|
46946
47288
|
const { session: freshSession, extensionsResult: freshExtensionsResult } = await pi.createAgentSession({
|
|
@@ -46959,6 +47301,9 @@ async function main() {
|
|
|
46959
47301
|
session: freshSession,
|
|
46960
47302
|
modelRegistry
|
|
46961
47303
|
});
|
|
47304
|
+
await freshSession.bindExtensions({
|
|
47305
|
+
uiContext: extensionUIBridge.createUIContext()
|
|
47306
|
+
});
|
|
46962
47307
|
return createServerSessionAdapter({
|
|
46963
47308
|
session: freshSession,
|
|
46964
47309
|
extensionsResult: freshExtensionsResult
|
|
@@ -46991,6 +47336,9 @@ async function main() {
|
|
|
46991
47336
|
session: resumedSession,
|
|
46992
47337
|
modelRegistry
|
|
46993
47338
|
});
|
|
47339
|
+
await resumedSession.bindExtensions({
|
|
47340
|
+
uiContext: extensionUIBridge.createUIContext()
|
|
47341
|
+
});
|
|
46994
47342
|
return createServerSessionAdapter({
|
|
46995
47343
|
session: resumedSession,
|
|
46996
47344
|
extensionsResult: resumedExtensionsResult
|