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.
Files changed (243) hide show
  1. package/.firebase/hosting.d2Vi.cache +9 -0
  2. package/.firebaserc +14 -0
  3. package/AUTONOMOUS_FEATURES.md +192 -0
  4. package/README.md +482 -32
  5. package/bin/ft.js +2 -0
  6. package/bin/src/commands/agent.d.ts.map +1 -0
  7. package/bin/src/commands/agent.js +1230 -0
  8. package/bin/src/commands/agent.js.map +1 -0
  9. package/bin/src/commands/audit.d.ts.map +1 -0
  10. package/bin/src/commands/audit.js +104 -0
  11. package/bin/src/commands/audit.js.map +1 -0
  12. package/bin/src/commands/authCommand.d.ts.map +1 -0
  13. package/bin/src/commands/authCommand.js +38 -0
  14. package/bin/src/commands/authCommand.js.map +1 -0
  15. package/bin/src/commands/benchmark.d.ts.map +1 -0
  16. package/bin/src/commands/benchmark.js +135 -0
  17. package/bin/src/commands/benchmark.js.map +1 -0
  18. package/bin/src/commands/brief.d.ts.map +1 -1
  19. package/bin/src/commands/brief.js +28 -19
  20. package/bin/src/commands/brief.js.map +1 -1
  21. package/bin/src/commands/configCommand.d.ts.map +1 -0
  22. package/bin/src/commands/configCommand.js +53 -0
  23. package/bin/src/commands/configCommand.js.map +1 -0
  24. package/bin/src/commands/dashboard.d.ts.map +1 -0
  25. package/bin/src/commands/dashboard.js +41 -0
  26. package/bin/src/commands/dashboard.js.map +1 -0
  27. package/bin/src/commands/history.d.ts.map +1 -0
  28. package/bin/src/commands/history.js +20 -0
  29. package/bin/src/commands/history.js.map +1 -0
  30. package/bin/src/commands/init.d.ts.map +1 -1
  31. package/bin/src/commands/init.js +411 -78
  32. package/bin/src/commands/init.js.map +1 -1
  33. package/bin/src/commands/login.d.ts.map +1 -0
  34. package/bin/src/commands/login.js +190 -0
  35. package/bin/src/commands/login.js.map +1 -0
  36. package/bin/src/commands/logout.d.ts.map +1 -0
  37. package/bin/src/commands/logout.js +15 -0
  38. package/bin/src/commands/logout.js.map +1 -0
  39. package/bin/src/commands/mcp.d.ts.map +1 -0
  40. package/bin/src/commands/mcp.js +393 -0
  41. package/bin/src/commands/mcp.js.map +1 -0
  42. package/bin/src/commands/owners.js +6 -6
  43. package/bin/src/commands/owners.js.map +1 -1
  44. package/bin/src/commands/plan.d.ts.map +1 -0
  45. package/bin/src/commands/plan.js +152 -0
  46. package/bin/src/commands/plan.js.map +1 -0
  47. package/bin/src/commands/quota.d.ts.map +1 -0
  48. package/bin/src/commands/quota.js +32 -0
  49. package/bin/src/commands/quota.js.map +1 -0
  50. package/bin/src/commands/recommend.d.ts.map +1 -0
  51. package/bin/src/commands/recommend.js +46 -0
  52. package/bin/src/commands/recommend.js.map +1 -0
  53. package/bin/src/commands/risky.d.ts.map +1 -1
  54. package/bin/src/commands/risky.js +235 -39
  55. package/bin/src/commands/risky.js.map +1 -1
  56. package/bin/src/commands/setup.d.ts.map +1 -1
  57. package/bin/src/commands/setup.js +411 -16
  58. package/bin/src/commands/setup.js.map +1 -1
  59. package/bin/src/commands/signup.d.ts.map +1 -0
  60. package/bin/src/commands/signup.js +118 -0
  61. package/bin/src/commands/signup.js.map +1 -0
  62. package/bin/src/commands/undo.d.ts.map +1 -0
  63. package/bin/src/commands/undo.js +59 -0
  64. package/bin/src/commands/undo.js.map +1 -0
  65. package/bin/src/commands/web.d.ts.map +1 -0
  66. package/bin/src/commands/web.js +90 -0
  67. package/bin/src/commands/web.js.map +1 -0
  68. package/bin/src/discovery/config.d.ts.map +1 -0
  69. package/bin/src/discovery/config.js +41 -0
  70. package/bin/src/discovery/config.js.map +1 -0
  71. package/bin/src/discovery/display.d.ts.map +1 -0
  72. package/bin/src/discovery/display.js +57 -0
  73. package/bin/src/discovery/display.js.map +1 -0
  74. package/bin/src/discovery/messages.d.ts.map +1 -0
  75. package/bin/src/discovery/messages.js +7 -0
  76. package/bin/src/discovery/messages.js.map +1 -0
  77. package/bin/src/discovery/signals.d.ts.map +1 -0
  78. package/bin/src/discovery/signals.js +33 -0
  79. package/bin/src/discovery/signals.js.map +1 -0
  80. package/bin/src/engine/actionVerifier.d.ts.map +1 -0
  81. package/bin/src/engine/actionVerifier.js +283 -0
  82. package/bin/src/engine/actionVerifier.js.map +1 -0
  83. package/bin/src/engine/agentEngine.d.ts.map +1 -0
  84. package/bin/src/engine/agentEngine.js +2188 -0
  85. package/bin/src/engine/agentEngine.js.map +1 -0
  86. package/bin/src/engine/agentState.d.ts.map +1 -0
  87. package/bin/src/engine/agentState.js +381 -0
  88. package/bin/src/engine/agentState.js.map +1 -0
  89. package/bin/src/engine/browser/browserController.d.ts.map +1 -0
  90. package/bin/src/engine/browser/browserController.js +737 -0
  91. package/bin/src/engine/browser/browserController.js.map +1 -0
  92. package/bin/src/engine/browser/index.d.ts.map +1 -0
  93. package/bin/src/engine/browser/index.js +2 -0
  94. package/bin/src/engine/browser/index.js.map +1 -0
  95. package/bin/src/engine/computer/computerController.d.ts.map +1 -0
  96. package/bin/src/engine/computer/computerController.js +773 -0
  97. package/bin/src/engine/computer/computerController.js.map +1 -0
  98. package/bin/src/engine/computer/computerDiagnostic.d.ts.map +1 -0
  99. package/bin/src/engine/computer/computerDiagnostic.js +71 -0
  100. package/bin/src/engine/computer/computerDiagnostic.js.map +1 -0
  101. package/bin/src/engine/computer/index.d.ts.map +1 -0
  102. package/bin/src/engine/computer/index.js +4 -0
  103. package/bin/src/engine/computer/index.js.map +1 -0
  104. package/bin/src/engine/computer/mockComputer.d.ts.map +1 -0
  105. package/bin/src/engine/computer/mockComputer.js +260 -0
  106. package/bin/src/engine/computer/mockComputer.js.map +1 -0
  107. package/bin/src/engine/computer/scoutDriver.cs +1161 -0
  108. package/bin/src/engine/computer/scout_driver.exe +0 -0
  109. package/bin/src/engine/git.d.ts.map +1 -1
  110. package/bin/src/engine/git.js +71 -0
  111. package/bin/src/engine/git.js.map +1 -1
  112. package/bin/src/engine/llm.d.ts.map +1 -1
  113. package/bin/src/engine/llm.js +1272 -23
  114. package/bin/src/engine/llm.js.map +1 -1
  115. package/bin/src/engine/planner.d.ts.map +1 -0
  116. package/bin/src/engine/planner.js +321 -0
  117. package/bin/src/engine/planner.js.map +1 -0
  118. package/bin/src/engine/runtime/agentRuntime.d.ts.map +1 -0
  119. package/bin/src/engine/runtime/agentRuntime.js +1258 -0
  120. package/bin/src/engine/runtime/agentRuntime.js.map +1 -0
  121. package/bin/src/engine/runtime/index.d.ts.map +1 -0
  122. package/bin/src/engine/runtime/index.js +2 -0
  123. package/bin/src/engine/runtime/index.js.map +1 -0
  124. package/bin/src/engine/safety.d.ts.map +1 -0
  125. package/bin/src/engine/safety.js +316 -0
  126. package/bin/src/engine/safety.js.map +1 -0
  127. package/bin/src/engine/scoutAudioRecorder.cs +215 -0
  128. package/bin/src/engine/scout_audio_recorder.exe +0 -0
  129. package/bin/src/engine/toolRegistry.d.ts.map +1 -0
  130. package/bin/src/engine/toolRegistry.js +521 -0
  131. package/bin/src/engine/toolRegistry.js.map +1 -0
  132. package/bin/src/engine/verifier.d.ts.map +1 -0
  133. package/bin/src/engine/verifier.js +414 -0
  134. package/bin/src/engine/verifier.js.map +1 -0
  135. package/bin/src/engine/voiceEngine.d.ts.map +1 -0
  136. package/bin/src/engine/voiceEngine.js +474 -0
  137. package/bin/src/engine/voiceEngine.js.map +1 -0
  138. package/bin/src/engine/web.d.ts.map +1 -0
  139. package/bin/src/engine/web.js +161 -0
  140. package/bin/src/engine/web.js.map +1 -0
  141. package/bin/src/engine/workspace/agentWorkspace.d.ts.map +1 -0
  142. package/bin/src/engine/workspace/agentWorkspace.js +505 -0
  143. package/bin/src/engine/workspace/agentWorkspace.js.map +1 -0
  144. package/bin/src/engine/workspace/index.d.ts.map +1 -0
  145. package/bin/src/engine/workspace/index.js +2 -0
  146. package/bin/src/engine/workspace/index.js.map +1 -0
  147. package/bin/src/index.js +365 -23
  148. package/bin/src/index.js.map +1 -1
  149. package/bin/src/mcp/client.d.ts.map +1 -0
  150. package/bin/src/mcp/client.js +127 -0
  151. package/bin/src/mcp/client.js.map +1 -0
  152. package/bin/src/mcp/discovery.d.ts.map +1 -0
  153. package/bin/src/mcp/discovery.js +91 -0
  154. package/bin/src/mcp/discovery.js.map +1 -0
  155. package/bin/src/mcp/health.d.ts.map +1 -0
  156. package/bin/src/mcp/health.js +44 -0
  157. package/bin/src/mcp/health.js.map +1 -0
  158. package/bin/src/mcp/index.d.ts.map +1 -0
  159. package/bin/src/mcp/index.js +15 -0
  160. package/bin/src/mcp/index.js.map +1 -0
  161. package/bin/src/mcp/manager.d.ts.map +1 -0
  162. package/bin/src/mcp/manager.js +648 -0
  163. package/bin/src/mcp/manager.js.map +1 -0
  164. package/bin/src/mcp/permissions.d.ts.map +1 -0
  165. package/bin/src/mcp/permissions.js +50 -0
  166. package/bin/src/mcp/permissions.js.map +1 -0
  167. package/bin/src/mcp/persistence.d.ts.map +1 -0
  168. package/bin/src/mcp/persistence.js +171 -0
  169. package/bin/src/mcp/persistence.js.map +1 -0
  170. package/bin/src/mcp/recovery.d.ts.map +1 -0
  171. package/bin/src/mcp/recovery.js +75 -0
  172. package/bin/src/mcp/recovery.js.map +1 -0
  173. package/bin/src/mcp/telemetry.d.ts.map +1 -0
  174. package/bin/src/mcp/telemetry.js +82 -0
  175. package/bin/src/mcp/telemetry.js.map +1 -0
  176. package/bin/src/mcp/toolRouter.d.ts.map +1 -0
  177. package/bin/src/mcp/toolRouter.js +127 -0
  178. package/bin/src/mcp/toolRouter.js.map +1 -0
  179. package/bin/src/mcp/transport.d.ts.map +1 -0
  180. package/bin/src/mcp/transport.js +328 -0
  181. package/bin/src/mcp/transport.js.map +1 -0
  182. package/bin/src/mcp/types.d.ts.map +1 -0
  183. package/bin/src/mcp/types.js +6 -0
  184. package/bin/src/mcp/types.js.map +1 -0
  185. package/bin/src/server/server.d.ts.map +1 -0
  186. package/bin/src/server/server.js +989 -0
  187. package/bin/src/server/server.js.map +1 -0
  188. package/bin/src/utils/admin.d.ts.map +1 -0
  189. package/bin/src/utils/admin.js +231 -0
  190. package/bin/src/utils/admin.js.map +1 -0
  191. package/bin/src/utils/auditLogger.d.ts.map +1 -0
  192. package/bin/src/utils/auditLogger.js +70 -0
  193. package/bin/src/utils/auditLogger.js.map +1 -0
  194. package/bin/src/utils/auth.d.ts.map +1 -0
  195. package/bin/src/utils/auth.js +679 -0
  196. package/bin/src/utils/auth.js.map +1 -0
  197. package/bin/src/utils/branding.d.ts.map +1 -0
  198. package/bin/src/utils/branding.js +14 -0
  199. package/bin/src/utils/branding.js.map +1 -0
  200. package/bin/src/utils/config.d.ts.map +1 -1
  201. package/bin/src/utils/config.js +124 -3
  202. package/bin/src/utils/config.js.map +1 -1
  203. package/bin/src/utils/email.d.ts.map +1 -0
  204. package/bin/src/utils/email.js +267 -0
  205. package/bin/src/utils/email.js.map +1 -0
  206. package/bin/src/utils/firebaseAuth.d.ts.map +1 -0
  207. package/bin/src/utils/firebaseAuth.js +174 -0
  208. package/bin/src/utils/firebaseAuth.js.map +1 -0
  209. package/bin/src/utils/holoseneAuth.d.ts.map +1 -0
  210. package/bin/src/utils/holoseneAuth.js +307 -0
  211. package/bin/src/utils/holoseneAuth.js.map +1 -0
  212. package/bin/src/utils/language.d.ts.map +1 -0
  213. package/bin/src/utils/language.js +181 -0
  214. package/bin/src/utils/language.js.map +1 -0
  215. package/bin/src/utils/markdown.d.ts.map +1 -0
  216. package/bin/src/utils/markdown.js +215 -0
  217. package/bin/src/utils/markdown.js.map +1 -0
  218. package/bin/src/utils/payment.d.ts.map +1 -0
  219. package/bin/src/utils/payment.js +290 -0
  220. package/bin/src/utils/payment.js.map +1 -0
  221. package/bin/src/utils/pricing.d.ts.map +1 -0
  222. package/bin/src/utils/pricing.js +165 -0
  223. package/bin/src/utils/pricing.js.map +1 -0
  224. package/bin/src/utils/razorpay.d.ts.map +1 -0
  225. package/bin/src/utils/razorpay.js +144 -0
  226. package/bin/src/utils/razorpay.js.map +1 -0
  227. package/bin/src/utils/session.d.ts.map +1 -0
  228. package/bin/src/utils/session.js +82 -0
  229. package/bin/src/utils/session.js.map +1 -0
  230. package/bin/src/utils/updateCheck.d.ts.map +1 -0
  231. package/bin/src/utils/updateCheck.js +108 -0
  232. package/bin/src/utils/updateCheck.js.map +1 -0
  233. package/downloads/invoice-2026-03.pdf +1 -0
  234. package/firebase.json +74 -0
  235. package/package.json +49 -46
  236. package/web/ScoutFavicon.png +0 -0
  237. package/web/app.js +3944 -0
  238. package/web/styles.css +3081 -0
  239. package/.ft/context.json +0 -18
  240. package/email-template.html +0 -223
  241. package/email-template.txt +0 -45
  242. package/scripts/send-brevo-campaign.cjs +0 -167
  243. 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":"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"}
@@ -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(/&#x27;/g, "'")
6
+ .replace(/&quot;/g, '"')
7
+ .replace(/&apos;/g, "'")
8
+ .replace(/&lt;/g, '<')
9
+ .replace(/&gt;/g, '>')
10
+ .replace(/&amp;/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