nolo-cli 0.1.8 → 0.1.10
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 +32 -0
- package/agentRuntimeCommands.ts +3 -3
- package/ai/agent/_executeModel.ts +118 -0
- package/ai/agent/agentSlice.ts +525 -0
- package/ai/agent/appWorkingMemory.ts +126 -0
- package/ai/agent/avatarUtils.ts +24 -0
- package/ai/agent/buildEditingContext.ts +373 -0
- package/ai/agent/buildSystemPrompt.ts +532 -0
- package/ai/agent/cleanAgentMessages.ts +140 -0
- package/ai/agent/cliChatClient.ts +119 -0
- package/ai/agent/cliExecutor.ts +733 -0
- package/ai/agent/cliPrompt.ts +10 -0
- package/ai/agent/contextCompiler.ts +107 -0
- package/ai/agent/contextLayerContract.ts +44 -0
- package/ai/agent/createAgentSchema.ts +234 -0
- package/ai/agent/executeToolCall.ts +58 -0
- package/ai/agent/fetchAgentContexts.ts +42 -0
- package/ai/agent/generatePrompt.ts +3 -0
- package/ai/agent/getFullChatContextKeys.ts +168 -0
- package/ai/agent/hooks/fetchPublicAgents.ts +133 -0
- package/ai/agent/hooks/useAgentConfig.ts +61 -0
- package/ai/agent/hooks/useAgentDialog.ts +35 -0
- package/ai/agent/hooks/useAgentFormValidation.ts +202 -0
- package/ai/agent/hooks/usePublicAgents.ts +473 -0
- package/ai/agent/machineRunPermissions.ts +95 -0
- package/ai/agent/persistMessageWithFixedId.ts +37 -0
- package/ai/agent/planSlice.ts +259 -0
- package/ai/agent/referenceUtils.ts +229 -0
- package/ai/agent/runAgentBackground.ts +238 -0
- package/ai/agent/runAgentClientLoop.ts +138 -0
- package/ai/agent/runtimeGuidance.ts +97 -0
- package/ai/agent/runtimeServerBase.ts +37 -0
- package/ai/agent/server/fetchPublicAgents.ts +128 -0
- package/ai/agent/startParallelAgentStreams.ts +424 -0
- package/ai/agent/startupProtocol.ts +53 -0
- package/ai/agent/streamAgentChatTurn.ts +1278 -0
- package/ai/agent/streamAgentChatTurnUtils.ts +738 -0
- package/ai/agent/types.ts +71 -0
- package/ai/agent/utils/imageOutput.ts +33 -0
- package/ai/agent/utils/sortUtils.ts +250 -0
- package/ai/agent/web/referencePickerUtils.ts +146 -0
- package/ai/ai.locale.ts +1079 -0
- package/ai/chat/accumulateToolCallChunks.ts +95 -0
- package/ai/chat/fetchUtils.native.ts +276 -0
- package/ai/chat/fetchUtils.ts +153 -0
- package/ai/chat/parseApiError.ts +64 -0
- package/ai/chat/parseMultilineSSE.ts +95 -0
- package/ai/chat/sendOpenAICompletionsRequest.native.ts +682 -0
- package/ai/chat/sendOpenAICompletionsRequest.ts +703 -0
- package/ai/chat/sendOpenAIResponseRequest.ts +491 -0
- package/ai/chat/shouldUseServerProxy.ts +18 -0
- package/ai/chat/sseClient.native.ts +91 -0
- package/ai/chat/sseClient.ts +67 -0
- package/ai/chat/streamReader.native.ts +31 -0
- package/ai/chat/streamReader.ts +62 -0
- package/ai/chat/updateTotalUsage.ts +72 -0
- package/ai/context/buildReferenceContext.ts +437 -0
- package/ai/context/calculateContextUsage.ts +133 -0
- package/ai/context/retention.ts +165 -0
- package/ai/context/tokenUtils.ts +78 -0
- package/ai/index.ts +1 -0
- package/ai/llm/calculateGeminiImageTokens.ts +57 -0
- package/ai/llm/deepinfra.ts +28 -0
- package/ai/llm/fireworks.ts +50 -0
- package/ai/llm/generateRequestBody.ts +165 -0
- package/ai/llm/getModelContextWindow.ts +84 -0
- package/ai/llm/getNoloKey.ts +31 -0
- package/ai/llm/getPricing.ts +199 -0
- package/ai/llm/hooks/useModelPricing.ts +75 -0
- package/ai/llm/imagePricing.ts +40 -0
- package/ai/llm/isResponseAPIModel.ts +13 -0
- package/ai/llm/mimo.ts +71 -0
- package/ai/llm/mistral.ts +22 -0
- package/ai/llm/modelAvatar.ts +427 -0
- package/ai/llm/models.ts +45 -0
- package/ai/llm/openrouterModels.ts +269 -0
- package/ai/llm/providers.ts +306 -0
- package/ai/llm/reasoningModels.ts +28 -0
- package/ai/llm/types.ts +59 -0
- package/ai/llm/usageRequestOptions.ts +59 -0
- package/ai/memory/capture.ts +148 -0
- package/ai/memory/consolidate.ts +104 -0
- package/ai/memory/delete.ts +147 -0
- package/ai/memory/overlay.ts +84 -0
- package/ai/memory/query.ts +38 -0
- package/ai/memory/queryShared.ts +160 -0
- package/ai/memory/rank.ts +105 -0
- package/ai/memory/recentRelationshipRecap.ts +249 -0
- package/ai/memory/remember.ts +167 -0
- package/ai/memory/runtime.ts +76 -0
- package/ai/memory/store.ts +20 -0
- package/ai/memory/storeShared.ts +76 -0
- package/ai/memory/types.ts +46 -0
- package/ai/memory/understanding.ts +349 -0
- package/ai/memory/understandingGreeting.ts +264 -0
- package/ai/messages/type.ts +20 -0
- package/ai/policy/personalizationDialog.ts +333 -0
- package/ai/policy/runtimePolicy.ts +440 -0
- package/ai/policy/selfUpdateFields.ts +48 -0
- package/ai/policy/types.ts +64 -0
- package/ai/skills/referenceRuntime.ts +274 -0
- package/ai/skills/skillDiagnostics.ts +251 -0
- package/ai/skills/skillDocBuilder.ts +139 -0
- package/ai/skills/skillDocProtocol.ts +434 -0
- package/ai/skills/skillReferenceSummary.ts +63 -0
- package/ai/skills/skillSummaryMarker.ts +26 -0
- package/ai/token/calculatePrice.ts +544 -0
- package/ai/token/db.ts +98 -0
- package/ai/token/externalToolCost.ts +330 -0
- package/ai/token/hooks/useRecords.ts +65 -0
- package/ai/token/missingUsageEstimate.ts +42 -0
- package/ai/token/modelUsageQuery.ts +252 -0
- package/ai/token/normalizeUsage.ts +84 -0
- package/ai/token/openaiImageGenerationUsage.ts +56 -0
- package/ai/token/prepareTokenUsageData.ts +88 -0
- package/ai/token/query.ts +88 -0
- package/ai/token/queryUserTokens.ts +59 -0
- package/ai/token/resolveBillingTarget.ts +52 -0
- package/ai/token/saveTokenRecord.ts +53 -0
- package/ai/token/serverDialogProjection.ts +78 -0
- package/ai/token/serverTokenWriter.ts +143 -0
- package/ai/token/stats.ts +21 -0
- package/ai/token/tokenThunks.ts +24 -0
- package/ai/token/types.ts +93 -0
- package/ai/tools/agent/agentTools.ts +176 -0
- package/ai/tools/agent/agentUpdateShared.ts +311 -0
- package/ai/tools/agent/callAgentTool.ts +139 -0
- package/ai/tools/agent/createAgentTool.ts +512 -0
- package/ai/tools/agent/createDialogTool.ts +69 -0
- package/ai/tools/agent/createSkillAgentTool.ts +62 -0
- package/ai/tools/agent/parallelBudget.ts +221 -0
- package/ai/tools/agent/presets/appBuilderPreset.ts +145 -0
- package/ai/tools/agent/runLlmTool.ts +96 -0
- package/ai/tools/agent/runStreamingAgentTool.ts +73 -0
- package/ai/tools/agent/skillAgentArgs.ts +106 -0
- package/ai/tools/agent/skillAgentPreset.ts +89 -0
- package/ai/tools/agent/streamParallelAgentsTool.ts +122 -0
- package/ai/tools/agent/updateAgentTool.ts +96 -0
- package/ai/tools/agent/updateSelfTool.ts +113 -0
- package/ai/tools/amazonProductScraperTool.ts +86 -0
- package/ai/tools/apifyActorClient.ts +45 -0
- package/ai/tools/appEditGuard.ts +372 -0
- package/ai/tools/appReadSnapshot.ts +153 -0
- package/ai/tools/appTools.ts +1549 -0
- package/ai/tools/applyEditTool.ts +256 -0
- package/ai/tools/applyLineEditsTool.ts +312 -0
- package/ai/tools/browserTools/click.ts +33 -0
- package/ai/tools/browserTools/closeSession.ts +29 -0
- package/ai/tools/browserTools/common.ts +27 -0
- package/ai/tools/browserTools/openSession.ts +48 -0
- package/ai/tools/browserTools/readContent.ts +38 -0
- package/ai/tools/browserTools/selectOption.ts +46 -0
- package/ai/tools/browserTools/typeText.ts +42 -0
- package/ai/tools/category/createCategoryTool.ts +66 -0
- package/ai/tools/category/queryContentsByCategoryTool.ts +69 -0
- package/ai/tools/category/updateContentCategoryTool.ts +75 -0
- package/ai/tools/cfBrowserTools.ts +319 -0
- package/ai/tools/cfSpeechToTextTool.ts +49 -0
- package/ai/tools/checkEnvTool.ts +65 -0
- package/ai/tools/cloudflareCrawlTool.ts +289 -0
- package/ai/tools/codeSearchTool.ts +111 -0
- package/ai/tools/codeTools.ts +101 -0
- package/ai/tools/createDocTool.ts +132 -0
- package/ai/tools/createPlanTool.ts +999 -0
- package/ai/tools/createSkillDocTool.ts +155 -0
- package/ai/tools/createWorkflowTool.ts +154 -0
- package/ai/tools/deepseekOcrTool.ts +34 -0
- package/ai/tools/delayTool.ts +31 -0
- package/ai/tools/deleteSpacesTool.ts +325 -0
- package/ai/tools/deleteSpacesToolModel.ts +159 -0
- package/ai/tools/devReloadUtils.ts +29 -0
- package/ai/tools/dialogMessageSearch.ts +137 -0
- package/ai/tools/doctorSkillTool.ts +72 -0
- package/ai/tools/ecommerceScraperTool.ts +86 -0
- package/ai/tools/emailTools.ts +549 -0
- package/ai/tools/evalSkillTool.ts +92 -0
- package/ai/tools/exaSearchTool.ts +64 -0
- package/ai/tools/execBashTool.ts +379 -0
- package/ai/tools/executeSqlTool.ts +192 -0
- package/ai/tools/fetchWebpageSupport.ts +309 -0
- package/ai/tools/fetchWebpageTool.ts +84 -0
- package/ai/tools/geminiImagePreviewTool.ts +361 -0
- package/ai/tools/generateDocxTool.ts +215 -0
- package/ai/tools/googleSearchScraperTool.ts +106 -0
- package/ai/tools/importDataTool.ts +133 -0
- package/ai/tools/importSkillTool.ts +162 -0
- package/ai/tools/index.ts +1858 -0
- package/ai/tools/listFilesTool.ts +82 -0
- package/ai/tools/listUserSpacesTool.ts +113 -0
- package/ai/tools/modelUsageTools.ts +142 -0
- package/ai/tools/olmOcrTool.ts +34 -0
- package/ai/tools/openaiImageTool.ts +218 -0
- package/ai/tools/paddleOcrTool.ts +34 -0
- package/ai/tools/prepareTools.ts +23 -0
- package/ai/tools/readDocTool.ts +84 -0
- package/ai/tools/readFileTool.ts +211 -0
- package/ai/tools/readTool.ts +163 -0
- package/ai/tools/readXPostTool.ts +233 -0
- package/ai/tools/rememberMemoryTool.ts +84 -0
- package/ai/tools/remotionVideoTool.ts +151 -0
- package/ai/tools/searchDialogMessagesTool.ts +222 -0
- package/ai/tools/searchRepoTool.ts +115 -0
- package/ai/tools/searchWorkspaceTool.ts +259 -0
- package/ai/tools/skillFollowup.ts +86 -0
- package/ai/tools/surfWeatherTool.ts +169 -0
- package/ai/tools/table/addTableRowTool.ts +217 -0
- package/ai/tools/table/createTableTool.ts +315 -0
- package/ai/tools/table/rowTools.ts +366 -0
- package/ai/tools/table/schemaTools.ts +244 -0
- package/ai/tools/table/shareTableTool.ts +148 -0
- package/ai/tools/table/toolShared.ts +129 -0
- package/ai/tools/toolApiClient.ts +198 -0
- package/ai/tools/toolNameAliases.ts +57 -0
- package/ai/tools/toolResultError.ts +42 -0
- package/ai/tools/toolRunSlice.ts +303 -0
- package/ai/tools/toolSchemaCompatibility.ts +53 -0
- package/ai/tools/toolVisibility.ts +4 -0
- package/ai/tools/types.ts +20 -0
- package/ai/tools/uiAskChoiceTool.ts +104 -0
- package/ai/tools/updateContentTitleTool.ts +84 -0
- package/ai/tools/updateDocTool.ts +105 -0
- package/ai/tools/updateUserPreferenceProfileTool.ts +145 -0
- package/ai/tools/whisperTool.ts +77 -0
- package/ai/tools/writeFileTool.ts +210 -0
- package/ai/tools/youtubeScraperTool.ts +116 -0
- package/ai/tools/ziweiChartTool.ts +678 -0
- package/ai/types.ts +55 -0
- package/ai/workflow/workflowExecutor.ts +323 -0
- package/ai/workflow/workflowSlice.ts +73 -0
- package/ai/workflow/workflowTypes.ts +106 -0
- package/client/compactDialog.ts +222 -0
- package/connector-experimental/capabilities.ts +73 -0
- package/connector-experimental/codexBinary.ts +41 -0
- package/connector-experimental/heartbeatLoop.ts +22 -0
- package/connector-experimental/index.ts +5 -0
- package/connector-experimental/machineInfo.ts +46 -0
- package/connector-experimental/protocol.ts +54 -0
- package/machineCommands.ts +4 -4
- package/package.json +22 -6
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
// packages/ai/tools/cloudflareCrawlTool.ts
|
|
2
|
+
// Cloudflare Browser Rendering /crawl 工具
|
|
3
|
+
// Docs: https://developers.cloudflare.com/browser-rendering/rest-api/crawl-endpoint/
|
|
4
|
+
|
|
5
|
+
import { callToolApi } from "./toolApiClient";
|
|
6
|
+
import { assertFetchableDocsUrl, discoverCanonicalDocsUrl } from "./fetchWebpageSupport";
|
|
7
|
+
|
|
8
|
+
// ──────────────────────────────────────────────────────────
|
|
9
|
+
// Schema
|
|
10
|
+
// ──────────────────────────────────────────────────────────
|
|
11
|
+
export const cloudflareCrawlFunctionSchema = {
|
|
12
|
+
name: "cloudflareCrawl",
|
|
13
|
+
description:
|
|
14
|
+
"使用 Cloudflare Browser Rendering 的 /crawl 接口,从指定 URL 出发自动发现并爬取整个站点(或指定深度/页数)," +
|
|
15
|
+
"支持 JS 渲染,返回 Markdown 格式内容,适合需要抓取多页且页面依赖 JavaScript 渲染的场景。" +
|
|
16
|
+
"如果目标是单页静态内容,优先使用 fetchWebpage;此工具适合多页爬取或需要完整站点索引的任务。",
|
|
17
|
+
parameters: {
|
|
18
|
+
type: "object",
|
|
19
|
+
properties: {
|
|
20
|
+
url: {
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "爬取起始 URL,必须是完整的 http/https 地址。",
|
|
23
|
+
},
|
|
24
|
+
limit: {
|
|
25
|
+
type: "integer",
|
|
26
|
+
description: "最多爬取的页面数量(默认 10,最大 100000)。建议先从小值开始测试。",
|
|
27
|
+
default: 10,
|
|
28
|
+
},
|
|
29
|
+
depth: {
|
|
30
|
+
type: "integer",
|
|
31
|
+
description: "从起始 URL 开始的最大链接层深度(默认不限)。",
|
|
32
|
+
},
|
|
33
|
+
formats: {
|
|
34
|
+
type: "array",
|
|
35
|
+
items: { type: "string", enum: ["markdown", "html"] },
|
|
36
|
+
description: "返回格式,支持 markdown(推荐)和 html,默认 [\"markdown\"]。",
|
|
37
|
+
default: ["markdown"],
|
|
38
|
+
},
|
|
39
|
+
render: {
|
|
40
|
+
type: "boolean",
|
|
41
|
+
description:
|
|
42
|
+
"是否启动无头浏览器执行 JavaScript(默认 true)。" +
|
|
43
|
+
"静态网站可设为 false 以加快速度。",
|
|
44
|
+
default: true,
|
|
45
|
+
},
|
|
46
|
+
source: {
|
|
47
|
+
type: "string",
|
|
48
|
+
enum: ["all", "sitemaps", "links"],
|
|
49
|
+
description: "URL 发现来源:all(默认)、sitemaps 或 links。",
|
|
50
|
+
},
|
|
51
|
+
includeSubdomains: {
|
|
52
|
+
type: "boolean",
|
|
53
|
+
description: "是否跟踪子域名链接(默认 false)。",
|
|
54
|
+
},
|
|
55
|
+
includePatterns: {
|
|
56
|
+
type: "array",
|
|
57
|
+
items: { type: "string" },
|
|
58
|
+
description: "只访问匹配这些通配符的 URL(* 匹配单段,** 匹配多段)。",
|
|
59
|
+
},
|
|
60
|
+
excludePatterns: {
|
|
61
|
+
type: "array",
|
|
62
|
+
items: { type: "string" },
|
|
63
|
+
description: "跳过匹配这些通配符的 URL(优先级高于 includePatterns)。",
|
|
64
|
+
},
|
|
65
|
+
wait: {
|
|
66
|
+
type: "boolean",
|
|
67
|
+
description:
|
|
68
|
+
"是否等待爬取完成后再返回结果(默认 true,服务端最多等 60s)。" +
|
|
69
|
+
"设为 false 时立即返回 jobId,可用 cloudflareCrawlStatus 工具轮询结果。",
|
|
70
|
+
default: true,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
required: ["url"],
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// ──────────────────────────────────────────────────────────
|
|
78
|
+
// Types
|
|
79
|
+
// ──────────────────────────────────────────────────────────
|
|
80
|
+
interface CrawlRecord {
|
|
81
|
+
url: string;
|
|
82
|
+
status: string;
|
|
83
|
+
markdown?: string;
|
|
84
|
+
html?: string;
|
|
85
|
+
metadata?: {
|
|
86
|
+
status: number;
|
|
87
|
+
title: string;
|
|
88
|
+
url: string;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
interface CrawlResult {
|
|
93
|
+
jobId: string;
|
|
94
|
+
status?: string;
|
|
95
|
+
total?: number;
|
|
96
|
+
finished?: number;
|
|
97
|
+
browserSecondsUsed?: number;
|
|
98
|
+
records?: CrawlRecord[];
|
|
99
|
+
cursor?: number;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ──────────────────────────────────────────────────────────
|
|
103
|
+
// Executor: 启动爬取(可选等待结果)
|
|
104
|
+
// ──────────────────────────────────────────────────────────
|
|
105
|
+
export async function cloudflareCrawlFunc(
|
|
106
|
+
args: {
|
|
107
|
+
url: string;
|
|
108
|
+
limit?: number;
|
|
109
|
+
depth?: number;
|
|
110
|
+
formats?: string[];
|
|
111
|
+
render?: boolean;
|
|
112
|
+
source?: string;
|
|
113
|
+
includeSubdomains?: boolean;
|
|
114
|
+
includePatterns?: string[];
|
|
115
|
+
excludePatterns?: string[];
|
|
116
|
+
wait?: boolean;
|
|
117
|
+
},
|
|
118
|
+
thunkApi: any
|
|
119
|
+
): Promise<{ rawData: any; displayData: string }> {
|
|
120
|
+
const {
|
|
121
|
+
url,
|
|
122
|
+
limit = 10,
|
|
123
|
+
depth,
|
|
124
|
+
formats = ["markdown"],
|
|
125
|
+
render = true,
|
|
126
|
+
source,
|
|
127
|
+
includeSubdomains,
|
|
128
|
+
includePatterns,
|
|
129
|
+
excludePatterns,
|
|
130
|
+
wait = true,
|
|
131
|
+
} = args;
|
|
132
|
+
|
|
133
|
+
if (!url || !url.startsWith("http")) {
|
|
134
|
+
throw new Error("必须提供有效的 http/https URL");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const resolution = await discoverCanonicalDocsUrl(url);
|
|
138
|
+
const targetUrl = resolution.resolvedUrl;
|
|
139
|
+
if (targetUrl !== url) {
|
|
140
|
+
await assertFetchableDocsUrl(targetUrl, fetch, url);
|
|
141
|
+
}
|
|
142
|
+
const normalizationNote =
|
|
143
|
+
targetUrl !== url ? `\n🧭 文档地址已规范化: ${url} → ${targetUrl}` : "";
|
|
144
|
+
|
|
145
|
+
const options: Record<string, any> = {};
|
|
146
|
+
if (includeSubdomains !== undefined) options.includeSubdomains = includeSubdomains;
|
|
147
|
+
if (includePatterns?.length) options.includePatterns = includePatterns;
|
|
148
|
+
if (excludePatterns?.length) options.excludePatterns = excludePatterns;
|
|
149
|
+
|
|
150
|
+
const body: Record<string, any> = { url: targetUrl, limit, formats, render, wait };
|
|
151
|
+
if (depth !== undefined) body.depth = depth;
|
|
152
|
+
if (source !== undefined) body.source = source;
|
|
153
|
+
if (Object.keys(options).length > 0) body.options = options;
|
|
154
|
+
|
|
155
|
+
const data = await callToolApi<CrawlResult>(
|
|
156
|
+
thunkApi,
|
|
157
|
+
"/api/cloudflare-crawl",
|
|
158
|
+
body,
|
|
159
|
+
{ withAuth: true }
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// 仅返回 jobId(未等待)
|
|
163
|
+
if (!wait || data.status === "running") {
|
|
164
|
+
const rawData = targetUrl === url ? data : { ...data, originalUrl: url, resolvedUrl: targetUrl };
|
|
165
|
+
return {
|
|
166
|
+
rawData,
|
|
167
|
+
displayData:
|
|
168
|
+
`🚀 爬取任务已启动 (jobId: ${data.jobId})\n目标: ${targetUrl}` +
|
|
169
|
+
normalizationNote +
|
|
170
|
+
`\n使用 cloudflareCrawlStatus 工具查询结果。`,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// 已完成:格式化结果
|
|
175
|
+
const records = data.records ?? [];
|
|
176
|
+
const completedCount = records.filter((r) => r.status === "completed").length;
|
|
177
|
+
const mdParts = records
|
|
178
|
+
.filter((r) => r.status === "completed" && r.markdown)
|
|
179
|
+
.map((r) => `## ${r.metadata?.title || r.url}\n> ${r.url}\n\n${r.markdown}`)
|
|
180
|
+
.join("\n\n---\n\n");
|
|
181
|
+
|
|
182
|
+
const rawData = {
|
|
183
|
+
jobId: data.jobId,
|
|
184
|
+
status: data.status,
|
|
185
|
+
total: data.total,
|
|
186
|
+
finished: data.finished,
|
|
187
|
+
browserSecondsUsed: data.browserSecondsUsed,
|
|
188
|
+
pages: records.map((r) => ({
|
|
189
|
+
url: r.url,
|
|
190
|
+
status: r.status,
|
|
191
|
+
title: r.metadata?.title,
|
|
192
|
+
content: r.markdown || r.html || "",
|
|
193
|
+
})),
|
|
194
|
+
...(targetUrl === url ? {} : { originalUrl: url, resolvedUrl: targetUrl }),
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const statusIcon = data.status === "completed" ? "✅" : "⚠️";
|
|
198
|
+
const displayData =
|
|
199
|
+
`${statusIcon} Cloudflare 爬取完成\n` +
|
|
200
|
+
`- 目标: ${targetUrl}\n` +
|
|
201
|
+
`- 状态: ${data.status}\n` +
|
|
202
|
+
`- 已完成页面: ${completedCount} / ${data.total ?? "?"}\n` +
|
|
203
|
+
`- 浏览器用时: ${data.browserSecondsUsed?.toFixed(1) ?? "?"}s` +
|
|
204
|
+
normalizationNote +
|
|
205
|
+
`\n\n` +
|
|
206
|
+
(mdParts ? `**内容摘要(前 3 页):**\n\n${records.slice(0, 3).map((r) => `- [${r.metadata?.title || r.url}](${r.url})`).join("\n")}` : "无可用内容");
|
|
207
|
+
|
|
208
|
+
return { rawData, displayData };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ──────────────────────────────────────────────────────────
|
|
212
|
+
// Schema: 查询已有任务状态
|
|
213
|
+
// ──────────────────────────────────────────────────────────
|
|
214
|
+
export const cloudflareCrawlStatusFunctionSchema = {
|
|
215
|
+
name: "cloudflareCrawlStatus",
|
|
216
|
+
description:
|
|
217
|
+
"查询由 cloudflareCrawl 工具创建的爬取任务的当前状态和结果。" +
|
|
218
|
+
"当 cloudflareCrawl 以 wait=false 模式启动时,使用此工具轮询结果。",
|
|
219
|
+
parameters: {
|
|
220
|
+
type: "object",
|
|
221
|
+
properties: {
|
|
222
|
+
jobId: {
|
|
223
|
+
type: "string",
|
|
224
|
+
description: "由 cloudflareCrawl 返回的任务 ID。",
|
|
225
|
+
},
|
|
226
|
+
limit: {
|
|
227
|
+
type: "integer",
|
|
228
|
+
description: "最多返回多少条记录(用于分页)。",
|
|
229
|
+
},
|
|
230
|
+
status: {
|
|
231
|
+
type: "string",
|
|
232
|
+
enum: ["queued", "completed", "disallowed", "skipped", "errored", "cancelled"],
|
|
233
|
+
description: "按 URL 状态过滤结果。",
|
|
234
|
+
},
|
|
235
|
+
cursor: {
|
|
236
|
+
type: "integer",
|
|
237
|
+
description: "分页游标,从上一页响应的 cursor 字段获取。",
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
required: ["jobId"],
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// ──────────────────────────────────────────────────────────
|
|
245
|
+
// Executor: 查询任务状态
|
|
246
|
+
// ──────────────────────────────────────────────────────────
|
|
247
|
+
export async function cloudflareCrawlStatusFunc(
|
|
248
|
+
args: { jobId: string; limit?: number; status?: string; cursor?: number },
|
|
249
|
+
thunkApi: any
|
|
250
|
+
): Promise<{ rawData: any; displayData: string }> {
|
|
251
|
+
const { jobId, limit, status, cursor } = args;
|
|
252
|
+
|
|
253
|
+
if (!jobId) throw new Error("必须提供 jobId");
|
|
254
|
+
|
|
255
|
+
// 通过 GET /api/cloudflare-crawl/:jobId 查询
|
|
256
|
+
const { getToolRequestContext } = await import("./toolApiClient");
|
|
257
|
+
const { baseUrl, token } = getToolRequestContext(thunkApi);
|
|
258
|
+
|
|
259
|
+
const params = new URLSearchParams();
|
|
260
|
+
if (limit !== undefined) params.set("limit", String(limit));
|
|
261
|
+
if (status) params.set("status", status);
|
|
262
|
+
if (cursor !== undefined) params.set("cursor", String(cursor));
|
|
263
|
+
const qs = params.toString() ? `?${params}` : "";
|
|
264
|
+
|
|
265
|
+
const headers: Record<string, string> = { "Content-Type": "application/json" };
|
|
266
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
267
|
+
|
|
268
|
+
const resp = await fetch(`${baseUrl}/api/cloudflare-crawl/${jobId}${qs}`, { headers });
|
|
269
|
+
if (!resp.ok) {
|
|
270
|
+
const err = await resp.json().catch(() => ({})) as any;
|
|
271
|
+
throw new Error(`查询爬取任务失败: ${err?.error?.message ?? resp.status}`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const data = await resp.json() as CrawlResult & { status: string };
|
|
275
|
+
|
|
276
|
+
const isRunning = data.status === "running";
|
|
277
|
+
const statusIcon = isRunning ? "🔄" : data.status === "completed" ? "✅" : "⚠️";
|
|
278
|
+
const records = (data as any).records ?? [];
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
rawData: data,
|
|
282
|
+
displayData:
|
|
283
|
+
`${statusIcon} 任务 ${jobId} 状态: ${data.status}\n` +
|
|
284
|
+
`- 总页面: ${(data as any).total ?? "?"}, 已完成: ${(data as any).finished ?? "?"}\n` +
|
|
285
|
+
(records.length > 0
|
|
286
|
+
? `\n已抓取页面:\n${records.slice(0, 5).map((r: CrawlRecord) => ` - ${r.metadata?.title || r.url} (${r.status})`).join("\n")}`
|
|
287
|
+
: ""),
|
|
288
|
+
};
|
|
289
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { getToolBaseUrl } from "./toolApiClient";
|
|
2
|
+
|
|
3
|
+
export type CodeSearchArgs = {
|
|
4
|
+
query?: string;
|
|
5
|
+
path?: string;
|
|
6
|
+
pathScope?: string;
|
|
7
|
+
glob?: string;
|
|
8
|
+
mode?: "content" | "files";
|
|
9
|
+
maxResults?: number;
|
|
10
|
+
maxFiles?: number;
|
|
11
|
+
caseSensitive?: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const codeSearchFunctionSchema = {
|
|
15
|
+
name: "codeSearch",
|
|
16
|
+
description: [
|
|
17
|
+
"使用 ripgrep (rg) 在项目代码中搜索文本,或按 glob 列出文件。",
|
|
18
|
+
"",
|
|
19
|
+
"推荐场景:",
|
|
20
|
+
"- mode=content:按关键词搜索代码内容,替代旧的 searchRepo。",
|
|
21
|
+
"- mode=files:按路径范围和 glob 列出文件,替代旧的 listFiles。",
|
|
22
|
+
"",
|
|
23
|
+
"参数说明:",
|
|
24
|
+
"- query:搜索文本;mode=content 时必填,mode=files 时可省略。",
|
|
25
|
+
"- path/pathScope:限制搜索目录,例如 'packages/server'。",
|
|
26
|
+
"- glob:可选文件匹配模式,例如 '**/*.ts'。",
|
|
27
|
+
"- maxResults:最大返回数量;兼容旧参数 maxFiles。",
|
|
28
|
+
"- caseSensitive:是否区分大小写,默认 false。",
|
|
29
|
+
].join("\n"),
|
|
30
|
+
parameters: {
|
|
31
|
+
type: "object",
|
|
32
|
+
properties: {
|
|
33
|
+
query: {
|
|
34
|
+
type: "string",
|
|
35
|
+
description: "可选:要搜索的文本。mode=content 时必须提供。",
|
|
36
|
+
},
|
|
37
|
+
path: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "可选:限制搜索目录,例如 'packages/ai'。",
|
|
40
|
+
},
|
|
41
|
+
pathScope: {
|
|
42
|
+
type: "string",
|
|
43
|
+
description: "兼容旧参数:限制搜索目录,例如 'packages/ai'。",
|
|
44
|
+
},
|
|
45
|
+
glob: {
|
|
46
|
+
type: "string",
|
|
47
|
+
description: "可选:文件 glob,例如 '**/*.ts'。",
|
|
48
|
+
},
|
|
49
|
+
mode: {
|
|
50
|
+
type: "string",
|
|
51
|
+
enum: ["content", "files"],
|
|
52
|
+
description: "搜索模式:content=搜内容,files=列文件。",
|
|
53
|
+
},
|
|
54
|
+
maxResults: {
|
|
55
|
+
type: "number",
|
|
56
|
+
description: "最大返回数量。兼容旧参数 maxFiles。",
|
|
57
|
+
},
|
|
58
|
+
maxFiles: {
|
|
59
|
+
type: "number",
|
|
60
|
+
description: "兼容旧参数:等价于 maxResults。",
|
|
61
|
+
},
|
|
62
|
+
caseSensitive: {
|
|
63
|
+
type: "boolean",
|
|
64
|
+
description: "是否区分大小写,默认 false。",
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export async function codeSearchFunc(
|
|
71
|
+
args: CodeSearchArgs,
|
|
72
|
+
thunkApi: any,
|
|
73
|
+
context?: { signal?: AbortSignal },
|
|
74
|
+
): Promise<{ rawData: any; displayData?: string }> {
|
|
75
|
+
const baseUrl = getToolBaseUrl(thunkApi);
|
|
76
|
+
const apiUrl = `${baseUrl}/api/code-search`;
|
|
77
|
+
const mode = args.mode ?? "content";
|
|
78
|
+
|
|
79
|
+
const response = await fetch(apiUrl, {
|
|
80
|
+
method: "POST",
|
|
81
|
+
headers: { "Content-Type": "application/json" },
|
|
82
|
+
signal: context?.signal,
|
|
83
|
+
body: JSON.stringify({
|
|
84
|
+
query: args.query,
|
|
85
|
+
path: args.path ?? args.pathScope,
|
|
86
|
+
glob: args.glob,
|
|
87
|
+
mode,
|
|
88
|
+
maxResults: args.maxResults ?? args.maxFiles,
|
|
89
|
+
caseSensitive: args.caseSensitive,
|
|
90
|
+
}),
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const data = await response.json() as any;
|
|
94
|
+
if (!response.ok || data?.error) {
|
|
95
|
+
throw new Error(data?.error || `codeSearch 请求失败: ${response.status}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (mode === "files") {
|
|
99
|
+
const files = Array.isArray(data.files) ? data.files : [];
|
|
100
|
+
return {
|
|
101
|
+
rawData: data,
|
|
102
|
+
displayData: `📁 codeSearch 列出 ${files.length} 个文件${data.truncated ? "(已截断)" : ""}`,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const hits = Array.isArray(data.hits) ? data.hits : [];
|
|
107
|
+
return {
|
|
108
|
+
rawData: data,
|
|
109
|
+
displayData: `🔍 codeSearch 找到 ${hits.length} 条匹配${data.truncated ? "(已截断)" : ""}`,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// 文件路径: packages/ai/tools/codeTools.ts
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
readFileFunctionSchema,
|
|
5
|
+
readFileFunc,
|
|
6
|
+
readFilePreviewFunc,
|
|
7
|
+
} from "./readFileTool";
|
|
8
|
+
import {
|
|
9
|
+
writeFileFunctionSchema,
|
|
10
|
+
writeFileFunc,
|
|
11
|
+
writeFilePreviewFunc,
|
|
12
|
+
} from "./writeFileTool";
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
codeSearchFunctionSchema,
|
|
16
|
+
codeSearchFunc,
|
|
17
|
+
} from "./codeSearchTool";
|
|
18
|
+
import {
|
|
19
|
+
applyEditFunctionSchema,
|
|
20
|
+
applyEditFunc,
|
|
21
|
+
} from "./applyEditTool";
|
|
22
|
+
import {
|
|
23
|
+
applyLineEditsFunctionSchema,
|
|
24
|
+
applyLineEditsFunc,
|
|
25
|
+
} from "./applyLineEditsTool";
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
import type { ToolDefinition } from "./index";
|
|
29
|
+
|
|
30
|
+
export const codeToolDefinitions: ToolDefinition[] = [
|
|
31
|
+
{
|
|
32
|
+
id: "readFile",
|
|
33
|
+
schema: readFileFunctionSchema,
|
|
34
|
+
executor: readFileFunc,
|
|
35
|
+
previewExecutor: readFilePreviewFunc,
|
|
36
|
+
description: {
|
|
37
|
+
name: "readFile",
|
|
38
|
+
description:
|
|
39
|
+
"读取项目中的指定文本文件的完整内容,常用于在修改前先查看代码。",
|
|
40
|
+
category: "代码 / 文件操作",
|
|
41
|
+
},
|
|
42
|
+
behavior: "data",
|
|
43
|
+
uiGroup: "data",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: "writeFile",
|
|
47
|
+
schema: writeFileFunctionSchema,
|
|
48
|
+
executor: writeFileFunc,
|
|
49
|
+
previewExecutor: writeFilePreviewFunc,
|
|
50
|
+
description: {
|
|
51
|
+
name: "writeFile",
|
|
52
|
+
description:
|
|
53
|
+
"在项目中创建或覆盖一个文件的完整内容,适合新建文件或整文件重写。",
|
|
54
|
+
category: "代码编辑",
|
|
55
|
+
},
|
|
56
|
+
behavior: "action",
|
|
57
|
+
cancelable: true,
|
|
58
|
+
uiGroup: "data",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: "applyEdit",
|
|
62
|
+
schema: applyEditFunctionSchema,
|
|
63
|
+
executor: applyEditFunc,
|
|
64
|
+
description: {
|
|
65
|
+
name: "applyEdit",
|
|
66
|
+
description:
|
|
67
|
+
"基于精确文本片段执行局部代码修改,默认唯一匹配,适合作为首选编辑工具。",
|
|
68
|
+
category: "代码编辑",
|
|
69
|
+
},
|
|
70
|
+
behavior: "action",
|
|
71
|
+
cancelable: true,
|
|
72
|
+
uiGroup: "data",
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
{
|
|
76
|
+
id: "codeSearch",
|
|
77
|
+
schema: codeSearchFunctionSchema,
|
|
78
|
+
executor: codeSearchFunc,
|
|
79
|
+
description: {
|
|
80
|
+
name: "codeSearch",
|
|
81
|
+
description: "基于 rg 搜索代码内容或列出文件,统一替代旧的 searchRepo/listFiles。",
|
|
82
|
+
category: "代码分析",
|
|
83
|
+
},
|
|
84
|
+
behavior: "data",
|
|
85
|
+
uiGroup: "data",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: "applyLineEdits",
|
|
89
|
+
schema: applyLineEditsFunctionSchema,
|
|
90
|
+
executor: applyLineEditsFunc,
|
|
91
|
+
description: {
|
|
92
|
+
name: "applyLineEdits",
|
|
93
|
+
description:
|
|
94
|
+
"对指定代码文件按行号执行精确的文本编辑操作,适合 applyEdit 不方便表达的局部修改。",
|
|
95
|
+
category: "代码编辑",
|
|
96
|
+
},
|
|
97
|
+
behavior: "action",
|
|
98
|
+
cancelable: true,
|
|
99
|
+
uiGroup: "data",
|
|
100
|
+
},
|
|
101
|
+
];
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// 文件路径: ai/tools/createDocTool.ts
|
|
2
|
+
|
|
3
|
+
import { createDoc } from "render/page/docSlice";
|
|
4
|
+
import { selectCurrentSpaceId } from "create/space/spaceSlice";
|
|
5
|
+
import type { RootState } from "app/store";
|
|
6
|
+
|
|
7
|
+
export interface CreateDocToolArgs {
|
|
8
|
+
title?: string;
|
|
9
|
+
spaceId?: string;
|
|
10
|
+
categoryId?: string;
|
|
11
|
+
content?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
export const createDocFunctionSchema = {
|
|
16
|
+
name: "createDoc",
|
|
17
|
+
description: [
|
|
18
|
+
"在当前空间中创建一个新文档(page),可以指定标题、分类和初始内容。",
|
|
19
|
+
"如果需要绑定到指定分类,请先通过其它工具(如 createCategory / queryContentsByCategory)获取真实的 categoryId 再调用本工具。",
|
|
20
|
+
].join("\n"),
|
|
21
|
+
parameters: {
|
|
22
|
+
type: "object",
|
|
23
|
+
properties: {
|
|
24
|
+
title: {
|
|
25
|
+
type: "string",
|
|
26
|
+
description:
|
|
27
|
+
"页面标题。如果未提供,将在执行器中使用默认标题(例如“新页面”或日期格式)。",
|
|
28
|
+
},
|
|
29
|
+
spaceId: {
|
|
30
|
+
type: "string",
|
|
31
|
+
description: [
|
|
32
|
+
"可选:要创建到哪个 space。",
|
|
33
|
+
"优先级:如果传了真实 spaceId,就用它;如果不传,则优先使用当前已选中的 space;如果当前也没有 space,则仍允许创建为不归属任何 space 的文档。",
|
|
34
|
+
'不需要指定时传空字符串 ""。',
|
|
35
|
+
].join("\n"),
|
|
36
|
+
},
|
|
37
|
+
categoryId: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: [
|
|
40
|
+
"页面所属的分类ID(数据库中的真实 ID):",
|
|
41
|
+
"- 只有在你已经从其它工具的结果或用户输入中拿到 categoryId 时,才填写真实 ID;",
|
|
42
|
+
'- 如果目前没有可用的分类ID(模型自己想的名称不算),请传空字符串 ""。',
|
|
43
|
+
].join("\n"),
|
|
44
|
+
},
|
|
45
|
+
content: {
|
|
46
|
+
type: "string",
|
|
47
|
+
description: [
|
|
48
|
+
"页面的初始内容,使用 Markdown 格式。",
|
|
49
|
+
"支持 mention 语法引用其他资源,格式:@[type:dbKey|显示标签]",
|
|
50
|
+
"支持的类型:",
|
|
51
|
+
"- @[page:PAGE-xxx|标题] 引用另一个页面,读取时会展开页面内容",
|
|
52
|
+
"- @[agent:agent-xxx|名称] 引用一个 Agent,可作为 reference 挂载",
|
|
53
|
+
"- @[space:space-xxx|名称] 引用一个 Space 目录",
|
|
54
|
+
"例:@[page:PAGE-abc|产品规范] @[agent:agent-xyz|写作助手]",
|
|
55
|
+
"不需要初始内容时传空字符串 \"\"。",
|
|
56
|
+
].join("\n"),
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
required: ["title", "categoryId", "content"],
|
|
60
|
+
} as const,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* [Executor] 'createDoc' 工具的执行函数。
|
|
65
|
+
* @param args - LLM 提供的参数: { title?: string, categoryId?: string, content?: string }
|
|
66
|
+
* @param thunkApi - Redux Thunk API
|
|
67
|
+
* @returns {Promise<{rawData: unknown, displayData: string}>}
|
|
68
|
+
*/
|
|
69
|
+
export async function createDocFunc(
|
|
70
|
+
args: CreateDocToolArgs,
|
|
71
|
+
thunkApi: any
|
|
72
|
+
): Promise<{ rawData: unknown; displayData: string }> {
|
|
73
|
+
const { dispatch, getState } = thunkApi;
|
|
74
|
+
const state = getState() as RootState;
|
|
75
|
+
|
|
76
|
+
console.log("[createDocTool] Received args:", args);
|
|
77
|
+
|
|
78
|
+
// 为参数提供默认值和安全处理
|
|
79
|
+
const rawTitle = (args.title ?? "").trim();
|
|
80
|
+
const title = rawTitle || "新页面";
|
|
81
|
+
|
|
82
|
+
const rawCategoryId = (args.categoryId ?? "").trim();
|
|
83
|
+
// 空字符串视为“未指定分类”,交给后续 createPageAction 进一步校验过滤
|
|
84
|
+
const categoryId = rawCategoryId || undefined;
|
|
85
|
+
|
|
86
|
+
const explicitSpaceId = (args.spaceId ?? "").trim() || undefined;
|
|
87
|
+
const currentSpaceId = selectCurrentSpaceId(state) || undefined;
|
|
88
|
+
const spaceId = explicitSpaceId ?? currentSpaceId;
|
|
89
|
+
|
|
90
|
+
const rawContent = (args.content ?? "").trim();
|
|
91
|
+
const content = rawContent || undefined;
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
console.log("[createDocTool] dispatching createDoc with:", {
|
|
95
|
+
title,
|
|
96
|
+
spaceId,
|
|
97
|
+
categoryId,
|
|
98
|
+
content,
|
|
99
|
+
});
|
|
100
|
+
const id = await (dispatch as any)(
|
|
101
|
+
createDoc({ title, spaceId, categoryId, content })
|
|
102
|
+
).unwrap();
|
|
103
|
+
|
|
104
|
+
console.log("[createDocTool] createDoc success, id:", id);
|
|
105
|
+
|
|
106
|
+
const rawData = {
|
|
107
|
+
success: true,
|
|
108
|
+
id,
|
|
109
|
+
dbKey: id,
|
|
110
|
+
title,
|
|
111
|
+
spaceId: spaceId ?? null,
|
|
112
|
+
categoryId: categoryId ?? null,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const displayData = categoryId
|
|
116
|
+
? `页面《${title}》已成功创建并关联到分类。`
|
|
117
|
+
: `页面《${title}》已成功创建。`;
|
|
118
|
+
|
|
119
|
+
return { rawData, displayData };
|
|
120
|
+
} catch (error: any) {
|
|
121
|
+
console.error("[createDocTool] Error creating doc:", error);
|
|
122
|
+
const msg =
|
|
123
|
+
typeof error?.message === "string" ? error.message : JSON.stringify(error);
|
|
124
|
+
throw new Error(`创建文档时出错: ${msg}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// backward-compat aliases
|
|
129
|
+
/** @deprecated use createDocFunctionSchema */
|
|
130
|
+
export const createPageFunctionSchema = createDocFunctionSchema;
|
|
131
|
+
/** @deprecated use createDocFunc */
|
|
132
|
+
export { createDocFunc as createPageFunc };
|