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_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 输入框
|
* 上传图片到 Gemini 输入框
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ server.registerTool(
|
|||||||
inputSchema: {
|
inputSchema: {
|
||||||
prompt: z.string().describe("图片的详细描述词。提示:描述越详细越好,包含风格、构图、色调等关键词能显著提升生成质量"),
|
prompt: z.string().describe("图片的详细描述词。提示:描述越详细越好,包含风格、构图、色调等关键词能显著提升生成质量"),
|
||||||
newSession: z.boolean().default(false).describe(
|
newSession: z.boolean().default(false).describe(
|
||||||
"是否新建会话。true= 开启全新对话(推荐生成全新图片时使用); false= 复用当前会话(适合基于上下文迭代修改)"
|
"是否新建会话。true= 开启全新对话(推荐生成全新图片时使用); false= 复用当前会话(适合基于上下文迭代修改,默认应该为)"
|
||||||
),
|
),
|
||||||
referenceImages: z.array(z.string()).default([]).describe(
|
referenceImages: z.array(z.string()).default([]).describe(
|
||||||
"参考图片的本地文件路径数组,例如 [\"/path/to/ref1.png\", \"/path/to/ref2.jpg\"]。图片会在发送 prompt 前上传到 Gemini 输入框"
|
"参考图片的本地文件路径数组,例如 [\"/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