opc-agent 3.0.1 → 4.0.1

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.
Files changed (216) hide show
  1. package/README.md +404 -74
  2. package/README.zh-CN.md +82 -0
  3. package/dist/channels/dingtalk.d.ts +17 -0
  4. package/dist/channels/dingtalk.js +38 -0
  5. package/dist/channels/googlechat.d.ts +14 -0
  6. package/dist/channels/googlechat.js +37 -0
  7. package/dist/channels/imessage.d.ts +13 -0
  8. package/dist/channels/imessage.js +28 -0
  9. package/dist/channels/irc.d.ts +20 -0
  10. package/dist/channels/irc.js +71 -0
  11. package/dist/channels/line.d.ts +14 -0
  12. package/dist/channels/line.js +28 -0
  13. package/dist/channels/matrix.d.ts +15 -0
  14. package/dist/channels/matrix.js +28 -0
  15. package/dist/channels/mattermost.d.ts +18 -0
  16. package/dist/channels/mattermost.js +49 -0
  17. package/dist/channels/msteams.d.ts +14 -0
  18. package/dist/channels/msteams.js +28 -0
  19. package/dist/channels/nostr.d.ts +14 -0
  20. package/dist/channels/nostr.js +28 -0
  21. package/dist/channels/qq.d.ts +15 -0
  22. package/dist/channels/qq.js +28 -0
  23. package/dist/channels/signal.d.ts +14 -0
  24. package/dist/channels/signal.js +28 -0
  25. package/dist/channels/sms.d.ts +15 -0
  26. package/dist/channels/sms.js +28 -0
  27. package/dist/channels/twitch.d.ts +17 -0
  28. package/dist/channels/twitch.js +59 -0
  29. package/dist/channels/voice-call.d.ts +27 -0
  30. package/dist/channels/voice-call.js +82 -0
  31. package/dist/channels/whatsapp.d.ts +14 -0
  32. package/dist/channels/whatsapp.js +28 -0
  33. package/dist/cli/chat.d.ts +2 -0
  34. package/dist/cli/chat.js +134 -0
  35. package/dist/cli/setup.d.ts +4 -0
  36. package/dist/cli/setup.js +303 -0
  37. package/dist/cli.js +142 -6
  38. package/dist/core/api-server.d.ts +25 -0
  39. package/dist/core/api-server.js +286 -0
  40. package/dist/core/audio.d.ts +50 -0
  41. package/dist/core/audio.js +68 -0
  42. package/dist/core/context-discovery.d.ts +16 -0
  43. package/dist/core/context-discovery.js +107 -0
  44. package/dist/core/context-refs.d.ts +29 -0
  45. package/dist/core/context-refs.js +162 -0
  46. package/dist/core/gateway.d.ts +53 -0
  47. package/dist/core/gateway.js +80 -0
  48. package/dist/core/heartbeat.d.ts +19 -0
  49. package/dist/core/heartbeat.js +50 -0
  50. package/dist/core/hooks.d.ts +28 -0
  51. package/dist/core/hooks.js +82 -0
  52. package/dist/core/ide-bridge.d.ts +53 -0
  53. package/dist/core/ide-bridge.js +97 -0
  54. package/dist/core/node-network.d.ts +23 -0
  55. package/dist/core/node-network.js +77 -0
  56. package/dist/core/profiles.d.ts +27 -0
  57. package/dist/core/profiles.js +131 -0
  58. package/dist/core/sandbox.d.ts +25 -0
  59. package/dist/core/sandbox.js +84 -1
  60. package/dist/core/session-manager.d.ts +33 -0
  61. package/dist/core/session-manager.js +157 -0
  62. package/dist/core/vision.d.ts +45 -0
  63. package/dist/core/vision.js +177 -0
  64. package/dist/hub/brain-seed.d.ts +14 -0
  65. package/dist/hub/brain-seed.js +77 -0
  66. package/dist/hub/client.d.ts +25 -0
  67. package/dist/hub/client.js +44 -0
  68. package/dist/index.d.ts +66 -1
  69. package/dist/index.js +95 -3
  70. package/dist/memory/context-compressor.d.ts +43 -0
  71. package/dist/memory/context-compressor.js +167 -0
  72. package/dist/memory/index.d.ts +4 -0
  73. package/dist/memory/index.js +5 -1
  74. package/dist/memory/user-profiler.d.ts +50 -0
  75. package/dist/memory/user-profiler.js +201 -0
  76. package/dist/providers/index.d.ts +1 -1
  77. package/dist/providers/index.js +54 -1
  78. package/dist/scheduler/cron-engine.d.ts +41 -0
  79. package/dist/scheduler/cron-engine.js +200 -0
  80. package/dist/scheduler/index.d.ts +3 -0
  81. package/dist/scheduler/index.js +7 -0
  82. package/dist/schema/oad.d.ts +12 -12
  83. package/dist/security/approvals.d.ts +53 -0
  84. package/dist/security/approvals.js +115 -0
  85. package/dist/security/elevated.d.ts +41 -0
  86. package/dist/security/elevated.js +89 -0
  87. package/dist/security/index.d.ts +6 -0
  88. package/dist/security/index.js +7 -1
  89. package/dist/security/secrets.d.ts +34 -0
  90. package/dist/security/secrets.js +115 -0
  91. package/dist/skills/builtin/index.d.ts +6 -0
  92. package/dist/skills/builtin/index.js +402 -0
  93. package/dist/skills/marketplace.d.ts +30 -0
  94. package/dist/skills/marketplace.js +142 -0
  95. package/dist/skills/types.d.ts +34 -0
  96. package/dist/skills/types.js +16 -0
  97. package/dist/studio/server.d.ts +25 -0
  98. package/dist/studio/server.js +780 -0
  99. package/dist/studio/templates-data.d.ts +21 -0
  100. package/dist/studio/templates-data.js +148 -0
  101. package/dist/studio-ui/index.html +2502 -1073
  102. package/dist/tools/builtin/browser.d.ts +47 -0
  103. package/dist/tools/builtin/browser.js +284 -0
  104. package/dist/tools/builtin/home-assistant.d.ts +12 -0
  105. package/dist/tools/builtin/home-assistant.js +126 -0
  106. package/dist/tools/builtin/index.d.ts +7 -1
  107. package/dist/tools/builtin/index.js +23 -2
  108. package/dist/tools/builtin/rl-tools.d.ts +13 -0
  109. package/dist/tools/builtin/rl-tools.js +228 -0
  110. package/dist/tools/builtin/vision.d.ts +6 -0
  111. package/dist/tools/builtin/vision.js +61 -0
  112. package/dist/tools/builtin/web-search.d.ts +9 -0
  113. package/dist/tools/builtin/web-search.js +150 -0
  114. package/dist/tools/document-processor.d.ts +39 -0
  115. package/dist/tools/document-processor.js +188 -0
  116. package/dist/tools/image-generator.d.ts +42 -0
  117. package/dist/tools/image-generator.js +136 -0
  118. package/dist/tools/web-scraper.d.ts +20 -0
  119. package/dist/tools/web-scraper.js +148 -0
  120. package/dist/tools/web-search.d.ts +51 -0
  121. package/dist/tools/web-search.js +152 -0
  122. package/install.ps1 +154 -0
  123. package/install.sh +164 -0
  124. package/package.json +63 -52
  125. package/src/channels/dingtalk.ts +46 -0
  126. package/src/channels/googlechat.ts +42 -0
  127. package/src/channels/imessage.ts +32 -0
  128. package/src/channels/irc.ts +82 -0
  129. package/src/channels/line.ts +33 -0
  130. package/src/channels/matrix.ts +34 -0
  131. package/src/channels/mattermost.ts +57 -0
  132. package/src/channels/msteams.ts +33 -0
  133. package/src/channels/nostr.ts +33 -0
  134. package/src/channels/qq.ts +34 -0
  135. package/src/channels/signal.ts +33 -0
  136. package/src/channels/sms.ts +34 -0
  137. package/src/channels/twitch.ts +65 -0
  138. package/src/channels/voice-call.ts +100 -0
  139. package/src/channels/whatsapp.ts +33 -0
  140. package/src/cli/chat.ts +99 -0
  141. package/src/cli/setup.ts +314 -0
  142. package/src/cli.ts +148 -6
  143. package/src/core/api-server.ts +277 -0
  144. package/src/core/audio.ts +98 -0
  145. package/src/core/context-discovery.ts +85 -0
  146. package/src/core/context-refs.ts +140 -0
  147. package/src/core/gateway.ts +106 -0
  148. package/src/core/heartbeat.ts +51 -0
  149. package/src/core/hooks.ts +105 -0
  150. package/src/core/ide-bridge.ts +133 -0
  151. package/src/core/node-network.ts +86 -0
  152. package/src/core/profiles.ts +122 -0
  153. package/src/core/sandbox.ts +100 -0
  154. package/src/core/session-manager.ts +137 -0
  155. package/src/core/vision.ts +180 -0
  156. package/src/hub/brain-seed.ts +54 -0
  157. package/src/hub/client.ts +60 -0
  158. package/src/index.ts +86 -1
  159. package/src/memory/context-compressor.ts +189 -0
  160. package/src/memory/index.ts +4 -0
  161. package/src/memory/user-profiler.ts +215 -0
  162. package/src/providers/index.ts +64 -1
  163. package/src/scheduler/cron-engine.ts +191 -0
  164. package/src/scheduler/index.ts +2 -0
  165. package/src/security/approvals.ts +143 -0
  166. package/src/security/elevated.ts +105 -0
  167. package/src/security/index.ts +6 -0
  168. package/src/security/secrets.ts +129 -0
  169. package/src/skills/builtin/index.ts +408 -0
  170. package/src/skills/marketplace.ts +113 -0
  171. package/src/skills/types.ts +42 -0
  172. package/src/studio/server.ts +1591 -791
  173. package/src/studio/templates-data.ts +178 -0
  174. package/src/studio-ui/index.html +2502 -1073
  175. package/src/tools/builtin/browser.ts +299 -0
  176. package/src/tools/builtin/home-assistant.ts +116 -0
  177. package/src/tools/builtin/index.ts +37 -28
  178. package/src/tools/builtin/rl-tools.ts +243 -0
  179. package/src/tools/builtin/vision.ts +64 -0
  180. package/src/tools/builtin/web-search.ts +126 -0
  181. package/src/tools/document-processor.ts +213 -0
  182. package/src/tools/image-generator.ts +150 -0
  183. package/src/tools/web-scraper.ts +179 -0
  184. package/src/tools/web-search.ts +180 -0
  185. package/tests/api-server.test.ts +148 -0
  186. package/tests/approvals.test.ts +89 -0
  187. package/tests/audio.test.ts +40 -0
  188. package/tests/browser.test.ts +179 -0
  189. package/tests/builtin-tools.test.ts +83 -83
  190. package/tests/channels-extra.test.ts +45 -0
  191. package/tests/context-compressor.test.ts +172 -0
  192. package/tests/context-refs.test.ts +121 -0
  193. package/tests/cron-engine.test.ts +101 -0
  194. package/tests/document-processor.test.ts +69 -0
  195. package/tests/e2e-nocode.test.ts +442 -0
  196. package/tests/elevated.test.ts +69 -0
  197. package/tests/gateway.test.ts +63 -71
  198. package/tests/home-assistant.test.ts +40 -0
  199. package/tests/hooks.test.ts +79 -0
  200. package/tests/ide-bridge.test.ts +38 -0
  201. package/tests/image-generator.test.ts +84 -0
  202. package/tests/node-network.test.ts +74 -0
  203. package/tests/profiles.test.ts +61 -0
  204. package/tests/rl-tools.test.ts +93 -0
  205. package/tests/sandbox-manager.test.ts +46 -0
  206. package/tests/secrets.test.ts +107 -0
  207. package/tests/settings-api.test.ts +148 -0
  208. package/tests/setup.test.ts +73 -0
  209. package/tests/studio.test.ts +402 -229
  210. package/tests/tools/builtin-extended.test.ts +138 -138
  211. package/tests/user-profiler.test.ts +169 -0
  212. package/tests/v090-features.test.ts +254 -0
  213. package/tests/vision.test.ts +61 -0
  214. package/tests/voice-call.test.ts +47 -0
  215. package/tests/voice-interaction.test.ts +38 -0
  216. package/tests/web-search.test.ts +155 -0
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Image Generator — multi-backend image generation tool.
3
+ * Supports DALL·E (OpenAI), Stable Diffusion (local), and Replicate.
4
+ */
5
+ export interface ImageGenConfig {
6
+ provider?: 'dalle' | 'stable-diffusion' | 'replicate';
7
+ openaiApiKey?: string;
8
+ replicateApiKey?: string;
9
+ sdApiUrl?: string;
10
+ defaultModel?: string;
11
+ defaultSize?: string;
12
+ }
13
+ export interface ImageGenResult {
14
+ success: boolean;
15
+ url?: string;
16
+ base64?: string;
17
+ error?: string;
18
+ provider: string;
19
+ }
20
+ export declare class ImageGenerator {
21
+ private config;
22
+ constructor(config?: ImageGenConfig);
23
+ /** Auto-detect best available provider */
24
+ detectProvider(): string | null;
25
+ /** Get configuration status for the settings UI */
26
+ getStatus(): {
27
+ configured: boolean;
28
+ providers: {
29
+ name: string;
30
+ configured: boolean;
31
+ }[];
32
+ };
33
+ generate(prompt: string, options?: {
34
+ provider?: string;
35
+ size?: string;
36
+ model?: string;
37
+ }): Promise<ImageGenResult>;
38
+ private generateDalle;
39
+ private generateSD;
40
+ private generateReplicate;
41
+ }
42
+ //# sourceMappingURL=image-generator.d.ts.map
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ /**
3
+ * Image Generator — multi-backend image generation tool.
4
+ * Supports DALL·E (OpenAI), Stable Diffusion (local), and Replicate.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.ImageGenerator = void 0;
8
+ class ImageGenerator {
9
+ config;
10
+ constructor(config) {
11
+ this.config = {
12
+ provider: config?.provider,
13
+ openaiApiKey: config?.openaiApiKey || process.env.OPENAI_API_KEY,
14
+ replicateApiKey: config?.replicateApiKey || process.env.REPLICATE_API_TOKEN,
15
+ sdApiUrl: config?.sdApiUrl || process.env.SD_API_URL,
16
+ defaultModel: config?.defaultModel || 'dall-e-3',
17
+ defaultSize: config?.defaultSize || '1024x1024',
18
+ };
19
+ }
20
+ /** Auto-detect best available provider */
21
+ detectProvider() {
22
+ if (this.config.openaiApiKey)
23
+ return 'dalle';
24
+ if (this.config.sdApiUrl)
25
+ return 'stable-diffusion';
26
+ if (this.config.replicateApiKey)
27
+ return 'replicate';
28
+ return null;
29
+ }
30
+ /** Get configuration status for the settings UI */
31
+ getStatus() {
32
+ return {
33
+ configured: !!this.detectProvider(),
34
+ providers: [
35
+ { name: 'dalle', configured: !!this.config.openaiApiKey },
36
+ { name: 'stable-diffusion', configured: !!this.config.sdApiUrl },
37
+ { name: 'replicate', configured: !!this.config.replicateApiKey },
38
+ ],
39
+ };
40
+ }
41
+ async generate(prompt, options) {
42
+ const provider = options?.provider || this.config.provider || this.detectProvider();
43
+ if (!provider) {
44
+ return { success: false, error: 'No image generation provider configured. Please set OPENAI_API_KEY, SD_API_URL, or REPLICATE_API_TOKEN.', provider: 'none' };
45
+ }
46
+ switch (provider) {
47
+ case 'dalle': return this.generateDalle(prompt, options);
48
+ case 'stable-diffusion': return this.generateSD(prompt, options);
49
+ case 'replicate': return this.generateReplicate(prompt, options);
50
+ default: return { success: false, error: `Unknown provider: ${provider}`, provider };
51
+ }
52
+ }
53
+ async generateDalle(prompt, options) {
54
+ const apiKey = this.config.openaiApiKey;
55
+ if (!apiKey)
56
+ return { success: false, error: 'OPENAI_API_KEY not configured', provider: 'dalle' };
57
+ try {
58
+ const res = await fetch('https://api.openai.com/v1/images/generations', {
59
+ method: 'POST',
60
+ headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
61
+ body: JSON.stringify({
62
+ model: options?.model || this.config.defaultModel || 'dall-e-3',
63
+ prompt,
64
+ size: options?.size || this.config.defaultSize || '1024x1024',
65
+ n: 1,
66
+ }),
67
+ });
68
+ const data = await res.json();
69
+ if (data.error)
70
+ return { success: false, error: data.error.message, provider: 'dalle' };
71
+ const url = data.data?.[0]?.url;
72
+ return url ? { success: true, url, provider: 'dalle' } : { success: false, error: 'No image returned', provider: 'dalle' };
73
+ }
74
+ catch (err) {
75
+ return { success: false, error: err.message, provider: 'dalle' };
76
+ }
77
+ }
78
+ async generateSD(prompt, options) {
79
+ const apiUrl = this.config.sdApiUrl;
80
+ if (!apiUrl)
81
+ return { success: false, error: 'SD_API_URL not configured', provider: 'stable-diffusion' };
82
+ try {
83
+ const [w, h] = (options?.size || '1024x1024').split('x').map(Number);
84
+ const res = await fetch(`${apiUrl}/sdapi/v1/txt2img`, {
85
+ method: 'POST',
86
+ headers: { 'Content-Type': 'application/json' },
87
+ body: JSON.stringify({ prompt, width: w || 1024, height: h || 1024 }),
88
+ });
89
+ const data = await res.json();
90
+ if (data.images?.length) {
91
+ return { success: true, base64: data.images[0], provider: 'stable-diffusion' };
92
+ }
93
+ return { success: false, error: 'No image generated', provider: 'stable-diffusion' };
94
+ }
95
+ catch (err) {
96
+ return { success: false, error: err.message, provider: 'stable-diffusion' };
97
+ }
98
+ }
99
+ async generateReplicate(prompt, _options) {
100
+ const apiKey = this.config.replicateApiKey;
101
+ if (!apiKey)
102
+ return { success: false, error: 'REPLICATE_API_TOKEN not configured', provider: 'replicate' };
103
+ try {
104
+ const res = await fetch('https://api.replicate.com/v1/predictions', {
105
+ method: 'POST',
106
+ headers: { 'Authorization': `Token ${apiKey}`, 'Content-Type': 'application/json' },
107
+ body: JSON.stringify({
108
+ version: 'stability-ai/sdxl:39ed52f2a78e934b3ba6e2a89f5b1c712de7dfea535525255b1aa35c5565e08b',
109
+ input: { prompt },
110
+ }),
111
+ });
112
+ const prediction = await res.json();
113
+ if (prediction.error)
114
+ return { success: false, error: prediction.error, provider: 'replicate' };
115
+ // Poll for completion (max 60s)
116
+ const getUrl = prediction.urls?.get || `https://api.replicate.com/v1/predictions/${prediction.id}`;
117
+ for (let i = 0; i < 30; i++) {
118
+ await new Promise(r => setTimeout(r, 2000));
119
+ const poll = await fetch(getUrl, { headers: { 'Authorization': `Token ${apiKey}` } });
120
+ const result = await poll.json();
121
+ if (result.status === 'succeeded' && result.output?.length) {
122
+ return { success: true, url: result.output[0], provider: 'replicate' };
123
+ }
124
+ if (result.status === 'failed') {
125
+ return { success: false, error: result.error || 'Generation failed', provider: 'replicate' };
126
+ }
127
+ }
128
+ return { success: false, error: 'Timeout waiting for image generation', provider: 'replicate' };
129
+ }
130
+ catch (err) {
131
+ return { success: false, error: err.message, provider: 'replicate' };
132
+ }
133
+ }
134
+ }
135
+ exports.ImageGenerator = ImageGenerator;
136
+ //# sourceMappingURL=image-generator.js.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Web Scraper - v0.10.0
3
+ * Fetch URL content and extract readable text in markdown format.
4
+ * Uses a simple readability-style extraction (no external dependencies).
5
+ */
6
+ export interface ScrapedContent {
7
+ title: string;
8
+ content: string;
9
+ url: string;
10
+ wordCount: number;
11
+ }
12
+ /**
13
+ * Fetch a URL and extract readable content as markdown.
14
+ */
15
+ export declare function scrapeUrl(url: string, maxLength?: number): Promise<ScrapedContent>;
16
+ /**
17
+ * Extract readable content from HTML using simple heuristics.
18
+ */
19
+ export declare function extractReadableContent(html: string, url: string, maxLength?: number): ScrapedContent;
20
+ //# sourceMappingURL=web-scraper.d.ts.map
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ /**
3
+ * Web Scraper - v0.10.0
4
+ * Fetch URL content and extract readable text in markdown format.
5
+ * Uses a simple readability-style extraction (no external dependencies).
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.scrapeUrl = scrapeUrl;
9
+ exports.extractReadableContent = extractReadableContent;
10
+ const MAX_CONTENT_LENGTH = 5000;
11
+ /**
12
+ * Fetch a URL and extract readable content as markdown.
13
+ */
14
+ async function scrapeUrl(url, maxLength = MAX_CONTENT_LENGTH) {
15
+ const response = await fetch(url, {
16
+ headers: {
17
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
18
+ Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
19
+ },
20
+ signal: AbortSignal.timeout(15000),
21
+ redirect: 'follow',
22
+ });
23
+ const contentType = response.headers.get('content-type') || '';
24
+ const text = await response.text();
25
+ // If not HTML, return raw text
26
+ if (!contentType.includes('html')) {
27
+ const truncated = text.slice(0, maxLength);
28
+ return {
29
+ title: url,
30
+ content: truncated,
31
+ url,
32
+ wordCount: truncated.split(/\s+/).length,
33
+ };
34
+ }
35
+ return extractReadableContent(text, url, maxLength);
36
+ }
37
+ /**
38
+ * Extract readable content from HTML using simple heuristics.
39
+ */
40
+ function extractReadableContent(html, url, maxLength = MAX_CONTENT_LENGTH) {
41
+ // Extract title
42
+ const titleMatch = html.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
43
+ const title = titleMatch ? decodeEntities(titleMatch[1]).trim() : url;
44
+ // Remove non-content elements
45
+ let content = html;
46
+ // Remove script, style, nav, header, footer, aside, iframe
47
+ const removePatterns = [
48
+ /<script[\s\S]*?<\/script>/gi,
49
+ /<style[\s\S]*?<\/style>/gi,
50
+ /<nav[\s\S]*?<\/nav>/gi,
51
+ /<footer[\s\S]*?<\/footer>/gi,
52
+ /<aside[\s\S]*?<\/aside>/gi,
53
+ /<iframe[\s\S]*?<\/iframe>/gi,
54
+ /<noscript[\s\S]*?<\/noscript>/gi,
55
+ /<!--[\s\S]*?-->/g,
56
+ ];
57
+ for (const pattern of removePatterns) {
58
+ content = content.replace(pattern, '');
59
+ }
60
+ // Try to find main content area
61
+ const mainContent = findMainContent(content);
62
+ content = mainContent || content;
63
+ // Convert to markdown-ish text
64
+ content = htmlToMarkdown(content);
65
+ // Clean up whitespace
66
+ content = content
67
+ .replace(/\n{3,}/g, '\n\n')
68
+ .replace(/[ \t]+/g, ' ')
69
+ .trim();
70
+ // Truncate
71
+ if (content.length > maxLength) {
72
+ content = content.slice(0, maxLength) + '\n\n...[truncated]';
73
+ }
74
+ return {
75
+ title,
76
+ content,
77
+ url,
78
+ wordCount: content.split(/\s+/).filter(Boolean).length,
79
+ };
80
+ }
81
+ /**
82
+ * Try to find the main content area of the page.
83
+ */
84
+ function findMainContent(html) {
85
+ // Try common content selectors
86
+ const patterns = [
87
+ /<article[^>]*>([\s\S]*?)<\/article>/i,
88
+ /<main[^>]*>([\s\S]*?)<\/main>/i,
89
+ /<div[^>]*class="[^"]*(?:content|article|post|entry|main)[^"]*"[^>]*>([\s\S]*?)<\/div>/i,
90
+ /<div[^>]*id="[^"]*(?:content|article|post|entry|main)[^"]*"[^>]*>([\s\S]*?)<\/div>/i,
91
+ ];
92
+ for (const pattern of patterns) {
93
+ const match = html.match(pattern);
94
+ if (match && match[1] && match[1].length > 200) {
95
+ return match[1];
96
+ }
97
+ }
98
+ // Fallback: find body content
99
+ const bodyMatch = html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
100
+ return bodyMatch ? bodyMatch[1] : null;
101
+ }
102
+ /**
103
+ * Simple HTML to Markdown conversion.
104
+ */
105
+ function htmlToMarkdown(html) {
106
+ let md = html;
107
+ // Headers
108
+ md = md.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, '\n# $1\n');
109
+ md = md.replace(/<h2[^>]*>([\s\S]*?)<\/h2>/gi, '\n## $1\n');
110
+ md = md.replace(/<h3[^>]*>([\s\S]*?)<\/h3>/gi, '\n### $1\n');
111
+ md = md.replace(/<h4[^>]*>([\s\S]*?)<\/h4>/gi, '\n#### $1\n');
112
+ md = md.replace(/<h5[^>]*>([\s\S]*?)<\/h5>/gi, '\n##### $1\n');
113
+ md = md.replace(/<h6[^>]*>([\s\S]*?)<\/h6>/gi, '\n###### $1\n');
114
+ // Paragraphs and line breaks
115
+ md = md.replace(/<p[^>]*>/gi, '\n');
116
+ md = md.replace(/<\/p>/gi, '\n');
117
+ md = md.replace(/<br\s*\/?>/gi, '\n');
118
+ // Links
119
+ md = md.replace(/<a[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi, '[$2]($1)');
120
+ // Bold and italic
121
+ md = md.replace(/<(?:strong|b)[^>]*>([\s\S]*?)<\/(?:strong|b)>/gi, '**$1**');
122
+ md = md.replace(/<(?:em|i)[^>]*>([\s\S]*?)<\/(?:em|i)>/gi, '*$1*');
123
+ // Code
124
+ md = md.replace(/<code[^>]*>([\s\S]*?)<\/code>/gi, '`$1`');
125
+ md = md.replace(/<pre[^>]*>([\s\S]*?)<\/pre>/gi, '\n```\n$1\n```\n');
126
+ // Lists
127
+ md = md.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, '- $1\n');
128
+ // Blockquote
129
+ md = md.replace(/<blockquote[^>]*>([\s\S]*?)<\/blockquote>/gi, '\n> $1\n');
130
+ // Remove remaining HTML tags
131
+ md = md.replace(/<[^>]+>/g, '');
132
+ // Decode entities
133
+ md = decodeEntities(md);
134
+ return md;
135
+ }
136
+ function decodeEntities(text) {
137
+ return text
138
+ .replace(/&amp;/g, '&')
139
+ .replace(/&lt;/g, '<')
140
+ .replace(/&gt;/g, '>')
141
+ .replace(/&quot;/g, '"')
142
+ .replace(/&#x27;/g, "'")
143
+ .replace(/&#39;/g, "'")
144
+ .replace(/&nbsp;/g, ' ')
145
+ .replace(/&#(\d+);/g, (_, n) => String.fromCharCode(parseInt(n)))
146
+ .replace(/&#x([0-9a-fA-F]+);/g, (_, n) => String.fromCharCode(parseInt(n, 16)));
147
+ }
148
+ //# sourceMappingURL=web-scraper.js.map
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Web Search Engine Manager - v0.10.0
3
+ * Supports multiple search backends with automatic fallback.
4
+ * Default: DuckDuckGo (free, no API key required).
5
+ */
6
+ export interface SearchResult {
7
+ title: string;
8
+ url: string;
9
+ snippet: string;
10
+ }
11
+ export interface SearchOptions {
12
+ maxResults?: number;
13
+ engine?: SearchEngine;
14
+ }
15
+ export type SearchEngine = 'duckduckgo' | 'brave' | 'searxng' | 'google';
16
+ export interface SearchEngineConfig {
17
+ enabled: boolean;
18
+ apiKey?: string;
19
+ baseUrl?: string;
20
+ }
21
+ export interface WebSearchConfig {
22
+ defaultEngine: SearchEngine;
23
+ enabled: boolean;
24
+ engines: Partial<Record<SearchEngine, SearchEngineConfig>>;
25
+ }
26
+ export declare const DEFAULT_SEARCH_CONFIG: WebSearchConfig;
27
+ /**
28
+ * Parse DuckDuckGo HTML search results.
29
+ */
30
+ export declare function parseDuckDuckGoHTML(html: string): SearchResult[];
31
+ /**
32
+ * Search using DuckDuckGo HTML interface (no API key needed).
33
+ */
34
+ export declare function searchDuckDuckGo(query: string, maxResults?: number): Promise<SearchResult[]>;
35
+ /**
36
+ * Search using Brave Search API.
37
+ */
38
+ export declare function searchBrave(query: string, apiKey: string, maxResults?: number): Promise<SearchResult[]>;
39
+ /**
40
+ * Search using SearXNG instance.
41
+ */
42
+ export declare function searchSearXNG(query: string, baseUrl: string, maxResults?: number): Promise<SearchResult[]>;
43
+ /**
44
+ * Search using Google Custom Search API.
45
+ */
46
+ export declare function searchGoogle(query: string, apiKey: string, maxResults?: number): Promise<SearchResult[]>;
47
+ /**
48
+ * Unified search function with fallback.
49
+ */
50
+ export declare function webSearch(query: string, config?: WebSearchConfig, options?: SearchOptions): Promise<SearchResult[]>;
51
+ //# sourceMappingURL=web-search.d.ts.map
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ /**
3
+ * Web Search Engine Manager - v0.10.0
4
+ * Supports multiple search backends with automatic fallback.
5
+ * Default: DuckDuckGo (free, no API key required).
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.DEFAULT_SEARCH_CONFIG = void 0;
9
+ exports.parseDuckDuckGoHTML = parseDuckDuckGoHTML;
10
+ exports.searchDuckDuckGo = searchDuckDuckGo;
11
+ exports.searchBrave = searchBrave;
12
+ exports.searchSearXNG = searchSearXNG;
13
+ exports.searchGoogle = searchGoogle;
14
+ exports.webSearch = webSearch;
15
+ exports.DEFAULT_SEARCH_CONFIG = {
16
+ defaultEngine: 'duckduckgo',
17
+ enabled: true,
18
+ engines: {
19
+ duckduckgo: { enabled: true },
20
+ },
21
+ };
22
+ /**
23
+ * Parse DuckDuckGo HTML search results.
24
+ */
25
+ function parseDuckDuckGoHTML(html) {
26
+ const results = [];
27
+ // Match result blocks: <a class="result__a" href="...">title</a> ... <a class="result__snippet">snippet</a>
28
+ const resultBlocks = html.split(/class="result__body"/);
29
+ for (let i = 1; i < resultBlocks.length && results.length < 10; i++) {
30
+ const block = resultBlocks[i];
31
+ // Extract URL and title from result__a
32
+ const linkMatch = block.match(/class="result__a"[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/);
33
+ if (!linkMatch)
34
+ continue;
35
+ let url = linkMatch[1];
36
+ const title = stripHTML(linkMatch[2]).trim();
37
+ // DuckDuckGo wraps URLs in redirect, extract actual URL
38
+ const uddgMatch = url.match(/[?&]uddg=([^&]+)/);
39
+ if (uddgMatch) {
40
+ url = decodeURIComponent(uddgMatch[1]);
41
+ }
42
+ // Extract snippet
43
+ const snippetMatch = block.match(/class="result__snippet"[^>]*>([\s\S]*?)<\/a>/);
44
+ const snippet = snippetMatch ? stripHTML(snippetMatch[1]).trim() : '';
45
+ if (title && url) {
46
+ results.push({ title, url, snippet });
47
+ }
48
+ }
49
+ return results;
50
+ }
51
+ /**
52
+ * Search using DuckDuckGo HTML interface (no API key needed).
53
+ */
54
+ async function searchDuckDuckGo(query, maxResults = 5) {
55
+ const url = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
56
+ const response = await fetch(url, {
57
+ headers: {
58
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
59
+ },
60
+ signal: AbortSignal.timeout(15000),
61
+ });
62
+ const html = await response.text();
63
+ return parseDuckDuckGoHTML(html).slice(0, maxResults);
64
+ }
65
+ /**
66
+ * Search using Brave Search API.
67
+ */
68
+ async function searchBrave(query, apiKey, maxResults = 5) {
69
+ const url = `https://api.search.brave.com/res/v1/web/search?q=${encodeURIComponent(query)}&count=${maxResults}`;
70
+ const response = await fetch(url, {
71
+ headers: { 'X-Subscription-Token': apiKey, Accept: 'application/json' },
72
+ signal: AbortSignal.timeout(15000),
73
+ });
74
+ const data = await response.json();
75
+ return (data.web?.results || []).slice(0, maxResults).map((r) => ({
76
+ title: r.title || '',
77
+ url: r.url || '',
78
+ snippet: r.description || '',
79
+ }));
80
+ }
81
+ /**
82
+ * Search using SearXNG instance.
83
+ */
84
+ async function searchSearXNG(query, baseUrl, maxResults = 5) {
85
+ const url = `${baseUrl.replace(/\/$/, '')}/search?q=${encodeURIComponent(query)}&format=json`;
86
+ const response = await fetch(url, { signal: AbortSignal.timeout(15000) });
87
+ const data = await response.json();
88
+ return (data.results || []).slice(0, maxResults).map((r) => ({
89
+ title: r.title || '',
90
+ url: r.url || '',
91
+ snippet: r.content || '',
92
+ }));
93
+ }
94
+ /**
95
+ * Search using Google Custom Search API.
96
+ */
97
+ async function searchGoogle(query, apiKey, maxResults = 5) {
98
+ // apiKey format: "key:cx" (API key and Custom Search Engine ID)
99
+ const [key, cx] = apiKey.split(':');
100
+ const url = `https://www.googleapis.com/customsearch/v1?q=${encodeURIComponent(query)}&key=${key}&cx=${cx}&num=${maxResults}`;
101
+ const response = await fetch(url, { signal: AbortSignal.timeout(15000) });
102
+ const data = await response.json();
103
+ return (data.items || []).slice(0, maxResults).map((r) => ({
104
+ title: r.title || '',
105
+ url: r.link || '',
106
+ snippet: r.snippet || '',
107
+ }));
108
+ }
109
+ /**
110
+ * Unified search function with fallback.
111
+ */
112
+ async function webSearch(query, config, options) {
113
+ const cfg = config || exports.DEFAULT_SEARCH_CONFIG;
114
+ if (!cfg.enabled)
115
+ return [];
116
+ const maxResults = options?.maxResults || 5;
117
+ const engine = options?.engine || cfg.defaultEngine;
118
+ // Try requested engine first, then fallback chain
119
+ const fallbackOrder = [engine, 'duckduckgo', 'brave', 'searxng', 'google']
120
+ .filter((e, i, arr) => arr.indexOf(e) === i);
121
+ for (const eng of fallbackOrder) {
122
+ const engCfg = cfg.engines[eng];
123
+ if (engCfg && !engCfg.enabled)
124
+ continue;
125
+ try {
126
+ switch (eng) {
127
+ case 'duckduckgo':
128
+ return await searchDuckDuckGo(query, maxResults);
129
+ case 'brave':
130
+ if (engCfg?.apiKey)
131
+ return await searchBrave(query, engCfg.apiKey, maxResults);
132
+ continue;
133
+ case 'searxng':
134
+ if (engCfg?.baseUrl)
135
+ return await searchSearXNG(query, engCfg.baseUrl, maxResults);
136
+ continue;
137
+ case 'google':
138
+ if (engCfg?.apiKey)
139
+ return await searchGoogle(query, engCfg.apiKey, maxResults);
140
+ continue;
141
+ }
142
+ }
143
+ catch {
144
+ continue; // Fallback to next engine
145
+ }
146
+ }
147
+ return [];
148
+ }
149
+ function stripHTML(html) {
150
+ return html.replace(/<[^>]+>/g, '').replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"').replace(/&#x27;/g, "'").replace(/&nbsp;/g, ' ');
151
+ }
152
+ //# sourceMappingURL=web-search.js.map