ft-scout 1.0.8 → 1.1.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/.firebase/hosting.d2Vi.cache +9 -0
- package/.firebaserc +14 -0
- package/AUTONOMOUS_FEATURES.md +192 -0
- package/README.md +482 -32
- package/bin/ft.js +2 -0
- package/bin/src/commands/agent.d.ts.map +1 -0
- package/bin/src/commands/agent.js +1230 -0
- package/bin/src/commands/agent.js.map +1 -0
- package/bin/src/commands/audit.d.ts.map +1 -0
- package/bin/src/commands/audit.js +104 -0
- package/bin/src/commands/audit.js.map +1 -0
- package/bin/src/commands/authCommand.d.ts.map +1 -0
- package/bin/src/commands/authCommand.js +38 -0
- package/bin/src/commands/authCommand.js.map +1 -0
- package/bin/src/commands/benchmark.d.ts.map +1 -0
- package/bin/src/commands/benchmark.js +135 -0
- package/bin/src/commands/benchmark.js.map +1 -0
- package/bin/src/commands/brief.d.ts.map +1 -1
- package/bin/src/commands/brief.js +28 -19
- package/bin/src/commands/brief.js.map +1 -1
- package/bin/src/commands/configCommand.d.ts.map +1 -0
- package/bin/src/commands/configCommand.js +53 -0
- package/bin/src/commands/configCommand.js.map +1 -0
- package/bin/src/commands/dashboard.d.ts.map +1 -0
- package/bin/src/commands/dashboard.js +41 -0
- package/bin/src/commands/dashboard.js.map +1 -0
- package/bin/src/commands/history.d.ts.map +1 -0
- package/bin/src/commands/history.js +20 -0
- package/bin/src/commands/history.js.map +1 -0
- package/bin/src/commands/init.d.ts.map +1 -1
- package/bin/src/commands/init.js +411 -78
- package/bin/src/commands/init.js.map +1 -1
- package/bin/src/commands/login.d.ts.map +1 -0
- package/bin/src/commands/login.js +190 -0
- package/bin/src/commands/login.js.map +1 -0
- package/bin/src/commands/logout.d.ts.map +1 -0
- package/bin/src/commands/logout.js +15 -0
- package/bin/src/commands/logout.js.map +1 -0
- package/bin/src/commands/mcp.d.ts.map +1 -0
- package/bin/src/commands/mcp.js +393 -0
- package/bin/src/commands/mcp.js.map +1 -0
- package/bin/src/commands/owners.js +6 -6
- package/bin/src/commands/owners.js.map +1 -1
- package/bin/src/commands/plan.d.ts.map +1 -0
- package/bin/src/commands/plan.js +152 -0
- package/bin/src/commands/plan.js.map +1 -0
- package/bin/src/commands/quota.d.ts.map +1 -0
- package/bin/src/commands/quota.js +32 -0
- package/bin/src/commands/quota.js.map +1 -0
- package/bin/src/commands/recommend.d.ts.map +1 -0
- package/bin/src/commands/recommend.js +46 -0
- package/bin/src/commands/recommend.js.map +1 -0
- package/bin/src/commands/risky.d.ts.map +1 -1
- package/bin/src/commands/risky.js +235 -39
- package/bin/src/commands/risky.js.map +1 -1
- package/bin/src/commands/setup.d.ts.map +1 -1
- package/bin/src/commands/setup.js +411 -16
- package/bin/src/commands/setup.js.map +1 -1
- package/bin/src/commands/signup.d.ts.map +1 -0
- package/bin/src/commands/signup.js +118 -0
- package/bin/src/commands/signup.js.map +1 -0
- package/bin/src/commands/undo.d.ts.map +1 -0
- package/bin/src/commands/undo.js +59 -0
- package/bin/src/commands/undo.js.map +1 -0
- package/bin/src/commands/web.d.ts.map +1 -0
- package/bin/src/commands/web.js +90 -0
- package/bin/src/commands/web.js.map +1 -0
- package/bin/src/discovery/config.d.ts.map +1 -0
- package/bin/src/discovery/config.js +41 -0
- package/bin/src/discovery/config.js.map +1 -0
- package/bin/src/discovery/display.d.ts.map +1 -0
- package/bin/src/discovery/display.js +57 -0
- package/bin/src/discovery/display.js.map +1 -0
- package/bin/src/discovery/messages.d.ts.map +1 -0
- package/bin/src/discovery/messages.js +7 -0
- package/bin/src/discovery/messages.js.map +1 -0
- package/bin/src/discovery/signals.d.ts.map +1 -0
- package/bin/src/discovery/signals.js +33 -0
- package/bin/src/discovery/signals.js.map +1 -0
- package/bin/src/engine/actionVerifier.d.ts.map +1 -0
- package/bin/src/engine/actionVerifier.js +283 -0
- package/bin/src/engine/actionVerifier.js.map +1 -0
- package/bin/src/engine/agentEngine.d.ts.map +1 -0
- package/bin/src/engine/agentEngine.js +2188 -0
- package/bin/src/engine/agentEngine.js.map +1 -0
- package/bin/src/engine/agentState.d.ts.map +1 -0
- package/bin/src/engine/agentState.js +381 -0
- package/bin/src/engine/agentState.js.map +1 -0
- package/bin/src/engine/browser/browserController.d.ts.map +1 -0
- package/bin/src/engine/browser/browserController.js +737 -0
- package/bin/src/engine/browser/browserController.js.map +1 -0
- package/bin/src/engine/browser/index.d.ts.map +1 -0
- package/bin/src/engine/browser/index.js +2 -0
- package/bin/src/engine/browser/index.js.map +1 -0
- package/bin/src/engine/computer/computerController.d.ts.map +1 -0
- package/bin/src/engine/computer/computerController.js +773 -0
- package/bin/src/engine/computer/computerController.js.map +1 -0
- package/bin/src/engine/computer/computerDiagnostic.d.ts.map +1 -0
- package/bin/src/engine/computer/computerDiagnostic.js +71 -0
- package/bin/src/engine/computer/computerDiagnostic.js.map +1 -0
- package/bin/src/engine/computer/index.d.ts.map +1 -0
- package/bin/src/engine/computer/index.js +4 -0
- package/bin/src/engine/computer/index.js.map +1 -0
- package/bin/src/engine/computer/mockComputer.d.ts.map +1 -0
- package/bin/src/engine/computer/mockComputer.js +260 -0
- package/bin/src/engine/computer/mockComputer.js.map +1 -0
- package/bin/src/engine/computer/scoutDriver.cs +1161 -0
- package/bin/src/engine/computer/scout_driver.exe +0 -0
- package/bin/src/engine/git.d.ts.map +1 -1
- package/bin/src/engine/git.js +71 -0
- package/bin/src/engine/git.js.map +1 -1
- package/bin/src/engine/llm.d.ts.map +1 -1
- package/bin/src/engine/llm.js +1272 -23
- package/bin/src/engine/llm.js.map +1 -1
- package/bin/src/engine/planner.d.ts.map +1 -0
- package/bin/src/engine/planner.js +321 -0
- package/bin/src/engine/planner.js.map +1 -0
- package/bin/src/engine/runtime/agentRuntime.d.ts.map +1 -0
- package/bin/src/engine/runtime/agentRuntime.js +1258 -0
- package/bin/src/engine/runtime/agentRuntime.js.map +1 -0
- package/bin/src/engine/runtime/index.d.ts.map +1 -0
- package/bin/src/engine/runtime/index.js +2 -0
- package/bin/src/engine/runtime/index.js.map +1 -0
- package/bin/src/engine/safety.d.ts.map +1 -0
- package/bin/src/engine/safety.js +316 -0
- package/bin/src/engine/safety.js.map +1 -0
- package/bin/src/engine/scoutAudioRecorder.cs +215 -0
- package/bin/src/engine/scout_audio_recorder.exe +0 -0
- package/bin/src/engine/toolRegistry.d.ts.map +1 -0
- package/bin/src/engine/toolRegistry.js +521 -0
- package/bin/src/engine/toolRegistry.js.map +1 -0
- package/bin/src/engine/verifier.d.ts.map +1 -0
- package/bin/src/engine/verifier.js +414 -0
- package/bin/src/engine/verifier.js.map +1 -0
- package/bin/src/engine/voiceEngine.d.ts.map +1 -0
- package/bin/src/engine/voiceEngine.js +474 -0
- package/bin/src/engine/voiceEngine.js.map +1 -0
- package/bin/src/engine/web.d.ts.map +1 -0
- package/bin/src/engine/web.js +161 -0
- package/bin/src/engine/web.js.map +1 -0
- package/bin/src/engine/workspace/agentWorkspace.d.ts.map +1 -0
- package/bin/src/engine/workspace/agentWorkspace.js +505 -0
- package/bin/src/engine/workspace/agentWorkspace.js.map +1 -0
- package/bin/src/engine/workspace/index.d.ts.map +1 -0
- package/bin/src/engine/workspace/index.js +2 -0
- package/bin/src/engine/workspace/index.js.map +1 -0
- package/bin/src/index.js +365 -23
- package/bin/src/index.js.map +1 -1
- package/bin/src/mcp/client.d.ts.map +1 -0
- package/bin/src/mcp/client.js +127 -0
- package/bin/src/mcp/client.js.map +1 -0
- package/bin/src/mcp/discovery.d.ts.map +1 -0
- package/bin/src/mcp/discovery.js +91 -0
- package/bin/src/mcp/discovery.js.map +1 -0
- package/bin/src/mcp/health.d.ts.map +1 -0
- package/bin/src/mcp/health.js +44 -0
- package/bin/src/mcp/health.js.map +1 -0
- package/bin/src/mcp/index.d.ts.map +1 -0
- package/bin/src/mcp/index.js +15 -0
- package/bin/src/mcp/index.js.map +1 -0
- package/bin/src/mcp/manager.d.ts.map +1 -0
- package/bin/src/mcp/manager.js +648 -0
- package/bin/src/mcp/manager.js.map +1 -0
- package/bin/src/mcp/permissions.d.ts.map +1 -0
- package/bin/src/mcp/permissions.js +50 -0
- package/bin/src/mcp/permissions.js.map +1 -0
- package/bin/src/mcp/persistence.d.ts.map +1 -0
- package/bin/src/mcp/persistence.js +171 -0
- package/bin/src/mcp/persistence.js.map +1 -0
- package/bin/src/mcp/recovery.d.ts.map +1 -0
- package/bin/src/mcp/recovery.js +75 -0
- package/bin/src/mcp/recovery.js.map +1 -0
- package/bin/src/mcp/telemetry.d.ts.map +1 -0
- package/bin/src/mcp/telemetry.js +82 -0
- package/bin/src/mcp/telemetry.js.map +1 -0
- package/bin/src/mcp/toolRouter.d.ts.map +1 -0
- package/bin/src/mcp/toolRouter.js +127 -0
- package/bin/src/mcp/toolRouter.js.map +1 -0
- package/bin/src/mcp/transport.d.ts.map +1 -0
- package/bin/src/mcp/transport.js +328 -0
- package/bin/src/mcp/transport.js.map +1 -0
- package/bin/src/mcp/types.d.ts.map +1 -0
- package/bin/src/mcp/types.js +6 -0
- package/bin/src/mcp/types.js.map +1 -0
- package/bin/src/server/server.d.ts.map +1 -0
- package/bin/src/server/server.js +989 -0
- package/bin/src/server/server.js.map +1 -0
- package/bin/src/utils/admin.d.ts.map +1 -0
- package/bin/src/utils/admin.js +231 -0
- package/bin/src/utils/admin.js.map +1 -0
- package/bin/src/utils/auditLogger.d.ts.map +1 -0
- package/bin/src/utils/auditLogger.js +70 -0
- package/bin/src/utils/auditLogger.js.map +1 -0
- package/bin/src/utils/auth.d.ts.map +1 -0
- package/bin/src/utils/auth.js +679 -0
- package/bin/src/utils/auth.js.map +1 -0
- package/bin/src/utils/branding.d.ts.map +1 -0
- package/bin/src/utils/branding.js +14 -0
- package/bin/src/utils/branding.js.map +1 -0
- package/bin/src/utils/config.d.ts.map +1 -1
- package/bin/src/utils/config.js +124 -3
- package/bin/src/utils/config.js.map +1 -1
- package/bin/src/utils/email.d.ts.map +1 -0
- package/bin/src/utils/email.js +267 -0
- package/bin/src/utils/email.js.map +1 -0
- package/bin/src/utils/firebaseAuth.d.ts.map +1 -0
- package/bin/src/utils/firebaseAuth.js +174 -0
- package/bin/src/utils/firebaseAuth.js.map +1 -0
- package/bin/src/utils/holoseneAuth.d.ts.map +1 -0
- package/bin/src/utils/holoseneAuth.js +307 -0
- package/bin/src/utils/holoseneAuth.js.map +1 -0
- package/bin/src/utils/language.d.ts.map +1 -0
- package/bin/src/utils/language.js +181 -0
- package/bin/src/utils/language.js.map +1 -0
- package/bin/src/utils/markdown.d.ts.map +1 -0
- package/bin/src/utils/markdown.js +215 -0
- package/bin/src/utils/markdown.js.map +1 -0
- package/bin/src/utils/payment.d.ts.map +1 -0
- package/bin/src/utils/payment.js +290 -0
- package/bin/src/utils/payment.js.map +1 -0
- package/bin/src/utils/pricing.d.ts.map +1 -0
- package/bin/src/utils/pricing.js +165 -0
- package/bin/src/utils/pricing.js.map +1 -0
- package/bin/src/utils/razorpay.d.ts.map +1 -0
- package/bin/src/utils/razorpay.js +144 -0
- package/bin/src/utils/razorpay.js.map +1 -0
- package/bin/src/utils/session.d.ts.map +1 -0
- package/bin/src/utils/session.js +82 -0
- package/bin/src/utils/session.js.map +1 -0
- package/bin/src/utils/updateCheck.d.ts.map +1 -0
- package/bin/src/utils/updateCheck.js +108 -0
- package/bin/src/utils/updateCheck.js.map +1 -0
- package/downloads/invoice-2026-03.pdf +1 -0
- package/firebase.json +74 -0
- package/package.json +49 -46
- package/web/ScoutFavicon.png +0 -0
- package/web/app.js +3944 -0
- package/web/styles.css +3081 -0
- package/.ft/context.json +0 -18
- package/email-template.html +0 -223
- package/email-template.txt +0 -45
- package/scripts/send-brevo-campaign.cjs +0 -167
- package/scripts/send-email.js +0 -77
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { exec, spawn } from 'child_process';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { select, isCancel, spinner } from '@clack/prompts';
|
|
9
|
+
import { OpenAI } from 'openai';
|
|
10
|
+
import { isProOrHigher } from '../utils/auth.js';
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = path.dirname(__filename);
|
|
13
|
+
const execAsync = promisify(exec);
|
|
14
|
+
export async function promptInteractionMode() {
|
|
15
|
+
try {
|
|
16
|
+
const choice = await select({
|
|
17
|
+
message: chalk.bold.cyan('Select Interaction Mode:'),
|
|
18
|
+
options: [
|
|
19
|
+
{ value: 'text', label: 'Text Interaction', hint: 'Text command input -> Text response' },
|
|
20
|
+
{ value: 'audio', label: 'Audio Interaction', hint: 'Voice command input (STT) -> Text + Voice response (TTS)' },
|
|
21
|
+
],
|
|
22
|
+
});
|
|
23
|
+
if (isCancel(choice))
|
|
24
|
+
return 'text';
|
|
25
|
+
return choice;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return 'text';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Strip markdown formatting, code blocks, URLs, and special characters for clean TTS speech.
|
|
33
|
+
*/
|
|
34
|
+
export function cleanTextForSpeech(text) {
|
|
35
|
+
if (!text)
|
|
36
|
+
return '';
|
|
37
|
+
return text
|
|
38
|
+
.replace(/```[\s\S]*?```/g, ' [Code block omitted] ')
|
|
39
|
+
.replace(/`[^`]+`/g, '')
|
|
40
|
+
.replace(/https?:\/\/\S+/g, '')
|
|
41
|
+
.replace(/[#*_\-~`|>]/g, ' ')
|
|
42
|
+
.replace(/\s+/g, ' ')
|
|
43
|
+
.trim();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Packs raw 16-bit Mono PCM audio into a standard WAV container buffer.
|
|
47
|
+
*/
|
|
48
|
+
function pcmToWavBuffer(pcmBuffer, sampleRate = 24000) {
|
|
49
|
+
const header = Buffer.alloc(44);
|
|
50
|
+
const numChannels = 1;
|
|
51
|
+
const bitsPerSample = 16;
|
|
52
|
+
const byteRate = sampleRate * numChannels * (bitsPerSample / 8);
|
|
53
|
+
const blockAlign = numChannels * (bitsPerSample / 8);
|
|
54
|
+
const dataLength = pcmBuffer.length;
|
|
55
|
+
header.write('RIFF', 0);
|
|
56
|
+
header.writeUInt32LE(36 + dataLength, 4);
|
|
57
|
+
header.write('WAVE', 8);
|
|
58
|
+
header.write('fmt ', 12);
|
|
59
|
+
header.writeUInt32LE(16, 16);
|
|
60
|
+
header.writeUInt16LE(1, 20); // PCM format
|
|
61
|
+
header.writeUInt16LE(numChannels, 22);
|
|
62
|
+
header.writeUInt32LE(sampleRate, 24);
|
|
63
|
+
header.writeUInt32LE(byteRate, 28);
|
|
64
|
+
header.writeUInt16LE(blockAlign, 32);
|
|
65
|
+
header.writeUInt16LE(bitsPerSample, 34);
|
|
66
|
+
header.write('data', 36);
|
|
67
|
+
header.writeUInt32LE(dataLength, 40);
|
|
68
|
+
return Buffer.concat([header, pcmBuffer]);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Synthesizes hyper-realistic human voice using ElevenLabs AI for PRO & ENTERPRISE tier users.
|
|
72
|
+
*/
|
|
73
|
+
async function speakWithElevenLabs(text, options) {
|
|
74
|
+
const apiKey = process.env.ELEVENLABS_API_KEY;
|
|
75
|
+
if (!apiKey)
|
|
76
|
+
return false;
|
|
77
|
+
// Default: Adam (Authoritative & natural tech narrator: pNInz6obpgDQGcFmaJgB)
|
|
78
|
+
const voiceId = options?.voiceId || process.env.ELEVENLABS_VOICE_ID || 'pNInz6obpgDQGcFmaJgB';
|
|
79
|
+
try {
|
|
80
|
+
const url = `https://api.elevenlabs.io/v1/text-to-speech/${voiceId}?output_format=pcm_24000`;
|
|
81
|
+
const response = await fetch(url, {
|
|
82
|
+
method: 'POST',
|
|
83
|
+
headers: {
|
|
84
|
+
'xi-api-key': apiKey,
|
|
85
|
+
'Content-Type': 'application/json',
|
|
86
|
+
},
|
|
87
|
+
body: JSON.stringify({
|
|
88
|
+
text,
|
|
89
|
+
model_id: 'eleven_turbo_v2_5',
|
|
90
|
+
voice_settings: {
|
|
91
|
+
stability: 0.5,
|
|
92
|
+
similarity_boost: 0.8,
|
|
93
|
+
},
|
|
94
|
+
}),
|
|
95
|
+
});
|
|
96
|
+
if (!response.ok) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
100
|
+
const pcmBuffer = Buffer.from(arrayBuffer);
|
|
101
|
+
if (pcmBuffer.length < 100)
|
|
102
|
+
return false;
|
|
103
|
+
const wavBuffer = pcmToWavBuffer(pcmBuffer, 24000);
|
|
104
|
+
const tempWav = path.join(os.tmpdir(), `scout_tts_${Date.now()}.wav`);
|
|
105
|
+
fs.writeFileSync(tempWav, wavBuffer);
|
|
106
|
+
const platform = process.platform;
|
|
107
|
+
if (platform === 'win32') {
|
|
108
|
+
const escapedPath = tempWav.replace(/'/g, "''");
|
|
109
|
+
const psScript = `$player = New-Object System.Media.SoundPlayer '${escapedPath}'; $player.PlaySync();`;
|
|
110
|
+
if (options?.async) {
|
|
111
|
+
const child = spawn('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', `${psScript} Remove-Item -Force '${escapedPath}'`], {
|
|
112
|
+
detached: true,
|
|
113
|
+
stdio: 'ignore',
|
|
114
|
+
});
|
|
115
|
+
child.unref();
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
await new Promise((resolve) => {
|
|
119
|
+
const child = spawn('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', psScript]);
|
|
120
|
+
child.on('close', () => {
|
|
121
|
+
try {
|
|
122
|
+
if (fs.existsSync(tempWav))
|
|
123
|
+
fs.unlinkSync(tempWav);
|
|
124
|
+
}
|
|
125
|
+
catch { }
|
|
126
|
+
resolve();
|
|
127
|
+
});
|
|
128
|
+
child.on('error', () => {
|
|
129
|
+
try {
|
|
130
|
+
if (fs.existsSync(tempWav))
|
|
131
|
+
fs.unlinkSync(tempWav);
|
|
132
|
+
}
|
|
133
|
+
catch { }
|
|
134
|
+
resolve();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
else if (platform === 'darwin') {
|
|
140
|
+
if (options?.async) {
|
|
141
|
+
exec(`afplay "${tempWav}" && rm -f "${tempWav}"`);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
await execAsync(`afplay "${tempWav}"`);
|
|
145
|
+
try {
|
|
146
|
+
if (fs.existsSync(tempWav))
|
|
147
|
+
fs.unlinkSync(tempWav);
|
|
148
|
+
}
|
|
149
|
+
catch { }
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
// Linux
|
|
154
|
+
if (options?.async) {
|
|
155
|
+
exec(`(aplay "${tempWav}" || paplay "${tempWav}") && rm -f "${tempWav}"`);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
await execAsync(`aplay "${tempWav}" || paplay "${tempWav}"`);
|
|
159
|
+
try {
|
|
160
|
+
if (fs.existsSync(tempWav))
|
|
161
|
+
fs.unlinkSync(tempWav);
|
|
162
|
+
}
|
|
163
|
+
catch { }
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Text-to-Speech (TTS) Engine:
|
|
174
|
+
* - PRO / ENTERPRISE: Uses ElevenLabs AI for hyper-realistic human voice (covers API cost).
|
|
175
|
+
* - FREE TIER / OFFLINE: Uses native OS TTS (SAPI5 on Windows, say on macOS, spd-say on Linux) with zero API cost.
|
|
176
|
+
*/
|
|
177
|
+
export async function speakText(text, options) {
|
|
178
|
+
const cleanStr = cleanTextForSpeech(text);
|
|
179
|
+
if (!cleanStr)
|
|
180
|
+
return;
|
|
181
|
+
const truncatedText = cleanStr.length > 500 ? cleanStr.slice(0, 500) + '...' : cleanStr;
|
|
182
|
+
// 🎙️ PRO/ENTERPRISE TIER ROUTING: ElevenLabs Human Voice
|
|
183
|
+
const isPro = isProOrHigher(options?.tierOverride);
|
|
184
|
+
const elevenKey = process.env.ELEVENLABS_API_KEY;
|
|
185
|
+
if (isPro && elevenKey) {
|
|
186
|
+
try {
|
|
187
|
+
const spoken = await speakWithElevenLabs(truncatedText, options);
|
|
188
|
+
if (spoken)
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// Fall back to native OS TTS if quota or network issue
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// 🤖 FREE TIER / OFFLINE FALLBACK: Native SAPI5 / say / spd-say
|
|
196
|
+
const platform = process.platform;
|
|
197
|
+
try {
|
|
198
|
+
if (platform === 'win32') {
|
|
199
|
+
const escaped = truncatedText.replace(/"/g, '`"').replace(/'/g, "''");
|
|
200
|
+
const psScript = `Add-Type -AssemblyName System.Speech; $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer; $synth.Speak("${escaped}");`;
|
|
201
|
+
if (options?.async) {
|
|
202
|
+
const child = spawn('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', psScript], { detached: true, stdio: 'ignore' });
|
|
203
|
+
child.unref();
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
await new Promise((resolve) => {
|
|
207
|
+
const child = spawn('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', psScript]);
|
|
208
|
+
child.on('close', () => resolve());
|
|
209
|
+
child.on('error', () => resolve());
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else if (platform === 'darwin') {
|
|
214
|
+
const escaped = truncatedText.replace(/"/g, '\\"');
|
|
215
|
+
const cmd = `say "${escaped}"`;
|
|
216
|
+
if (options?.async) {
|
|
217
|
+
exec(cmd);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
await execAsync(cmd);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
// Linux fallback
|
|
225
|
+
const escaped = truncatedText.replace(/"/g, '\\"');
|
|
226
|
+
const cmd = `spd-say "${escaped}" || espeak "${escaped}"`;
|
|
227
|
+
if (options?.async) {
|
|
228
|
+
exec(cmd);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
await execAsync(cmd);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
// Ignore speech synthesis errors gracefully
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Ensures the native Windows audio recorder executable is compiled and available.
|
|
241
|
+
*/
|
|
242
|
+
async function ensureWindowsAudioRecorder() {
|
|
243
|
+
if (process.platform !== 'win32')
|
|
244
|
+
return null;
|
|
245
|
+
const candidateExes = [
|
|
246
|
+
path.join(__dirname, 'scout_audio_recorder.exe'),
|
|
247
|
+
path.join(os.tmpdir(), 'scout_audio_recorder.exe'),
|
|
248
|
+
];
|
|
249
|
+
const csSourceCandidates = [
|
|
250
|
+
path.join(__dirname, 'scoutAudioRecorder.cs'),
|
|
251
|
+
path.join(process.cwd(), 'bin', 'src', 'engine', 'scoutAudioRecorder.cs'),
|
|
252
|
+
];
|
|
253
|
+
const csSource = csSourceCandidates.find((p) => fs.existsSync(p));
|
|
254
|
+
const cscExe = 'C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\csc.exe';
|
|
255
|
+
for (const exePath of candidateExes) {
|
|
256
|
+
if (fs.existsSync(exePath)) {
|
|
257
|
+
if (csSource && fs.existsSync(cscExe)) {
|
|
258
|
+
try {
|
|
259
|
+
const srcMtime = fs.statSync(csSource).mtimeMs;
|
|
260
|
+
const exeMtime = fs.statSync(exePath).mtimeMs;
|
|
261
|
+
if (srcMtime > exeMtime) {
|
|
262
|
+
await execAsync(`"${cscExe}" /nologo /optimize /t:exe /out:"${exePath}" "${csSource}"`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
catch { }
|
|
266
|
+
}
|
|
267
|
+
return exePath;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (csSource && fs.existsSync(cscExe)) {
|
|
271
|
+
const targetExe = candidateExes[0] ?? path.join(__dirname, 'scout_audio_recorder.exe');
|
|
272
|
+
try {
|
|
273
|
+
await execAsync(`"${cscExe}" /nologo /optimize /t:exe /out:"${targetExe}" "${csSource}"`);
|
|
274
|
+
if (fs.existsSync(targetExe)) {
|
|
275
|
+
return targetExe;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
// Try in temp dir if targetExe write failed
|
|
280
|
+
const tempExe = candidateExes[1] ?? path.join(os.tmpdir(), 'scout_audio_recorder.exe');
|
|
281
|
+
try {
|
|
282
|
+
await execAsync(`"${cscExe}" /nologo /optimize /t:exe /out:"${tempExe}" "${csSource}"`);
|
|
283
|
+
if (fs.existsSync(tempExe)) {
|
|
284
|
+
return tempExe;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
catch { }
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Records audio using the native audio recorder binary with automatic Voice Activity Detection (VAD).
|
|
294
|
+
*/
|
|
295
|
+
async function recordAudioToWav(recorderExe, wavPath, maxDurationSecs, silenceCutoffMs, s) {
|
|
296
|
+
return new Promise((resolve) => {
|
|
297
|
+
const child = spawn(recorderExe, [wavPath, String(maxDurationSecs), String(silenceCutoffMs)], {
|
|
298
|
+
windowsHide: true,
|
|
299
|
+
});
|
|
300
|
+
child.stdout?.on('data', (data) => {
|
|
301
|
+
const str = data.toString();
|
|
302
|
+
if (str.includes('SPEECH_DETECTED')) {
|
|
303
|
+
s.message(chalk.cyan(' Voice detected! Listening... (will auto-submit when you stop speaking)'));
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
child.on('close', (code) => {
|
|
307
|
+
if (code === 0 && fs.existsSync(wavPath)) {
|
|
308
|
+
const stat = fs.statSync(wavPath);
|
|
309
|
+
resolve(stat.size > 1000);
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
resolve(false);
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
child.on('error', () => {
|
|
316
|
+
resolve(false);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Transcribes recorded audio using Groq Whisper (lightning fast) or OpenAI Whisper (fallback).
|
|
322
|
+
*/
|
|
323
|
+
async function transcribeAudioWithWhisper(wavPath) {
|
|
324
|
+
const groqKey = process.env.GROQ_API_KEY;
|
|
325
|
+
const openaiKey = process.env.OPENAI_API_KEY;
|
|
326
|
+
const technicalPrompt = 'Developer terminal command: open chrome, create file, run test, fix bug, inspect git diff, build project, start server, deploy';
|
|
327
|
+
// 1. Try Groq Whisper (ultra-fast, ~250ms)
|
|
328
|
+
if (groqKey) {
|
|
329
|
+
try {
|
|
330
|
+
const groqClient = new OpenAI({
|
|
331
|
+
apiKey: groqKey,
|
|
332
|
+
baseURL: 'https://api.groq.com/openai/v1',
|
|
333
|
+
timeout: 12000,
|
|
334
|
+
});
|
|
335
|
+
const transcription = await groqClient.audio.transcriptions.create({
|
|
336
|
+
file: fs.createReadStream(wavPath),
|
|
337
|
+
model: 'whisper-large-v3-turbo',
|
|
338
|
+
prompt: technicalPrompt,
|
|
339
|
+
temperature: 0.0,
|
|
340
|
+
});
|
|
341
|
+
if (transcription && transcription.text && transcription.text.trim()) {
|
|
342
|
+
return transcription.text.trim();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
// Groq Whisper failed or timed out, attempt OpenAI fallback below
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
// 2. Try OpenAI Whisper (whisper-1)
|
|
350
|
+
if (openaiKey) {
|
|
351
|
+
try {
|
|
352
|
+
const openaiClient = new OpenAI({
|
|
353
|
+
apiKey: openaiKey,
|
|
354
|
+
timeout: 15000,
|
|
355
|
+
});
|
|
356
|
+
const transcription = await openaiClient.audio.transcriptions.create({
|
|
357
|
+
file: fs.createReadStream(wavPath),
|
|
358
|
+
model: 'whisper-1',
|
|
359
|
+
prompt: technicalPrompt,
|
|
360
|
+
temperature: 0.0,
|
|
361
|
+
});
|
|
362
|
+
if (transcription && transcription.text && transcription.text.trim()) {
|
|
363
|
+
return transcription.text.trim();
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
catch {
|
|
367
|
+
// OpenAI Whisper failed
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Legacy offline fallback using Windows SAPI System.Speech.
|
|
374
|
+
*/
|
|
375
|
+
function listenSpeechToTextLegacy(timeoutSecs, s) {
|
|
376
|
+
const platform = process.platform;
|
|
377
|
+
if (platform === 'win32') {
|
|
378
|
+
return new Promise((resolve) => {
|
|
379
|
+
const psScript = `
|
|
380
|
+
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
381
|
+
Add-Type -AssemblyName System.Speech
|
|
382
|
+
try {
|
|
383
|
+
$culture = New-Object System.Globalization.CultureInfo("en-US")
|
|
384
|
+
$engine = New-Object System.Speech.Recognition.SpeechRecognitionEngine($culture)
|
|
385
|
+
$engine.SetInputToDefaultAudioDevice()
|
|
386
|
+
$grammar = New-Object System.Speech.Recognition.DictationGrammar
|
|
387
|
+
$engine.LoadGrammar($grammar)
|
|
388
|
+
$result = $engine.Recognize([TimeSpan]::FromSeconds(${timeoutSecs}))
|
|
389
|
+
if ($result -and $result.Text) {
|
|
390
|
+
[Console]::WriteLine("TRANSCRIPT:" + $result.Text)
|
|
391
|
+
} else {
|
|
392
|
+
[Console]::WriteLine("NO_SPEECH")
|
|
393
|
+
}
|
|
394
|
+
} catch {
|
|
395
|
+
[Console]::WriteLine("ERROR:" + $_.Exception.Message)
|
|
396
|
+
}
|
|
397
|
+
`.trim();
|
|
398
|
+
const child = spawn('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', psScript]);
|
|
399
|
+
let output = '';
|
|
400
|
+
child.stdout?.on('data', (data) => {
|
|
401
|
+
output += data.toString();
|
|
402
|
+
});
|
|
403
|
+
child.on('close', () => {
|
|
404
|
+
s.stop('Terminal microphone recording complete.');
|
|
405
|
+
let transcript = '';
|
|
406
|
+
const lines = output.split('\n').map((l) => l.trim());
|
|
407
|
+
for (const line of lines) {
|
|
408
|
+
if (line.startsWith('TRANSCRIPT:')) {
|
|
409
|
+
transcript = line.replace('TRANSCRIPT:', '').trim();
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
resolve(transcript || '');
|
|
414
|
+
});
|
|
415
|
+
child.on('error', () => {
|
|
416
|
+
s.stop(chalk.yellow('Terminal microphone listener ended.'));
|
|
417
|
+
resolve('');
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
return new Promise((resolve) => {
|
|
423
|
+
setTimeout(() => {
|
|
424
|
+
s.stop(chalk.yellow('Terminal microphone listening finished.'));
|
|
425
|
+
resolve('');
|
|
426
|
+
}, 3000);
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Native Speech-to-Text (STT) directly inside the terminal.
|
|
432
|
+
* Automatically utilizes neural Whisper (Groq/OpenAI) for 99.9% accuracy on tech commands,
|
|
433
|
+
* with automatic Voice Activity Detection (VAD) and graceful legacy fallback.
|
|
434
|
+
*/
|
|
435
|
+
export async function listenSpeechToText(options) {
|
|
436
|
+
const timeoutSecs = Math.max(10, Math.min(60, Math.round((options?.timeoutMs || 20000) / 1000)));
|
|
437
|
+
const silenceCutoffMs = 2500; // 2.5 seconds natural pause debounce
|
|
438
|
+
const s = spinner();
|
|
439
|
+
s.start(chalk.cyan(` Listening (${timeoutSecs}s max)...`));
|
|
440
|
+
const groqKey = process.env.GROQ_API_KEY;
|
|
441
|
+
const openaiKey = process.env.OPENAI_API_KEY;
|
|
442
|
+
const hasWhisperKey = Boolean(groqKey || openaiKey);
|
|
443
|
+
if (process.platform === 'win32' && hasWhisperKey) {
|
|
444
|
+
const recorderExe = await ensureWindowsAudioRecorder();
|
|
445
|
+
if (recorderExe) {
|
|
446
|
+
const tempWav = path.join(os.tmpdir(), `scout_mic_${Date.now()}.wav`);
|
|
447
|
+
try {
|
|
448
|
+
const recorded = await recordAudioToWav(recorderExe, tempWav, timeoutSecs, silenceCutoffMs, s);
|
|
449
|
+
if (recorded) {
|
|
450
|
+
s.message(chalk.magenta(' Transcribing with Neural Whisper STT...'));
|
|
451
|
+
const transcript = await transcribeAudioWithWhisper(tempWav);
|
|
452
|
+
if (transcript) {
|
|
453
|
+
s.stop(chalk.green(' Speech recognized via Neural Whisper.'));
|
|
454
|
+
return transcript;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
catch {
|
|
459
|
+
// Fall through to legacy
|
|
460
|
+
}
|
|
461
|
+
finally {
|
|
462
|
+
if (fs.existsSync(tempWav)) {
|
|
463
|
+
try {
|
|
464
|
+
fs.unlinkSync(tempWav);
|
|
465
|
+
}
|
|
466
|
+
catch { }
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
// Fallback to legacy System.Speech if no recorder or offline
|
|
472
|
+
return listenSpeechToTextLegacy(timeoutSecs, s);
|
|
473
|
+
}
|
|
474
|
+
//# sourceMappingURL=voiceEngine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voiceEngine.js","sourceRoot":"","sources":["voiceEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,UAAU,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAE3C,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAElC,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACvC,IAAI,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC;YACxB,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,0BAA0B,CAAC;YACpD,OAAO,EAAE;gBACL,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,qCAAqC,EAAE;gBACzF,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,mBAAmB,EAAE,IAAI,EAAE,0DAA0D,EAAE;aACnH;SACJ,CAAC,CAAC;QACH,IAAI,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC;QACpC,OAAO,MAA0B,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,MAAM,CAAC;IAClB,CAAC;AACL,CAAC;AAUD;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC3C,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,OAAO,IAAI;SACN,OAAO,CAAC,iBAAiB,EAAE,wBAAwB,CAAC;SACpD,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC;SAC9B,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC;SAC5B,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,SAAiB,EAAE,UAAU,GAAG,KAAK;IACzD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAChC,MAAM,WAAW,GAAG,CAAC,CAAC;IACtB,MAAM,aAAa,GAAG,EAAE,CAAC;IACzB,MAAM,QAAQ,GAAG,UAAU,GAAG,WAAW,GAAG,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC;IAChE,MAAM,UAAU,GAAG,WAAW,GAAG,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,CAAC;IAEpC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACxB,MAAM,CAAC,aAAa,CAAC,EAAE,GAAG,UAAU,EAAE,CAAC,CAAC,CAAC;IACzC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACxB,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACzB,MAAM,CAAC,aAAa,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAC7B,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa;IAC1C,MAAM,CAAC,aAAa,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACtC,MAAM,CAAC,aAAa,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACrC,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACnC,MAAM,CAAC,aAAa,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACrC,MAAM,CAAC,aAAa,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IACxC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACzB,MAAM,CAAC,aAAa,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAErC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,mBAAmB,CAAC,IAAY,EAAE,OAAsB;IACnE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAC9C,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAE1B,8EAA8E;IAC9E,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,sBAAsB,CAAC;IAE9F,IAAI,CAAC;QACD,MAAM,GAAG,GAAG,+CAA+C,OAAO,0BAA0B,CAAC;QAC7F,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC9B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACL,YAAY,EAAE,MAAM;gBACpB,cAAc,EAAE,kBAAkB;aACrC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACjB,IAAI;gBACJ,QAAQ,EAAE,mBAAmB;gBAC7B,cAAc,EAAE;oBACZ,SAAS,EAAE,GAAG;oBACd,gBAAgB,EAAE,GAAG;iBACxB;aACJ,CAAC;SACL,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACf,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC;QACjD,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC3C,IAAI,SAAS,CAAC,MAAM,GAAG,GAAG;YAAE,OAAO,KAAK,CAAC;QAEzC,MAAM,SAAS,GAAG,cAAc,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACnD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,aAAa,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACtE,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAErC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QAClC,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;YACvB,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAChD,MAAM,QAAQ,GAAG,kDAAkD,WAAW,wBAAwB,CAAC;YACvG,IAAI,OAAO,EAAE,KAAK,EAAE,CAAC;gBACjB,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC,YAAY,EAAE,kBAAkB,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,QAAQ,wBAAwB,WAAW,GAAG,CAAC,EAAE;oBAC3I,QAAQ,EAAE,IAAI;oBACd,KAAK,EAAE,QAAQ;iBAClB,CAAC,CAAC;gBACH,KAAK,CAAC,KAAK,EAAE,CAAC;YAClB,CAAC;iBAAM,CAAC;gBACJ,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;oBAChC,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC,YAAY,EAAE,kBAAkB,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;oBACtG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;wBACnB,IAAI,CAAC;4BAAC,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;gCAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;wBAAC,CAAC;wBAAC,MAAM,CAAC,CAAA,CAAC;wBACpE,OAAO,EAAE,CAAC;oBACd,CAAC,CAAC,CAAC;oBACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;wBACnB,IAAI,CAAC;4BAAC,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;gCAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;wBAAC,CAAC;wBAAC,MAAM,CAAC,CAAA,CAAC;wBACpE,OAAO,EAAE,CAAC;oBACd,CAAC,CAAC,CAAC;gBACP,CAAC,CAAC,CAAC;YACP,CAAC;QACL,CAAC;aAAM,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC/B,IAAI,OAAO,EAAE,KAAK,EAAE,CAAC;gBACjB,IAAI,CAAC,WAAW,OAAO,eAAe,OAAO,GAAG,CAAC,CAAC;YACtD,CAAC;iBAAM,CAAC;gBACJ,MAAM,SAAS,CAAC,WAAW,OAAO,GAAG,CAAC,CAAC;gBACvC,IAAI,CAAC;oBAAC,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;wBAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAA,CAAC;YACxE,CAAC;QACL,CAAC;aAAM,CAAC;YACJ,QAAQ;YACR,IAAI,OAAO,EAAE,KAAK,EAAE,CAAC;gBACjB,IAAI,CAAC,WAAW,OAAO,gBAAgB,OAAO,gBAAgB,OAAO,GAAG,CAAC,CAAC;YAC9E,CAAC;iBAAM,CAAC;gBACJ,MAAM,SAAS,CAAC,UAAU,OAAO,gBAAgB,OAAO,GAAG,CAAC,CAAC;gBAC7D,IAAI,CAAC;oBAAC,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;wBAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAA,CAAC;YACxE,CAAC;QACL,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY,EAAE,OAAsB;IAChE,MAAM,QAAQ,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC,QAAQ;QAAE,OAAO;IAEtB,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;IAExF,0DAA0D;IAC1D,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACnD,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAEjD,IAAI,KAAK,IAAI,SAAS,EAAE,CAAC;QACrB,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;YACjE,IAAI,MAAM;gBAAE,OAAO;QACvB,CAAC;QAAC,MAAM,CAAC;YACL,uDAAuD;QAC3D,CAAC;IACL,CAAC;IAED,gEAAgE;IAChE,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,IAAI,CAAC;QACD,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACtE,MAAM,QAAQ,GAAG,sHAAsH,OAAO,KAAK,CAAC;YAEpJ,IAAI,OAAO,EAAE,KAAK,EAAE,CAAC;gBACjB,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC,YAAY,EAAE,kBAAkB,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;gBAC3I,KAAK,CAAC,KAAK,EAAE,CAAC;YAClB,CAAC;iBAAM,CAAC;gBACJ,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;oBAChC,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC,YAAY,EAAE,kBAAkB,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;oBACtG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;oBACnC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;gBACvC,CAAC,CAAC,CAAC;YACP,CAAC;QACL,CAAC;aAAM,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC/B,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACnD,MAAM,GAAG,GAAG,QAAQ,OAAO,GAAG,CAAC;YAC/B,IAAI,OAAO,EAAE,KAAK,EAAE,CAAC;gBACjB,IAAI,CAAC,GAAG,CAAC,CAAC;YACd,CAAC;iBAAM,CAAC;gBACJ,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;QACL,CAAC;aAAM,CAAC;YACJ,iBAAiB;YACjB,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACnD,MAAM,GAAG,GAAG,YAAY,OAAO,gBAAgB,OAAO,GAAG,CAAC;YAC1D,IAAI,OAAO,EAAE,KAAK,EAAE,CAAC;gBACjB,IAAI,CAAC,GAAG,CAAC,CAAC;YACd,CAAC;iBAAM,CAAC;gBACJ,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;QACL,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACL,4CAA4C;IAChD,CAAC;AACL,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,0BAA0B;IACrC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IAE9C,MAAM,aAAa,GAAG;QAClB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,0BAA0B,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,0BAA0B,CAAC;KACrD,CAAC;IAEF,MAAM,kBAAkB,GAAG;QACvB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,uBAAuB,CAAC;QAC7C,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,uBAAuB,CAAC;KAC5E,CAAC;IAEF,MAAM,QAAQ,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAClE,MAAM,MAAM,GAAG,8DAA8D,CAAC;IAE9E,KAAK,MAAM,OAAO,IAAI,aAAa,EAAE,CAAC;QAClC,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACzB,IAAI,QAAQ,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBACpC,IAAI,CAAC;oBACD,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC;oBAC/C,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC;oBAC9C,IAAI,QAAQ,GAAG,QAAQ,EAAE,CAAC;wBACtB,MAAM,SAAS,CAAC,IAAI,MAAM,oCAAoC,OAAO,MAAM,QAAQ,GAAG,CAAC,CAAC;oBAC5F,CAAC;gBACL,CAAC;gBAAC,MAAM,CAAC,CAAA,CAAC;YACd,CAAC;YACD,OAAO,OAAO,CAAC;QACnB,CAAC;IACL,CAAC;IAED,IAAI,QAAQ,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACpC,MAAM,SAAS,GAAW,aAAa,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,0BAA0B,CAAC,CAAC;QAC/F,IAAI,CAAC;YACD,MAAM,SAAS,CAAC,IAAI,MAAM,oCAAoC,SAAS,MAAM,QAAQ,GAAG,CAAC,CAAC;YAC1F,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC3B,OAAO,SAAS,CAAC;YACrB,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACL,4CAA4C;YAC5C,MAAM,OAAO,GAAW,aAAa,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,0BAA0B,CAAC,CAAC;YAC/F,IAAI,CAAC;gBACD,MAAM,SAAS,CAAC,IAAI,MAAM,oCAAoC,OAAO,MAAM,QAAQ,GAAG,CAAC,CAAC;gBACxF,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;oBACzB,OAAO,OAAO,CAAC;gBACnB,CAAC;YACL,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;QACd,CAAC;IACL,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,gBAAgB,CAC3B,WAAmB,EACnB,OAAe,EACf,eAAuB,EACvB,eAAuB,EACvB,CAA6B;IAE7B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE;YAC1F,WAAW,EAAE,IAAI;SACpB,CAAC,CAAC;QAEH,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC5B,IAAI,GAAG,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBAClC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,yEAAyE,CAAC,CAAC,CAAC;YACrG,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACvB,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;gBACvC,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;gBAClC,OAAO,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;YAC9B,CAAC;iBAAM,CAAC;gBACJ,OAAO,CAAC,KAAK,CAAC,CAAC;YACnB,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACnB,OAAO,CAAC,KAAK,CAAC,CAAC;QACnB,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,0BAA0B,CAAC,OAAe;IACrD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IACzC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAE7C,MAAM,eAAe,GAAG,gIAAgI,CAAC;IAEzJ,2CAA2C;IAC3C,IAAI,OAAO,EAAE,CAAC;QACV,IAAI,CAAC;YACD,MAAM,UAAU,GAAG,IAAI,MAAM,CAAC;gBAC1B,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,gCAAgC;gBACzC,OAAO,EAAE,KAAK;aACjB,CAAC,CAAC;YAEH,MAAM,aAAa,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC;gBAC/D,IAAI,EAAE,EAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC;gBAClC,KAAK,EAAE,wBAAwB;gBAC/B,MAAM,EAAE,eAAe;gBACvB,WAAW,EAAE,GAAG;aACnB,CAAC,CAAC;YAEH,IAAI,aAAa,IAAI,aAAa,CAAC,IAAI,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;gBACnE,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACrC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACL,kEAAkE;QACtE,CAAC;IACL,CAAC;IAED,oCAAoC;IACpC,IAAI,SAAS,EAAE,CAAC;QACZ,IAAI,CAAC;YACD,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC;gBAC5B,MAAM,EAAE,SAAS;gBACjB,OAAO,EAAE,KAAK;aACjB,CAAC,CAAC;YAEH,MAAM,aAAa,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC;gBACjE,IAAI,EAAE,EAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC;gBAClC,KAAK,EAAE,WAAW;gBAClB,MAAM,EAAE,eAAe;gBACvB,WAAW,EAAE,GAAG;aACnB,CAAC,CAAC;YAEH,IAAI,aAAa,IAAI,aAAa,CAAC,IAAI,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;gBACnE,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACrC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACL,wBAAwB;QAC5B,CAAC;IACL,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,wBAAwB,CAAC,WAAmB,EAAE,CAA6B;IAChF,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC3B,MAAM,QAAQ,GAAG;;;;;;;;;0DAS6B,WAAW;;;;;;;;;CASpE,CAAC,IAAI,EAAE,CAAC;YAEG,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC,YAAY,EAAE,kBAAkB,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;YAEtG,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;gBAC9B,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC9B,CAAC,CAAC,CAAC;YAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBACnB,CAAC,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;gBAElD,IAAI,UAAU,GAAG,EAAE,CAAC;gBACpB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;gBACtD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBACvB,IAAI,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;wBACjC,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;wBACpD,MAAM;oBACV,CAAC;gBACL,CAAC;gBAED,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;YAC9B,CAAC,CAAC,CAAC;YAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBACnB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,qCAAqC,CAAC,CAAC,CAAC;gBAC5D,OAAO,CAAC,EAAE,CAAC,CAAC;YAChB,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACP,CAAC;SAAM,CAAC;QACJ,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC3B,UAAU,CAAC,GAAG,EAAE;gBACZ,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,yCAAyC,CAAC,CAAC,CAAC;gBAChE,OAAO,CAAC,EAAE,CAAC,CAAC;YAChB,CAAC,EAAE,IAAI,CAAC,CAAC;QACb,CAAC,CAAC,CAAC;IACP,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAAgC;IACrE,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,SAAS,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACjG,MAAM,eAAe,GAAG,IAAI,CAAC,CAAC,qCAAqC;IACnE,MAAM,CAAC,GAAG,OAAO,EAAE,CAAC;IACpB,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,eAAe,WAAW,WAAW,CAAC,CAAC,CAAC;IAE3D,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IACzC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC7C,MAAM,aAAa,GAAG,OAAO,CAAC,OAAO,IAAI,SAAS,CAAC,CAAC;IAEpD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,aAAa,EAAE,CAAC;QAChD,MAAM,WAAW,GAAG,MAAM,0BAA0B,EAAE,CAAC;QACvD,IAAI,WAAW,EAAE,CAAC;YACd,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,aAAa,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YACtE,IAAI,CAAC;gBACD,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC;gBAC/F,IAAI,QAAQ,EAAE,CAAC;oBACX,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,0CAA0C,CAAC,CAAC,CAAC;oBACrE,MAAM,UAAU,GAAG,MAAM,0BAA0B,CAAC,OAAO,CAAC,CAAC;oBAC7D,IAAI,UAAU,EAAE,CAAC;wBACb,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC,CAAC;wBAC9D,OAAO,UAAU,CAAC;oBACtB,CAAC;gBACL,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACL,yBAAyB;YAC7B,CAAC;oBAAS,CAAC;gBACP,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;oBACzB,IAAI,CAAC;wBACD,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;oBAC3B,CAAC;oBAAC,MAAM,CAAC,CAAA,CAAC;gBACd,CAAC;YACL,CAAC;QACL,CAAC;IACL,CAAC;IAED,6DAA6D;IAC7D,OAAO,wBAAwB,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["web.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,eAAe,EAAE,CAAC;CAC9B;AAcD;;GAEG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,GAAE,MAAU,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CA6FjG;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,GAAE,MAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAgC/F;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAsB5E"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
function decodeHTMLEntities(text) {
|
|
2
|
+
if (!text)
|
|
3
|
+
return '';
|
|
4
|
+
return text
|
|
5
|
+
.replace(/'/g, "'")
|
|
6
|
+
.replace(/"/g, '"')
|
|
7
|
+
.replace(/'/g, "'")
|
|
8
|
+
.replace(/</g, '<')
|
|
9
|
+
.replace(/>/g, '>')
|
|
10
|
+
.replace(/&/g, '&')
|
|
11
|
+
.replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(Number(dec)));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Perform a live web search using DuckDuckGo (HTML endpoint) with fallback support for Tavily / Serper API keys if provided in environment.
|
|
15
|
+
*/
|
|
16
|
+
export async function searchWeb(query, maxResults = 5) {
|
|
17
|
+
const cleanQuery = query.trim();
|
|
18
|
+
if (!cleanQuery)
|
|
19
|
+
return [];
|
|
20
|
+
// Check for Tavily API key in environment
|
|
21
|
+
const tavilyKey = process.env.TAVILY_API_KEY || process.env.TAVILY_KEY;
|
|
22
|
+
if (tavilyKey) {
|
|
23
|
+
try {
|
|
24
|
+
const res = await fetch('https://api.tavily.com/search', {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: { 'Content-Type': 'application/json' },
|
|
27
|
+
body: JSON.stringify({ api_key: tavilyKey, query: cleanQuery, max_results: maxResults }),
|
|
28
|
+
signal: AbortSignal.timeout(6000),
|
|
29
|
+
});
|
|
30
|
+
if (res.ok) {
|
|
31
|
+
const data = (await res.json());
|
|
32
|
+
if (data.results && Array.isArray(data.results)) {
|
|
33
|
+
return data.results.map((r) => ({
|
|
34
|
+
title: r.title || 'Untitled',
|
|
35
|
+
url: r.url || '',
|
|
36
|
+
snippet: r.content || r.snippet || '',
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch { }
|
|
42
|
+
}
|
|
43
|
+
// Check for Serper API key in environment
|
|
44
|
+
const serperKey = process.env.SERPER_API_KEY;
|
|
45
|
+
if (serperKey) {
|
|
46
|
+
try {
|
|
47
|
+
const res = await fetch('https://google.serper.dev/search', {
|
|
48
|
+
method: 'POST',
|
|
49
|
+
headers: {
|
|
50
|
+
'X-API-KEY': serperKey,
|
|
51
|
+
'Content-Type': 'application/json',
|
|
52
|
+
},
|
|
53
|
+
body: JSON.stringify({ q: cleanQuery, num: maxResults }),
|
|
54
|
+
signal: AbortSignal.timeout(6000),
|
|
55
|
+
});
|
|
56
|
+
if (res.ok) {
|
|
57
|
+
const data = (await res.json());
|
|
58
|
+
if (data.organic && Array.isArray(data.organic)) {
|
|
59
|
+
return data.organic.map((r) => ({
|
|
60
|
+
title: r.title || 'Untitled',
|
|
61
|
+
url: r.link || '',
|
|
62
|
+
snippet: r.snippet || '',
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch { }
|
|
68
|
+
}
|
|
69
|
+
// Fallback: Free DuckDuckGo HTML Search
|
|
70
|
+
try {
|
|
71
|
+
const res = await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(cleanQuery)}`, {
|
|
72
|
+
headers: {
|
|
73
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
|
|
74
|
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
75
|
+
'Accept-Language': 'en-US,en;q=0.9',
|
|
76
|
+
},
|
|
77
|
+
signal: AbortSignal.timeout(7000),
|
|
78
|
+
});
|
|
79
|
+
if (!res.ok)
|
|
80
|
+
return [];
|
|
81
|
+
const html = await res.text();
|
|
82
|
+
const regex = /<a[^>]*class="result__a"[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>[\s\S]*?(?:class="result__snippet"[^>]*>([\s\S]*?)<\/(?:a|td|div)>)/gi;
|
|
83
|
+
const results = [];
|
|
84
|
+
let match;
|
|
85
|
+
while ((match = regex.exec(html)) !== null && results.length < maxResults) {
|
|
86
|
+
let rawUrl = match[1] || '';
|
|
87
|
+
if (rawUrl.includes('uddg=')) {
|
|
88
|
+
const uddgMatch = rawUrl.match(/uddg=([^&]+)/);
|
|
89
|
+
if (uddgMatch && uddgMatch[1]) {
|
|
90
|
+
rawUrl = decodeURIComponent(uddgMatch[1]);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const title = decodeHTMLEntities((match[2] || '').replace(/<[^>]+>/g, '').trim());
|
|
94
|
+
const snippet = decodeHTMLEntities((match[3] || '').replace(/<[^>]+>/g, '').trim());
|
|
95
|
+
if (title && rawUrl && !rawUrl.startsWith('/')) {
|
|
96
|
+
results.push({ title, url: rawUrl, snippet });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return results;
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Fetch text content of a specific webpage
|
|
107
|
+
*/
|
|
108
|
+
export async function fetchWebPage(targetUrl, maxLength = 2500) {
|
|
109
|
+
if (!targetUrl || (!targetUrl.startsWith('http://') && !targetUrl.startsWith('https://'))) {
|
|
110
|
+
return '';
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const res = await fetch(targetUrl, {
|
|
114
|
+
headers: {
|
|
115
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
|
|
116
|
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
117
|
+
},
|
|
118
|
+
signal: AbortSignal.timeout(6000),
|
|
119
|
+
});
|
|
120
|
+
if (!res.ok)
|
|
121
|
+
return '';
|
|
122
|
+
const html = await res.text();
|
|
123
|
+
const cleanText = html
|
|
124
|
+
.replace(/<script[\s\S]*?<\/script>/gi, '')
|
|
125
|
+
.replace(/<style[\s\S]*?<\/style>/gi, '')
|
|
126
|
+
.replace(/<nav[\s\S]*?<\/nav>/gi, '')
|
|
127
|
+
.replace(/<footer[\s\S]*?<\/footer>/gi, '')
|
|
128
|
+
.replace(/<header[\s\S]*?<\/header>/gi, '')
|
|
129
|
+
.replace(/<[^>]+>/g, ' ')
|
|
130
|
+
.replace(/\s+/g, ' ')
|
|
131
|
+
.trim();
|
|
132
|
+
return decodeHTMLEntities(cleanText).slice(0, maxLength);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return '';
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Build rich formatted web search context string for LLM prompts
|
|
140
|
+
*/
|
|
141
|
+
export async function getWebContext(query) {
|
|
142
|
+
const sources = await searchWeb(query, 5);
|
|
143
|
+
if (sources.length === 0) {
|
|
144
|
+
return { context: '', sources: [] };
|
|
145
|
+
}
|
|
146
|
+
let formatted = ` LIVE INTERNET SEARCH RESULTS FOR QUERY: "${query}"\n\n`;
|
|
147
|
+
sources.forEach((s, idx) => {
|
|
148
|
+
formatted += `[Source ${idx + 1}]: ${s.title}\n`;
|
|
149
|
+
formatted += `URL: ${s.url}\n`;
|
|
150
|
+
formatted += `Snippet: ${s.snippet}\n\n`;
|
|
151
|
+
});
|
|
152
|
+
// Fetch top 1 result details if available
|
|
153
|
+
if (sources[0] && sources[0].url) {
|
|
154
|
+
const pageText = await fetchWebPage(sources[0].url, 1500);
|
|
155
|
+
if (pageText) {
|
|
156
|
+
formatted += `--- Extracted Content from Top Result (${sources[0].title}) ---\n${pageText}\n\n`;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return { context: formatted, sources };
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=web.js.map
|