aiden-runtime 3.16.0
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/LICENSE +661 -0
- package/README.md +465 -0
- package/config/devos.config.json +186 -0
- package/config/hardware.json +9 -0
- package/config/model-selection.json +7 -0
- package/config/setup-complete.json +20 -0
- package/dist/api/routes/computerUse.js +112 -0
- package/dist/api/server.js +6870 -0
- package/dist/bin/npx-init.js +71 -0
- package/dist/coordination/commandGate.js +115 -0
- package/dist/coordination/livePulse.js +127 -0
- package/dist/core/agentLoop.js +2718 -0
- package/dist/core/agentShield.js +231 -0
- package/dist/core/aidenIdentity.js +215 -0
- package/dist/core/aidenPersonality.js +166 -0
- package/dist/core/aidenSdk.js +374 -0
- package/dist/core/asyncTasks.js +82 -0
- package/dist/core/auditTrail.js +61 -0
- package/dist/core/auxiliaryClient.js +114 -0
- package/dist/core/bgLLM.js +108 -0
- package/dist/core/bm25.js +68 -0
- package/dist/core/callbackSystem.js +64 -0
- package/dist/core/channels/adapter.js +6 -0
- package/dist/core/channels/discord.js +173 -0
- package/dist/core/channels/email.js +253 -0
- package/dist/core/channels/imessage.js +164 -0
- package/dist/core/channels/manager.js +96 -0
- package/dist/core/channels/signal.js +140 -0
- package/dist/core/channels/slack.js +139 -0
- package/dist/core/channels/twilio.js +144 -0
- package/dist/core/channels/webhook.js +186 -0
- package/dist/core/channels/whatsapp.js +185 -0
- package/dist/core/clarifyBus.js +75 -0
- package/dist/core/codeInterpreter.js +82 -0
- package/dist/core/computerControl.js +439 -0
- package/dist/core/conversationMemory.js +334 -0
- package/dist/core/costTracker.js +221 -0
- package/dist/core/cronManager.js +217 -0
- package/dist/core/deepKB.js +77 -0
- package/dist/core/doctor.js +279 -0
- package/dist/core/dreamEngine.js +334 -0
- package/dist/core/entityGraph.js +169 -0
- package/dist/core/eventBus.js +16 -0
- package/dist/core/evolutionAnalyzer.js +153 -0
- package/dist/core/executionLoop.js +309 -0
- package/dist/core/executor.js +224 -0
- package/dist/core/failureAnalyzer.js +166 -0
- package/dist/core/fastPathExpansion.js +82 -0
- package/dist/core/faultEngine.js +106 -0
- package/dist/core/featureGates.js +70 -0
- package/dist/core/fileIngestion.js +113 -0
- package/dist/core/gateway.js +97 -0
- package/dist/core/goalTracker.js +75 -0
- package/dist/core/growthEngine.js +168 -0
- package/dist/core/hardwareDetector.js +98 -0
- package/dist/core/hooks.js +45 -0
- package/dist/core/httpKeepalive.js +46 -0
- package/dist/core/hybridSearch.js +101 -0
- package/dist/core/importers.js +164 -0
- package/dist/core/instinctSystem.js +223 -0
- package/dist/core/knowledgeBase.js +351 -0
- package/dist/core/learningMemory.js +121 -0
- package/dist/core/lessonsBrowser.js +125 -0
- package/dist/core/licenseManager.js +399 -0
- package/dist/core/logBuffer.js +85 -0
- package/dist/core/machineId.js +87 -0
- package/dist/core/mcpClient.js +442 -0
- package/dist/core/memoryDistiller.js +165 -0
- package/dist/core/memoryExtractor.js +212 -0
- package/dist/core/memoryIds.js +213 -0
- package/dist/core/memoryPreamble.js +113 -0
- package/dist/core/memoryQuery.js +136 -0
- package/dist/core/memoryRecall.js +140 -0
- package/dist/core/memoryStrategy.js +201 -0
- package/dist/core/messageValidator.js +85 -0
- package/dist/core/modelDiscovery.js +108 -0
- package/dist/core/modelRouter.js +118 -0
- package/dist/core/morningBriefing.js +203 -0
- package/dist/core/multiGoalValidator.js +51 -0
- package/dist/core/parallelExecutor.js +43 -0
- package/dist/core/passiveSkillObserver.js +204 -0
- package/dist/core/paths.js +57 -0
- package/dist/core/patternDetector.js +83 -0
- package/dist/core/planResponseRepair.js +64 -0
- package/dist/core/planTool.js +111 -0
- package/dist/core/playwrightBridge.js +356 -0
- package/dist/core/pluginSystem.js +121 -0
- package/dist/core/privateMode.js +85 -0
- package/dist/core/reactLoop.js +156 -0
- package/dist/core/recipeEngine.js +166 -0
- package/dist/core/responseCache.js +128 -0
- package/dist/core/runSandbox.js +132 -0
- package/dist/core/sandboxRunner.js +200 -0
- package/dist/core/scheduler.js +543 -0
- package/dist/core/secretScanner.js +49 -0
- package/dist/core/semanticMemory.js +223 -0
- package/dist/core/sessionMemory.js +259 -0
- package/dist/core/sessionRouter.js +91 -0
- package/dist/core/sessionSearch.js +163 -0
- package/dist/core/setupWizard.js +225 -0
- package/dist/core/skillImporter.js +303 -0
- package/dist/core/skillLibrary.js +144 -0
- package/dist/core/skillLoader.js +471 -0
- package/dist/core/skillTeacher.js +352 -0
- package/dist/core/skillValidator.js +210 -0
- package/dist/core/skillWriter.js +384 -0
- package/dist/core/slashAsTool.js +226 -0
- package/dist/core/spawnManager.js +197 -0
- package/dist/core/statusVerbs.js +43 -0
- package/dist/core/swarmManager.js +109 -0
- package/dist/core/taskQueue.js +119 -0
- package/dist/core/taskRecovery.js +128 -0
- package/dist/core/taskState.js +168 -0
- package/dist/core/telegramBot.js +152 -0
- package/dist/core/todoManager.js +70 -0
- package/dist/core/toolNameRepair.js +71 -0
- package/dist/core/toolRegistry.js +2730 -0
- package/dist/core/tools/calendarTool.js +98 -0
- package/dist/core/tools/companyFilingsTool.js +98 -0
- package/dist/core/tools/gmailTool.js +87 -0
- package/dist/core/tools/marketDataTool.js +135 -0
- package/dist/core/tools/socialResearchTool.js +121 -0
- package/dist/core/truthCheck.js +57 -0
- package/dist/core/updateChecker.js +74 -0
- package/dist/core/userCognitionProfile.js +238 -0
- package/dist/core/userProfile.js +341 -0
- package/dist/core/version.js +5 -0
- package/dist/core/visionAnalyze.js +161 -0
- package/dist/core/voice/audio.js +187 -0
- package/dist/core/voice/stt.js +226 -0
- package/dist/core/voice/tts.js +310 -0
- package/dist/core/voiceInput.js +118 -0
- package/dist/core/voiceOutput.js +130 -0
- package/dist/core/webSearch.js +326 -0
- package/dist/core/workflowTracker.js +72 -0
- package/dist/core/workspaceMemory.js +54 -0
- package/dist/core/youtubeTranscript.js +224 -0
- package/dist/integrations/computerUse/apiRegistry.js +113 -0
- package/dist/integrations/computerUse/screenAgent.js +203 -0
- package/dist/integrations/computerUse/visionLoop.js +296 -0
- package/dist/memory/memoryLayers.js +143 -0
- package/dist/providers/boa.js +93 -0
- package/dist/providers/cerebras.js +70 -0
- package/dist/providers/custom.js +89 -0
- package/dist/providers/gemini.js +82 -0
- package/dist/providers/groq.js +92 -0
- package/dist/providers/index.js +149 -0
- package/dist/providers/nvidia.js +70 -0
- package/dist/providers/ollama.js +99 -0
- package/dist/providers/openrouter.js +74 -0
- package/dist/providers/router.js +497 -0
- package/dist/providers/types.js +6 -0
- package/dist/security/browserVault.js +129 -0
- package/dist/security/dataGuard.js +89 -0
- package/dist/tools/eonetTool.js +72 -0
- package/dist/types/computerUse.js +2 -0
- package/dist/types/executor.js +2 -0
- package/dist-bundle/cli.js +357859 -0
- package/package.json +256 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// DevOS — Autonomous AI Execution System
|
|
4
|
+
// Copyright (c) 2026 Shiva Deore. All rights reserved.
|
|
5
|
+
// ============================================================
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.reliableWebSearch = reliableWebSearch;
|
|
8
|
+
exports.deepResearch = deepResearch;
|
|
9
|
+
exports.checkSearxNG = checkSearxNG;
|
|
10
|
+
// ── Constants ─────────────────────────────────────────────────
|
|
11
|
+
const SEARXNG_URL = process.env.SEARXNG_URL || 'http://localhost:8888';
|
|
12
|
+
const BRAVE_API_KEY = process.env.BRAVE_SEARCH_API_KEY || '';
|
|
13
|
+
const SEARCH_TIMEOUT = 10000;
|
|
14
|
+
const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36';
|
|
15
|
+
// ── METHOD 1: SearxNG ──────────────────────────────────────────
|
|
16
|
+
async function searchViaSearxNG(query) {
|
|
17
|
+
try {
|
|
18
|
+
const url = `${SEARXNG_URL}/search?q=${encodeURIComponent(query)}&format=json&language=en&categories=general`;
|
|
19
|
+
const res = await fetch(url, {
|
|
20
|
+
headers: { 'User-Agent': USER_AGENT, 'Accept': 'application/json' },
|
|
21
|
+
signal: AbortSignal.timeout(SEARCH_TIMEOUT),
|
|
22
|
+
});
|
|
23
|
+
if (!res.ok) {
|
|
24
|
+
console.warn(`[webSearch] SearxNG returned ${res.status}`);
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const data = await res.json();
|
|
28
|
+
const results = (data.results || []).slice(0, 10).map((r) => ({
|
|
29
|
+
title: r.title || '',
|
|
30
|
+
url: r.url || '',
|
|
31
|
+
snippet: r.content || '',
|
|
32
|
+
source: 'searxng',
|
|
33
|
+
}));
|
|
34
|
+
if (results.length === 0)
|
|
35
|
+
return null;
|
|
36
|
+
const lines = results.map(r => `**${r.title}**\n${r.snippet}\n${r.url}`);
|
|
37
|
+
const output = `[SearxNG Results for "${query}"]\n\n${lines.join('\n\n')}`;
|
|
38
|
+
console.log(`[webSearch] SearxNG: ${results.length} results`);
|
|
39
|
+
return { success: true, output, method: 'searxng', results };
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
console.warn(`[webSearch] SearxNG failed: ${e.message}`);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// ── METHOD 2: Brave Search API ────────────────────────────────
|
|
47
|
+
async function searchViaBrave(query) {
|
|
48
|
+
if (!BRAVE_API_KEY)
|
|
49
|
+
return null;
|
|
50
|
+
try {
|
|
51
|
+
const url = `https://api.search.brave.com/res/v1/web/search?q=${encodeURIComponent(query)}&count=10`;
|
|
52
|
+
const res = await fetch(url, {
|
|
53
|
+
headers: {
|
|
54
|
+
'Accept': 'application/json',
|
|
55
|
+
'Accept-Encoding': 'gzip',
|
|
56
|
+
'X-Subscription-Token': BRAVE_API_KEY,
|
|
57
|
+
},
|
|
58
|
+
signal: AbortSignal.timeout(SEARCH_TIMEOUT),
|
|
59
|
+
});
|
|
60
|
+
if (!res.ok) {
|
|
61
|
+
console.warn(`[webSearch] Brave API returned ${res.status}`);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const data = await res.json();
|
|
65
|
+
const webHits = data?.web?.results || [];
|
|
66
|
+
if (webHits.length === 0)
|
|
67
|
+
return null;
|
|
68
|
+
const results = webHits.map(r => ({
|
|
69
|
+
title: r.title || '',
|
|
70
|
+
url: r.url || '',
|
|
71
|
+
snippet: r.description || '',
|
|
72
|
+
source: 'brave',
|
|
73
|
+
}));
|
|
74
|
+
const lines = results.map(r => `**${r.title}**\n${r.snippet}\n${r.url}`);
|
|
75
|
+
const output = `[Brave Search Results for "${query}"]\n\n${lines.join('\n\n')}`;
|
|
76
|
+
console.log(`[webSearch] Brave: ${results.length} results`);
|
|
77
|
+
return { success: true, output, method: 'brave', results };
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
console.warn(`[webSearch] Brave failed: ${e.message}`);
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// ── METHOD 3: DuckDuckGo (Instant API + HTML scrape) ──────────
|
|
85
|
+
async function searchViaDDG(query) {
|
|
86
|
+
const parts = [];
|
|
87
|
+
// DDG Instant Answer API
|
|
88
|
+
try {
|
|
89
|
+
const ddgUrl = `https://api.duckduckgo.com/?q=${encodeURIComponent(query)}&format=json&no_html=1&skip_disambig=1`;
|
|
90
|
+
const ddgRes = await fetch(ddgUrl, {
|
|
91
|
+
headers: { 'User-Agent': USER_AGENT },
|
|
92
|
+
signal: AbortSignal.timeout(8000),
|
|
93
|
+
});
|
|
94
|
+
const ddgData = await ddgRes.json();
|
|
95
|
+
if (ddgData.Answer)
|
|
96
|
+
parts.push(`Answer: ${ddgData.Answer}`);
|
|
97
|
+
if (ddgData.Abstract)
|
|
98
|
+
parts.push(`Summary: ${ddgData.Abstract}`);
|
|
99
|
+
if (ddgData.AbstractText && !ddgData.Abstract)
|
|
100
|
+
parts.push(ddgData.AbstractText);
|
|
101
|
+
if (ddgData.RelatedTopics?.length) {
|
|
102
|
+
const topics = ddgData.RelatedTopics
|
|
103
|
+
.slice(0, 6)
|
|
104
|
+
.map(t => t.Text || t.Result || '')
|
|
105
|
+
.filter(Boolean);
|
|
106
|
+
if (topics.length)
|
|
107
|
+
parts.push(`Related: ${topics.join('. ')}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
console.warn(`[webSearch] DDG Instant failed: ${e.message}`);
|
|
112
|
+
}
|
|
113
|
+
// DDG HTML scrape — get snippet text + page content
|
|
114
|
+
try {
|
|
115
|
+
const htmlRes = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`, {
|
|
116
|
+
headers: { 'User-Agent': USER_AGENT },
|
|
117
|
+
signal: AbortSignal.timeout(10000),
|
|
118
|
+
});
|
|
119
|
+
const html = await htmlRes.text();
|
|
120
|
+
// Extract snippets
|
|
121
|
+
const snippetMatches = [...html.matchAll(/class="result__snippet"[^>]*>([\s\S]*?)<\/a>/g)];
|
|
122
|
+
const snippets = snippetMatches
|
|
123
|
+
.map(m => m[1].replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim())
|
|
124
|
+
.filter(s => s.length > 30)
|
|
125
|
+
.slice(0, 5);
|
|
126
|
+
if (snippets.length > 0) {
|
|
127
|
+
parts.push(`Search Snippets:\n${snippets.join('\n')}`);
|
|
128
|
+
}
|
|
129
|
+
// Fetch top 2 result pages
|
|
130
|
+
const urlMatches = [...html.matchAll(/uddg=(https?[^&"]+)/g)];
|
|
131
|
+
const urls = urlMatches
|
|
132
|
+
.map(m => decodeURIComponent(m[1]))
|
|
133
|
+
.filter(u => !u.includes('duckduckgo.com') && !u.includes('youtube.com') && u.startsWith('https'))
|
|
134
|
+
.filter((u, i, arr) => arr.indexOf(u) === i)
|
|
135
|
+
.slice(0, 2);
|
|
136
|
+
const pageTexts = await Promise.all(urls.map(async (url) => {
|
|
137
|
+
try {
|
|
138
|
+
const r = await fetch(url, {
|
|
139
|
+
headers: { 'User-Agent': USER_AGENT },
|
|
140
|
+
signal: AbortSignal.timeout(7000),
|
|
141
|
+
});
|
|
142
|
+
if (!r.ok)
|
|
143
|
+
return null;
|
|
144
|
+
const text = await r.text();
|
|
145
|
+
const clean = text
|
|
146
|
+
.replace(/<script[\s\S]*?<\/script>/gi, '')
|
|
147
|
+
.replace(/<style[\s\S]*?<\/style>/gi, '')
|
|
148
|
+
.replace(/<nav[\s\S]*?<\/nav>/gi, '')
|
|
149
|
+
.replace(/<header[\s\S]*?<\/header>/gi, '')
|
|
150
|
+
.replace(/<footer[\s\S]*?<\/footer>/gi, '')
|
|
151
|
+
.replace(/<[^>]+>/g, ' ')
|
|
152
|
+
.replace(/\s+/g, ' ')
|
|
153
|
+
.trim();
|
|
154
|
+
if (clean.length < 200)
|
|
155
|
+
return null;
|
|
156
|
+
return `[${url}]\n${clean.slice(0, 1500)}`;
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}));
|
|
162
|
+
const validPages = pageTexts.filter(Boolean);
|
|
163
|
+
if (validPages.length > 0)
|
|
164
|
+
parts.push(...validPages);
|
|
165
|
+
}
|
|
166
|
+
catch (e) {
|
|
167
|
+
console.warn(`[webSearch] DDG HTML scrape failed: ${e.message}`);
|
|
168
|
+
}
|
|
169
|
+
if (parts.length === 0)
|
|
170
|
+
return null;
|
|
171
|
+
const output = `[DuckDuckGo Results for "${query}"]\n\n${parts.join('\n\n')}`;
|
|
172
|
+
console.log(`[webSearch] DDG: ${parts.length} sections`);
|
|
173
|
+
return { success: true, output, method: 'ddg' };
|
|
174
|
+
}
|
|
175
|
+
// ── METHOD 4: Wikipedia ───────────────────────────────────────
|
|
176
|
+
async function searchViaWikipedia(query) {
|
|
177
|
+
try {
|
|
178
|
+
const searchRes = await fetch(`https://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=${encodeURIComponent(query)}&srlimit=5&format=json&origin=*`, { signal: AbortSignal.timeout(6000) });
|
|
179
|
+
const searchData = await searchRes.json();
|
|
180
|
+
const hits = searchData?.query?.search || [];
|
|
181
|
+
if (hits.length === 0)
|
|
182
|
+
return null;
|
|
183
|
+
const topTitle = hits[0].title;
|
|
184
|
+
const summaryRes = await fetch(`https://en.wikipedia.org/api/rest_v1/page/summary/${encodeURIComponent(topTitle)}`, { signal: AbortSignal.timeout(6000) });
|
|
185
|
+
if (!summaryRes.ok)
|
|
186
|
+
return null;
|
|
187
|
+
const wiki = await summaryRes.json();
|
|
188
|
+
if (!wiki.extract || wiki.extract.length < 50)
|
|
189
|
+
return null;
|
|
190
|
+
const snippets = hits
|
|
191
|
+
.slice(1, 4)
|
|
192
|
+
.map(h => h.snippet?.replace(/<[^>]+>/g, '') || '')
|
|
193
|
+
.filter(s => s.length > 20);
|
|
194
|
+
const extra = snippets.length > 0 ? `\n\nRelated: ${snippets.join(' | ')}` : '';
|
|
195
|
+
const output = `[Wikipedia: ${wiki.title}]\n${wiki.extract.slice(0, 1500)}${extra}`;
|
|
196
|
+
console.log(`[webSearch] Wikipedia: ${wiki.extract.length} chars for "${wiki.title}"`);
|
|
197
|
+
return { success: true, output, method: 'wikipedia' };
|
|
198
|
+
}
|
|
199
|
+
catch (e) {
|
|
200
|
+
console.warn(`[webSearch] Wikipedia failed: ${e.message}`);
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// ── Weather shortcut ──────────────────────────────────────────
|
|
205
|
+
async function fetchWeather(query) {
|
|
206
|
+
const city = query
|
|
207
|
+
.replace(/what(?:'s| is) the weather/gi, '')
|
|
208
|
+
.replace(/\b(weather|forecast|today|current|temperature|rain|snow|sunny|cloudy|humidity|wind|in|for)\b/gi, '')
|
|
209
|
+
.replace(/\s+/g, ' ')
|
|
210
|
+
.trim() || 'auto';
|
|
211
|
+
try {
|
|
212
|
+
const wr = await fetch(`https://wttr.in/${encodeURIComponent(city)}?format=j1`, { signal: AbortSignal.timeout(8000) });
|
|
213
|
+
const data = await wr.json();
|
|
214
|
+
const cc = data.current_condition?.[0];
|
|
215
|
+
const area = data.nearest_area?.[0];
|
|
216
|
+
if (!cc || !area)
|
|
217
|
+
return null;
|
|
218
|
+
const location = [area.areaName?.[0]?.value, area.country?.[0]?.value].filter(Boolean).join(', ');
|
|
219
|
+
const desc = cc.weatherDesc?.[0]?.value || '';
|
|
220
|
+
let out = `Weather for ${location || city}:\n`;
|
|
221
|
+
out += `Condition: ${desc}\n`;
|
|
222
|
+
out += `Temperature: ${cc.temp_C}°C / ${cc.temp_F}°F (feels like ${cc.FeelsLikeC}°C)\n`;
|
|
223
|
+
out += `Humidity: ${cc.humidity}% | Wind: ${cc.windspeedKmph} km/h ${cc.winddir16Point} | Visibility: ${cc.visibility} km | UV: ${cc.uvIndex}\n`;
|
|
224
|
+
const forecasts = (data.weather || []).slice(0, 3);
|
|
225
|
+
if (forecasts.length) {
|
|
226
|
+
out += '\n3-Day Forecast:\n';
|
|
227
|
+
for (const day of forecasts) {
|
|
228
|
+
const mid = day.hourly?.[4]?.weatherDesc?.[0]?.value || '';
|
|
229
|
+
out += ` ${day.date}: High ${day.maxtempC}°C / Low ${day.mintempC}°C${mid ? ' — ' + mid : ''}\n`;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
console.log(`[webSearch] Weather: retrieved for "${city}"`);
|
|
233
|
+
return { success: true, output: out.trim(), method: 'wttr.in' };
|
|
234
|
+
}
|
|
235
|
+
catch (e) {
|
|
236
|
+
console.warn(`[webSearch] Weather failed: ${e.message}`);
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// ── Main exported function ────────────────────────────────────
|
|
241
|
+
async function reliableWebSearch(query) {
|
|
242
|
+
if (!query?.trim())
|
|
243
|
+
return { success: false, output: '', error: 'No query provided' };
|
|
244
|
+
console.log(`[webSearch] Query: "${query}"`);
|
|
245
|
+
// Weather shortcut
|
|
246
|
+
if (/weather|temperature|forecast|rain|snow|sunny|cloudy|humidity|wind/i.test(query)) {
|
|
247
|
+
const weather = await fetchWeather(query);
|
|
248
|
+
if (weather)
|
|
249
|
+
return { success: true, output: weather.output };
|
|
250
|
+
}
|
|
251
|
+
// Method 1 — SearxNG
|
|
252
|
+
const searxResult = await searchViaSearxNG(query);
|
|
253
|
+
if (searxResult) {
|
|
254
|
+
console.log(`[webSearch] ✓ SearxNG succeeded`);
|
|
255
|
+
return { success: true, output: searxResult.output.slice(0, 10000) };
|
|
256
|
+
}
|
|
257
|
+
// Method 2 — Brave
|
|
258
|
+
const braveResult = await searchViaBrave(query);
|
|
259
|
+
if (braveResult) {
|
|
260
|
+
console.log(`[webSearch] ✓ Brave succeeded`);
|
|
261
|
+
return { success: true, output: braveResult.output.slice(0, 10000) };
|
|
262
|
+
}
|
|
263
|
+
// Method 3 — DDG
|
|
264
|
+
const ddgResult = await searchViaDDG(query);
|
|
265
|
+
if (ddgResult) {
|
|
266
|
+
console.log(`[webSearch] ✓ DDG succeeded`);
|
|
267
|
+
return { success: true, output: ddgResult.output.slice(0, 10000) };
|
|
268
|
+
}
|
|
269
|
+
// Method 4 — Wikipedia
|
|
270
|
+
const wikiResult = await searchViaWikipedia(query);
|
|
271
|
+
if (wikiResult) {
|
|
272
|
+
console.log(`[webSearch] ✓ Wikipedia fallback`);
|
|
273
|
+
return { success: true, output: wikiResult.output };
|
|
274
|
+
}
|
|
275
|
+
console.warn(`[webSearch] All methods failed for: "${query}"`);
|
|
276
|
+
return {
|
|
277
|
+
success: false,
|
|
278
|
+
output: '',
|
|
279
|
+
error: `Web search failed for "${query}" — all 4 methods exhausted (SearxNG, Brave, DuckDuckGo, Wikipedia). Try starting SearxNG: .\\scripts\\start-searxng.ps1`,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
// ── Deep research — 3-pass synthesis ─────────────────────────
|
|
283
|
+
async function deepResearch(topic) {
|
|
284
|
+
if (!topic?.trim())
|
|
285
|
+
return { success: false, output: '', error: 'No topic provided' };
|
|
286
|
+
console.log(`[deepResearch] Topic: "${topic}"`);
|
|
287
|
+
const parts = [];
|
|
288
|
+
// Pass 1: Broad
|
|
289
|
+
console.log(`[deepResearch] Pass 1: broad`);
|
|
290
|
+
const broad = await reliableWebSearch(topic);
|
|
291
|
+
if (broad.success && broad.output.length > 100) {
|
|
292
|
+
parts.push(`=== PASS 1: BROAD RESEARCH ===\n${broad.output}`);
|
|
293
|
+
}
|
|
294
|
+
// Pass 2: Latest 2026
|
|
295
|
+
const latestQ = `${topic} 2026 latest`;
|
|
296
|
+
console.log(`[deepResearch] Pass 2: latest — "${latestQ}"`);
|
|
297
|
+
const latest = await reliableWebSearch(latestQ);
|
|
298
|
+
if (latest.success && latest.output.length > 100) {
|
|
299
|
+
parts.push(`=== PASS 2: LATEST (2026) ===\n${latest.output}`);
|
|
300
|
+
}
|
|
301
|
+
// Pass 3: Comparison / review
|
|
302
|
+
const compareQ = `best top ${topic} comparison review`;
|
|
303
|
+
console.log(`[deepResearch] Pass 3: comparison — "${compareQ}"`);
|
|
304
|
+
const compare = await reliableWebSearch(compareQ);
|
|
305
|
+
if (compare.success && compare.output.length > 100) {
|
|
306
|
+
parts.push(`=== PASS 3: COMPARISON & REVIEWS ===\n${compare.output}`);
|
|
307
|
+
}
|
|
308
|
+
if (parts.length === 0) {
|
|
309
|
+
return { success: false, output: '', error: `No research results found for: ${topic}` };
|
|
310
|
+
}
|
|
311
|
+
const combined = parts.join('\n\n');
|
|
312
|
+
console.log(`[deepResearch] Complete: ${combined.length} chars across ${parts.length} passes`);
|
|
313
|
+
return { success: true, output: combined.slice(0, 15000) };
|
|
314
|
+
}
|
|
315
|
+
// ── SearxNG health check ──────────────────────────────────────
|
|
316
|
+
async function checkSearxNG() {
|
|
317
|
+
try {
|
|
318
|
+
const res = await fetch(`${SEARXNG_URL}/search?q=test&format=json`, {
|
|
319
|
+
signal: AbortSignal.timeout(3000),
|
|
320
|
+
});
|
|
321
|
+
return res.ok;
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// DevOS — Autonomous AI Execution System
|
|
4
|
+
// Copyright (c) 2026 Shiva Deore. All rights reserved.
|
|
5
|
+
// ============================================================
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.startWorkflow = startWorkflow;
|
|
8
|
+
exports.addNode = addNode;
|
|
9
|
+
exports.updateNode = updateNode;
|
|
10
|
+
exports.addEdge = addEdge;
|
|
11
|
+
exports.completeWorkflow = completeWorkflow;
|
|
12
|
+
exports.getWorkflow = getWorkflow;
|
|
13
|
+
// core/workflowTracker.ts — Real-time agent orchestration tracker.
|
|
14
|
+
// Emits workflow events onto eventBus so api/server.ts can forward
|
|
15
|
+
// them to the dashboard via the existing /api/stream SSE channel.
|
|
16
|
+
const eventBus_1 = require("./eventBus");
|
|
17
|
+
// ── State ──────────────────────────────────────────────────────
|
|
18
|
+
let currentWorkflow = null;
|
|
19
|
+
// ── Internal emit ──────────────────────────────────────────────
|
|
20
|
+
function emit(type, payload) {
|
|
21
|
+
eventBus_1.eventBus.emit('workflow_event', { type, ...payload });
|
|
22
|
+
}
|
|
23
|
+
// ── Public API ─────────────────────────────────────────────────
|
|
24
|
+
function startWorkflow(goal) {
|
|
25
|
+
const id = `wf_${Date.now()}`;
|
|
26
|
+
currentWorkflow = {
|
|
27
|
+
id, goal, nodes: [], edges: [],
|
|
28
|
+
status: 'active',
|
|
29
|
+
totalCost: 0,
|
|
30
|
+
totalDuration: 0,
|
|
31
|
+
startedAt: Date.now(),
|
|
32
|
+
};
|
|
33
|
+
emit('workflow:start', { workflow: currentWorkflow });
|
|
34
|
+
return id;
|
|
35
|
+
}
|
|
36
|
+
function addNode(node) {
|
|
37
|
+
if (!currentWorkflow)
|
|
38
|
+
return;
|
|
39
|
+
if (currentWorkflow.nodes.some(n => n.id === node.id))
|
|
40
|
+
return;
|
|
41
|
+
currentWorkflow.nodes.push(node);
|
|
42
|
+
emit('workflow:node:add', { node });
|
|
43
|
+
}
|
|
44
|
+
function updateNode(nodeId, update) {
|
|
45
|
+
if (!currentWorkflow)
|
|
46
|
+
return;
|
|
47
|
+
const node = currentWorkflow.nodes.find(n => n.id === nodeId);
|
|
48
|
+
if (!node)
|
|
49
|
+
return;
|
|
50
|
+
Object.assign(node, update);
|
|
51
|
+
if (update.cost)
|
|
52
|
+
currentWorkflow.totalCost += update.cost;
|
|
53
|
+
emit('workflow:node:update', { nodeId, update });
|
|
54
|
+
}
|
|
55
|
+
function addEdge(edge) {
|
|
56
|
+
if (!currentWorkflow)
|
|
57
|
+
return;
|
|
58
|
+
if (currentWorkflow.edges.some(e => e.id === edge.id))
|
|
59
|
+
return;
|
|
60
|
+
currentWorkflow.edges.push(edge);
|
|
61
|
+
emit('workflow:edge', { edge });
|
|
62
|
+
}
|
|
63
|
+
function completeWorkflow(status) {
|
|
64
|
+
if (!currentWorkflow)
|
|
65
|
+
return;
|
|
66
|
+
currentWorkflow.status = status;
|
|
67
|
+
currentWorkflow.totalDuration = Date.now() - (currentWorkflow.startedAt ?? 0);
|
|
68
|
+
emit('workflow:complete', { workflow: currentWorkflow });
|
|
69
|
+
}
|
|
70
|
+
function getWorkflow() {
|
|
71
|
+
return currentWorkflow;
|
|
72
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// DevOS — Autonomous AI Execution System
|
|
4
|
+
// Copyright (c) 2026 Shiva Deore. All rights reserved.
|
|
5
|
+
// ============================================================
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.WorkspaceMemory = void 0;
|
|
11
|
+
// core/workspaceMemory.ts — Per-task file workspace.
|
|
12
|
+
// Each task gets an isolated directory for intermediate artifacts.
|
|
13
|
+
const fs_1 = __importDefault(require("fs"));
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
15
|
+
class WorkspaceMemory {
|
|
16
|
+
constructor(taskId) {
|
|
17
|
+
this.taskDir = path_1.default.join(process.cwd(), 'workspace', 'tasks', taskId);
|
|
18
|
+
fs_1.default.mkdirSync(this.taskDir, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
write(filename, content) {
|
|
21
|
+
const filePath = path_1.default.join(this.taskDir, filename);
|
|
22
|
+
fs_1.default.writeFileSync(filePath, content, 'utf-8');
|
|
23
|
+
return filePath;
|
|
24
|
+
}
|
|
25
|
+
read(filename) {
|
|
26
|
+
const filePath = path_1.default.join(this.taskDir, filename);
|
|
27
|
+
if (!fs_1.default.existsSync(filePath))
|
|
28
|
+
return null;
|
|
29
|
+
return fs_1.default.readFileSync(filePath, 'utf-8');
|
|
30
|
+
}
|
|
31
|
+
append(filename, content) {
|
|
32
|
+
const filePath = path_1.default.join(this.taskDir, filename);
|
|
33
|
+
fs_1.default.appendFileSync(filePath, content + '\n', 'utf-8');
|
|
34
|
+
return filePath;
|
|
35
|
+
}
|
|
36
|
+
exists(filename) {
|
|
37
|
+
return fs_1.default.existsSync(path_1.default.join(this.taskDir, filename));
|
|
38
|
+
}
|
|
39
|
+
getPath(filename) {
|
|
40
|
+
return path_1.default.join(this.taskDir, filename);
|
|
41
|
+
}
|
|
42
|
+
getDir() {
|
|
43
|
+
return this.taskDir;
|
|
44
|
+
}
|
|
45
|
+
list() {
|
|
46
|
+
try {
|
|
47
|
+
return fs_1.default.readdirSync(this.taskDir);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.WorkspaceMemory = WorkspaceMemory;
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// DevOS — Autonomous AI Execution System
|
|
4
|
+
// Copyright (c) 2026 Shiva Deore. All rights reserved.
|
|
5
|
+
// ============================================================
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.extractVideoId = extractVideoId;
|
|
41
|
+
exports.extractYouTubeTranscript = extractYouTubeTranscript;
|
|
42
|
+
// core/youtubeTranscript.ts — Extract transcripts from YouTube
|
|
43
|
+
// videos using the timedtext API (no API key required).
|
|
44
|
+
// Falls back to yt-dlp if the page fetch is blocked, then to a
|
|
45
|
+
// helpful error message asking the user to paste the transcript.
|
|
46
|
+
const child_process_1 = require("child_process");
|
|
47
|
+
const util_1 = require("util");
|
|
48
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
49
|
+
// ── Video ID extraction ────────────────────────────────────────
|
|
50
|
+
function extractVideoId(url) {
|
|
51
|
+
const patterns = [
|
|
52
|
+
/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/,
|
|
53
|
+
/^([a-zA-Z0-9_-]{11})$/,
|
|
54
|
+
];
|
|
55
|
+
for (const pattern of patterns) {
|
|
56
|
+
const match = url.match(pattern);
|
|
57
|
+
if (match)
|
|
58
|
+
return match[1];
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
// ── HTML entity decoder ────────────────────────────────────────
|
|
63
|
+
function decodeHtmlEntities(text) {
|
|
64
|
+
return text
|
|
65
|
+
.replace(/&/g, '&')
|
|
66
|
+
.replace(/</g, '<')
|
|
67
|
+
.replace(/>/g, '>')
|
|
68
|
+
.replace(/'/g, "'")
|
|
69
|
+
.replace(/"/g, '"')
|
|
70
|
+
.replace(/'/g, "'")
|
|
71
|
+
.replace(///g, '/')
|
|
72
|
+
.replace(/<[^>]*>/g, ''); // strip any inline tags
|
|
73
|
+
}
|
|
74
|
+
// ── Method 1: YouTube timedtext API (no key needed) ───────────
|
|
75
|
+
async function fetchViaTimedtextApi(videoId) {
|
|
76
|
+
const pageUrl = `https://www.youtube.com/watch?v=${videoId}`;
|
|
77
|
+
const controller = new AbortController();
|
|
78
|
+
const timer = setTimeout(() => controller.abort(), 15000);
|
|
79
|
+
let pageHtml;
|
|
80
|
+
try {
|
|
81
|
+
const resp = await fetch(pageUrl, {
|
|
82
|
+
signal: controller.signal,
|
|
83
|
+
headers: {
|
|
84
|
+
'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',
|
|
85
|
+
'Accept-Language': 'en-US,en;q=0.9',
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
pageHtml = await resp.text();
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
clearTimeout(timer);
|
|
92
|
+
}
|
|
93
|
+
// Extract caption tracks JSON from page source
|
|
94
|
+
const captionMatch = pageHtml.match(/"captionTracks":\[(\{.*?\})\]/);
|
|
95
|
+
if (!captionMatch) {
|
|
96
|
+
console.log('[YouTube] No captions found in page source');
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
let captionData;
|
|
100
|
+
try {
|
|
101
|
+
captionData = JSON.parse(`[${captionMatch[1]}]`);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
console.log('[YouTube] Failed to parse captionTracks JSON');
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
// Prefer English, fall back to first available track
|
|
108
|
+
const track = captionData.find((t) => t.languageCode === 'en') ||
|
|
109
|
+
captionData.find((t) => t.languageCode?.startsWith('en')) ||
|
|
110
|
+
captionData[0];
|
|
111
|
+
if (!track?.baseUrl)
|
|
112
|
+
return null;
|
|
113
|
+
// Fetch the XML transcript
|
|
114
|
+
const xmlResp = await fetch(track.baseUrl);
|
|
115
|
+
const xml = await xmlResp.text();
|
|
116
|
+
// Parse <text start="…" dur="…">…</text> entries
|
|
117
|
+
const entries = [];
|
|
118
|
+
const regex = /<text start="([\d.]+)" dur="([\d.]+)"[^>]*>([\s\S]*?)<\/text>/g;
|
|
119
|
+
let m;
|
|
120
|
+
while ((m = regex.exec(xml)) !== null) {
|
|
121
|
+
const text = decodeHtmlEntities(m[3]).trim();
|
|
122
|
+
if (text) {
|
|
123
|
+
entries.push({
|
|
124
|
+
start: parseFloat(m[1]),
|
|
125
|
+
duration: parseFloat(m[2]),
|
|
126
|
+
text,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (entries.length === 0)
|
|
131
|
+
return null;
|
|
132
|
+
// Extract page title
|
|
133
|
+
const titleMatch = pageHtml.match(/<title>(.*?)<\/title>/);
|
|
134
|
+
const title = titleMatch?.[1]?.replace(/ - YouTube$/, '').trim() || 'YouTube Video';
|
|
135
|
+
const fullText = entries.map(e => e.text).join(' ');
|
|
136
|
+
return { title, transcript: entries, fullText };
|
|
137
|
+
}
|
|
138
|
+
// ── Method 2: yt-dlp fallback ──────────────────────────────────
|
|
139
|
+
async function fetchViaYtDlp(videoId) {
|
|
140
|
+
const url = `https://www.youtube.com/watch?v=${videoId}`;
|
|
141
|
+
// Check if yt-dlp is available
|
|
142
|
+
try {
|
|
143
|
+
await execAsync('yt-dlp --version', { timeout: 5000 });
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
console.log('[YouTube] yt-dlp not installed — skipping fallback');
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
console.log('[YouTube] Trying yt-dlp fallback...');
|
|
150
|
+
// --write-auto-sub + --skip-download + --sub-format vtt
|
|
151
|
+
const cmd = `yt-dlp --write-auto-sub --skip-download --sub-lang en --sub-format vtt -o "/tmp/yt_%(id)s" "${url}"`;
|
|
152
|
+
try {
|
|
153
|
+
await execAsync(cmd, { timeout: 60000 });
|
|
154
|
+
}
|
|
155
|
+
catch (e) {
|
|
156
|
+
console.log('[YouTube] yt-dlp failed:', e.message?.slice(0, 80));
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
// Read the generated VTT file
|
|
160
|
+
const { readFileSync, existsSync } = await Promise.resolve().then(() => __importStar(require('fs')));
|
|
161
|
+
const vttPath = `/tmp/yt_${videoId}.en.vtt`;
|
|
162
|
+
if (!existsSync(vttPath)) {
|
|
163
|
+
console.log('[YouTube] yt-dlp did not produce a VTT file');
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
const vtt = readFileSync(vttPath, 'utf8');
|
|
167
|
+
// Parse VTT — strip cue headers and HTML tags, collect text lines
|
|
168
|
+
const lines = vtt.split('\n');
|
|
169
|
+
const texts = [];
|
|
170
|
+
const timeRe = /^\d{2}:\d{2}:\d{2}\.\d{3} --> /;
|
|
171
|
+
for (const line of lines) {
|
|
172
|
+
const trimmed = line.trim();
|
|
173
|
+
if (!trimmed || trimmed === 'WEBVTT' || timeRe.test(trimmed))
|
|
174
|
+
continue;
|
|
175
|
+
if (/^NOTE|^STYLE|^REGION/.test(trimmed))
|
|
176
|
+
continue;
|
|
177
|
+
const clean = trimmed.replace(/<[^>]+>/g, '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
178
|
+
if (clean)
|
|
179
|
+
texts.push(clean);
|
|
180
|
+
}
|
|
181
|
+
if (texts.length === 0)
|
|
182
|
+
return null;
|
|
183
|
+
// Deduplicate consecutive identical lines (VTT often repeats)
|
|
184
|
+
const deduped = texts.filter((t, i) => i === 0 || t !== texts[i - 1]);
|
|
185
|
+
const fullText = deduped.join(' ');
|
|
186
|
+
return {
|
|
187
|
+
title: `YouTube Video (${videoId})`,
|
|
188
|
+
transcript: deduped.map((text, i) => ({ text, start: i, duration: 1 })),
|
|
189
|
+
fullText,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
// ── Public entry point ─────────────────────────────────────────
|
|
193
|
+
async function extractYouTubeTranscript(url) {
|
|
194
|
+
const videoId = extractVideoId(url);
|
|
195
|
+
if (!videoId) {
|
|
196
|
+
console.log('[YouTube] Could not extract video ID from URL:', url);
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
console.log(`[YouTube] Extracting transcript for video: ${videoId}`);
|
|
200
|
+
// Try primary method (timedtext API via page fetch)
|
|
201
|
+
try {
|
|
202
|
+
const result = await fetchViaTimedtextApi(videoId);
|
|
203
|
+
if (result) {
|
|
204
|
+
console.log(`[YouTube] Got transcript via timedtext API: ${result.transcript.length} segments`);
|
|
205
|
+
return result;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
console.log('[YouTube] timedtext API failed:', err.message?.slice(0, 120));
|
|
210
|
+
}
|
|
211
|
+
// Try yt-dlp fallback
|
|
212
|
+
try {
|
|
213
|
+
const result = await fetchViaYtDlp(videoId);
|
|
214
|
+
if (result) {
|
|
215
|
+
console.log(`[YouTube] Got transcript via yt-dlp: ${result.transcript.length} segments`);
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
console.log('[YouTube] yt-dlp fallback failed:', err.message?.slice(0, 80));
|
|
221
|
+
}
|
|
222
|
+
console.log('[YouTube] All extraction methods failed for:', videoId);
|
|
223
|
+
return null;
|
|
224
|
+
}
|