zrocclaw 0.0.13 → 0.0.15
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/LICENSE +21 -0
- package/package.json +3 -2
- package/server/server.js +372 -59
- package/server/static/assets/{Chat-I4ZIdxyC.js → Chat-4RxQRwRb.js} +3 -3
- package/server/static/assets/Config-BT-iqhcX.js +1 -0
- package/server/static/assets/{Welcome-DPYUQXhQ.js → Welcome-CGBkelb3.js} +1 -1
- package/server/static/assets/index-B3xx8wNP.js +2 -0
- package/server/static/assets/index-CnpxtwAn.css +2 -0
- package/server/static/assets/runtime-core.esm-bundler-soWVaCX3.js +1 -0
- package/server/static/index.html +3 -3
- package/server/static/assets/Config-BnWzqU3z.js +0 -1
- package/server/static/assets/index-B4HOtFAZ.css +0 -2
- package/server/static/assets/index-emQuqO8G.js +0 -2
- package/server/static/assets/runtime-core.esm-bundler-DEKlsnJd.js +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 zroclee
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zrocclaw",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "个人专属浏览器AI助手命令行工具",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"zrocclaw",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"files": [
|
|
21
21
|
"bin",
|
|
22
22
|
"server",
|
|
23
|
-
"README.md"
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
24
25
|
],
|
|
25
26
|
"scripts": {},
|
|
26
27
|
"dependencies": {
|
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-
|
|
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
|
|
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
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
res.json(
|
|
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
|
-
|
|
199
|
-
|
|
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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
329
|
+
await import_promises.default.access(workspaceSkillsPath);
|
|
330
|
+
} catch (e) {
|
|
331
|
+
if (e.code === "ENOENT") {
|
|
332
|
+
await writeSkillsConfig([]);
|
|
333
|
+
return res.json([]);
|
|
206
334
|
}
|
|
207
335
|
}
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
res.json(newConfig);
|
|
336
|
+
const config = await readSkillsConfig();
|
|
337
|
+
res.json(config);
|
|
211
338
|
} catch (error) {
|
|
212
|
-
console.error("\
|
|
213
|
-
res.status(500).json({ 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" });
|
|
437
|
+
}
|
|
438
|
+
throw e;
|
|
439
|
+
}
|
|
440
|
+
} catch (error) {
|
|
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;
|
|
@@ -218,13 +447,13 @@ var config_default = router;
|
|
|
218
447
|
// src/routes/chat.ts
|
|
219
448
|
var import_express2 = require("express");
|
|
220
449
|
|
|
221
|
-
// ../../packages/core/dist/chunk-
|
|
450
|
+
// ../../packages/core/dist/chunk-CY5XCIYW.mjs
|
|
222
451
|
var import_playwright = require("playwright");
|
|
223
452
|
var PlaywrightManager = class _PlaywrightManager {
|
|
224
453
|
static instance;
|
|
225
454
|
browser = null;
|
|
226
455
|
context = null;
|
|
227
|
-
|
|
456
|
+
activePage = null;
|
|
228
457
|
constructor() {
|
|
229
458
|
}
|
|
230
459
|
static getInstance() {
|
|
@@ -234,13 +463,38 @@ var PlaywrightManager = class _PlaywrightManager {
|
|
|
234
463
|
return _PlaywrightManager.instance;
|
|
235
464
|
}
|
|
236
465
|
async getPage() {
|
|
237
|
-
if (!this.
|
|
466
|
+
if (!this.activePage || this.activePage.isClosed()) {
|
|
238
467
|
await this.init();
|
|
239
468
|
}
|
|
240
|
-
if (!this.
|
|
469
|
+
if (!this.activePage) {
|
|
241
470
|
throw new Error("Failed to initialize Playwright page.");
|
|
242
471
|
}
|
|
243
|
-
return this.
|
|
472
|
+
return this.activePage;
|
|
473
|
+
}
|
|
474
|
+
async getPageMetadata() {
|
|
475
|
+
if (!this.context) return { tabs: [], activeTabIndex: -1 };
|
|
476
|
+
const pages = this.context.pages();
|
|
477
|
+
const tabs = await Promise.all(
|
|
478
|
+
pages.map(async (p, i) => {
|
|
479
|
+
try {
|
|
480
|
+
return { index: i, url: p.url(), title: await p.title() };
|
|
481
|
+
} catch {
|
|
482
|
+
return { index: i, url: p.url(), title: "Unknown" };
|
|
483
|
+
}
|
|
484
|
+
})
|
|
485
|
+
);
|
|
486
|
+
const activeTabIndex = pages.findIndex((p) => p === this.activePage);
|
|
487
|
+
return { tabs, activeTabIndex };
|
|
488
|
+
}
|
|
489
|
+
async switchPage(index) {
|
|
490
|
+
if (!this.context) throw new Error("Context not initialized");
|
|
491
|
+
const pages = this.context.pages();
|
|
492
|
+
if (index >= 0 && index < pages.length) {
|
|
493
|
+
this.activePage = pages[index];
|
|
494
|
+
await this.activePage.bringToFront();
|
|
495
|
+
return this.activePage;
|
|
496
|
+
}
|
|
497
|
+
throw new Error(`Invalid page index: ${index}`);
|
|
244
498
|
}
|
|
245
499
|
async start() {
|
|
246
500
|
await this.init();
|
|
@@ -254,7 +508,7 @@ var PlaywrightManager = class _PlaywrightManager {
|
|
|
254
508
|
this.browser.on("disconnected", () => {
|
|
255
509
|
this.browser = null;
|
|
256
510
|
this.context = null;
|
|
257
|
-
this.
|
|
511
|
+
this.activePage = null;
|
|
258
512
|
});
|
|
259
513
|
}
|
|
260
514
|
if (!this.context) {
|
|
@@ -262,16 +516,27 @@ var PlaywrightManager = class _PlaywrightManager {
|
|
|
262
516
|
viewport: { width: 1280, height: 800 },
|
|
263
517
|
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
|
264
518
|
});
|
|
519
|
+
this.context.on("page", (page) => {
|
|
520
|
+
this.activePage = page;
|
|
521
|
+
page.on("close", () => {
|
|
522
|
+
if (this.activePage === page) {
|
|
523
|
+
const pages = this.context?.pages() || [];
|
|
524
|
+
this.activePage = pages.length > 0 ? pages[pages.length - 1] : null;
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
});
|
|
265
528
|
this.context.on("close", () => {
|
|
266
529
|
this.context = null;
|
|
267
|
-
this.
|
|
530
|
+
this.activePage = null;
|
|
268
531
|
});
|
|
269
532
|
}
|
|
270
|
-
if (!this.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
this.
|
|
274
|
-
}
|
|
533
|
+
if (!this.activePage || this.activePage.isClosed()) {
|
|
534
|
+
const pages = this.context.pages();
|
|
535
|
+
if (pages.length > 0) {
|
|
536
|
+
this.activePage = pages[pages.length - 1];
|
|
537
|
+
} else {
|
|
538
|
+
this.activePage = await this.context.newPage();
|
|
539
|
+
}
|
|
275
540
|
}
|
|
276
541
|
}
|
|
277
542
|
async close() {
|
|
@@ -279,7 +544,7 @@ var PlaywrightManager = class _PlaywrightManager {
|
|
|
279
544
|
await this.browser.close();
|
|
280
545
|
this.browser = null;
|
|
281
546
|
this.context = null;
|
|
282
|
-
this.
|
|
547
|
+
this.activePage = null;
|
|
283
548
|
}
|
|
284
549
|
}
|
|
285
550
|
};
|
|
@@ -371,13 +636,20 @@ var PlaywrightExecutor = class {
|
|
|
371
636
|
case "wait":
|
|
372
637
|
await this.page.waitForTimeout(payload?.delay || 1e3);
|
|
373
638
|
break;
|
|
639
|
+
case "switchTab":
|
|
640
|
+
if (payload?.tabIndex !== void 0) {
|
|
641
|
+
this.page = await playwrightManager.switchPage(payload.tabIndex);
|
|
642
|
+
} else {
|
|
643
|
+
throw new Error("switchTab action requires payload.tabIndex");
|
|
644
|
+
}
|
|
645
|
+
break;
|
|
374
646
|
default:
|
|
375
647
|
throw new Error(`Unsupported action type: ${type}`);
|
|
376
648
|
}
|
|
377
649
|
}
|
|
378
650
|
};
|
|
379
651
|
|
|
380
|
-
// ../../packages/core/dist/chunk-
|
|
652
|
+
// ../../packages/core/dist/chunk-JNJYWTIE.mjs
|
|
381
653
|
var import_openai = require("@langchain/openai");
|
|
382
654
|
var import_langchain = require("langchain");
|
|
383
655
|
var import_messages = require("@langchain/core/messages");
|
|
@@ -632,9 +904,10 @@ var listFilesTool = (0, import_tools2.tool)(
|
|
|
632
904
|
}
|
|
633
905
|
);
|
|
634
906
|
var extractPageStateTool = (0, import_tools3.tool)(
|
|
635
|
-
async () => {
|
|
907
|
+
async (input) => {
|
|
908
|
+
const includeText = input?.includeText === "true";
|
|
636
909
|
const p = await playwrightManager.getPage();
|
|
637
|
-
const
|
|
910
|
+
const evaluateResult = await p.evaluate((includeTextArg) => {
|
|
638
911
|
document.querySelectorAll(".ai-label-container").forEach((el) => el.remove());
|
|
639
912
|
document.querySelectorAll("[idu-mark-id]").forEach((el) => el.removeAttribute("idu-mark-id"));
|
|
640
913
|
const selectors = [
|
|
@@ -696,18 +969,51 @@ var extractPageStateTool = (0, import_tools3.tool)(
|
|
|
696
969
|
});
|
|
697
970
|
}
|
|
698
971
|
});
|
|
699
|
-
|
|
700
|
-
|
|
972
|
+
let viewportText = void 0;
|
|
973
|
+
if (includeTextArg) {
|
|
974
|
+
const walker = document.createTreeWalker(
|
|
975
|
+
document.body,
|
|
976
|
+
NodeFilter.SHOW_TEXT,
|
|
977
|
+
null
|
|
978
|
+
);
|
|
979
|
+
let node;
|
|
980
|
+
const textParts = [];
|
|
981
|
+
while (node = walker.nextNode()) {
|
|
982
|
+
const parent = node.parentElement;
|
|
983
|
+
const text = node.nodeValue?.trim();
|
|
984
|
+
if (parent && text) {
|
|
985
|
+
const tagName = parent.tagName.toLowerCase();
|
|
986
|
+
if (tagName !== "script" && tagName !== "style" && tagName !== "noscript") {
|
|
987
|
+
if (isVisible(parent)) {
|
|
988
|
+
const rect = parent.getBoundingClientRect();
|
|
989
|
+
if (isInViewport(rect)) {
|
|
990
|
+
textParts.push(text);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
viewportText = Array.from(new Set(textParts)).join("\n");
|
|
997
|
+
}
|
|
998
|
+
return { metadata, viewportText };
|
|
999
|
+
}, includeText);
|
|
701
1000
|
const title = await p.title();
|
|
1001
|
+
const pageMetadata = await playwrightManager.getPageMetadata();
|
|
702
1002
|
return {
|
|
703
1003
|
url: p.url(),
|
|
704
1004
|
title,
|
|
705
|
-
|
|
1005
|
+
tabs: pageMetadata.tabs,
|
|
1006
|
+
activeTabIndex: pageMetadata.activeTabIndex,
|
|
1007
|
+
elements: evaluateResult.metadata,
|
|
1008
|
+
...includeText && evaluateResult.viewportText ? { text: evaluateResult.viewportText } : {}
|
|
706
1009
|
};
|
|
707
1010
|
},
|
|
708
1011
|
{
|
|
709
1012
|
name: "extract_page_state",
|
|
710
|
-
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"
|
|
1013
|
+
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\uFF0C\u540C\u65F6\u63D0\u4F9B\u5F53\u524D\u6253\u5F00\u7684\u6240\u6709\u6807\u7B7E\u9875(tabs)\u4FE1\u606F\u548C\u5F53\u524D\u6FC0\u6D3B\u7684\u6807\u7B7E\u9875\u7D22\u5F15(activeTabIndex)\u3002\u53EF\u901A\u8FC7 includeText \u53C2\u6570\u989D\u5916\u83B7\u53D6\u89C6\u53E3\u5185\u7684\u7EAF\u6587\u672C\u4FE1\u606F\u3002",
|
|
1014
|
+
schema: z3.object({
|
|
1015
|
+
includeText: z3.string().optional().default("false").describe('\u662F\u5426\u83B7\u53D6\u5F53\u524D\u89C6\u53E3\u5185\u7684\u7EAF\u6587\u672C\u4FE1\u606F\uFF0C\u4F20\u5165\u5B57\u7B26\u4E32 "true" \u6216 "false"')
|
|
1016
|
+
})
|
|
711
1017
|
}
|
|
712
1018
|
);
|
|
713
1019
|
var executePlaywrightActionsTool = (0, import_tools3.tool)(
|
|
@@ -735,7 +1041,8 @@ var executePlaywrightActionsTool = (0, import_tools3.tool)(
|
|
|
735
1041
|
"check",
|
|
736
1042
|
"selectOption",
|
|
737
1043
|
"wait",
|
|
738
|
-
"goto"
|
|
1044
|
+
"goto",
|
|
1045
|
+
"switchTab"
|
|
739
1046
|
]).describe("\u52A8\u4F5C\u7C7B\u578B"),
|
|
740
1047
|
target: z3.object({
|
|
741
1048
|
type: z3.enum(["markId", "css", "role", "text"]),
|
|
@@ -747,7 +1054,8 @@ var executePlaywrightActionsTool = (0, import_tools3.tool)(
|
|
|
747
1054
|
key: z3.string().optional(),
|
|
748
1055
|
delay: z3.number().optional(),
|
|
749
1056
|
options: z3.array(z3.string()).optional(),
|
|
750
|
-
url: z3.string().optional()
|
|
1057
|
+
url: z3.string().optional(),
|
|
1058
|
+
tabIndex: z3.number().optional().describe("\u7528\u4E8E switchTab \u52A8\u4F5C\uFF0C\u6307\u5B9A\u8981\u5207\u6362\u7684\u6807\u7B7E\u9875\u7D22\u5F15")
|
|
751
1059
|
}).optional().describe("\u52A8\u4F5C\u53C2\u6570")
|
|
752
1060
|
})
|
|
753
1061
|
).describe("\u52A8\u4F5C\u5217\u8868")
|
|
@@ -770,10 +1078,10 @@ var PLAYWRIGHT_PROMPT = `
|
|
|
770
1078
|
* **\u4F55\u65F6\u4F7F\u7528**: \u6839\u636E\u7528\u6237\u6307\u4EE4\u751F\u6210\u64CD\u4F5C\u8BA1\u5212\u5E76\u8C03\u7528\u6B64\u5DE5\u5177\u3002
|
|
771
1079
|
|
|
772
1080
|
2. **extract_page_state**:
|
|
773
|
-
* **\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
|
|
774
|
-
* **\u8F93\u5165**: \
|
|
775
|
-
* **\u8F93\u51FA**: \u5305\u542B\u9875\u9762 URL\u3001Title
|
|
776
|
-
* **\u4F55\u65F6\u4F7F\u7528**: \u5728\u9700\u8981\u7406\u89E3\u9875\u9762\u7ED3\u6784\
|
|
1081
|
+
* **\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\u540C\u65F6\u63D0\u4F9B\u5F53\u524D\u6253\u5F00\u7684\u6240\u6709\u6807\u7B7E\u9875(tabs)\u4FE1\u606F\u548C\u5F53\u524D\u6FC0\u6D3B\u7684\u6807\u7B7E\u9875\u7D22\u5F15(activeTabIndex)\u3002
|
|
1082
|
+
* **\u8F93\u5165**: \u53EF\u9009\u53C2\u6570 \`includeText\` (\u5B57\u7B26\u4E32\uFF0C"true" \u6216 "false")\uFF0C\u7528\u4E8E\u989D\u5916\u83B7\u53D6\u5F53\u524D\u89C6\u53E3\u5185\u7684\u7EAF\u6587\u672C\u4FE1\u606F\u3002
|
|
1083
|
+
* **\u8F93\u51FA**: \u5305\u542B\u9875\u9762 URL\u3001Title\u3001\u6240\u6709\u6253\u5F00\u7684\u6807\u7B7E\u9875\u5217\u8868\uFF08tabs\uFF0C\u5305\u542B\u7D22\u5F15\u3001URL\u548CTitle\uFF09\u3001\u5F53\u524D\u6FC0\u6D3B\u6807\u7B7E\u9875\u7D22\u5F15\uFF08activeTabIndex\uFF09\uFF0C\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
|
|
1084
|
+
* **\u4F55\u65F6\u4F7F\u7528**: \u5728\u9700\u8981\u7406\u89E3\u9875\u9762\u7ED3\u6784\u3001\u7CBE\u786E\u5B9A\u4F4D\u5143\u7D20\uFF0C\u6216\u4E86\u89E3\u5F53\u524D\u6253\u5F00\u4E86\u54EA\u4E9B\u6807\u7B7E\u9875\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\u5982\u679C\u53D1\u73B0\u9875\u9762\u6253\u5F00\u4E86\u65B0\u7A97\u53E3\uFF0C\u53EF\u7ED3\u5408 \`switchTab\` \u52A8\u4F5C\u5207\u6362\u5230\u5BF9\u5E94\u6807\u7B7E\u9875\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**
|
|
777
1085
|
|
|
778
1086
|
### Utility
|
|
779
1087
|
3. **get_current_time**:
|
|
@@ -800,9 +1108,13 @@ var PLAYWRIGHT_PROMPT = `
|
|
|
800
1108
|
* **\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
|
|
801
1109
|
* **\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
|
|
802
1110
|
|
|
803
|
-
2. **
|
|
1111
|
+
2. **SKILLS.json (\u7528\u6237\u914D\u7F6E\u6280\u80FD\u5217\u8868)**:
|
|
1112
|
+
* \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
|
|
1113
|
+
* \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
|
|
1114
|
+
|
|
1115
|
+
3. **skills/ (\u6280\u80FD\u76EE\u5F55)**:
|
|
804
1116
|
* \u4F4D\u4E8E \`skills/\` \u6587\u4EF6\u5939\u4E0B\u3002
|
|
805
|
-
* **\u7528\u9014**: \u5B58\u653E\u5177\u4F53\u7684\u6280\u80FD\u6587\u6863\u6216\u590D\u6742\u4EFB\u52A1\u7684\u64CD\u4F5C\u6307\u5357\u3002
|
|
1117
|
+
* **\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
|
|
806
1118
|
* **\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
|
|
807
1119
|
|
|
808
1120
|
## Workflow (Standard Operating Procedure)
|
|
@@ -845,7 +1157,7 @@ interface BrowserAction {
|
|
|
845
1157
|
payload?: ActionPayload; // \u52A8\u4F5C\u53C2\u6570
|
|
846
1158
|
}
|
|
847
1159
|
|
|
848
|
-
type ActionType = 'click' | 'fill' | 'press' | 'hover' | 'check' | 'selectOption' | 'wait' | 'goto';
|
|
1160
|
+
type ActionType = 'click' | 'fill' | 'press' | 'hover' | 'check' | 'selectOption' | 'wait' | 'goto' | 'switchTab';
|
|
849
1161
|
|
|
850
1162
|
type LocatorStrategy =
|
|
851
1163
|
| 'css' // CSS \u9009\u62E9\u5668
|
|
@@ -864,6 +1176,7 @@ interface ActionPayload {
|
|
|
864
1176
|
delay?: number; // \u7528\u4E8E wait (\u6BEB\u79D2)
|
|
865
1177
|
options?: string[]; // \u7528\u4E8E selectOption
|
|
866
1178
|
url?: string; // \u7528\u4E8E goto
|
|
1179
|
+
tabIndex?: number; // \u7528\u4E8E switchTab\uFF0C\u6307\u5B9A\u8981\u5207\u6362\u7684\u6807\u7B7E\u9875\u7D22\u5F15
|
|
867
1180
|
}
|
|
868
1181
|
\`\`\`
|
|
869
1182
|
|
|
@@ -1102,9 +1415,9 @@ var streamInvoke = async function* (query, thread_id, modelConfig) {
|
|
|
1102
1415
|
// src/routes/chat.ts
|
|
1103
1416
|
var import_promises2 = __toESM(require("fs/promises"));
|
|
1104
1417
|
var import_path3 = __toESM(require("path"));
|
|
1105
|
-
var
|
|
1418
|
+
var import_crypto2 = __toESM(require("crypto"));
|
|
1106
1419
|
var configDir2 = getConfigPath();
|
|
1107
|
-
var
|
|
1420
|
+
var configFilePath = import_path3.default.join(configDir2, "model.json");
|
|
1108
1421
|
var router2 = (0, import_express2.Router)();
|
|
1109
1422
|
router2.get("/", (req, res) => {
|
|
1110
1423
|
res.json({ status: "OK" });
|
|
@@ -1116,7 +1429,7 @@ router2.post("/stream", async (req, res) => {
|
|
|
1116
1429
|
if (!query || !thread_id) {
|
|
1117
1430
|
return res.status(400).json({ error: "Missing query or thread_id in request body" });
|
|
1118
1431
|
}
|
|
1119
|
-
const configContent = await import_promises2.default.readFile(
|
|
1432
|
+
const configContent = await import_promises2.default.readFile(configFilePath, "utf-8");
|
|
1120
1433
|
const config = JSON.parse(configContent);
|
|
1121
1434
|
const modelConfig = config.defaultModel;
|
|
1122
1435
|
if (!modelConfig) {
|
|
@@ -1143,7 +1456,7 @@ router2.get("/session", (req, res) => {
|
|
|
1143
1456
|
const isNew = req.query.new === "true";
|
|
1144
1457
|
let sessionId = session_default.getSessionId();
|
|
1145
1458
|
if (!sessionId || isNew) {
|
|
1146
|
-
sessionId =
|
|
1459
|
+
sessionId = import_crypto2.default.randomUUID();
|
|
1147
1460
|
session_default.setSessionId(sessionId);
|
|
1148
1461
|
}
|
|
1149
1462
|
res.json({ sessionId });
|