zrocclaw 0.0.12 → 0.0.14

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 CHANGED
@@ -6,6 +6,14 @@ ZrocClaw 官方命令行工具,用于快速启动和管理你的专属浏览
6
6
 
7
7
  本工具提供了简易的命令,底层依赖 `pm2` 来守护后台进程,帮助你一键启动、停止、或更新 ZrocClaw 服务。
8
8
 
9
+ ## 前置条件
10
+
11
+ 在安装 CLI 工具前,请确保您的系统满足以下要求:
12
+
13
+ - **Node.js**: 版本需 **>= 22.0.0**。
14
+ - **包管理器**: `npm` 或 `pnpm`。
15
+ - **浏览器**: 需本地安装 **Google Chrome** 或 Chromium 浏览器(以支持 ZrocClaw 后台的浏览器自动化操作)。
16
+
9
17
  ## 安装
10
18
 
11
19
  推荐进行全局安装,以便在任何终端路径下均可执行:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zrocclaw",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "description": "个人专属浏览器AI助手命令行工具",
5
5
  "keywords": [
6
6
  "zrocclaw",
package/server/server.js CHANGED
@@ -30,7 +30,7 @@ var import_helmet = __toESM(require("helmet"));
30
30
  // src/routes/config.ts
31
31
  var import_express = require("express");
32
32
 
33
- // ../../packages/core/dist/chunk-46XX2E7T.mjs
33
+ // ../../packages/core/dist/chunk-5YB6N3QX.mjs
34
34
  var os = __toESM(require("os"), 1);
35
35
  var path = __toESM(require("path"), 1);
36
36
  var fs = __toESM(require("fs"), 1);
@@ -79,6 +79,10 @@ function getHistoryPath(subPath) {
79
79
  const historyPath = path.join(getConfigPath(), "history");
80
80
  return resolveAndEnsurePath(historyPath, subPath);
81
81
  }
82
+ function getSkillsPath(subPath) {
83
+ const skillsPath = path.join(getWorkspacePath(), "skills");
84
+ return resolveAndEnsurePath(skillsPath, subPath);
85
+ }
82
86
  var SessionModel = class _SessionModel {
83
87
  static instance;
84
88
  filePath;
@@ -167,27 +171,37 @@ var session_default = sessionModel;
167
171
  // src/routes/config.ts
168
172
  var import_promises = __toESM(require("fs/promises"));
169
173
  var import_path2 = __toESM(require("path"));
174
+ var import_crypto = __toESM(require("crypto"));
170
175
  var router = (0, import_express.Router)();
171
176
  router.get("/", (req, res) => {
172
177
  res.json({ configPath: getConfigPath() });
173
178
  });
174
179
  var configDir = getConfigPath();
175
- var configFilePath = import_path2.default.join(configDir, "model.json");
180
+ var configModelPath = import_path2.default.join(configDir, "model.json");
181
+ async function readModelConfig() {
182
+ try {
183
+ await import_promises.default.access(configModelPath);
184
+ const data = await import_promises.default.readFile(configModelPath, "utf-8");
185
+ return JSON.parse(data);
186
+ } catch (error) {
187
+ if (error.code === "ENOENT") {
188
+ return {};
189
+ }
190
+ throw error;
191
+ }
192
+ }
193
+ async function writeModelConfig(config) {
194
+ await import_promises.default.mkdir(configDir, { recursive: true });
195
+ await import_promises.default.writeFile(configModelPath, JSON.stringify(config, null, 2), "utf-8");
196
+ }
176
197
  router.get("/model", async (req, res) => {
177
198
  try {
178
- try {
179
- await import_promises.default.access(configFilePath);
180
- const data = await import_promises.default.readFile(configFilePath, "utf-8");
181
- res.json(JSON.parse(data));
182
- } catch (error) {
183
- if (error.code === "ENOENT") {
184
- await import_promises.default.mkdir(configDir, { recursive: true });
185
- await import_promises.default.writeFile(configFilePath, "{}", "utf-8");
186
- res.json({});
187
- } else {
188
- throw error;
189
- }
199
+ const config = await readModelConfig();
200
+ if (Object.keys(config).length === 0) {
201
+ await writeModelConfig({});
202
+ return res.json({});
190
203
  }
204
+ res.json(config);
191
205
  } catch (error) {
192
206
  console.error("\u83B7\u53D6\u6A21\u578B\u5931\u8D25:", error);
193
207
  res.status(500).json({ error: "\u83B7\u53D6\u6A21\u578B\u5931\u8D25" });
@@ -195,22 +209,237 @@ router.get("/model", async (req, res) => {
195
209
  });
196
210
  router.post("/model", async (req, res) => {
197
211
  try {
198
- await import_promises.default.mkdir(configDir, { recursive: true });
199
- let currentConfig = {};
212
+ const { modelName, provider, apiKey, baseURL } = req.body;
213
+ const newModel = {
214
+ id: import_crypto.default.randomUUID(),
215
+ modelName,
216
+ provider,
217
+ apiKey,
218
+ baseURL
219
+ };
220
+ const config = await readModelConfig();
221
+ if (!Array.isArray(config.models)) {
222
+ config.models = [];
223
+ }
224
+ config.models.unshift(newModel);
225
+ if (!config.defaultModel) {
226
+ config.defaultModel = newModel;
227
+ }
228
+ await writeModelConfig(config);
229
+ res.json(newModel);
230
+ } catch (error) {
231
+ console.error("\u65B0\u589E\u6A21\u578B\u5931\u8D25:", error);
232
+ res.status(500).json({ error: "\u65B0\u589E\u6A21\u578B\u5931\u8D25" });
233
+ }
234
+ });
235
+ router.post("/model/edit", async (req, res) => {
236
+ try {
237
+ const modelData = req.body;
238
+ if (!modelData || !modelData.id) {
239
+ return res.status(400).json({ error: "\u7F3A\u5C11\u6A21\u578BID" });
240
+ }
241
+ const config = await readModelConfig();
242
+ if (!Array.isArray(config.models)) {
243
+ config.models = [];
244
+ }
245
+ const index = config.models.findIndex((m) => m.id === modelData.id);
246
+ if (index === -1) {
247
+ return res.status(404).json({ error: "\u6A21\u578B\u4E0D\u5B58\u5728" });
248
+ }
249
+ config.models[index] = { ...config.models[index], ...modelData };
250
+ if (config.defaultModel && config.defaultModel.id === modelData.id) {
251
+ config.defaultModel = config.models[index];
252
+ }
253
+ await writeModelConfig(config);
254
+ res.json(config.models[index]);
255
+ } catch (error) {
256
+ console.error("\u7F16\u8F91\u6A21\u578B\u5931\u8D25:", error);
257
+ res.status(500).json({ error: "\u7F16\u8F91\u6A21\u578B\u5931\u8D25" });
258
+ }
259
+ });
260
+ router.post("/model/delete", async (req, res) => {
261
+ try {
262
+ const { id } = req.body;
263
+ if (!id) {
264
+ return res.status(400).json({ error: "\u7F3A\u5C11\u6A21\u578BID" });
265
+ }
266
+ const config = await readModelConfig();
267
+ if (!Array.isArray(config.models)) {
268
+ config.models = [];
269
+ }
270
+ const initialLength = config.models.length;
271
+ config.models = config.models.filter((m) => m.id !== id);
272
+ if (config.models.length === initialLength) {
273
+ return res.status(404).json({ error: "\u6A21\u578B\u4E0D\u5B58\u5728" });
274
+ }
275
+ if (config.defaultModel && config.defaultModel.id === id) {
276
+ config.defaultModel = config.models.length > 0 ? config.models[0] : null;
277
+ }
278
+ await writeModelConfig(config);
279
+ res.json({ success: true, message: "\u5220\u9664\u6210\u529F" });
280
+ } catch (error) {
281
+ console.error("\u5220\u9664\u6A21\u578B\u5931\u8D25:", error);
282
+ res.status(500).json({ error: "\u5220\u9664\u6A21\u578B\u5931\u8D25" });
283
+ }
284
+ });
285
+ router.post("/model/default", async (req, res) => {
286
+ try {
287
+ const { id } = req.body;
288
+ if (!id) {
289
+ return res.status(400).json({ error: "\u7F3A\u5C11\u6A21\u578BID" });
290
+ }
291
+ const config = await readModelConfig();
292
+ if (!Array.isArray(config.models)) {
293
+ config.models = [];
294
+ }
295
+ const model = config.models.find((m) => m.id === id);
296
+ if (!model) {
297
+ return res.status(404).json({ error: "\u6A21\u578B\u4E0D\u5B58\u5728" });
298
+ }
299
+ config.defaultModel = model;
300
+ await writeModelConfig(config);
301
+ res.json({ success: true, defaultModel: config.defaultModel });
302
+ } catch (error) {
303
+ console.error("\u8BBE\u7F6E\u9ED8\u8BA4\u6A21\u578B\u5931\u8D25:", error);
304
+ res.status(500).json({ error: "\u8BBE\u7F6E\u9ED8\u8BA4\u6A21\u578B\u5931\u8D25" });
305
+ }
306
+ });
307
+ var workspaceDir = getWorkspacePath();
308
+ var workspaceSkillsPath = import_path2.default.join(workspaceDir, "SKILLS.json");
309
+ var skillsDir = getSkillsPath();
310
+ async function readSkillsConfig() {
311
+ try {
312
+ await import_promises.default.access(workspaceSkillsPath);
313
+ const data = await import_promises.default.readFile(workspaceSkillsPath, "utf-8");
314
+ return JSON.parse(data);
315
+ } catch (error) {
316
+ if (error.code === "ENOENT") {
317
+ return [];
318
+ }
319
+ throw error;
320
+ }
321
+ }
322
+ async function writeSkillsConfig(config) {
323
+ await import_promises.default.mkdir(workspaceDir, { recursive: true });
324
+ await import_promises.default.writeFile(workspaceSkillsPath, JSON.stringify(config, null, 2), "utf-8");
325
+ }
326
+ router.get("/skills", async (req, res) => {
327
+ try {
200
328
  try {
201
- const data = await import_promises.default.readFile(configFilePath, "utf-8");
202
- currentConfig = JSON.parse(data);
203
- } catch (error) {
204
- if (error.code !== "ENOENT") {
205
- throw error;
329
+ await import_promises.default.access(workspaceSkillsPath);
330
+ } catch (e) {
331
+ if (e.code === "ENOENT") {
332
+ await writeSkillsConfig([]);
333
+ return res.json([]);
334
+ }
335
+ }
336
+ const config = await readSkillsConfig();
337
+ res.json(config);
338
+ } catch (error) {
339
+ console.error("\u83B7\u53D6\u6280\u80FD\u5217\u8868\u5931\u8D25:", error);
340
+ res.status(500).json({ error: "\u83B7\u53D6\u6280\u80FD\u5217\u8868\u5931\u8D25" });
341
+ }
342
+ });
343
+ router.post("/skills", async (req, res) => {
344
+ try {
345
+ const { name, summary, content } = req.body;
346
+ const id = import_crypto.default.randomUUID();
347
+ const newSkill = {
348
+ id,
349
+ name,
350
+ summary,
351
+ path: `./skills/${id}.md`
352
+ };
353
+ const skills = await readSkillsConfig();
354
+ if (!Array.isArray(skills)) {
355
+ throw new Error("skills.json \u683C\u5F0F\u9519\u8BEF");
356
+ }
357
+ skills.unshift(newSkill);
358
+ await writeSkillsConfig(skills);
359
+ await import_promises.default.mkdir(skillsDir, { recursive: true });
360
+ const mdPath = import_path2.default.join(skillsDir, `${id}.md`);
361
+ await import_promises.default.writeFile(mdPath, content || "", "utf-8");
362
+ res.json(newSkill);
363
+ } catch (error) {
364
+ console.error("\u65B0\u589E\u6280\u80FD\u5931\u8D25:", error);
365
+ res.status(500).json({ error: "\u65B0\u589E\u6280\u80FD\u5931\u8D25" });
366
+ }
367
+ });
368
+ router.post("/skills/edit", async (req, res) => {
369
+ try {
370
+ const { id, name, summary, content } = req.body;
371
+ if (!id) {
372
+ return res.status(400).json({ error: "\u7F3A\u5C11\u6280\u80FDID" });
373
+ }
374
+ const skills = await readSkillsConfig();
375
+ if (!Array.isArray(skills)) {
376
+ throw new Error("skills.json \u683C\u5F0F\u9519\u8BEF");
377
+ }
378
+ const index = skills.findIndex((s) => s.id === id);
379
+ if (index === -1) {
380
+ return res.status(404).json({ error: "\u6280\u80FD\u4E0D\u5B58\u5728" });
381
+ }
382
+ skills[index] = { ...skills[index], name, summary };
383
+ await writeSkillsConfig(skills);
384
+ await import_promises.default.mkdir(skillsDir, { recursive: true });
385
+ const mdPath = import_path2.default.join(skillsDir, `${id}.md`);
386
+ await import_promises.default.writeFile(mdPath, content || "", "utf-8");
387
+ res.json(skills[index]);
388
+ } catch (error) {
389
+ console.error("\u7F16\u8F91\u6280\u80FD\u5931\u8D25:", error);
390
+ res.status(500).json({ error: "\u7F16\u8F91\u6280\u80FD\u5931\u8D25" });
391
+ }
392
+ });
393
+ router.post("/skills/delete", async (req, res) => {
394
+ try {
395
+ const { id } = req.body;
396
+ if (!id) {
397
+ return res.status(400).json({ error: "\u7F3A\u5C11\u6280\u80FDID" });
398
+ }
399
+ let skills = await readSkillsConfig();
400
+ if (!Array.isArray(skills)) {
401
+ throw new Error("skills.json \u683C\u5F0F\u9519\u8BEF");
402
+ }
403
+ const initialLength = skills.length;
404
+ skills = skills.filter((s) => s.id !== id);
405
+ if (skills.length === initialLength) {
406
+ return res.status(404).json({ error: "\u6280\u80FD\u4E0D\u5B58\u5728" });
407
+ }
408
+ await writeSkillsConfig(skills);
409
+ const mdPath = import_path2.default.join(skillsDir, `${id}.md`);
410
+ try {
411
+ await import_promises.default.unlink(mdPath);
412
+ } catch (e) {
413
+ if (e.code !== "ENOENT") {
414
+ console.error("\u5220\u9664\u6280\u80FD\u6587\u4EF6\u5931\u8D25:", e);
415
+ }
416
+ }
417
+ res.json({ success: true, message: "\u5220\u9664\u6210\u529F" });
418
+ } catch (error) {
419
+ console.error("\u5220\u9664\u6280\u80FD\u5931\u8D25:", error);
420
+ res.status(500).json({ error: "\u5220\u9664\u6280\u80FD\u5931\u8D25" });
421
+ }
422
+ });
423
+ router.post("/skills/detail", async (req, res) => {
424
+ try {
425
+ const { id } = req.body;
426
+ if (!id) {
427
+ return res.status(400).json({ error: "\u7F3A\u5C11\u6280\u80FDID" });
428
+ }
429
+ const mdPath = import_path2.default.join(skillsDir, `${id}.md`);
430
+ try {
431
+ await import_promises.default.access(mdPath);
432
+ const content = await import_promises.default.readFile(mdPath, "utf-8");
433
+ res.json({ content });
434
+ } catch (e) {
435
+ if (e.code === "ENOENT") {
436
+ return res.status(404).json({ error: "\u6280\u80FD\u6587\u4EF6\u4E0D\u5B58\u5728" });
206
437
  }
438
+ throw e;
207
439
  }
208
- const newConfig = { ...currentConfig, ...req.body };
209
- await import_promises.default.writeFile(configFilePath, JSON.stringify(newConfig, null, 2), "utf-8");
210
- res.json(newConfig);
211
440
  } catch (error) {
212
- console.error("\u4FEE\u6539\u6A21\u578B\u5931\u8D25:", error);
213
- res.status(500).json({ error: "\u4FEE\u6539\u6A21\u578B\u5931\u8D25" });
441
+ console.error("\u83B7\u53D6\u6280\u80FD\u8BE6\u60C5\u5931\u8D25:", error);
442
+ res.status(500).json({ error: "\u83B7\u53D6\u6280\u80FD\u8BE6\u60C5\u5931\u8D25" });
214
443
  }
215
444
  });
216
445
  var config_default = router;
@@ -377,7 +606,7 @@ var PlaywrightExecutor = class {
377
606
  }
378
607
  };
379
608
 
380
- // ../../packages/core/dist/chunk-RBTGQCL7.mjs
609
+ // ../../packages/core/dist/chunk-PEGXHECU.mjs
381
610
  var import_openai = require("@langchain/openai");
382
611
  var import_langchain = require("langchain");
383
612
  var import_messages = require("@langchain/core/messages");
@@ -515,14 +744,28 @@ var readFileTool = (0, import_tools2.tool)(
515
744
  }
516
745
  );
517
746
  var writeFileTool = (0, import_tools2.tool)(
518
- async ({ filePath, content }) => {
747
+ async ({ filePath, content, startLine, endLine }) => {
519
748
  const targetPath = resolveSafePath(filePath);
520
749
  try {
521
750
  const stat2 = await fs4.stat(targetPath);
522
751
  if (!stat2.isFile()) {
523
752
  throw new Error("\u76EE\u6807\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6");
524
753
  }
525
- await fs4.writeFile(targetPath, content || "", { encoding: "utf-8" });
754
+ if (startLine !== void 0) {
755
+ const fileContent = await fs4.readFile(targetPath, "utf-8");
756
+ const lines = fileContent.split("\n");
757
+ const startIdx = Math.max(0, startLine - 1);
758
+ let deleteCount = 1;
759
+ if (endLine !== void 0) {
760
+ const endIdx = endLine - 1;
761
+ deleteCount = Math.max(0, endIdx - startIdx + 1);
762
+ }
763
+ const contentLines = content.split("\n");
764
+ lines.splice(startIdx, deleteCount, ...contentLines);
765
+ await fs4.writeFile(targetPath, lines.join("\n"), { encoding: "utf-8" });
766
+ } else {
767
+ await fs4.writeFile(targetPath, content || "", { encoding: "utf-8" });
768
+ }
526
769
  return JSON.stringify({
527
770
  success: true,
528
771
  operation: "write_file",
@@ -539,10 +782,12 @@ var writeFileTool = (0, import_tools2.tool)(
539
782
  },
540
783
  {
541
784
  name: "write_file",
542
- description: "\u5728\u5DE5\u4F5C\u76EE\u5F55\u5185\u8986\u76D6\u5199\u5165\u5DF2\u6709\u6587\u4EF6\u3002\u6CE8\u610F\uFF1AfilePath\u5FC5\u987B\u662F\u76F8\u5BF9\u5DE5\u4F5C\u76EE\u5F55\u7684\u8DEF\u5F84\u3002",
785
+ description: "\u5728\u5DE5\u4F5C\u76EE\u5F55\u5185\u5199\u5165\u5DF2\u6709\u6587\u4EF6\u3002\u53EF\u4EE5\u5168\u91CF\u8986\u76D6\uFF0C\u4E5F\u53EF\u4EE5\u6307\u5B9A\u884C\u53F7\u8FDB\u884C\u5C40\u90E8\u66FF\u6362/\u63D2\u5165\u4EE5\u8282\u7701 token\u3002\u6CE8\u610F\uFF1AfilePath\u5FC5\u987B\u662F\u76F8\u5BF9\u5DE5\u4F5C\u76EE\u5F55\u7684\u8DEF\u5F84\u3002",
543
786
  schema: z2.object({
544
787
  filePath: z2.string().describe("\u76F8\u5BF9\u5DE5\u4F5C\u76EE\u5F55\u7684\u6587\u4EF6\u8DEF\u5F84"),
545
- content: z2.string().describe("\u8981\u5199\u5165\u7684\u6587\u4EF6\u5185\u5BB9")
788
+ content: z2.string().describe("\u8981\u5199\u5165\u6216\u63D2\u5165\u7684\u6587\u4EF6\u5185\u5BB9"),
789
+ startLine: z2.number().optional().describe("\u8D77\u59CB\u884C\u53F7\uFF08\u4ECE1\u5F00\u59CB\uFF09\u3002\u5982\u679C\u63D0\u4F9B\u6B64\u53C2\u6570\uFF0C\u5219\u5728\u6307\u5B9A\u884C\u8FDB\u884C\u5C40\u90E8\u66FF\u6362\u6216\u63D2\u5165\u3002\u5982\u679C\u672A\u63D0\u4F9B\uFF0C\u5219\u5168\u91CF\u8986\u76D6\u6587\u4EF6\u3002"),
790
+ endLine: z2.number().optional().describe("\u7ED3\u675F\u884C\u53F7\uFF08\u4ECE1\u5F00\u59CB\uFF0C\u5305\u542B\u8BE5\u884C\uFF09\u3002\u5982\u679C\u63D0\u4F9B\u6B64\u53C2\u6570\uFF0C\u5C06\u628A\u4ECE startLine \u5230 endLine \u7684\u5185\u5BB9\u66FF\u6362\u4E3A\u65B0 content\uFF1B\u5982\u679C\u4EC5\u63D0\u4F9B startLine\uFF0C\u5219\u4EC5\u66FF\u6362\u8BE5\u884C\uFF1B\u5982\u679C\u8981\u5728\u67D0\u884C\u524D\u5355\u7EAF\u63D2\u5165\u800C\u4E0D\u5220\u9664\u539F\u5185\u5BB9\uFF0C\u53EF\u5C06 endLine \u8BBE\u4E3A startLine - 1\u3002")
546
791
  })
547
792
  }
548
793
  );
@@ -616,9 +861,10 @@ var listFilesTool = (0, import_tools2.tool)(
616
861
  }
617
862
  );
618
863
  var extractPageStateTool = (0, import_tools3.tool)(
619
- async () => {
864
+ async (input) => {
865
+ const includeText = input?.includeText ?? false;
620
866
  const p = await playwrightManager.getPage();
621
- const elementsMetadata = await p.evaluate(() => {
867
+ const evaluateResult = await p.evaluate((includeTextArg) => {
622
868
  document.querySelectorAll(".ai-label-container").forEach((el) => el.remove());
623
869
  document.querySelectorAll("[idu-mark-id]").forEach((el) => el.removeAttribute("idu-mark-id"));
624
870
  const selectors = [
@@ -680,18 +926,48 @@ var extractPageStateTool = (0, import_tools3.tool)(
680
926
  });
681
927
  }
682
928
  });
683
- return metadata;
684
- });
929
+ let viewportText = void 0;
930
+ if (includeTextArg) {
931
+ const walker = document.createTreeWalker(
932
+ document.body,
933
+ NodeFilter.SHOW_TEXT,
934
+ null
935
+ );
936
+ let node;
937
+ const textParts = [];
938
+ while (node = walker.nextNode()) {
939
+ const parent = node.parentElement;
940
+ const text = node.nodeValue?.trim();
941
+ if (parent && text) {
942
+ const tagName = parent.tagName.toLowerCase();
943
+ if (tagName !== "script" && tagName !== "style" && tagName !== "noscript") {
944
+ if (isVisible(parent)) {
945
+ const rect = parent.getBoundingClientRect();
946
+ if (isInViewport(rect)) {
947
+ textParts.push(text);
948
+ }
949
+ }
950
+ }
951
+ }
952
+ }
953
+ viewportText = Array.from(new Set(textParts)).join("\n");
954
+ }
955
+ return { metadata, viewportText };
956
+ }, includeText);
685
957
  const title = await p.title();
686
958
  return {
687
959
  url: p.url(),
688
960
  title,
689
- elements: elementsMetadata
961
+ elements: evaluateResult.metadata,
962
+ ...includeText && evaluateResult.viewportText ? { text: evaluateResult.viewportText } : {}
690
963
  };
691
964
  },
692
965
  {
693
966
  name: "extract_page_state",
694
- description: "\u6293\u53D6\u5F53\u524D\u9875\u9762\u7684\u5173\u952E\u4EA4\u4E92\u5143\u7D20\uFF0C\u5E76\u8F93\u51FA\u5E26\u7F16\u53F7\u7684\u5143\u7D20\u6458\u8981\u4E0E\u4E0A\u4E0B\u6587\u6458\u8981"
967
+ description: "\u6293\u53D6\u5F53\u524D\u9875\u9762\u7684\u5173\u952E\u4EA4\u4E92\u5143\u7D20\uFF0C\u5E76\u8F93\u51FA\u5E26\u7F16\u53F7\u7684\u5143\u7D20\u6458\u8981\u4E0E\u4E0A\u4E0B\u6587\u6458\u8981\u3002\u53EF\u901A\u8FC7 includeText \u53C2\u6570\u989D\u5916\u83B7\u53D6\u89C6\u53E3\u5185\u7684\u7EAF\u6587\u672C\u4FE1\u606F\u3002",
968
+ schema: z3.object({
969
+ includeText: z3.boolean().optional().default(false).describe("\u662F\u5426\u83B7\u53D6\u5F53\u524D\u89C6\u53E3\u5185\u7684\u7EAF\u6587\u672C\u4FE1\u606F")
970
+ })
695
971
  }
696
972
  );
697
973
  var executePlaywrightActionsTool = (0, import_tools3.tool)(
@@ -755,9 +1031,9 @@ var PLAYWRIGHT_PROMPT = `
755
1031
 
756
1032
  2. **extract_page_state**:
757
1033
  * **\u7528\u9014**: \u83B7\u53D6\u5F53\u524D\u9875\u9762\u4E0A\u4E0B\u6587\u6458\u8981\uFF0C\u5E76\u5728\u9875\u9762\u4E0A\u6807\u8BB0\u53EF\u4EA4\u4E92\u5143\u7D20\uFF08Set-of-Mark\uFF09\u3002
758
- * **\u8F93\u5165**: \u65E0\u3002
759
- * **\u8F93\u51FA**: \u5305\u542B\u9875\u9762 URL\u3001Title \u4EE5\u53CA\u5E26\u6709 ID \u6807\u8BB0\u7684\u53EF\u4EA4\u4E92\u5143\u7D20\u5217\u8868\u3002
760
- * **\u4F55\u65F6\u4F7F\u7528**: \u5728\u9700\u8981\u7406\u89E3\u9875\u9762\u7ED3\u6784\u6216\u7CBE\u786E\u5B9A\u4F4D\u5143\u7D20\u65F6\u4F7F\u7528\u3002\u8FD4\u56DE\u7684\u5143\u7D20 ID \u53EF\u7528\u4E8E\u751F\u6210\u7CBE\u786E\u7684 CSS \u9009\u62E9\u5668\uFF08\u5982 \`[idu-mark-id="123"]\`\uFF09\u3002
1034
+ * **\u8F93\u5165**: \u53EF\u9009\u53C2\u6570 \`includeText\` (\u5E03\u5C14\u503C)\uFF0C\u7528\u4E8E\u989D\u5916\u83B7\u53D6\u5F53\u524D\u89C6\u53E3\u5185\u7684\u7EAF\u6587\u672C\u4FE1\u606F\u3002
1035
+ * **\u8F93\u51FA**: \u5305\u542B\u9875\u9762 URL\u3001Title \u4EE5\u53CA\u5E26\u6709 ID \u6807\u8BB0\u7684\u53EF\u4EA4\u4E92\u5143\u7D20\u5217\u8868\uFF08\u5F00\u542F \`includeText\` \u65F6\u8FD8\u4F1A\u5305\u542B\u89C6\u53E3\u5185\u7684\u7EAF\u6587\u672C\u4FE1\u606F\uFF09\u3002
1036
+ * **\u4F55\u65F6\u4F7F\u7528**: \u5728\u9700\u8981\u7406\u89E3\u9875\u9762\u7ED3\u6784\u6216\u7CBE\u786E\u5B9A\u4F4D\u5143\u7D20\u65F6\u4F7F\u7528\u3002\u8FD4\u56DE\u7684\u5143\u7D20 ID \u53EF\u7528\u4E8E\u751F\u6210\u7CBE\u786E\u7684 CSS \u9009\u62E9\u5668\uFF08\u5982 \`[idu-mark-id="123"]\`\uFF09\u3002**\u6CE8\u610F\uFF1A\u4E00\u822C\u7EAF\u64CD\u4F5C\u9875\u9762\u65F6\u4E0D\u9700\u8981\u6253\u5F00 \`includeText\` \u5F00\u5173\uFF0C\u4EE5\u514D\u6D6A\u8D39\u5B9D\u8D35\u7684 Token\u3002\u53EA\u6709\u5728\u786E\u5B9E\u9700\u8981\u83B7\u53D6\u9875\u9762\u66F4\u591A\u6587\u672C\u4FE1\u606F\u8FDB\u884C\u5185\u5BB9\u7406\u89E3\u65F6\uFF0C\u624D\u5C06 \`includeText\` \u8BBE\u4E3A true\u3002**
761
1037
 
762
1038
  ### Utility
763
1039
  3. **get_current_time**:
@@ -765,14 +1041,14 @@ var PLAYWRIGHT_PROMPT = `
765
1041
  * **\u8F93\u5165**: \u53EF\u9009\u683C\u5F0F\u5316\u5B57\u7B26\u4E32\u548C\u65F6\u533A\u3002
766
1042
 
767
1043
  ### File System & Memory
768
- \u4F60\u62E5\u6709\u5BF9\u5DE5\u4F5C\u76EE\u5F55 \`server/agents/memory\`\uFF08\u6216\u8005\u901A\u8FC7 file_operations \u83B7\u53D6\u7684\u6839\u76EE\u5F55\uFF09\u7684\u6587\u4EF6\u64CD\u4F5C\u6743\u9650\u3002
1044
+ \u4F60\u62E5\u6709\u5BF9\u5DE5\u4F5C\u76EE\u5F55\uFF08\u6216\u6587\u4EF6\u64CD\u4F5C\u5DE5\u5177\u6307\u5B9A\u7684\u6839\u76EE\u5F55\uFF09\u7684\u6587\u4EF6\u64CD\u4F5C\u6743\u9650\u3002
769
1045
 
770
- 4. **file_operations**: \u7EDF\u4E00\u7684\u6587\u4EF6\u64CD\u4F5C\u5DE5\u5177\uFF0C\u652F\u6301\u901A\u8FC7 \`action\` \u53C2\u6570\u6267\u884C\u4EE5\u4E0B\u64CD\u4F5C\uFF1A
771
- * \`create\`: \u521B\u5EFA\u65B0\u6587\u4EF6\u3002
772
- * \`read\`: \u8BFB\u53D6\u6587\u4EF6\u5185\u5BB9\u3002
773
- * \`write\`: \u8986\u76D6\u5199\u5165\u6587\u4EF6\u5185\u5BB9\u3002
774
- * \`delete\`: \u5220\u9664\u6587\u4EF6\u6216\u76EE\u5F55\u3002
775
- * \`list\`: \u67E5\u770B\u6587\u4EF6\u5939\u4E0B\u6587\u4EF6\u5217\u8868\u3002
1046
+ 4. **\u6587\u4EF6\u64CD\u4F5C\u5DE5\u5177\u7EC4**: \u63D0\u4F9B\u4E86\u4E00\u7EC4\u539F\u5B50\u5316\u7684\u6587\u4EF6\u64CD\u4F5C\u5DE5\u5177\uFF1A
1047
+ * \`create_file_or_directory\`: \u5728\u5DE5\u4F5C\u76EE\u5F55\u5185\u65B0\u5EFA\u6587\u4EF6\u6216\u76EE\u5F55\u3002
1048
+ * \`read_file\`: \u5728\u5DE5\u4F5C\u76EE\u5F55\u5185\u8BFB\u53D6\u5DF2\u6709\u6587\u4EF6\u5185\u5BB9\u3002
1049
+ * \`write_file\`: \u5728\u5DE5\u4F5C\u76EE\u5F55\u5185\u5199\u5165\u5DF2\u6709\u6587\u4EF6\u3002\u652F\u6301\u5168\u91CF\u8986\u76D6\uFF0C\u4E5F\u652F\u6301\u901A\u8FC7\u6307\u5B9A\u884C\u53F7 (startLine / endLine) \u8FDB\u884C\u5C40\u90E8\u66FF\u6362\u6216\u63D2\u5165\u4EE5\u8282\u7701 token\u3002
1050
+ * \`delete_file_or_directory\`: \u5728\u5DE5\u4F5C\u76EE\u5F55\u5185\u5220\u9664\u6587\u4EF6\u6216\u76EE\u5F55\u3002
1051
+ * \`list_files\`: \u5217\u51FA\u5DE5\u4F5C\u76EE\u5F55\u5185\u7684\u76EE\u5F55\u5185\u5BB9\u3002
776
1052
 
777
1053
  ## Long-term Memory & Skills
778
1054
 
@@ -784,9 +1060,13 @@ var PLAYWRIGHT_PROMPT = `
784
1060
  * **\u8BFB\u53D6**: \u7CFB\u7EDF\u4F1A\u5728\u6BCF\u6B21\u5BF9\u8BDD\u5F00\u59CB\u65F6\u8BFB\u53D6\u6B64\u6587\u4EF6\uFF08\u6216\u4F60\u53EF\u4EE5\u4E3B\u52A8\u8BFB\u53D6\uFF09\uFF0C\u8BF7\u9075\u5FAA\u5176\u4E2D\u7684\u6307\u793A\u3002
785
1061
  * **\u5199\u5165**: \u5F53\u7528\u6237\u901A\u8FC7\u6307\u4EE4\u544A\u77E5\u65B0\u7684\u4E60\u60EF\u3001\u89C4\u5B9A\uFF0C\u6216\u4F60\u5B66\u4F1A\u4E86\u65B0\u6280\u80FD\u65F6\uFF0C\u8BF7\u52A1\u5FC5\u66F4\u65B0\u6B64\u6587\u4EF6\u3002
786
1062
 
787
- 2. **skills/ (\u6280\u80FD\u76EE\u5F55)**:
1063
+ 2. **SKILLS.json (\u7528\u6237\u914D\u7F6E\u6280\u80FD\u5217\u8868)**:
1064
+ * \u4F4D\u4E8E\u6839\u76EE\u5F55\u4E0B\u7684 \`SKILLS.json\` \u6587\u4EF6\u5B58\u653E\u7528\u6237\u624B\u52A8\u914D\u7F6E\u7684\u6280\u80FD\u5217\u8868\u3002
1065
+ * \u4F60\u53EF\u4EE5\u901A\u8FC7\u8BFB\u53D6\u8BE5\u6587\u4EF6\u83B7\u53D6\u6280\u80FD\u5217\u8868\uFF08\u5305\u542B ID\u3001\u540D\u79F0\u3001\u6458\u8981\u548C\u8DEF\u5F84\uFF09\u3002
1066
+
1067
+ 3. **skills/ (\u6280\u80FD\u76EE\u5F55)**:
788
1068
  * \u4F4D\u4E8E \`skills/\` \u6587\u4EF6\u5939\u4E0B\u3002
789
- * **\u7528\u9014**: \u5B58\u653E\u5177\u4F53\u7684\u6280\u80FD\u6587\u6863\u6216\u590D\u6742\u4EFB\u52A1\u7684\u64CD\u4F5C\u6307\u5357\u3002
1069
+ * **\u7528\u9014**: \u5B58\u653E\u5177\u4F53\u7684\u6280\u80FD\u6587\u6863\u6216\u590D\u6742\u4EFB\u52A1\u7684\u64CD\u4F5C\u6307\u5357\u3002\u4F60\u53EF\u4EE5\u6839\u636E \`SKILLS.json\` \u4E2D\u7684 ID\uFF0C\u8BFB\u53D6\u5BF9\u5E94 \`skills/\${id}.md\` \u62FF\u5230\u6280\u80FD\u7684\u6B63\u6587\u5185\u5BB9\u3002
790
1070
  * **\u7EF4\u62A4**: \u5F53\u4F60\u4E60\u5F97\u4E00\u4E2A\u65B0\u7684\u590D\u6742\u64CD\u4F5C\u6D41\u7A0B\u65F6\uFF0C\u5E94\u5C06\u5176\u8BE6\u7EC6\u6B65\u9AA4\u6574\u7406\u4E3A\u4E00\u4E2A Markdown \u6587\u4EF6\u4FDD\u5B58\u5728 \`skills/\` \u76EE\u5F55\u4E0B\uFF0C\u5E76\u5728 \`AGENTS.md\` \u4E2D\u6DFB\u52A0\u8BE5\u6280\u80FD\u7684\u6458\u8981\u548C\u94FE\u63A5\u3002
791
1071
 
792
1072
  ## Workflow (Standard Operating Procedure)
@@ -1086,9 +1366,9 @@ var streamInvoke = async function* (query, thread_id, modelConfig) {
1086
1366
  // src/routes/chat.ts
1087
1367
  var import_promises2 = __toESM(require("fs/promises"));
1088
1368
  var import_path3 = __toESM(require("path"));
1089
- var import_crypto = __toESM(require("crypto"));
1369
+ var import_crypto2 = __toESM(require("crypto"));
1090
1370
  var configDir2 = getConfigPath();
1091
- var configFilePath2 = import_path3.default.join(configDir2, "model.json");
1371
+ var configFilePath = import_path3.default.join(configDir2, "model.json");
1092
1372
  var router2 = (0, import_express2.Router)();
1093
1373
  router2.get("/", (req, res) => {
1094
1374
  res.json({ status: "OK" });
@@ -1100,7 +1380,7 @@ router2.post("/stream", async (req, res) => {
1100
1380
  if (!query || !thread_id) {
1101
1381
  return res.status(400).json({ error: "Missing query or thread_id in request body" });
1102
1382
  }
1103
- const configContent = await import_promises2.default.readFile(configFilePath2, "utf-8");
1383
+ const configContent = await import_promises2.default.readFile(configFilePath, "utf-8");
1104
1384
  const config = JSON.parse(configContent);
1105
1385
  const modelConfig = config.defaultModel;
1106
1386
  if (!modelConfig) {
@@ -1127,7 +1407,7 @@ router2.get("/session", (req, res) => {
1127
1407
  const isNew = req.query.new === "true";
1128
1408
  let sessionId = session_default.getSessionId();
1129
1409
  if (!sessionId || isNew) {
1130
- sessionId = import_crypto.default.randomUUID();
1410
+ sessionId = import_crypto2.default.randomUUID();
1131
1411
  session_default.setSessionId(sessionId);
1132
1412
  }
1133
1413
  res.json({ sessionId });