@johpaz/hive-sdk 0.0.12 → 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/.github/CODEOWNERS +9 -0
- package/.github/workflows/publish.yml +89 -0
- package/.github/workflows/version-bump.yml +102 -0
- package/CHANGELOG.md +38 -0
- package/README.md +158 -0
- package/bun.lock +543 -0
- package/bunfig.toml +7 -0
- package/docs/API-AGENTS.md +316 -0
- package/docs/API-CONTEXT-COMPILER.md +252 -0
- package/docs/API-DAG-SCHEDULER.md +273 -0
- package/docs/API-TOOLS-SKILLS-CHANNELS.md +293 -0
- package/docs/API-WORKERS-EVENTS.md +152 -0
- package/docs/INDEX.md +141 -0
- package/docs/README.md +68 -0
- package/package.json +54 -105
- package/packages/cli/package.json +17 -0
- package/packages/cli/src/commands/init.ts +56 -0
- package/packages/cli/src/commands/run.ts +45 -0
- package/packages/cli/src/commands/test.ts +42 -0
- package/packages/cli/src/commands/trace.ts +55 -0
- package/packages/cli/src/index.ts +43 -0
- package/packages/core/package.json +58 -0
- package/packages/core/src/ace/Curator.ts +158 -0
- package/packages/core/src/ace/Reflector.ts +200 -0
- package/packages/core/src/ace/Tracer.ts +100 -0
- package/packages/core/src/ace/index.ts +4 -0
- package/packages/core/src/agent/AgentRunner.ts +699 -0
- package/packages/core/src/agent/Compaction.ts +221 -0
- package/packages/core/src/agent/ContextCompiler.ts +567 -0
- package/packages/core/src/agent/ContextGuard.ts +91 -0
- package/packages/core/src/agent/ConversationStore.ts +244 -0
- package/packages/core/src/agent/Hooks.ts +166 -0
- package/packages/core/src/agent/NativeTools.ts +31 -0
- package/packages/core/src/agent/PromptBuilder.ts +169 -0
- package/packages/core/src/agent/Service.ts +267 -0
- package/packages/core/src/agent/StuckLoop.ts +133 -0
- package/packages/core/src/agent/index.ts +12 -0
- package/packages/core/src/agent/providers/LLMClient.ts +149 -0
- package/packages/core/src/agent/providers/anthropic.ts +212 -0
- package/packages/core/src/agent/providers/gemini.ts +215 -0
- package/packages/core/src/agent/providers/index.ts +199 -0
- package/packages/core/src/agent/providers/interface.ts +195 -0
- package/packages/core/src/agent/providers/ollama.ts +175 -0
- package/packages/core/src/agent/providers/openai-compat.ts +231 -0
- package/packages/core/src/agent/providers.ts +1 -0
- package/packages/core/src/agent/selectors/PlaybookSelector.ts +147 -0
- package/packages/core/src/agent/selectors/SkillSelector.ts +478 -0
- package/packages/core/src/agent/selectors/ToolSelector.ts +577 -0
- package/packages/core/src/agent/selectors/index.ts +6 -0
- package/packages/core/src/api/createAgent.test.ts +48 -0
- package/packages/core/src/api/createAgent.ts +122 -0
- package/packages/core/src/api/index.ts +2 -0
- package/packages/core/src/canvas/CanvasManager.ts +390 -0
- package/packages/core/src/canvas/a2ui-tools.ts +255 -0
- package/packages/core/src/canvas/canvas-tools.ts +448 -0
- package/packages/core/src/canvas/emitter.ts +149 -0
- package/packages/core/src/canvas/index.ts +6 -0
- package/packages/core/src/config/index.ts +2 -0
- package/packages/core/src/config/loader.ts +554 -0
- package/packages/core/src/ethics/EthicsGuard.test.ts +54 -0
- package/packages/core/src/ethics/EthicsGuard.ts +66 -0
- package/packages/core/src/ethics/index.ts +2 -0
- package/packages/core/src/gateway/channel-notify.test.ts +14 -0
- package/packages/core/src/gateway/channel-notify.ts +12 -0
- package/packages/core/src/gateway/index.ts +1 -0
- package/packages/core/src/index.ts +37 -0
- package/packages/core/src/mcp/MCPClient.ts +439 -0
- package/packages/core/src/mcp/MCPToolAdapter.ts +176 -0
- package/packages/core/src/mcp/config.ts +13 -0
- package/packages/core/src/mcp/hot-reload.ts +147 -0
- package/packages/core/src/mcp/index.ts +11 -0
- package/packages/core/src/mcp/logger.ts +42 -0
- package/packages/core/src/mcp/singleton.ts +21 -0
- package/packages/core/src/mcp/transports/index.ts +67 -0
- package/packages/core/src/mcp/transports/sse.ts +241 -0
- package/packages/core/src/mcp/transports/websocket.ts +159 -0
- package/packages/core/src/memory/Scratchpad.test.ts +47 -0
- package/packages/core/src/memory/Scratchpad.ts +37 -0
- package/packages/core/src/memory/Storage.ts +6 -0
- package/packages/core/src/memory/index.ts +2 -0
- package/packages/core/src/multimodal/VisionService.ts +293 -0
- package/packages/core/src/multimodal/index.ts +2 -0
- package/packages/core/src/multimodal/types.ts +28 -0
- package/packages/core/src/security/Pairing.ts +250 -0
- package/packages/core/src/security/RateLimit.ts +270 -0
- package/packages/core/src/security/index.ts +4 -0
- package/packages/core/src/skills/SkillLoader.ts +388 -0
- package/packages/core/src/skills/bundled-data.generated.ts +3332 -0
- package/packages/core/src/skills/defineSkill.ts +18 -0
- package/packages/core/src/skills/index.ts +4 -0
- package/packages/core/src/state/index.ts +2 -0
- package/packages/core/src/state/store.ts +312 -0
- package/packages/core/src/storage/SQLiteStorage.ts +407 -0
- package/packages/core/src/storage/crypto.ts +101 -0
- package/packages/core/src/storage/index.ts +10 -0
- package/packages/core/src/storage/onboarding.ts +1603 -0
- package/packages/core/src/storage/schema.ts +689 -0
- package/packages/core/src/storage/seed.ts +740 -0
- package/packages/core/src/storage/usage.ts +374 -0
- package/packages/core/src/swarm/AgentBus.ts +460 -0
- package/packages/core/src/swarm/AgentExecutor.ts +53 -0
- package/packages/core/src/swarm/Coordinator.ts +251 -0
- package/packages/core/src/swarm/EventBridge.ts +122 -0
- package/packages/core/src/swarm/EventBus.ts +169 -0
- package/packages/core/src/swarm/TaskGraph.ts +192 -0
- package/packages/core/src/swarm/TaskNode.ts +97 -0
- package/packages/core/src/swarm/TaskResult.ts +22 -0
- package/packages/core/src/swarm/WorkerPool.ts +236 -0
- package/packages/core/src/swarm/errors.ts +37 -0
- package/packages/core/src/swarm/index.ts +30 -0
- package/packages/core/src/swarm/presets/HiveLearnPreset.ts +99 -0
- package/packages/core/src/swarm/presets/ResearchPreset.ts +97 -0
- package/packages/core/src/swarm/presets/index.ts +4 -0
- package/packages/core/src/swarm/strategies/ParallelStrategy.ts +21 -0
- package/packages/core/src/swarm/strategies/PriorityStrategy.ts +46 -0
- package/packages/core/src/swarm/strategies/index.ts +3 -0
- package/packages/core/src/swarm/types.ts +164 -0
- package/packages/core/src/tools/ToolExecutor.ts +58 -0
- package/packages/core/src/tools/ToolRegistry.test.ts +98 -0
- package/packages/core/src/tools/ToolRegistry.ts +61 -0
- package/packages/core/src/tools/agents/get-available-models.ts +118 -0
- package/packages/core/src/tools/agents/index.ts +715 -0
- package/packages/core/src/tools/bridge-events.ts +26 -0
- package/packages/core/src/tools/canvas/index.ts +375 -0
- package/packages/core/src/tools/cli/index.ts +142 -0
- package/packages/core/src/tools/codebridge/index.ts +342 -0
- package/packages/core/src/tools/core/index.ts +476 -0
- package/packages/core/src/tools/cron/index.ts +626 -0
- package/packages/core/src/tools/filesystem/fs-delete.ts +78 -0
- package/packages/core/src/tools/filesystem/fs-edit.ts +106 -0
- package/packages/core/src/tools/filesystem/fs-exists.ts +63 -0
- package/packages/core/src/tools/filesystem/fs-glob.ts +108 -0
- package/packages/core/src/tools/filesystem/fs-list.ts +129 -0
- package/packages/core/src/tools/filesystem/fs-read.ts +72 -0
- package/packages/core/src/tools/filesystem/fs-write.ts +67 -0
- package/packages/core/src/tools/filesystem/index.ts +34 -0
- package/packages/core/src/tools/filesystem/workspace-guard.ts +62 -0
- package/packages/core/src/tools/index.ts +231 -0
- package/packages/core/src/tools/meeting/index.ts +363 -0
- package/packages/core/src/tools/office/index.ts +47 -0
- package/packages/core/src/tools/office/office-escribir-docx.ts +192 -0
- package/packages/core/src/tools/office/office-escribir-pdf.ts +172 -0
- package/packages/core/src/tools/office/office-escribir-pptx.ts +174 -0
- package/packages/core/src/tools/office/office-escribir-xlsx.ts +116 -0
- package/packages/core/src/tools/office/office-leer-docx.ts +93 -0
- package/packages/core/src/tools/office/office-leer-pdf.ts +114 -0
- package/packages/core/src/tools/office/office-leer-pptx.ts +136 -0
- package/packages/core/src/tools/office/office-leer-xlsx.ts +124 -0
- package/packages/core/src/tools/projects/index.ts +37 -0
- package/packages/core/src/tools/projects/project-create.ts +94 -0
- package/packages/core/src/tools/projects/project-done.ts +66 -0
- package/packages/core/src/tools/projects/project-fail.ts +66 -0
- package/packages/core/src/tools/projects/project-list.ts +96 -0
- package/packages/core/src/tools/projects/project-update.ts +72 -0
- package/packages/core/src/tools/projects/task-create.ts +68 -0
- package/packages/core/src/tools/projects/task-evaluate.ts +93 -0
- package/packages/core/src/tools/projects/task-update.ts +93 -0
- package/packages/core/src/tools/types.ts +39 -0
- package/packages/core/src/tools/voice/index.ts +104 -0
- package/packages/core/src/tools/web/browser-click.ts +78 -0
- package/packages/core/src/tools/web/browser-extract.ts +139 -0
- package/packages/core/src/tools/web/browser-navigate.ts +106 -0
- package/packages/core/src/tools/web/browser-screenshot.ts +87 -0
- package/packages/core/src/tools/web/browser-script.ts +88 -0
- package/packages/core/src/tools/web/browser-service.ts +554 -0
- package/packages/core/src/tools/web/browser-type.ts +101 -0
- package/packages/core/src/tools/web/browser-wait.ts +136 -0
- package/packages/core/src/tools/web/index.ts +41 -0
- package/packages/core/src/tools/web/web-fetch.ts +78 -0
- package/packages/core/src/tools/web/web-search.ts +123 -0
- package/packages/core/src/utils/benchmark.ts +80 -0
- package/packages/core/src/utils/crypto.ts +73 -0
- package/packages/core/src/utils/date.ts +42 -0
- package/packages/core/src/utils/index.ts +10 -0
- package/packages/core/src/utils/logger.ts +389 -0
- package/packages/core/src/utils/retry.ts +70 -0
- package/packages/core/src/utils/toon.ts +253 -0
- package/packages/core/src/voice/index.ts +656 -0
- package/test/setup-db.ts +216 -0
- package/tsconfig.json +39 -0
- package/src/agents.ts +0 -1
- package/src/canvas.ts +0 -1
- package/src/channels.ts +0 -1
- package/src/config.ts +0 -1
- package/src/events.ts +0 -1
- package/src/gateway.ts +0 -1
- package/src/index.ts +0 -304
- package/src/mcp.ts +0 -1
- package/src/multimodal.ts +0 -1
- package/src/scheduler.ts +0 -1
- package/src/security.ts +0 -1
- package/src/skills.ts +0 -1
- package/src/state.ts +0 -1
- package/src/storage.ts +0 -1
- package/src/tools.ts +0 -1
- package/src/tts.ts +0 -1
- package/src/types.ts +0 -82
- package/src/utils.ts +0 -1
- package/src/voice.ts +0 -1
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* browser_wait - Wait for element or condition on page
|
|
3
|
+
*
|
|
4
|
+
* @category web
|
|
5
|
+
* @seedId browser_wait
|
|
6
|
+
* @spanish esperar, wait, condición, elemento, selector
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Tool } from "../types.ts";
|
|
10
|
+
import { logger } from "../../utils/logger.ts";
|
|
11
|
+
import { getBrowserService, waitForSelector, waitForCondition } from "./browser-service.ts";
|
|
12
|
+
|
|
13
|
+
const log = logger.child("browser-wait");
|
|
14
|
+
|
|
15
|
+
export const browserWaitTool: Tool = {
|
|
16
|
+
name: "browser_wait",
|
|
17
|
+
description: "Wait for an element to appear or condition to be met on the page. Spanish: esperar, wait, condición, elemento, selector",
|
|
18
|
+
parameters: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
url: {
|
|
22
|
+
type: "string",
|
|
23
|
+
description: "URL to navigate to before waiting (optional)",
|
|
24
|
+
},
|
|
25
|
+
selector: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "CSS selector to wait for (optional if condition provided)",
|
|
28
|
+
},
|
|
29
|
+
condition: {
|
|
30
|
+
type: "string",
|
|
31
|
+
description: "JavaScript expression to evaluate (optional if selector provided)",
|
|
32
|
+
},
|
|
33
|
+
timeout: {
|
|
34
|
+
type: "number",
|
|
35
|
+
description: "Timeout in milliseconds (default: 30000)",
|
|
36
|
+
},
|
|
37
|
+
state: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "Element state: visible, hidden, attached (default: visible)",
|
|
40
|
+
enum: ["visible", "hidden", "attached"],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
required: [],
|
|
44
|
+
},
|
|
45
|
+
execute: async (params: Record<string, unknown>) => {
|
|
46
|
+
const url = params.url as string | undefined;
|
|
47
|
+
const selector = params.selector as string | undefined;
|
|
48
|
+
const condition = params.condition as string | undefined;
|
|
49
|
+
const timeout = (params.timeout as number) ?? 30000;
|
|
50
|
+
const state = (params.state as string) ?? "visible";
|
|
51
|
+
|
|
52
|
+
const browserService = getBrowserService();
|
|
53
|
+
if (!browserService?.isAvailable()) {
|
|
54
|
+
log.warn("Browser not available");
|
|
55
|
+
return {
|
|
56
|
+
ok: false,
|
|
57
|
+
error: "Browser automation not available. Install Chrome/Chromium.",
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!selector && !condition) {
|
|
62
|
+
return {
|
|
63
|
+
ok: false,
|
|
64
|
+
error: "Either 'selector' or 'condition' must be provided",
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
log.info(`Waiting${selector ? ` for selector: ${selector}` : ""}${condition ? ` for condition: ${condition}` : ""}${url ? ` on ${url}` : ""}`);
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const view = await browserService.getView();
|
|
72
|
+
if (!view) return { ok: false, error: "Browser automation not available. Install Chrome/Chromium." };
|
|
73
|
+
|
|
74
|
+
if (url) {
|
|
75
|
+
await view.navigate(url);
|
|
76
|
+
await Bun.sleep(500);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const startTime = Date.now();
|
|
80
|
+
let found = false;
|
|
81
|
+
|
|
82
|
+
if (selector) {
|
|
83
|
+
const isXPath = selector.startsWith("xpath:");
|
|
84
|
+
const actualSelector = isXPath ? selector.slice(6) : selector;
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
if (isXPath) {
|
|
88
|
+
const xpathExpr = `(() => {
|
|
89
|
+
const r = document.evaluate(
|
|
90
|
+
${JSON.stringify(actualSelector)}, document, null,
|
|
91
|
+
XPathResult.FIRST_ORDERED_NODE_TYPE, null
|
|
92
|
+
);
|
|
93
|
+
return r.singleNodeValue !== null;
|
|
94
|
+
})()`;
|
|
95
|
+
await waitForCondition(view, xpathExpr, timeout);
|
|
96
|
+
} else {
|
|
97
|
+
await waitForSelector(view, actualSelector, timeout);
|
|
98
|
+
}
|
|
99
|
+
found = true;
|
|
100
|
+
} catch {
|
|
101
|
+
log.warn(`Selector "${actualSelector}" not found within ${timeout}ms`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (condition) {
|
|
106
|
+
try {
|
|
107
|
+
await waitForCondition(view, condition, timeout);
|
|
108
|
+
found = true;
|
|
109
|
+
} catch {
|
|
110
|
+
log.warn(`Condition not met within ${timeout}ms`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const elapsed = Date.now() - startTime;
|
|
115
|
+
const currentUrl = view.url;
|
|
116
|
+
|
|
117
|
+
log.info(`Wait completed in ${elapsed}ms on ${currentUrl} (found=${found})`);
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
ok: true,
|
|
121
|
+
found,
|
|
122
|
+
url: currentUrl,
|
|
123
|
+
selector,
|
|
124
|
+
condition,
|
|
125
|
+
state,
|
|
126
|
+
elapsedMs: elapsed,
|
|
127
|
+
};
|
|
128
|
+
} catch (error) {
|
|
129
|
+
log.error(`Wait failed: ${(error as Error).message}`);
|
|
130
|
+
return {
|
|
131
|
+
ok: false,
|
|
132
|
+
error: `Failed to wait: ${(error as Error).message}`,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Tools - Browser automation + Web utilities
|
|
3
|
+
*
|
|
4
|
+
* Browser tools use Puppeteer/Chromium (auto-managed).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Tool } from "../types.ts";
|
|
8
|
+
import { webSearchTool } from "./web-search.ts";
|
|
9
|
+
import { webFetchTool } from "./web-fetch.ts";
|
|
10
|
+
import { browserNavigateTool } from "./browser-navigate.ts";
|
|
11
|
+
import { browserScreenshotTool } from "./browser-screenshot.ts";
|
|
12
|
+
import { browserClickTool } from "./browser-click.ts";
|
|
13
|
+
import { browserTypeTool } from "./browser-type.ts";
|
|
14
|
+
import { browserExtractTool } from "./browser-extract.ts";
|
|
15
|
+
import { browserScriptTool } from "./browser-script.ts";
|
|
16
|
+
import { browserWaitTool } from "./browser-wait.ts";
|
|
17
|
+
|
|
18
|
+
export function createTools(): Tool[] {
|
|
19
|
+
return [
|
|
20
|
+
webSearchTool,
|
|
21
|
+
webFetchTool,
|
|
22
|
+
browserNavigateTool,
|
|
23
|
+
browserScreenshotTool,
|
|
24
|
+
browserClickTool,
|
|
25
|
+
browserTypeTool,
|
|
26
|
+
browserExtractTool,
|
|
27
|
+
browserScriptTool,
|
|
28
|
+
browserWaitTool,
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export * from "./web-search.ts";
|
|
33
|
+
export * from "./web-fetch.ts";
|
|
34
|
+
export * from "./browser-navigate.ts";
|
|
35
|
+
export * from "./browser-screenshot.ts";
|
|
36
|
+
export * from "./browser-click.ts";
|
|
37
|
+
export * from "./browser-type.ts";
|
|
38
|
+
export * from "./browser-extract.ts";
|
|
39
|
+
export * from "./browser-script.ts";
|
|
40
|
+
export * from "./browser-wait.ts";
|
|
41
|
+
export * from "./browser-service.ts";
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* web_fetch - Fetch plain content from a URL
|
|
3
|
+
*
|
|
4
|
+
* @category web
|
|
5
|
+
* @seedId web_fetch
|
|
6
|
+
* @spanish obtener página, descargar contenido, extraer texto de url
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Tool } from "../types.ts";
|
|
10
|
+
import { logger } from "../../utils/logger.ts";
|
|
11
|
+
|
|
12
|
+
const log = logger.child("web-fetch");
|
|
13
|
+
|
|
14
|
+
export const webFetchTool: Tool = {
|
|
15
|
+
name: "web_fetch",
|
|
16
|
+
description: "Fetch plain content from a URL (lightweight, no JS). Spanish: obtener página, descargar contenido, extraer texto de url",
|
|
17
|
+
parameters: {
|
|
18
|
+
type: "object",
|
|
19
|
+
properties: {
|
|
20
|
+
url: {
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "The URL to fetch content from",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
required: ["url"],
|
|
26
|
+
},
|
|
27
|
+
execute: async (params: Record<string, unknown>) => {
|
|
28
|
+
const url = params.url as string;
|
|
29
|
+
|
|
30
|
+
log.info(`Fetching: ${url}`);
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const response = await fetch(url, {
|
|
34
|
+
headers: {
|
|
35
|
+
"User-Agent": "Mozilla/5.0 (compatible; HiveBot/1.0)",
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const contentType = response.headers.get("content-type") || "";
|
|
44
|
+
let content: string;
|
|
45
|
+
|
|
46
|
+
if (contentType.includes("application/json")) {
|
|
47
|
+
const json = await response.json();
|
|
48
|
+
content = JSON.stringify(json, null, 2);
|
|
49
|
+
} else if (contentType.includes("text/html")) {
|
|
50
|
+
const html = await response.text();
|
|
51
|
+
// Strip HTML tags
|
|
52
|
+
content = html
|
|
53
|
+
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "")
|
|
54
|
+
.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "")
|
|
55
|
+
.replace(/<[^>]+>/g, " ")
|
|
56
|
+
.replace(/\s+/g, " ")
|
|
57
|
+
.trim()
|
|
58
|
+
.slice(0, 50000);
|
|
59
|
+
} else {
|
|
60
|
+
content = await response.text();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
ok: true,
|
|
65
|
+
url,
|
|
66
|
+
content,
|
|
67
|
+
contentType,
|
|
68
|
+
length: content.length,
|
|
69
|
+
};
|
|
70
|
+
} catch (error) {
|
|
71
|
+
log.error(`Fetch failed: ${(error as Error).message}`);
|
|
72
|
+
return {
|
|
73
|
+
ok: false,
|
|
74
|
+
error: `Failed to fetch URL: ${(error as Error).message}`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* web_search - Search the web for current information
|
|
3
|
+
*
|
|
4
|
+
* @category web
|
|
5
|
+
* @seedId web_search
|
|
6
|
+
* @spanish buscar en internet, búsqueda web, noticias, información
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Tool } from "../types.ts";
|
|
10
|
+
import { logger } from "../../utils/logger.ts";
|
|
11
|
+
|
|
12
|
+
const log = logger.child("web-search");
|
|
13
|
+
|
|
14
|
+
const TIMEOUT_MS = 10_000;
|
|
15
|
+
|
|
16
|
+
interface SearchResult {
|
|
17
|
+
title: string;
|
|
18
|
+
url: string;
|
|
19
|
+
snippet: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function decodeUddg(href: string): string {
|
|
23
|
+
try {
|
|
24
|
+
const uddg = new URL("https:" + href).searchParams.get("uddg");
|
|
25
|
+
return uddg ? decodeURIComponent(uddg) : href;
|
|
26
|
+
} catch {
|
|
27
|
+
return href;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isAdUrl(url: string): boolean {
|
|
32
|
+
return url.includes("duckduckgo.com/y.js") || url.includes("ad_provider") || url.includes("ad_domain");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function searchDuckDuckGo(query: string, numResults: number): Promise<SearchResult[]> {
|
|
36
|
+
const searchUrl = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
|
|
37
|
+
|
|
38
|
+
log.info(`Requesting DuckDuckGo HTML: ${searchUrl}`);
|
|
39
|
+
|
|
40
|
+
const response = await fetch(searchUrl, {
|
|
41
|
+
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
42
|
+
headers: {
|
|
43
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
throw new Error(`DuckDuckGo request failed: ${response.status}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const html = await response.text();
|
|
52
|
+
|
|
53
|
+
// Extract links: href contains //duckduckgo.com/l/?uddg=ENCODED_URL
|
|
54
|
+
const hrefs = [...html.matchAll(/<a[^>]+class="result__a"[^>]*href="([^"]+)"/g)].map((m) => m[1]);
|
|
55
|
+
const titles = [...html.matchAll(/<a[^>]+class="result__a"[^>]*>([^<]+)<\/a>/g)].map((m) => m[1].trim());
|
|
56
|
+
const rawSnippets = [...html.matchAll(/class="result__snippet"[^>]*>([\s\S]{0,400}?)<\/a>/g)].map((m) =>
|
|
57
|
+
m[1].replace(/<[^>]+>/g, "").trim()
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
log.debug(`DDG parsed: ${hrefs.length} links, ${titles.length} titles, ${rawSnippets.length} snippets`);
|
|
61
|
+
|
|
62
|
+
const results: SearchResult[] = [];
|
|
63
|
+
let snippetIdx = 0;
|
|
64
|
+
|
|
65
|
+
for (let i = 0; i < hrefs.length && results.length < numResults; i++) {
|
|
66
|
+
const rawUrl = hrefs[i] ?? "";
|
|
67
|
+
const url = decodeUddg(rawUrl);
|
|
68
|
+
|
|
69
|
+
// Skip ads
|
|
70
|
+
if (isAdUrl(url) || isAdUrl(rawUrl)) {
|
|
71
|
+
snippetIdx++; // ads also consume a snippet slot
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Advance snippet index past ads
|
|
76
|
+
while (snippetIdx < rawSnippets.length && isAdUrl(rawSnippets[snippetIdx] ?? "")) {
|
|
77
|
+
snippetIdx++;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
results.push({
|
|
81
|
+
title: titles[i] ?? url,
|
|
82
|
+
url,
|
|
83
|
+
snippet: rawSnippets[snippetIdx] ?? "",
|
|
84
|
+
});
|
|
85
|
+
snippetIdx++;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return results;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const webSearchTool: Tool = {
|
|
92
|
+
name: "web_search",
|
|
93
|
+
description: "Search the web for current information and research. Spanish: buscar en internet, búsqueda web, noticias, información",
|
|
94
|
+
parameters: {
|
|
95
|
+
type: "object",
|
|
96
|
+
properties: {
|
|
97
|
+
query: {
|
|
98
|
+
type: "string",
|
|
99
|
+
description: "The search query - be specific and include relevant keywords",
|
|
100
|
+
},
|
|
101
|
+
numResults: {
|
|
102
|
+
type: "number",
|
|
103
|
+
description: "Number of results to return (default: 5, max: 10)",
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
required: ["query"],
|
|
107
|
+
},
|
|
108
|
+
execute: async (params: Record<string, unknown>) => {
|
|
109
|
+
const query = params.query as string;
|
|
110
|
+
const numResults = Math.min((params.numResults as number) ?? 5, 10);
|
|
111
|
+
|
|
112
|
+
log.info(`Web search: "${query}"`);
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
const results = await searchDuckDuckGo(query, numResults);
|
|
116
|
+
log.info(`Web search returned ${results.length} results for "${query}"`);
|
|
117
|
+
return { ok: true, results, query, count: results.length };
|
|
118
|
+
} catch (error) {
|
|
119
|
+
log.error(`Search failed: ${(error as Error).message}`);
|
|
120
|
+
return { ok: false, error: `Web search failed: ${(error as Error).message}` };
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility for measuring resource usage (time and memory).
|
|
3
|
+
*/
|
|
4
|
+
export class Benchmark {
|
|
5
|
+
private startTime: number = 0;
|
|
6
|
+
private startMem: NodeJS.MemoryUsage | null = null;
|
|
7
|
+
private name: string;
|
|
8
|
+
|
|
9
|
+
constructor(name: string = "Benchmark") {
|
|
10
|
+
this.name = name;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Starts the benchmark timer and records initial memory usage.
|
|
15
|
+
*/
|
|
16
|
+
start() {
|
|
17
|
+
this.startTime = performance.now();
|
|
18
|
+
this.startMem = process.memoryUsage();
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Stops the benchmark and returns the metrics.
|
|
24
|
+
*/
|
|
25
|
+
stop() {
|
|
26
|
+
const endTime = performance.now();
|
|
27
|
+
const endMem = process.memoryUsage();
|
|
28
|
+
const duration = endTime - this.startTime;
|
|
29
|
+
|
|
30
|
+
const metrics = {
|
|
31
|
+
name: this.name,
|
|
32
|
+
durationMs: duration.toFixed(2),
|
|
33
|
+
heapUsed: this.formatMB(endMem.heapUsed),
|
|
34
|
+
heapTotal: this.formatMB(endMem.heapTotal),
|
|
35
|
+
rss: this.formatMB(endMem.rss),
|
|
36
|
+
external: this.formatMB(endMem.external),
|
|
37
|
+
heapDelta: this.startMem
|
|
38
|
+
? this.formatMB(endMem.heapUsed - this.startMem.heapUsed)
|
|
39
|
+
: "0.00 MB",
|
|
40
|
+
raw: {
|
|
41
|
+
duration,
|
|
42
|
+
memory: endMem,
|
|
43
|
+
delta: this.startMem
|
|
44
|
+
? endMem.heapUsed - this.startMem.heapUsed
|
|
45
|
+
: 0
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return metrics;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Pretty-prints the benchmark results to the console.
|
|
54
|
+
*/
|
|
55
|
+
print() {
|
|
56
|
+
const m = this.stop();
|
|
57
|
+
console.log(`\n📊 Benchmark: ${m.name}`);
|
|
58
|
+
console.table({
|
|
59
|
+
'Time': `${m.durationMs} ms`,
|
|
60
|
+
'Heap Used': m.heapUsed,
|
|
61
|
+
'Heap Total': m.heapTotal,
|
|
62
|
+
'RSS': m.rss,
|
|
63
|
+
'External': m.external,
|
|
64
|
+
'Memory Delta': m.heapDelta
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private formatMB(bytes: number) {
|
|
69
|
+
return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* static helper for quick one-off benchmarks.
|
|
74
|
+
*/
|
|
75
|
+
static async run(name: string, fn: () => Promise<any> | any) {
|
|
76
|
+
const b = new Benchmark(name).start();
|
|
77
|
+
await fn();
|
|
78
|
+
b.print();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
2
|
+
|
|
3
|
+
export function generateId(): string {
|
|
4
|
+
return crypto.randomUUID();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function generateShortId(length = 8): string {
|
|
8
|
+
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
9
|
+
let result = "";
|
|
10
|
+
const randomBytes = crypto.randomBytes(length);
|
|
11
|
+
|
|
12
|
+
for (let i = 0; i < length; i++) {
|
|
13
|
+
result += chars[randomBytes[i]! % chars.length];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function hashString(input: string): string {
|
|
20
|
+
return crypto.createHash("sha256").update(input).digest("hex");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function hashObject(obj: unknown): string {
|
|
24
|
+
return hashString(JSON.stringify(obj));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function hmacSign(
|
|
28
|
+
key: string,
|
|
29
|
+
data: string,
|
|
30
|
+
algorithm: "sha256" | "sha512" = "sha256"
|
|
31
|
+
): Promise<string> {
|
|
32
|
+
return crypto.createHmac(algorithm, key).update(data).digest("hex");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function hmacVerify(
|
|
36
|
+
key: string,
|
|
37
|
+
data: string,
|
|
38
|
+
signature: string,
|
|
39
|
+
algorithm: "sha256" | "sha512" = "sha256"
|
|
40
|
+
): Promise<boolean> {
|
|
41
|
+
const expected = await hmacSign(key, data, algorithm);
|
|
42
|
+
return crypto.timingSafeEqual(
|
|
43
|
+
Buffer.from(expected),
|
|
44
|
+
Buffer.from(signature)
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function encrypt(text: string, key: string): string {
|
|
49
|
+
const iv = crypto.randomBytes(16);
|
|
50
|
+
const derivedKey = crypto.scryptSync(key, "salt", 32);
|
|
51
|
+
const cipher = crypto.createCipheriv("aes-256-cbc", derivedKey, iv);
|
|
52
|
+
|
|
53
|
+
let encrypted = cipher.update(text, "utf8", "hex");
|
|
54
|
+
encrypted += cipher.final("hex");
|
|
55
|
+
|
|
56
|
+
return iv.toString("hex") + ":" + encrypted;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function decrypt(encryptedData: string, key: string): string {
|
|
60
|
+
const [ivHex, encrypted] = encryptedData.split(":");
|
|
61
|
+
if (!ivHex || !encrypted) {
|
|
62
|
+
throw new Error("Invalid encrypted data format");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const iv = Buffer.from(ivHex, "hex");
|
|
66
|
+
const derivedKey = crypto.scryptSync(key, "salt", 32);
|
|
67
|
+
const decipher = crypto.createDecipheriv("aes-256-cbc", derivedKey, iv);
|
|
68
|
+
|
|
69
|
+
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
70
|
+
decrypted += decipher.final("utf8");
|
|
71
|
+
|
|
72
|
+
return decrypted;
|
|
73
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for user-specific date and time formatting.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function getUserDate(timezone: string = "UTC", date: Date = new Date()): string {
|
|
6
|
+
try {
|
|
7
|
+
const formatter = new Intl.DateTimeFormat("en-GB", {
|
|
8
|
+
timeZone: timezone,
|
|
9
|
+
year: "numeric",
|
|
10
|
+
month: "2-digit",
|
|
11
|
+
day: "2-digit",
|
|
12
|
+
});
|
|
13
|
+
const parts = formatter.formatToParts(date);
|
|
14
|
+
const year = parts.find(p => p.type === "year")?.value;
|
|
15
|
+
const month = parts.find(p => p.type === "month")?.value;
|
|
16
|
+
const day = parts.find(p => p.type === "day")?.value;
|
|
17
|
+
return `${year}-${month}-${day}`;
|
|
18
|
+
} catch (e) {
|
|
19
|
+
// Fallback to UTC if timezone is invalid
|
|
20
|
+
return date.toISOString().split("T")[0];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getUserTime(timezone: string = "UTC", date: Date = new Date()): string {
|
|
25
|
+
try {
|
|
26
|
+
const formatter = new Intl.DateTimeFormat("en-GB", {
|
|
27
|
+
timeZone: timezone,
|
|
28
|
+
hour: "2-digit",
|
|
29
|
+
minute: "2-digit",
|
|
30
|
+
second: "2-digit",
|
|
31
|
+
hour12: false,
|
|
32
|
+
});
|
|
33
|
+
const parts = formatter.formatToParts(date);
|
|
34
|
+
const hour = parts.find(p => p.type === "hour")?.value;
|
|
35
|
+
const minute = parts.find(p => p.type === "minute")?.value;
|
|
36
|
+
const second = parts.find(p => p.type === "second")?.value;
|
|
37
|
+
return `${hour}:${minute}:${second}`;
|
|
38
|
+
} catch (e) {
|
|
39
|
+
// Fallback if formatting fails
|
|
40
|
+
return date.toISOString().split("T")[1].split(".")[0];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type { LogLevel, LogEntry, LogEntryListener, LoggerConfig, LogMeta } from "./logger.ts";
|
|
2
|
+
export { logger, onLogEntry, removeLogListener } from "./logger.ts";
|
|
3
|
+
export type { RetryOptions } from "./retry.ts";
|
|
4
|
+
export { sleep, retry, retryWithBackoff } from "./retry.ts";
|
|
5
|
+
export { generateId, generateShortId, hashString, hashObject, hmacSign, hmacVerify } from "./crypto.ts";
|
|
6
|
+
export { encrypt as utilsEncrypt, decrypt as utilsDecrypt } from "./crypto.ts";
|
|
7
|
+
export { Benchmark } from "./benchmark.ts";
|
|
8
|
+
export { getUserDate, getUserTime } from "./date.ts";
|
|
9
|
+
export type { ToonStringifyResult } from "./toon.ts";
|
|
10
|
+
export { estimateTokens, stringify, formatToolResult, formatMCPResponse, formatSkillOutput, formatContext, withToonFormat, reportCompression, getCompressionAnalysis } from "./toon.ts";
|