thaddeus 1.0.15 → 1.0.17
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/bin/thaddeus.js +61 -8
- package/package.json +1 -1
- package/src/commandCenter/server.ts +37 -4
package/bin/thaddeus.js
CHANGED
|
@@ -14,17 +14,70 @@ const ROOT = path.resolve(__dirname, '..');
|
|
|
14
14
|
// ── Find Bun ──────────────────────────────────────────────
|
|
15
15
|
const isWin = process.platform === 'win32';
|
|
16
16
|
const bunName = isWin ? 'bun.exe' : 'bun';
|
|
17
|
-
let bunPath = path.join(os.homedir(), '.bun', 'bin', bunName);
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
//
|
|
18
|
+
function findBun() {
|
|
19
|
+
// 1. ~/.bun/bin/bun (macOS/Linux standard, also some Windows setups)
|
|
20
|
+
const homeBun = path.join(os.homedir(), '.bun', 'bin', bunName);
|
|
21
|
+
if (fs.existsSync(homeBun)) return homeBun;
|
|
22
|
+
|
|
23
|
+
// 2. %LOCALAPPDATA%\bun\bun.exe (Windows official install location)
|
|
24
|
+
if (isWin) {
|
|
25
|
+
const localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
|
|
26
|
+
const winBun = path.join(localAppData, 'bun', bunName);
|
|
27
|
+
if (fs.existsSync(winBun)) return winBun;
|
|
28
|
+
// Also check %USERPROFILE%\.bun\bin
|
|
29
|
+
const userBun = path.join(os.homedir(), '.bun', 'bin', bunName);
|
|
30
|
+
if (fs.existsSync(userBun)) return userBun;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 3. System PATH
|
|
34
|
+
try {
|
|
35
|
+
const found = execSync(isWin ? 'where bun' : 'which bun', {
|
|
36
|
+
encoding: 'utf-8',
|
|
37
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
38
|
+
}).trim().split('\n')[0];
|
|
39
|
+
if (found && fs.existsSync(found)) return found;
|
|
40
|
+
} catch { /* not in PATH */ }
|
|
41
|
+
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function installBun() {
|
|
46
|
+
console.log('\x1b[33mBun not found — installing automatically...\x1b[0m');
|
|
21
47
|
try {
|
|
22
|
-
bunPath = execSync(isWin ? 'where bun' : 'which bun', { encoding: 'utf-8' }).trim().split('\n')[0];
|
|
23
|
-
} catch {
|
|
24
|
-
console.error('\x1b[31mERROR: Bun not found.\x1b[0m');
|
|
25
|
-
console.error('Install Bun first:');
|
|
26
48
|
if (isWin) {
|
|
27
|
-
|
|
49
|
+
// Use PowerShell with bypassed execution policy to install Bun
|
|
50
|
+
execSync(
|
|
51
|
+
'powershell -ExecutionPolicy Bypass -Command "irm bun.sh/install.ps1 | iex"',
|
|
52
|
+
{ stdio: 'inherit', timeout: 120000 }
|
|
53
|
+
);
|
|
54
|
+
} else {
|
|
55
|
+
execSync('curl -fsSL https://bun.sh/install | bash', {
|
|
56
|
+
stdio: 'inherit',
|
|
57
|
+
timeout: 120000,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
// Re-search after install
|
|
61
|
+
return findBun();
|
|
62
|
+
} catch (err) {
|
|
63
|
+
console.error('\x1b[31mAuto-install failed.\x1b[0m');
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let bunPath = findBun();
|
|
69
|
+
|
|
70
|
+
if (!bunPath) {
|
|
71
|
+
// Try auto-install
|
|
72
|
+
bunPath = installBun();
|
|
73
|
+
if (!bunPath) {
|
|
74
|
+
console.error('\x1b[31mERROR: Bun not found and auto-install failed.\x1b[0m');
|
|
75
|
+
console.error('Install Bun manually:');
|
|
76
|
+
if (isWin) {
|
|
77
|
+
console.error(' 1. Open PowerShell as Administrator');
|
|
78
|
+
console.error(' 2. Run: powershell -ExecutionPolicy Bypass -c "irm bun.sh/install.ps1 | iex"');
|
|
79
|
+
console.error(' 3. Close and reopen your terminal');
|
|
80
|
+
console.error(' 4. Run: thaddeus');
|
|
28
81
|
} else {
|
|
29
82
|
console.error(' curl -fsSL https://bun.sh/install | bash');
|
|
30
83
|
}
|
package/package.json
CHANGED
|
@@ -9,6 +9,7 @@ import { registerReactorCommand } from './reactorBus.js'
|
|
|
9
9
|
import { getTunnelUrl, getMobileUrl } from './tunnel.js'
|
|
10
10
|
import { toDataURL as qrToDataURL } from 'qrcode'
|
|
11
11
|
import { handlePhoneCommand, handlePhoneRegister, handlePhonePending, handlePhoneAck, handlePhoneStatus } from './phoneApi.js'
|
|
12
|
+
import { getAuthToken, getBackendUrl } from '../services/thaddeusAuth.js'
|
|
12
13
|
|
|
13
14
|
const STATIC_DIR = join(import.meta.dir, 'static')
|
|
14
15
|
const CERT_DIR = import.meta.dir
|
|
@@ -31,7 +32,8 @@ const MIME: Record<string, string> = {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
export function startCommandCenterServer(port = 7888) {
|
|
34
|
-
// STT transcription —
|
|
35
|
+
// STT transcription — proxy mode (no local keys) or direct mode (local keys)
|
|
36
|
+
const useProxy = process.env.THADDEUS_USE_PROXY === 'true' || process.env.THADDEUS_USE_PROXY === '1'
|
|
35
37
|
const GROQ_API_KEY = process.env.GROQ_API_KEY || ''
|
|
36
38
|
const OPENAI_API_KEY = process.env.OPENAI_API_KEY || ''
|
|
37
39
|
const useGroq = !!GROQ_API_KEY
|
|
@@ -41,8 +43,10 @@ export function startCommandCenterServer(port = 7888) {
|
|
|
41
43
|
: 'https://api.openai.com/v1/audio/transcriptions'
|
|
42
44
|
const WHISPER_MODEL = useGroq ? 'whisper-large-v3' : 'whisper-1'
|
|
43
45
|
|
|
44
|
-
if (!WHISPER_KEY) {
|
|
45
|
-
console.error('[Command Center] No STT API key set — transcription will fail')
|
|
46
|
+
if (!WHISPER_KEY && !useProxy) {
|
|
47
|
+
console.error('[Command Center] No STT API key set and proxy not enabled — transcription will fail')
|
|
48
|
+
} else if (useProxy && !WHISPER_KEY) {
|
|
49
|
+
console.log('[Command Center] Whisper STT via Render proxy')
|
|
46
50
|
} else {
|
|
47
51
|
console.log(`[Command Center] Whisper STT via ${useGroq ? 'primary' : 'secondary'} provider`)
|
|
48
52
|
}
|
|
@@ -126,7 +130,36 @@ export function startCommandCenterServer(port = 7888) {
|
|
|
126
130
|
const audioFile = formData.get('audio') as File | null
|
|
127
131
|
if (!audioFile) return new Response('Missing audio file', { status: 400 })
|
|
128
132
|
|
|
129
|
-
//
|
|
133
|
+
// Route through Render proxy when no local Whisper key
|
|
134
|
+
if (useProxy && !WHISPER_KEY) {
|
|
135
|
+
const authToken = getAuthToken()
|
|
136
|
+
const proxyForm = new FormData()
|
|
137
|
+
proxyForm.append('file', audioFile, 'audio.webm')
|
|
138
|
+
proxyForm.append('model', 'whisper-1')
|
|
139
|
+
proxyForm.append('language', 'en')
|
|
140
|
+
|
|
141
|
+
const resp = await fetch(`${getBackendUrl()}/api/thaddeus/stt`, {
|
|
142
|
+
method: 'POST',
|
|
143
|
+
headers: {
|
|
144
|
+
...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
|
|
145
|
+
},
|
|
146
|
+
body: proxyForm,
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
if (!resp.ok) {
|
|
150
|
+
const errText = await resp.text()
|
|
151
|
+
console.error('[Command Center] Proxy STT error:', errText)
|
|
152
|
+
return new Response(errText, { status: resp.status })
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const result = (await resp.json()) as { text: string }
|
|
156
|
+
return Response.json(
|
|
157
|
+
{ text: result.text },
|
|
158
|
+
{ headers: { 'Access-Control-Allow-Origin': '*' } },
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Direct Whisper call (local API key available)
|
|
130
163
|
const whisperForm = new FormData()
|
|
131
164
|
whisperForm.append('file', audioFile, 'audio.webm')
|
|
132
165
|
whisperForm.append('model', WHISPER_MODEL)
|