@spinabot/brigade 1.11.2 → 1.13.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/README.md +56 -0
- package/dist/agents/tools/edge-tts.d.ts +44 -0
- package/dist/agents/tools/edge-tts.d.ts.map +1 -0
- package/dist/agents/tools/edge-tts.js +142 -0
- package/dist/agents/tools/edge-tts.js.map +1 -0
- package/dist/agents/tools/generate-music-tool.d.ts +61 -0
- package/dist/agents/tools/generate-music-tool.d.ts.map +1 -0
- package/dist/agents/tools/generate-music-tool.js +286 -0
- package/dist/agents/tools/generate-music-tool.js.map +1 -0
- package/dist/agents/tools/generate-speech-tool.d.ts +69 -0
- package/dist/agents/tools/generate-speech-tool.d.ts.map +1 -0
- package/dist/agents/tools/generate-speech-tool.js +331 -0
- package/dist/agents/tools/generate-speech-tool.js.map +1 -0
- package/dist/agents/tools/generate-video-tool.d.ts +111 -0
- package/dist/agents/tools/generate-video-tool.d.ts.map +1 -0
- package/dist/agents/tools/generate-video-tool.js +1028 -0
- package/dist/agents/tools/generate-video-tool.js.map +1 -0
- package/dist/agents/tools/media-command.d.ts +47 -0
- package/dist/agents/tools/media-command.d.ts.map +1 -0
- package/dist/agents/tools/media-command.js +93 -0
- package/dist/agents/tools/media-command.js.map +1 -0
- package/dist/agents/tools/registry.d.ts.map +1 -1
- package/dist/agents/tools/registry.js +27 -0
- package/dist/agents/tools/registry.js.map +1 -1
- package/dist/agents/tools/transcribe-audio-tool.d.ts +96 -0
- package/dist/agents/tools/transcribe-audio-tool.d.ts.map +1 -0
- package/dist/agents/tools/transcribe-audio-tool.js +577 -0
- package/dist/agents/tools/transcribe-audio-tool.js.map +1 -0
- package/dist/buildstamp.json +1 -1
- package/dist/cli/commands/connect.d.ts +6 -0
- package/dist/cli/commands/connect.d.ts.map +1 -1
- package/dist/cli/commands/connect.js +7 -0
- package/dist/cli/commands/connect.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +2 -1
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/expose.d.ts.map +1 -1
- package/dist/cli/commands/expose.js +22 -3
- package/dist/cli/commands/expose.js.map +1 -1
- package/dist/cli/commands/gateway.d.ts +12 -0
- package/dist/cli/commands/gateway.d.ts.map +1 -1
- package/dist/cli/commands/gateway.js +114 -2
- package/dist/cli/commands/gateway.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +2 -1
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/program/build-program.d.ts.map +1 -1
- package/dist/cli/program/build-program.js +36 -0
- package/dist/cli/program/build-program.js.map +1 -1
- package/dist/config/io.d.ts +13 -0
- package/dist/config/io.d.ts.map +1 -1
- package/dist/config/io.js.map +1 -1
- package/dist/core/gateway-auth.d.ts +86 -0
- package/dist/core/gateway-auth.d.ts.map +1 -0
- package/dist/core/gateway-auth.js +156 -0
- package/dist/core/gateway-auth.js.map +1 -0
- package/dist/core/gateway-probe.d.ts +5 -0
- package/dist/core/gateway-probe.d.ts.map +1 -1
- package/dist/core/gateway-probe.js +2 -1
- package/dist/core/gateway-probe.js.map +1 -1
- package/dist/core/gateway-spawn.d.ts.map +1 -1
- package/dist/core/gateway-spawn.js +5 -2
- package/dist/core/gateway-spawn.js.map +1 -1
- package/dist/core/server.d.ts.map +1 -1
- package/dist/core/server.js +21 -1
- package/dist/core/server.js.map +1 -1
- package/dist/core/tunnel/auth-proxy.d.ts +3 -2
- package/dist/core/tunnel/auth-proxy.d.ts.map +1 -1
- package/dist/core/tunnel/auth-proxy.js +8 -34
- package/dist/core/tunnel/auth-proxy.js.map +1 -1
- package/dist/core/tunnel/manager.d.ts +4 -2
- package/dist/core/tunnel/manager.d.ts.map +1 -1
- package/dist/core/tunnel/manager.js +3 -2
- package/dist/core/tunnel/manager.js.map +1 -1
- package/dist/tui/client.d.ts +8 -0
- package/dist/tui/client.d.ts.map +1 -1
- package/dist/tui/client.js +5 -1
- package/dist/tui/client.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `transcribe_audio` tool — speech-to-text (STT), modeled on the proven
|
|
3
|
+
* `generate_speech` self-contained pattern (its mirror image: TTS out → STT in).
|
|
4
|
+
*
|
|
5
|
+
* Why this tool exists
|
|
6
|
+
* --------------------
|
|
7
|
+
* Same reasoning as `generate_speech`: without a first-class tool, "transcribe
|
|
8
|
+
* this recording" / "what does this voice note say" sends the model to raw
|
|
9
|
+
* `curl` against an STT API — the key flows through a shell, the multipart audio
|
|
10
|
+
* upload gets mangled, and the call is dropped. This tool owns the call
|
|
11
|
+
* in-process: stored auth, validated params, a parser that understands each
|
|
12
|
+
* provider's transcript shape, and a path guard so it can't read secrets.
|
|
13
|
+
*
|
|
14
|
+
* It is a READ / understand op (like `analyze_media`), NOT a billed-gating
|
|
15
|
+
* mutation: reads are not privileged, so `ownerOnly` is false. The path guard +
|
|
16
|
+
* allowed-root scoping are the real safety boundary, and they run for EVERY
|
|
17
|
+
* sender — a remote channel sender can never make Brigade transcribe a secret.
|
|
18
|
+
*
|
|
19
|
+
* Providers (auto-selected by which key is configured, preference order):
|
|
20
|
+
* • groq — POST /openai/v1/audio/transcriptions (whisper-large-v3-turbo)
|
|
21
|
+
* • openai — POST /v1/audio/transcriptions (whisper-1)
|
|
22
|
+
* • deepgram — POST /v1/listen (RAW bytes, not multipart) (nova-3)
|
|
23
|
+
* • elevenlabs — POST /v1/speech-to-text (scribe_v2)
|
|
24
|
+
* • mistral — POST /v1/audio/transcriptions (voxtral-mini-latest)
|
|
25
|
+
* • xai — POST /v1/stt (no model id)
|
|
26
|
+
* Keys resolve through `resolveMediaProviderKey` (the same credential-store +
|
|
27
|
+
* env path the media-understanding subsystem uses), so transcription works for
|
|
28
|
+
* whichever provider the operator already configured — no bespoke auth.
|
|
29
|
+
*
|
|
30
|
+
* Flow: read audio bytes (URL → fetch; local path → guarded read) → multipart
|
|
31
|
+
* (or raw, for deepgram) POST → the transcript TEXT is returned as `content`,
|
|
32
|
+
* with `details:{ok, provider, model, transcript, chars}`.
|
|
33
|
+
*/
|
|
34
|
+
import fs from "node:fs";
|
|
35
|
+
import fsp from "node:fs/promises";
|
|
36
|
+
import os from "node:os";
|
|
37
|
+
import path from "node:path";
|
|
38
|
+
import { Type } from "typebox";
|
|
39
|
+
import { resolveCacheDir, resolveOsCacheDir, resolveStateDir, DEFAULT_AGENT_ID, } from "../../config/paths.js";
|
|
40
|
+
import { validateOutboundMediaPath } from "../../security/media-path-guard.js";
|
|
41
|
+
import { loadConfig } from "../../core/config.js";
|
|
42
|
+
import { resolveMediaProviderKey } from "../media-understanding/config.js";
|
|
43
|
+
import { runCommandStt } from "./media-command.js";
|
|
44
|
+
import { BrigadeToolInputError, jsonResult } from "./common.js";
|
|
45
|
+
/** Transcription can take a while for long recordings; bound each HTTP call. */
|
|
46
|
+
const REQUEST_TIMEOUT_MS = 120_000;
|
|
47
|
+
/** Per-request HTTP timeout when FETCHING a URL source's bytes. */
|
|
48
|
+
const FETCH_TIMEOUT_MS = 45_000;
|
|
49
|
+
/** Hard cap on audio bytes read for ANY source — providers reject huge uploads. */
|
|
50
|
+
const MAX_AUDIO_BYTES = 48 * 1024 * 1024; // 48 MiB
|
|
51
|
+
/** Preference order when no provider is pinned: first keyed one wins. */
|
|
52
|
+
const PROVIDER_PREFERENCE = [
|
|
53
|
+
"groq",
|
|
54
|
+
"openai",
|
|
55
|
+
"deepgram",
|
|
56
|
+
"elevenlabs",
|
|
57
|
+
"mistral",
|
|
58
|
+
"xai",
|
|
59
|
+
"google",
|
|
60
|
+
"command",
|
|
61
|
+
];
|
|
62
|
+
/** Default model id per provider (xai's /v1/stt takes none). */
|
|
63
|
+
const DEFAULT_MODEL = {
|
|
64
|
+
groq: "whisper-large-v3-turbo",
|
|
65
|
+
openai: "whisper-1",
|
|
66
|
+
deepgram: "nova-3",
|
|
67
|
+
elevenlabs: "scribe_v2",
|
|
68
|
+
mistral: "voxtral-mini-latest",
|
|
69
|
+
xai: "",
|
|
70
|
+
google: "gemini-2.5-flash",
|
|
71
|
+
command: "",
|
|
72
|
+
};
|
|
73
|
+
const TranscribeAudioParams = Type.Object({
|
|
74
|
+
action: Type.Optional(Type.Union([Type.Literal("transcribe"), Type.Literal("list")], {
|
|
75
|
+
description: 'Optional: "transcribe" (default) or "list" to see which STT providers are configured.',
|
|
76
|
+
})),
|
|
77
|
+
source: Type.Optional(Type.String({
|
|
78
|
+
description: "Local file PATH or http(s) URL to an audio file to transcribe (mp3/wav/m4a/ogg/flac/aac/opus/webm). Required for action=transcribe.",
|
|
79
|
+
})),
|
|
80
|
+
provider: Type.Optional(Type.Union([
|
|
81
|
+
Type.Literal("groq"),
|
|
82
|
+
Type.Literal("openai"),
|
|
83
|
+
Type.Literal("deepgram"),
|
|
84
|
+
Type.Literal("elevenlabs"),
|
|
85
|
+
Type.Literal("mistral"),
|
|
86
|
+
Type.Literal("xai"),
|
|
87
|
+
Type.Literal("google"),
|
|
88
|
+
Type.Literal("command"),
|
|
89
|
+
], {
|
|
90
|
+
description: "Optional STT provider override. Default: the first one with a configured key (groq → openai → deepgram → elevenlabs → mistral → xai).",
|
|
91
|
+
})),
|
|
92
|
+
model: Type.Optional(Type.String({ description: "Optional model override for the chosen provider." })),
|
|
93
|
+
language: Type.Optional(Type.String({
|
|
94
|
+
description: 'Optional spoken-language hint as an ISO code (e.g. "es", "en", "fr"). Improves accuracy for non-English audio; supported by groq/openai/mistral/xai/deepgram.',
|
|
95
|
+
})),
|
|
96
|
+
});
|
|
97
|
+
export function makeTranscribeAudioTool(opts = {}) {
|
|
98
|
+
const agentId = opts.agentId ?? DEFAULT_AGENT_ID;
|
|
99
|
+
const fetchFn = opts.fetchFn ?? fetch;
|
|
100
|
+
const resolveKey = opts.resolveKey ?? ((p) => resolveMediaProviderKey(p, agentId));
|
|
101
|
+
const sttCommand = opts.sttCommand ?? resolveSttCommand();
|
|
102
|
+
// `command` (offline local CLI) is available iff configured; cloud providers need a key.
|
|
103
|
+
const isAvailable = (p) => p === "command" ? sttCommand.length > 0 : resolveKey(p).length > 0;
|
|
104
|
+
const readSource = opts.readSource ??
|
|
105
|
+
((source, signal) => acquireAudio(source, {
|
|
106
|
+
fetchFn,
|
|
107
|
+
maxBytes: MAX_AUDIO_BYTES,
|
|
108
|
+
...(opts.workspaceDir ? { workspaceDir: opts.workspaceDir } : {}),
|
|
109
|
+
...(opts.cwd ? { cwd: opts.cwd } : {}),
|
|
110
|
+
...(opts.ownerLocalAccess ? { ownerLocalAccess: true } : {}),
|
|
111
|
+
...(signal ? { signal } : {}),
|
|
112
|
+
}));
|
|
113
|
+
return {
|
|
114
|
+
name: "transcribe_audio",
|
|
115
|
+
label: "Transcribe Audio",
|
|
116
|
+
displaySummary: "transcribing audio",
|
|
117
|
+
// Read / understand capability — NOT owner-only (like analyze_media). It
|
|
118
|
+
// reads an audio file/URL the operator pointed at and returns the text; it
|
|
119
|
+
// never mutates state. The path guard runs for EVERY sender regardless of
|
|
120
|
+
// owner status, which is the real safety boundary.
|
|
121
|
+
ownerOnly: false,
|
|
122
|
+
description: [
|
|
123
|
+
"Transcribe spoken audio to text (speech-to-text). USE THIS — never call an STT API with bash/curl: the key must not flow through a shell, and the multipart upload is handled here.",
|
|
124
|
+
'action="transcribe" (default): requires `source` (a local audio path or http(s) URL). Returns the transcript TEXT.',
|
|
125
|
+
"Auto-selects the first configured provider (groq → openai → deepgram → elevenlabs → mistral → xai); override with `provider`/`model`/`language`.",
|
|
126
|
+
'action="list": show which STT providers have a configured key.',
|
|
127
|
+
].join(" "),
|
|
128
|
+
parameters: TranscribeAudioParams,
|
|
129
|
+
execute: async (_id, args, signal) => {
|
|
130
|
+
const action = args.action ?? "transcribe";
|
|
131
|
+
if (action === "list") {
|
|
132
|
+
const providers = PROVIDER_PREFERENCE.filter(isAvailable);
|
|
133
|
+
return jsonResult({
|
|
134
|
+
action,
|
|
135
|
+
providers,
|
|
136
|
+
ok: true,
|
|
137
|
+
message: providers.length > 0
|
|
138
|
+
? `${providers.length} STT provider(s) configured: ${providers.join(", ")}.`
|
|
139
|
+
: "No STT provider configured. Add a Groq, OpenAI, Deepgram, ElevenLabs, Mistral, or xAI key with `brigade onboard`.",
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
const source = (args.source ?? "").trim();
|
|
143
|
+
if (!source) {
|
|
144
|
+
return fail(action, "`source` is required for action=transcribe (a local audio path or http(s) URL).");
|
|
145
|
+
}
|
|
146
|
+
// Resolve the provider: explicit override (must be keyed) else first keyed.
|
|
147
|
+
let provider;
|
|
148
|
+
if (args.provider) {
|
|
149
|
+
if (!isAvailable(args.provider)) {
|
|
150
|
+
return fail(action, `Provider "${args.provider}" is not available (no key, or no local command configured). Add one with \`brigade onboard\`, or omit \`provider\` to auto-select.`);
|
|
151
|
+
}
|
|
152
|
+
provider = args.provider;
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
provider = PROVIDER_PREFERENCE.find(isAvailable);
|
|
156
|
+
}
|
|
157
|
+
if (!provider) {
|
|
158
|
+
return fail(action, "No STT provider is configured. Add a Groq, OpenAI, Deepgram, ElevenLabs, Mistral, or xAI API key with `brigade onboard` (then this tool auto-selects it).");
|
|
159
|
+
}
|
|
160
|
+
const apiKey = resolveKey(provider);
|
|
161
|
+
const model = args.model?.trim() || DEFAULT_MODEL[provider];
|
|
162
|
+
// Read the audio bytes (URL fetch or guarded local read).
|
|
163
|
+
let audio;
|
|
164
|
+
try {
|
|
165
|
+
audio = await readSource(source, signal);
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
169
|
+
return fail(action, `Could not read the audio source: ${msg}`, { provider, model });
|
|
170
|
+
}
|
|
171
|
+
if (audio.bytes.length === 0) {
|
|
172
|
+
return fail(action, "The audio source is empty (0 bytes).", { provider, model });
|
|
173
|
+
}
|
|
174
|
+
// Transcribe via the chosen provider.
|
|
175
|
+
let transcript;
|
|
176
|
+
try {
|
|
177
|
+
transcript =
|
|
178
|
+
provider === "command"
|
|
179
|
+
? runCommandStt(sttCommand, {
|
|
180
|
+
audioBytes: audio.bytes,
|
|
181
|
+
audioExt: audio.extension,
|
|
182
|
+
...(args.language ? { language: args.language.trim() } : {}),
|
|
183
|
+
}, opts.commandRunner ? { runFn: opts.commandRunner } : {})
|
|
184
|
+
: await transcribe({
|
|
185
|
+
provider,
|
|
186
|
+
fetchFn,
|
|
187
|
+
apiKey,
|
|
188
|
+
model,
|
|
189
|
+
audio,
|
|
190
|
+
...(args.language ? { language: args.language.trim() } : {}),
|
|
191
|
+
...(signal ? { signal } : {}),
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
return fail(action, `Transcription via ${provider} failed: ${err instanceof Error ? err.message : String(err)}`, {
|
|
196
|
+
provider,
|
|
197
|
+
model,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
const text = transcript.trim();
|
|
201
|
+
if (!text) {
|
|
202
|
+
return fail(action, `${provider} returned an empty transcript (no speech detected?).`, { provider, model });
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
content: [{ type: "text", text }],
|
|
206
|
+
details: {
|
|
207
|
+
action,
|
|
208
|
+
provider,
|
|
209
|
+
model,
|
|
210
|
+
transcript: text,
|
|
211
|
+
chars: text.length,
|
|
212
|
+
ok: true,
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/* ───────────────────────── provider plumbing ───────────────────────── */
|
|
219
|
+
async function transcribe(params) {
|
|
220
|
+
switch (params.provider) {
|
|
221
|
+
case "groq":
|
|
222
|
+
return transcribeOpenAICompatible(params, "https://api.groq.com/openai/v1/audio/transcriptions");
|
|
223
|
+
case "openai":
|
|
224
|
+
return transcribeOpenAICompatible(params, "https://api.openai.com/v1/audio/transcriptions");
|
|
225
|
+
case "mistral":
|
|
226
|
+
return transcribeOpenAICompatible(params, "https://api.mistral.ai/v1/audio/transcriptions");
|
|
227
|
+
case "xai":
|
|
228
|
+
return transcribeXai(params);
|
|
229
|
+
case "elevenlabs":
|
|
230
|
+
return transcribeElevenLabs(params);
|
|
231
|
+
case "deepgram":
|
|
232
|
+
return transcribeDeepgram(params);
|
|
233
|
+
case "google":
|
|
234
|
+
return transcribeGoogle(params);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Google Gemini — audio understanding via `generateContent`: the audio rides as
|
|
239
|
+
* inline base64 plus a "transcribe verbatim" instruction; the transcript is the
|
|
240
|
+
* joined text parts of the first candidate.
|
|
241
|
+
*/
|
|
242
|
+
async function transcribeGoogle(p) {
|
|
243
|
+
const instruction = p.language
|
|
244
|
+
? `Transcribe this audio verbatim (language: ${p.language}). Return ONLY the transcript text.`
|
|
245
|
+
: "Transcribe this audio verbatim. Return ONLY the transcript text.";
|
|
246
|
+
const url = `https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(p.model)}:generateContent?key=${encodeURIComponent(p.apiKey)}`;
|
|
247
|
+
const res = await p.fetchFn(url, {
|
|
248
|
+
method: "POST",
|
|
249
|
+
headers: { "Content-Type": "application/json" },
|
|
250
|
+
body: JSON.stringify({
|
|
251
|
+
contents: [
|
|
252
|
+
{
|
|
253
|
+
parts: [
|
|
254
|
+
{ inline_data: { mime_type: p.audio.mime, data: p.audio.bytes.toString("base64") } },
|
|
255
|
+
{ text: instruction },
|
|
256
|
+
],
|
|
257
|
+
},
|
|
258
|
+
],
|
|
259
|
+
}),
|
|
260
|
+
signal: withTimeout(p.signal, REQUEST_TIMEOUT_MS),
|
|
261
|
+
});
|
|
262
|
+
if (!res.ok)
|
|
263
|
+
throw new Error(`HTTP ${res.status} ${(await safeText(res)).slice(0, 200)}`);
|
|
264
|
+
const body = (await res.json());
|
|
265
|
+
return (body.candidates?.[0]?.content?.parts ?? [])
|
|
266
|
+
.map((x) => x.text ?? "")
|
|
267
|
+
.join("")
|
|
268
|
+
.trim();
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* groq / openai / mistral share the OpenAI-style multipart transcription shape:
|
|
272
|
+
* `FormData{ file, model, response_format:"json", language? }`, Bearer auth,
|
|
273
|
+
* response `{text}`.
|
|
274
|
+
*/
|
|
275
|
+
async function transcribeOpenAICompatible(p, url) {
|
|
276
|
+
const fd = new FormData();
|
|
277
|
+
fd.append("file", new Blob([p.audio.bytes], { type: p.audio.mime }), `audio.${p.audio.extension}`);
|
|
278
|
+
fd.append("model", p.model);
|
|
279
|
+
fd.append("response_format", "json");
|
|
280
|
+
if (p.language)
|
|
281
|
+
fd.append("language", p.language);
|
|
282
|
+
const res = await p.fetchFn(url, {
|
|
283
|
+
method: "POST",
|
|
284
|
+
headers: { Authorization: `Bearer ${p.apiKey}` },
|
|
285
|
+
body: fd,
|
|
286
|
+
signal: withTimeout(p.signal, REQUEST_TIMEOUT_MS),
|
|
287
|
+
});
|
|
288
|
+
if (!res.ok)
|
|
289
|
+
throw new Error(`HTTP ${res.status} ${(await safeText(res)).slice(0, 200)}`);
|
|
290
|
+
const body = (await res.json());
|
|
291
|
+
return body.text ?? "";
|
|
292
|
+
}
|
|
293
|
+
/** xAI `/v1/stt` — multipart `{ file, language? }`, Bearer; response `{text}`. */
|
|
294
|
+
async function transcribeXai(p) {
|
|
295
|
+
const fd = new FormData();
|
|
296
|
+
fd.append("file", new Blob([p.audio.bytes], { type: p.audio.mime }), `audio.${p.audio.extension}`);
|
|
297
|
+
if (p.language)
|
|
298
|
+
fd.append("language", p.language);
|
|
299
|
+
const res = await p.fetchFn("https://api.x.ai/v1/stt", {
|
|
300
|
+
method: "POST",
|
|
301
|
+
headers: { Authorization: `Bearer ${p.apiKey}` },
|
|
302
|
+
body: fd,
|
|
303
|
+
signal: withTimeout(p.signal, REQUEST_TIMEOUT_MS),
|
|
304
|
+
});
|
|
305
|
+
if (!res.ok)
|
|
306
|
+
throw new Error(`HTTP ${res.status} ${(await safeText(res)).slice(0, 200)}`);
|
|
307
|
+
const body = (await res.json());
|
|
308
|
+
return body.text ?? "";
|
|
309
|
+
}
|
|
310
|
+
/** ElevenLabs `/v1/speech-to-text` — multipart `{ file, model_id }`, `xi-api-key`; response `{text}`. */
|
|
311
|
+
async function transcribeElevenLabs(p) {
|
|
312
|
+
const fd = new FormData();
|
|
313
|
+
fd.append("file", new Blob([p.audio.bytes], { type: p.audio.mime }), `audio.${p.audio.extension}`);
|
|
314
|
+
fd.append("model_id", p.model);
|
|
315
|
+
const res = await p.fetchFn("https://api.elevenlabs.io/v1/speech-to-text", {
|
|
316
|
+
method: "POST",
|
|
317
|
+
headers: { "xi-api-key": p.apiKey },
|
|
318
|
+
body: fd,
|
|
319
|
+
signal: withTimeout(p.signal, REQUEST_TIMEOUT_MS),
|
|
320
|
+
});
|
|
321
|
+
if (!res.ok)
|
|
322
|
+
throw new Error(`HTTP ${res.status} ${(await safeText(res)).slice(0, 200)}`);
|
|
323
|
+
const body = (await res.json());
|
|
324
|
+
return body.text ?? "";
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Deepgram `/v1/listen` — RAW audio bytes (NOT multipart) as the body, with
|
|
328
|
+
* `Authorization: Token <key>` + `Content-Type: <mime>`. Transcript lives at
|
|
329
|
+
* `results.channels[0].alternatives[0].transcript`.
|
|
330
|
+
*/
|
|
331
|
+
async function transcribeDeepgram(p) {
|
|
332
|
+
let url = "https://api.deepgram.com/v1/listen?model=nova-3&smart_format=true";
|
|
333
|
+
if (p.language)
|
|
334
|
+
url += `&language=${encodeURIComponent(p.language)}`;
|
|
335
|
+
const res = await p.fetchFn(url, {
|
|
336
|
+
method: "POST",
|
|
337
|
+
headers: { Authorization: `Token ${p.apiKey}`, "Content-Type": p.audio.mime },
|
|
338
|
+
body: new Uint8Array(p.audio.bytes),
|
|
339
|
+
signal: withTimeout(p.signal, REQUEST_TIMEOUT_MS),
|
|
340
|
+
});
|
|
341
|
+
if (!res.ok)
|
|
342
|
+
throw new Error(`HTTP ${res.status} ${(await safeText(res)).slice(0, 200)}`);
|
|
343
|
+
const body = (await res.json());
|
|
344
|
+
return body.results?.channels?.[0]?.alternatives?.[0]?.transcript ?? "";
|
|
345
|
+
}
|
|
346
|
+
/* ───────────────────────── source acquisition ───────────────────────── */
|
|
347
|
+
/**
|
|
348
|
+
* Read the audio bytes for `source`. A URL is fetched (capped); a local path is
|
|
349
|
+
* read with the SAME safety posture as `analyze_media`:
|
|
350
|
+
* 1. media-path guard (refuse secrets / system files / credential dirs).
|
|
351
|
+
* 2. allowed-root scoping (must be under workspace / cwd / cache / temp /
|
|
352
|
+
* state media subtree) — refuses arbitrary absolute reads outside roots.
|
|
353
|
+
* Symlinks are resolved first so a benign name can't smuggle a denied target.
|
|
354
|
+
*/
|
|
355
|
+
async function acquireAudio(source, opts) {
|
|
356
|
+
const isUrl = /^https?:\/\//i.test(source);
|
|
357
|
+
if (isUrl) {
|
|
358
|
+
const res = await opts.fetchFn(source, {
|
|
359
|
+
method: "GET",
|
|
360
|
+
headers: { accept: "*/*" },
|
|
361
|
+
signal: withTimeout(opts.signal, FETCH_TIMEOUT_MS),
|
|
362
|
+
});
|
|
363
|
+
if (!res.ok) {
|
|
364
|
+
throw new Error(`fetch failed: HTTP ${res.status} for ${source}`);
|
|
365
|
+
}
|
|
366
|
+
const headerMime = res.headers.get("content-type")?.split(";")[0]?.trim() || undefined;
|
|
367
|
+
const full = Buffer.from(await res.arrayBuffer());
|
|
368
|
+
const bytes = full.length > opts.maxBytes ? full.subarray(0, opts.maxBytes) : full;
|
|
369
|
+
const ext = extensionOf(source) || extFromMime(headerMime) || "mp3";
|
|
370
|
+
const mime = headerMime || audioMimeFromExt(ext);
|
|
371
|
+
return { bytes, mime, extension: ext };
|
|
372
|
+
}
|
|
373
|
+
// Local path: guard + allowed-root scoping (mirrors analyze_media).
|
|
374
|
+
const verdict = validateOutboundMediaPath(source);
|
|
375
|
+
if (!verdict.ok) {
|
|
376
|
+
throw new BrigadeToolInputError(verdict.reason ?? "refusing to read that path");
|
|
377
|
+
}
|
|
378
|
+
let resolved;
|
|
379
|
+
try {
|
|
380
|
+
resolved = fs.realpathSync(path.resolve(source));
|
|
381
|
+
}
|
|
382
|
+
catch {
|
|
383
|
+
resolved = path.resolve(source);
|
|
384
|
+
}
|
|
385
|
+
const roots = allowedLocalRoots(opts);
|
|
386
|
+
if (!isInsideAnyRoot(resolved, roots)) {
|
|
387
|
+
throw new BrigadeToolInputError("refusing to read a path outside the allowed roots (workspace / current dir / cache / temp). " +
|
|
388
|
+
"Move the file into the workspace, or pass a URL.");
|
|
389
|
+
}
|
|
390
|
+
let stat;
|
|
391
|
+
try {
|
|
392
|
+
stat = await fsp.stat(resolved);
|
|
393
|
+
}
|
|
394
|
+
catch {
|
|
395
|
+
throw new BrigadeToolInputError(`file not found: ${source}`);
|
|
396
|
+
}
|
|
397
|
+
if (!stat.isFile())
|
|
398
|
+
throw new BrigadeToolInputError(`not a file: ${source}`);
|
|
399
|
+
if (stat.size === 0)
|
|
400
|
+
throw new BrigadeToolInputError(`file is empty: ${source}`);
|
|
401
|
+
const full = await fsp.readFile(resolved);
|
|
402
|
+
const bytes = full.length > opts.maxBytes ? full.subarray(0, opts.maxBytes) : full;
|
|
403
|
+
const ext = extensionOf(source) || "mp3";
|
|
404
|
+
return { bytes, mime: audioMimeFromExt(ext), extension: ext };
|
|
405
|
+
}
|
|
406
|
+
/** Roots a local source path is allowed to live under (workspace, cwd, OS cache/temp, state dir). */
|
|
407
|
+
function allowedLocalRoots(opts) {
|
|
408
|
+
const roots = new Set();
|
|
409
|
+
const add = (p) => {
|
|
410
|
+
if (!p)
|
|
411
|
+
return;
|
|
412
|
+
try {
|
|
413
|
+
roots.add(path.resolve(p));
|
|
414
|
+
}
|
|
415
|
+
catch {
|
|
416
|
+
/* ignore */
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
add(opts.workspaceDir);
|
|
420
|
+
add(opts.cwd);
|
|
421
|
+
add(resolveCacheDir());
|
|
422
|
+
add(process.env.TMPDIR || process.env.TEMP || process.env.TMP || "");
|
|
423
|
+
try {
|
|
424
|
+
add(os.tmpdir());
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
/* ignore */
|
|
428
|
+
}
|
|
429
|
+
// State-dir media subtree: where inbound attachments + generated media land
|
|
430
|
+
// in FILESYSTEM mode — allow it so the model can transcribe a file it just
|
|
431
|
+
// received.
|
|
432
|
+
try {
|
|
433
|
+
add(path.join(resolveStateDir(), "channels"));
|
|
434
|
+
add(path.join(resolveStateDir(), "cache"));
|
|
435
|
+
add(path.join(resolveStateDir(), "captures"));
|
|
436
|
+
add(path.join(resolveStateDir(), "workspace"));
|
|
437
|
+
}
|
|
438
|
+
catch {
|
|
439
|
+
/* ignore */
|
|
440
|
+
}
|
|
441
|
+
// CONVEX mode relocates inbound channel media to the OS cache dir; cover it
|
|
442
|
+
// (+ the channel subtrees) so "transcribe the voice note I just sent" works.
|
|
443
|
+
// The media-path guard still refuses secrets / system files independently.
|
|
444
|
+
try {
|
|
445
|
+
const osCache = resolveOsCacheDir();
|
|
446
|
+
add(osCache);
|
|
447
|
+
add(path.join(osCache, "channels"));
|
|
448
|
+
add(path.join(osCache, "bluebubbles"));
|
|
449
|
+
}
|
|
450
|
+
catch {
|
|
451
|
+
/* ignore */
|
|
452
|
+
}
|
|
453
|
+
// macOS Messages Attachments root — inbound iMessage media is surfaced as-is.
|
|
454
|
+
try {
|
|
455
|
+
add(path.join(os.homedir(), "Library", "Messages", "Attachments"));
|
|
456
|
+
}
|
|
457
|
+
catch {
|
|
458
|
+
/* ignore */
|
|
459
|
+
}
|
|
460
|
+
// OWNER local turns only: widen to the operator's home (Downloads/Desktop/…)
|
|
461
|
+
// so a user-typed absolute path resolves. A remote sender threads
|
|
462
|
+
// ownerLocalAccess:false → this stays off. The path guard still refuses
|
|
463
|
+
// secrets / credential dirs / system files even for the owner.
|
|
464
|
+
if (opts.ownerLocalAccess) {
|
|
465
|
+
try {
|
|
466
|
+
add(os.homedir());
|
|
467
|
+
}
|
|
468
|
+
catch {
|
|
469
|
+
/* ignore */
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return [...roots].filter((r) => r.length > 0);
|
|
473
|
+
}
|
|
474
|
+
/** True when `resolved` is inside one of `roots` (path.relative containment, no `..`). */
|
|
475
|
+
function isInsideAnyRoot(resolved, roots) {
|
|
476
|
+
for (const root of roots) {
|
|
477
|
+
const rel = path.relative(root, resolved);
|
|
478
|
+
if (rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel)))
|
|
479
|
+
return true;
|
|
480
|
+
}
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
/* ───────────────────────── helpers ───────────────────────── */
|
|
484
|
+
/** The local-STT command template (env override, then config), or "" if unset. */
|
|
485
|
+
function resolveSttCommand() {
|
|
486
|
+
const env = process.env.BRIGADE_STT_COMMAND?.trim();
|
|
487
|
+
if (env)
|
|
488
|
+
return env;
|
|
489
|
+
try {
|
|
490
|
+
const cfg = loadConfig();
|
|
491
|
+
const c = cfg.tools?.transcription?.command;
|
|
492
|
+
if (typeof c === "string" && c.trim())
|
|
493
|
+
return c.trim();
|
|
494
|
+
}
|
|
495
|
+
catch {
|
|
496
|
+
/* default below */
|
|
497
|
+
}
|
|
498
|
+
return "";
|
|
499
|
+
}
|
|
500
|
+
/** Pull a lowercase extension (no dot) from a path or URL pathname. */
|
|
501
|
+
export function extensionOf(source) {
|
|
502
|
+
let p = source;
|
|
503
|
+
try {
|
|
504
|
+
if (/^https?:\/\//i.test(source))
|
|
505
|
+
p = new URL(source).pathname;
|
|
506
|
+
}
|
|
507
|
+
catch {
|
|
508
|
+
/* not a URL — treat as a path */
|
|
509
|
+
}
|
|
510
|
+
return path.extname(p).toLowerCase().replace(/^\./, "");
|
|
511
|
+
}
|
|
512
|
+
/** Audio mime from extension — used when a local audio file has no declared MIME. */
|
|
513
|
+
function audioMimeFromExt(ext) {
|
|
514
|
+
switch (ext) {
|
|
515
|
+
case "wav":
|
|
516
|
+
return "audio/wav";
|
|
517
|
+
case "m4a":
|
|
518
|
+
return "audio/mp4";
|
|
519
|
+
case "mp4":
|
|
520
|
+
return "audio/mp4";
|
|
521
|
+
case "aac":
|
|
522
|
+
return "audio/aac";
|
|
523
|
+
case "flac":
|
|
524
|
+
return "audio/flac";
|
|
525
|
+
case "oga":
|
|
526
|
+
case "ogg":
|
|
527
|
+
return "audio/ogg";
|
|
528
|
+
case "opus":
|
|
529
|
+
return "audio/opus";
|
|
530
|
+
case "webm":
|
|
531
|
+
return "audio/webm";
|
|
532
|
+
default:
|
|
533
|
+
return "audio/mpeg";
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
/** Best-effort extension from a content-type (when a URL has no file extension). */
|
|
537
|
+
function extFromMime(mime) {
|
|
538
|
+
if (!mime)
|
|
539
|
+
return undefined;
|
|
540
|
+
const m = mime.toLowerCase();
|
|
541
|
+
if (m.includes("wav"))
|
|
542
|
+
return "wav";
|
|
543
|
+
if (m.includes("mp4") || m.includes("m4a"))
|
|
544
|
+
return "m4a";
|
|
545
|
+
if (m.includes("aac"))
|
|
546
|
+
return "aac";
|
|
547
|
+
if (m.includes("flac"))
|
|
548
|
+
return "flac";
|
|
549
|
+
if (m.includes("ogg"))
|
|
550
|
+
return "ogg";
|
|
551
|
+
if (m.includes("opus"))
|
|
552
|
+
return "opus";
|
|
553
|
+
if (m.includes("webm"))
|
|
554
|
+
return "webm";
|
|
555
|
+
if (m.includes("mpeg") || m.includes("mp3"))
|
|
556
|
+
return "mp3";
|
|
557
|
+
return undefined;
|
|
558
|
+
}
|
|
559
|
+
function fail(action, message, extra = {}) {
|
|
560
|
+
return jsonResult({ action, ok: false, message, ...extra });
|
|
561
|
+
}
|
|
562
|
+
async function safeText(res) {
|
|
563
|
+
try {
|
|
564
|
+
return await res.text();
|
|
565
|
+
}
|
|
566
|
+
catch {
|
|
567
|
+
return "";
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
/** Compose the caller's signal with a hard per-request timeout. */
|
|
571
|
+
function withTimeout(signal, ms) {
|
|
572
|
+
const timeoutSignal = AbortSignal.timeout(ms);
|
|
573
|
+
if (!signal)
|
|
574
|
+
return timeoutSignal;
|
|
575
|
+
return AbortSignal.any([signal, timeoutSignal]);
|
|
576
|
+
}
|
|
577
|
+
//# sourceMappingURL=transcribe-audio-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcribe-audio-tool.js","sourceRoot":"","sources":["../../../src/agents/tools/transcribe-audio-tool.ts"],"names":[],"mappings":"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"}
|
package/dist/buildstamp.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"builtAt":
|
|
1
|
+
{"builtAt":1782487324269,"head":"ab1a71d75d3e570070899e5e4d4552506ad4cd4b","version":"1.13.0"}
|
|
@@ -28,6 +28,12 @@ export interface ConnectCommandOptions {
|
|
|
28
28
|
port?: number;
|
|
29
29
|
/** Per-request timeout (ms). Default: 60_000 */
|
|
30
30
|
requestTimeoutMs?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Token for an authenticated gateway. Falls back to `BRIGADE_GATEWAY_TOKEN`
|
|
33
|
+
* then the local `gateway.auth` config when omitted; undefined for an
|
|
34
|
+
* unauthenticated gateway (the default), in which case no auth is sent.
|
|
35
|
+
*/
|
|
36
|
+
token?: string;
|
|
31
37
|
/**
|
|
32
38
|
* Bind the TUI to this agent id at startup — equivalent to opening the
|
|
33
39
|
* TUI and immediately running `/agent <id>`, but without the manual step.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/connect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,EAMN,GAAG,EACH,MAAM,wBAAwB,CAAC;AAgBhC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/connect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,EAMN,GAAG,EACH,MAAM,wBAAwB,CAAC;AAgBhC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAsBpD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,WAAW,EAAE,OAAO,GAAG,IAAI,CAejF;AAED,MAAM,WAAW,qBAAqB;IACrC,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC7B,iFAAiF;IACjF,KAAK,IAAI,OAAO,CAAC;IACjB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACtC,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,WAAW,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAGT;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,aAAa,CAAC,CAsHhG;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAClC,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,aAAa,EACrB,cAAc,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,aAAa,CAAC,CA+sExB"}
|
|
@@ -34,6 +34,8 @@ import { markTuiActive, restoreTerminal } from "../../ui/terminal-cleanup.js";
|
|
|
34
34
|
import { brand, editorTheme, markdownTheme } from "../../ui/theme.js";
|
|
35
35
|
import { summarizeToolResult } from "../../ui/tool-result.js";
|
|
36
36
|
import { BrigadeClient } from "../../tui/client.js";
|
|
37
|
+
import { loadConfig } from "../../core/config.js";
|
|
38
|
+
import { resolveClientToken } from "../../core/gateway-auth.js";
|
|
37
39
|
import { ApprovalPrompt } from "../../tui/approval-prompt.js";
|
|
38
40
|
import { computeExplain, filterGraphToSubtree, formatExplain, parseOrgSlash, renderDepartmentsOnly, } from "./org-slash.js";
|
|
39
41
|
import { renderPrideChartWithPins, BRIGADE_FOOTER_RULE, } from "../../agents/org/pride-template.js";
|
|
@@ -115,9 +117,14 @@ export async function runConnectCommand(opts = {}) {
|
|
|
115
117
|
process.exit(130);
|
|
116
118
|
};
|
|
117
119
|
process.once("SIGINT", onSigint);
|
|
120
|
+
// Resolve the gateway token (flag → BRIGADE_GATEWAY_TOKEN → local config).
|
|
121
|
+
// Undefined when the gateway is unauthenticated — the client then sends no
|
|
122
|
+
// auth header and connects exactly as before.
|
|
123
|
+
const token = resolveClientToken(loadConfig().gateway?.auth, { override: opts.token });
|
|
118
124
|
const client = new BrigadeClient({
|
|
119
125
|
url,
|
|
120
126
|
requestTimeoutMs: opts.requestTimeoutMs ?? 60_000,
|
|
127
|
+
...(token ? { token } : {}),
|
|
121
128
|
});
|
|
122
129
|
try {
|
|
123
130
|
await client.connect();
|