feat(gemini-ops): 新增页面导航功能,仅允许 gemini.google.com 域名
This commit is contained in:
6
SKILL.md
6
SKILL.md
@@ -98,6 +98,12 @@ MCP 工具调用(尤其是生图、等待回复等)可能耗时较长(60~1
|
||||
|--------|------|------|
|
||||
| `gemini_check_login` | 检查是否已登录 Google 账号 | 无 |
|
||||
|
||||
**页面导航:**
|
||||
|
||||
| 工具名 | 说明 | 入参 |
|
||||
|--------|------|------|
|
||||
| `gemini_navigate_to` | 打开指定的 Gemini 页面 URL(如历史会话链接),仅允许 gemini.google.com 域名 | `url`(目标 URL),`timeout`(默认30000ms) |
|
||||
|
||||
**诊断 & 恢复:**
|
||||
|
||||
| 工具名 | 说明 | 入参 |
|
||||
|
||||
@@ -905,6 +905,40 @@ export function createOps(page) {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 导航到指定的 Gemini 页面 URL
|
||||
*
|
||||
* 仅允许 gemini.google.com 域名下的地址(如指定会话 URL),
|
||||
* 其他域名会直接拒绝,防止浏览器被劫持到不安全页面。
|
||||
*
|
||||
* @param {string} url - 目标 URL,必须是 gemini.google.com 域名
|
||||
* @param {object} [options]
|
||||
* @param {number} [options.timeout=30000] - 等待页面加载的超时时间(ms)
|
||||
* @returns {Promise<{ok: boolean, url?: string, elapsed?: number, error?: string, detail?: string}>}
|
||||
*/
|
||||
async navigateTo(url, { timeout = 30_000 } = {}) {
|
||||
try {
|
||||
// 域名白名单校验
|
||||
const parsed = new URL(url);
|
||||
if (parsed.hostname !== 'gemini.google.com') {
|
||||
return {
|
||||
ok: false,
|
||||
error: 'invalid_domain',
|
||||
detail: `仅允许 gemini.google.com 域名,收到: ${parsed.hostname}`,
|
||||
};
|
||||
}
|
||||
|
||||
const start = Date.now();
|
||||
await page.goto(url, { waitUntil: 'networkidle2', timeout });
|
||||
const elapsed = Date.now() - start;
|
||||
const finalUrl = page.url();
|
||||
console.log(`[ops] 页面导航完成 → ${finalUrl} (${elapsed}ms)`);
|
||||
return { ok: true, url: finalUrl, elapsed };
|
||||
} catch (e) {
|
||||
return { ok: false, error: 'navigate_failed', detail: e.message };
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 上传图片到 Gemini 输入框
|
||||
*
|
||||
|
||||
@@ -45,7 +45,7 @@ server.registerTool(
|
||||
inputSchema: {
|
||||
prompt: z.string().describe("图片的详细描述词。提示:描述越详细越好,包含风格、构图、色调等关键词能显著提升生成质量"),
|
||||
newSession: z.boolean().default(false).describe(
|
||||
"是否新建会话。true= 开启全新对话(推荐生成全新图片时使用); false= 复用当前会话(适合基于上下文迭代修改)"
|
||||
"是否新建会话。true= 开启全新对话(推荐生成全新图片时使用); false= 复用当前会话(适合基于上下文迭代修改,默认应该为)"
|
||||
),
|
||||
referenceImages: z.array(z.string()).default([]).describe(
|
||||
"参考图片的本地文件路径数组,例如 [\"/path/to/ref1.png\", \"/path/to/ref2.jpg\"]。图片会在发送 prompt 前上传到 Gemini 输入框"
|
||||
@@ -540,6 +540,39 @@ server.registerTool(
|
||||
}
|
||||
);
|
||||
|
||||
// ─── 页面导航 ───
|
||||
|
||||
server.registerTool(
|
||||
"gemini_navigate_to",
|
||||
{
|
||||
description: "打开指定的 Gemini 页面 URL(如特定会话链接)。仅允许 gemini.google.com 域名,其他域名会被拒绝。适用于需要恢复到某个历史会话继续对话的场景",
|
||||
inputSchema: {
|
||||
url: z.string().url().describe(
|
||||
"目标 Gemini URL,例如 https://gemini.google.com/app/57ace74d20f70d13 。必须是 gemini.google.com 域名"
|
||||
),
|
||||
timeout: z.number().default(30000).describe("等待页面加载完成的超时(毫秒),默认 30000"),
|
||||
},
|
||||
},
|
||||
async ({ url, timeout }) => {
|
||||
try {
|
||||
const { ops } = await createGeminiSession();
|
||||
const result = await ops.navigateTo(url, { timeout });
|
||||
disconnect();
|
||||
|
||||
if (!result.ok) {
|
||||
let msg = `页面导航失败: ${result.error}`;
|
||||
if (result.detail) msg += `\n${result.detail}`;
|
||||
return { content: [{ type: "text", text: msg }], isError: true };
|
||||
}
|
||||
return {
|
||||
content: [{ type: "text", text: `已导航至: ${result.url}(耗时 ${result.elapsed}ms)` }],
|
||||
};
|
||||
} catch (err) {
|
||||
return { content: [{ type: "text", text: `执行崩溃: ${err.message}` }], isError: true };
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// ─── 浏览器信息 ───
|
||||
|
||||
// 查询浏览器信息
|
||||
|
||||
Reference in New Issue
Block a user