poe-code 15.0.21 → 15.0.22
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/dist/metafile.json +1 -1
- package/package.json +1 -1
- package/packages/safe-bash/dist/chunks/{chunk-UO33SQJU.js → chunk-2PQ3XOCQ.js} +239 -2
- package/packages/safe-bash/dist/chunks/chunk-2PQ3XOCQ.js.map +7 -0
- package/packages/safe-bash/dist/chunks/{chunk-WISNTI7E.js → chunk-3Y5PKSHX.js} +7 -11
- package/packages/safe-bash/dist/chunks/{chunk-WISNTI7E.js.map → chunk-3Y5PKSHX.js.map} +1 -1
- package/packages/safe-bash/dist/chunks/{chunk-VY5N6O6I.js → chunk-BXVGCJXW.js} +108 -21
- package/packages/safe-bash/dist/chunks/chunk-BXVGCJXW.js.map +7 -0
- package/packages/safe-bash/dist/chunks/chunk-C7QNJ3D4.js +13 -0
- package/packages/safe-bash/dist/chunks/chunk-C7QNJ3D4.js.map +7 -0
- package/packages/safe-bash/dist/chunks/{chunk-AWP6Z3RR.js → chunk-E3NZKSTA.js} +8 -12
- package/packages/safe-bash/dist/chunks/{chunk-AWP6Z3RR.js.map → chunk-E3NZKSTA.js.map} +1 -1
- package/packages/safe-bash/dist/chunks/{chunk-WXUZXY35.js → chunk-FMTBJ7SG.js} +3 -3
- package/packages/safe-bash/dist/chunks/{chunk-QX2X5EC5.js → chunk-H6E6F5KJ.js} +7 -11
- package/packages/safe-bash/dist/chunks/{chunk-QX2X5EC5.js.map → chunk-H6E6F5KJ.js.map} +1 -1
- package/packages/safe-bash/dist/chunks/{chunk-IDJQT32M.js → chunk-HQCBEAFK.js} +7 -11
- package/packages/safe-bash/dist/chunks/{chunk-IDJQT32M.js.map → chunk-HQCBEAFK.js.map} +1 -1
- package/packages/safe-bash/dist/chunks/{chunk-4V3NGZAG.js → chunk-IYFB6VED.js} +6 -10
- package/packages/safe-bash/dist/chunks/{chunk-4V3NGZAG.js.map → chunk-IYFB6VED.js.map} +1 -1
- package/packages/safe-bash/dist/chunks/chunk-KJYVSFGF.js +284 -0
- package/packages/safe-bash/dist/chunks/chunk-KJYVSFGF.js.map +7 -0
- package/packages/safe-bash/dist/chunks/{chunk-O7BRWSKX.js → chunk-LHKAA57T.js} +60 -17
- package/packages/safe-bash/dist/chunks/chunk-LHKAA57T.js.map +7 -0
- package/packages/safe-bash/dist/chunks/chunk-LR32SJ3D.js +915 -0
- package/packages/safe-bash/dist/chunks/chunk-LR32SJ3D.js.map +7 -0
- package/packages/safe-bash/dist/chunks/{chunk-EUNOVYVT.js → chunk-OECKXHVJ.js} +2 -2
- package/packages/safe-bash/dist/chunks/{chunk-DKT746UJ.js → chunk-PE5NZIA2.js} +5 -9
- package/packages/safe-bash/dist/chunks/{chunk-DKT746UJ.js.map → chunk-PE5NZIA2.js.map} +1 -1
- package/packages/safe-bash/dist/chunks/{chunk-4JW32W4B.js → chunk-PMBBUNNN.js} +7 -11
- package/packages/safe-bash/dist/chunks/{chunk-4JW32W4B.js.map → chunk-PMBBUNNN.js.map} +1 -1
- package/packages/safe-bash/dist/chunks/{chunk-YFFFBLBC.js → chunk-TJVKGNKA.js} +6 -10
- package/packages/safe-bash/dist/chunks/{chunk-YFFFBLBC.js.map → chunk-TJVKGNKA.js.map} +1 -1
- package/packages/safe-bash/dist/chunks/{chunk-FIY2BMTU.js → chunk-W3TLNFDB.js} +8 -12
- package/packages/safe-bash/dist/chunks/{chunk-FIY2BMTU.js.map → chunk-W3TLNFDB.js.map} +1 -1
- package/packages/safe-bash/dist/chunks/{chunk-PPFWG34Y.js → chunk-XBLVOMEJ.js} +2 -2
- package/packages/safe-bash/dist/chunks/{chunk-WJWYJDQE.js → chunk-XYVCXUGM.js} +7 -11
- package/packages/safe-bash/dist/chunks/{chunk-WJWYJDQE.js.map → chunk-XYVCXUGM.js.map} +1 -1
- package/packages/safe-bash/dist/commands/csplit/index.browser.js +4 -6
- package/packages/safe-bash/dist/commands/factor/index.browser.js +3 -5
- package/packages/safe-bash/dist/commands/getopt/index.browser.js +3 -5
- package/packages/safe-bash/dist/commands/hexdump/index.browser.js +3 -5
- package/packages/safe-bash/dist/commands/iconv/index.browser.js +3 -5
- package/packages/safe-bash/dist/commands/line-endings/index.browser.js +4 -6
- package/packages/safe-bash/dist/commands/llm/command.d.ts +3 -2
- package/packages/safe-bash/dist/commands/llm/command.d.ts.map +1 -1
- package/packages/safe-bash/dist/commands/llm/command.js +260 -181
- package/packages/safe-bash/dist/commands/llm/command.js.map +1 -1
- package/packages/safe-bash/dist/commands/llm/elevenlabs-stream.d.ts +3 -0
- package/packages/safe-bash/dist/commands/llm/elevenlabs-stream.d.ts.map +1 -0
- package/packages/safe-bash/dist/commands/llm/elevenlabs-stream.js +118 -0
- package/packages/safe-bash/dist/commands/llm/elevenlabs-stream.js.map +1 -0
- package/packages/safe-bash/dist/commands/llm/elevenlabs.d.ts +16 -0
- package/packages/safe-bash/dist/commands/llm/elevenlabs.d.ts.map +1 -0
- package/packages/safe-bash/dist/commands/llm/elevenlabs.js +120 -0
- package/packages/safe-bash/dist/commands/llm/elevenlabs.js.map +1 -0
- package/packages/safe-bash/dist/commands/llm/index.browser.js +10 -4
- package/packages/safe-bash/dist/commands/llm/index.d.ts +5 -5
- package/packages/safe-bash/dist/commands/llm/index.d.ts.map +1 -1
- package/packages/safe-bash/dist/commands/llm/index.js +3 -11
- package/packages/safe-bash/dist/commands/llm/index.js.map +1 -1
- package/packages/safe-bash/dist/commands/llm/mime.d.ts +2 -1
- package/packages/safe-bash/dist/commands/llm/mime.d.ts.map +1 -1
- package/packages/safe-bash/dist/commands/llm/mime.js +125 -21
- package/packages/safe-bash/dist/commands/llm/mime.js.map +1 -1
- package/packages/safe-bash/dist/commands/llm/openai-http.d.ts +9 -0
- package/packages/safe-bash/dist/commands/llm/openai-http.d.ts.map +1 -0
- package/packages/safe-bash/dist/commands/llm/openai-http.js +144 -0
- package/packages/safe-bash/dist/commands/llm/openai-http.js.map +1 -0
- package/packages/safe-bash/dist/commands/llm/openai-sse.d.ts +3 -0
- package/packages/safe-bash/dist/commands/llm/openai-sse.d.ts.map +1 -0
- package/packages/safe-bash/dist/commands/llm/openai-sse.js +67 -0
- package/packages/safe-bash/dist/commands/llm/openai-sse.js.map +1 -0
- package/packages/safe-bash/dist/commands/llm/openai.d.ts +15 -0
- package/packages/safe-bash/dist/commands/llm/openai.d.ts.map +1 -0
- package/packages/safe-bash/dist/commands/llm/openai.js +203 -0
- package/packages/safe-bash/dist/commands/llm/openai.js.map +1 -0
- package/packages/safe-bash/dist/commands/llm/providers/elevenlabs.d.ts +1 -15
- package/packages/safe-bash/dist/commands/llm/providers/elevenlabs.d.ts.map +1 -1
- package/packages/safe-bash/dist/commands/llm/providers/elevenlabs.js +1 -50
- package/packages/safe-bash/dist/commands/llm/providers/elevenlabs.js.map +1 -1
- package/packages/safe-bash/dist/commands/llm/providers/index.browser.js +2 -4
- package/packages/safe-bash/dist/commands/llm/providers/openai.d.ts +1 -14
- package/packages/safe-bash/dist/commands/llm/providers/openai.d.ts.map +1 -1
- package/packages/safe-bash/dist/commands/llm/providers/openai.js +1 -77
- package/packages/safe-bash/dist/commands/llm/providers/openai.js.map +1 -1
- package/packages/safe-bash/dist/commands/llm/providers/shared.d.ts +4 -14
- package/packages/safe-bash/dist/commands/llm/providers/shared.d.ts.map +1 -1
- package/packages/safe-bash/dist/commands/llm/providers/shared.js +3 -156
- package/packages/safe-bash/dist/commands/llm/providers/shared.js.map +1 -1
- package/packages/safe-bash/dist/commands/network/index.browser.js +8 -13
- package/packages/safe-bash/dist/commands/pr/index.browser.js +3 -5
- package/packages/safe-bash/dist/commands/tsort/index.browser.js +3 -5
- package/packages/safe-bash/dist/commands/xml/index.browser.js +4 -7
- package/packages/safe-bash/dist/commands/yq/index.browser.js +5 -8
- package/packages/safe-bash/dist/core.browser.js +36 -43
- package/packages/safe-bash/dist/core.browser.js.map +1 -1
- package/packages/safe-bash/dist/chunks/chunk-BZUQJTOZ.js +0 -322
- package/packages/safe-bash/dist/chunks/chunk-BZUQJTOZ.js.map +0 -7
- package/packages/safe-bash/dist/chunks/chunk-EUTSYRNM.js +0 -111
- package/packages/safe-bash/dist/chunks/chunk-EUTSYRNM.js.map +0 -7
- package/packages/safe-bash/dist/chunks/chunk-F5AOPLRZ.js +0 -355
- package/packages/safe-bash/dist/chunks/chunk-F5AOPLRZ.js.map +0 -7
- package/packages/safe-bash/dist/chunks/chunk-FDGESHPB.js +0 -224
- package/packages/safe-bash/dist/chunks/chunk-FDGESHPB.js.map +0 -7
- package/packages/safe-bash/dist/chunks/chunk-KYU6MXVK.js +0 -58
- package/packages/safe-bash/dist/chunks/chunk-KYU6MXVK.js.map +0 -7
- package/packages/safe-bash/dist/chunks/chunk-O7BRWSKX.js.map +0 -7
- package/packages/safe-bash/dist/chunks/chunk-TTTOMUAY.js +0 -33
- package/packages/safe-bash/dist/chunks/chunk-TTTOMUAY.js.map +0 -7
- package/packages/safe-bash/dist/chunks/chunk-UO33SQJU.js.map +0 -7
- package/packages/safe-bash/dist/chunks/chunk-VY5N6O6I.js.map +0 -7
- /package/packages/safe-bash/dist/chunks/{chunk-WXUZXY35.js.map → chunk-FMTBJ7SG.js.map} +0 -0
- /package/packages/safe-bash/dist/chunks/{chunk-EUNOVYVT.js.map → chunk-OECKXHVJ.js.map} +0 -0
- /package/packages/safe-bash/dist/chunks/{chunk-PPFWG34Y.js.map → chunk-XBLVOMEJ.js.map} +0 -0
|
@@ -0,0 +1,915 @@
|
|
|
1
|
+
import {
|
|
2
|
+
init_platform
|
|
3
|
+
} from "./chunk-2KL6W6NB.js";
|
|
4
|
+
|
|
5
|
+
// packages/safe-bash/src/commands/llm/openai.ts
|
|
6
|
+
init_platform();
|
|
7
|
+
|
|
8
|
+
// packages/safe-bash/src/commands/llm/openai-http.ts
|
|
9
|
+
init_platform();
|
|
10
|
+
function openAiAbortable(pending, signal) {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
const abort = () => {
|
|
13
|
+
signal.removeEventListener("abort", abort);
|
|
14
|
+
reject(signal.reason);
|
|
15
|
+
};
|
|
16
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
17
|
+
Promise.resolve(pending).then((value) => {
|
|
18
|
+
signal.removeEventListener("abort", abort);
|
|
19
|
+
if (signal.aborted) reject(signal.reason);
|
|
20
|
+
else resolve(value);
|
|
21
|
+
}, (error) => {
|
|
22
|
+
signal.removeEventListener("abort", abort);
|
|
23
|
+
reject(signal.aborted ? signal.reason : error);
|
|
24
|
+
});
|
|
25
|
+
if (signal.aborted) abort();
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async function* openAiBytes(source, signal, limit = 64 * 1024 * 1024) {
|
|
29
|
+
signal.throwIfAborted();
|
|
30
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
31
|
+
let failed = false, finished = false;
|
|
32
|
+
let size = 0;
|
|
33
|
+
try {
|
|
34
|
+
while (true) {
|
|
35
|
+
signal.throwIfAborted();
|
|
36
|
+
const next = await openAiAbortable(iterator.next(), signal);
|
|
37
|
+
if (next.done) {
|
|
38
|
+
finished = true;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!(next.value instanceof Uint8Array)) throw new Error("OpenAI returned a non-byte response chunk");
|
|
42
|
+
size += next.value.byteLength;
|
|
43
|
+
if (size > limit) throw new RangeError("Provider response byte limit exceeded");
|
|
44
|
+
yield Uint8Array.from(next.value);
|
|
45
|
+
}
|
|
46
|
+
} catch (error) {
|
|
47
|
+
failed = true;
|
|
48
|
+
throw signal.aborted ? signal.reason : error;
|
|
49
|
+
} finally {
|
|
50
|
+
if (!finished) {
|
|
51
|
+
const returned = Promise.resolve().then(() => iterator.return?.());
|
|
52
|
+
if (signal.aborted) void returned.catch(() => void 0);
|
|
53
|
+
else await openAiAbortable(returned, signal).catch((error) => {
|
|
54
|
+
if (!failed) throw error;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function openAiJson(response, signal, maxBytes = 64 * 1024 * 1024) {
|
|
60
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
61
|
+
let text = "", length = 0;
|
|
62
|
+
for await (const chunk of openAiBytes(response.body, signal, maxBytes)) {
|
|
63
|
+
length += chunk.byteLength;
|
|
64
|
+
if (length > maxBytes) throw new Error("OpenAI JSON response exceeds buffer limit");
|
|
65
|
+
text += decoder.decode(chunk, { stream: true });
|
|
66
|
+
}
|
|
67
|
+
text += decoder.decode();
|
|
68
|
+
let value;
|
|
69
|
+
try {
|
|
70
|
+
value = JSON.parse(text);
|
|
71
|
+
} catch {
|
|
72
|
+
throw new Error("OpenAI returned malformed JSON");
|
|
73
|
+
}
|
|
74
|
+
if (!openAiRecord(value)) throw new Error("OpenAI returned a non-object JSON response");
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
function openAiRecord(value) {
|
|
78
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
79
|
+
}
|
|
80
|
+
function openAiError(value) {
|
|
81
|
+
if (typeof value === "string" && value) return value.slice(0, 1024);
|
|
82
|
+
if (openAiRecord(value) && typeof value.message === "string") return value.message.slice(0, 1024);
|
|
83
|
+
return void 0;
|
|
84
|
+
}
|
|
85
|
+
async function* openAiResponse(transport, input) {
|
|
86
|
+
input.signal.throwIfAborted();
|
|
87
|
+
const cleanups = /* @__PURE__ */ new Map();
|
|
88
|
+
let closed = false, failed = false;
|
|
89
|
+
const run = (cleanup) => {
|
|
90
|
+
cleanup.pending ??= Promise.resolve().then(cleanup.run);
|
|
91
|
+
return cleanup.pending;
|
|
92
|
+
};
|
|
93
|
+
const register = (cleanup, receiver) => {
|
|
94
|
+
if (cleanups.has(cleanup)) return;
|
|
95
|
+
const entry = { run: () => cleanup.call(receiver) };
|
|
96
|
+
cleanups.set(cleanup, entry);
|
|
97
|
+
if (closed) void run(entry).catch(() => void 0);
|
|
98
|
+
};
|
|
99
|
+
const close = async () => {
|
|
100
|
+
closed = true;
|
|
101
|
+
const results = await Promise.allSettled([...cleanups.values()].map(run));
|
|
102
|
+
const rejected = results.find((result2) => result2.status === "rejected");
|
|
103
|
+
if (rejected?.status === "rejected") throw rejected.reason;
|
|
104
|
+
};
|
|
105
|
+
const abort = () => {
|
|
106
|
+
void close().catch(() => void 0);
|
|
107
|
+
};
|
|
108
|
+
input.signal.addEventListener("abort", abort, { once: true });
|
|
109
|
+
try {
|
|
110
|
+
const pending = Promise.resolve().then(() => {
|
|
111
|
+
input.signal.throwIfAborted();
|
|
112
|
+
return transport({ ...input, registerCleanup: register });
|
|
113
|
+
}).then((response2) => {
|
|
114
|
+
register(response2.dispose, response2);
|
|
115
|
+
return response2;
|
|
116
|
+
});
|
|
117
|
+
const response = await openAiAbortable(pending, input.signal);
|
|
118
|
+
if (!Number.isInteger(response.status) || response.status < 200 || response.status >= 300) {
|
|
119
|
+
let detail;
|
|
120
|
+
try {
|
|
121
|
+
detail = openAiError((await openAiJson(response, input.signal, 64 * 1024)).error);
|
|
122
|
+
} catch {
|
|
123
|
+
input.signal.throwIfAborted();
|
|
124
|
+
}
|
|
125
|
+
throw new Error(`OpenAI HTTP ${response.status}${detail ? `: ${detail}` : ""}`);
|
|
126
|
+
}
|
|
127
|
+
yield response;
|
|
128
|
+
} catch (error) {
|
|
129
|
+
failed = true;
|
|
130
|
+
throw input.signal.aborted ? input.signal.reason : error;
|
|
131
|
+
} finally {
|
|
132
|
+
input.signal.removeEventListener("abort", abort);
|
|
133
|
+
await close().catch((error) => {
|
|
134
|
+
if (!failed && !input.signal.aborted) throw error;
|
|
135
|
+
});
|
|
136
|
+
if (!failed) input.signal.throwIfAborted();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// packages/safe-bash/src/commands/llm/openai-sse.ts
|
|
141
|
+
init_platform();
|
|
142
|
+
async function* events(source, signal, limit, responseLimit) {
|
|
143
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
144
|
+
let line = "", data = [], size = 0, afterCarriageReturn = false;
|
|
145
|
+
for await (const chunk of openAiBytes(source, signal, responseLimit)) {
|
|
146
|
+
const text = decoder.decode(chunk, { stream: true });
|
|
147
|
+
for (const character of text) {
|
|
148
|
+
if (afterCarriageReturn && character === "\n") {
|
|
149
|
+
afterCarriageReturn = false;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
afterCarriageReturn = character === "\r";
|
|
153
|
+
const codePoint = character.codePointAt(0);
|
|
154
|
+
size += codePoint < 128 ? 1 : codePoint < 2048 ? 2 : codePoint < 65536 ? 3 : 4;
|
|
155
|
+
if (size > limit) throw new Error("OpenAI SSE event exceeds buffer limit: event byte limit exceeded");
|
|
156
|
+
if (character !== "\n" && character !== "\r") {
|
|
157
|
+
line += character;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (line === "") {
|
|
161
|
+
if (data.length > 0) yield data.join("\n");
|
|
162
|
+
data = [];
|
|
163
|
+
size = 0;
|
|
164
|
+
} else if (line === "data" || line.startsWith("data:")) {
|
|
165
|
+
const value = line.slice(5);
|
|
166
|
+
data.push(value.startsWith(" ") ? value.slice(1) : value);
|
|
167
|
+
}
|
|
168
|
+
line = "";
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
decoder.decode();
|
|
172
|
+
throw new Error("OpenAI chat stream ended before [DONE]");
|
|
173
|
+
}
|
|
174
|
+
async function* openAiChat(source, signal, limit = 1024 * 1024, responseLimit = 64 * 1024 * 1024) {
|
|
175
|
+
for await (const data of events(source, signal, limit, responseLimit)) {
|
|
176
|
+
signal.throwIfAborted();
|
|
177
|
+
if (data.trim() === "[DONE]") return;
|
|
178
|
+
let parsed;
|
|
179
|
+
try {
|
|
180
|
+
parsed = JSON.parse(data);
|
|
181
|
+
} catch {
|
|
182
|
+
throw new Error("OpenAI returned malformed SSE JSON");
|
|
183
|
+
}
|
|
184
|
+
if (!openAiRecord(parsed)) throw new Error("OpenAI returned a non-object SSE event");
|
|
185
|
+
if (parsed.error != null) throw new Error(`OpenAI: ${openAiError(parsed.error) ?? "stream failed"}`);
|
|
186
|
+
if (!Array.isArray(parsed.choices)) throw new Error("OpenAI SSE event has no choices");
|
|
187
|
+
for (const choice of parsed.choices) {
|
|
188
|
+
signal.throwIfAborted();
|
|
189
|
+
if (!openAiRecord(choice) || !openAiRecord(choice.delta)) throw new Error("OpenAI SSE event has a malformed delta");
|
|
190
|
+
const content = choice.delta.content;
|
|
191
|
+
if (content != null && typeof content !== "string") throw new Error("OpenAI SSE delta.content is not text");
|
|
192
|
+
if (typeof content === "string" && content.length > 0) yield content;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// packages/safe-bash/src/commands/llm/mime.ts
|
|
198
|
+
init_platform();
|
|
199
|
+
|
|
200
|
+
// packages/safe-bash/src/commands/file/classify.ts
|
|
201
|
+
init_platform();
|
|
202
|
+
var binary = { description: "data", mime: "application/octet-stream", encoding: "binary" };
|
|
203
|
+
function result(description, mime, encoding = "binary") {
|
|
204
|
+
return { description, mime, encoding };
|
|
205
|
+
}
|
|
206
|
+
function matches(bytes, signature, offset = 0) {
|
|
207
|
+
return bytes.length >= offset + signature.length && signature.every((value, index) => bytes[offset + index] === value);
|
|
208
|
+
}
|
|
209
|
+
function ascii(bytes, start, length) {
|
|
210
|
+
return String.fromCharCode(...bytes.subarray(start, start + length));
|
|
211
|
+
}
|
|
212
|
+
function magic(bytes) {
|
|
213
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
214
|
+
if (bytes.length >= 33 && matches(bytes, [137, 80, 78, 71, 13, 10, 26, 10]) && view.getUint32(8) === 13 && ascii(bytes, 12, 4) === "IHDR" && view.getUint32(16) > 0 && view.getUint32(20) > 0 && bytes[26] === 0 && bytes[27] === 0 && bytes[28] <= 1) {
|
|
215
|
+
const allowed = { 0: [1, 2, 4, 8, 16], 2: [8, 16], 3: [1, 2, 4, 8], 4: [8, 16], 6: [8, 16] };
|
|
216
|
+
if (allowed[bytes[25]]?.includes(bytes[24])) return result("PNG image data", "image/png");
|
|
217
|
+
}
|
|
218
|
+
if (bytes.length >= 13 && ["GIF87a", "GIF89a"].includes(ascii(bytes, 0, 6)) && view.getUint16(6, true) > 0 && view.getUint16(8, true) > 0) return result("GIF image data", "image/gif");
|
|
219
|
+
if (bytes.length >= 4 && matches(bytes, [255, 216, 255]) && bytes[3] >= 192 && bytes[3] <= 254) return result("JPEG image data", "image/jpeg");
|
|
220
|
+
if (bytes.length >= 20 && ascii(bytes, 0, 4) === "RIFF" && ascii(bytes, 8, 4) === "WEBP" && ["VP8 ", "VP8L", "VP8X"].includes(ascii(bytes, 12, 4)) && view.getUint32(4, true) >= 12) return result("WebP image data", "image/webp");
|
|
221
|
+
if (bytes.length >= 8 && (matches(bytes, [73, 73, 42, 0]) || matches(bytes, [77, 77, 0, 42]))) return result("TIFF image data", "image/tiff");
|
|
222
|
+
if (bytes.length >= 26 && ascii(bytes, 0, 2) === "BM" && [12, 40, 52, 56, 108, 124].includes(view.getUint32(14, true))) return result("PC bitmap image data", "image/bmp");
|
|
223
|
+
if (bytes.length >= 22 && matches(bytes, [0, 0, 1, 0]) && view.getUint16(4, true) > 0) return result("MS Windows icon resource", "image/vnd.microsoft.icon");
|
|
224
|
+
if (/^%PDF-[12]\.[0-9](?:\r|\n|\s)/u.test(ascii(bytes, 0, 16))) return result("PDF document", "application/pdf");
|
|
225
|
+
if (bytes.length >= 10 && matches(bytes, [31, 139, 8]) && (bytes[3] & 224) === 0) return result("gzip compressed data", "application/gzip");
|
|
226
|
+
if (bytes.length >= 10 && ascii(bytes, 0, 3) === "BZh" && bytes[3] >= 49 && bytes[3] <= 57 && (ascii(bytes, 4, 6) === "1AY&SY" || matches(bytes, [23, 114, 69, 56, 80, 144], 4))) return result("bzip2 compressed data", "application/x-bzip2");
|
|
227
|
+
if (bytes.length >= 12 && matches(bytes, [253, 55, 122, 88, 90, 0]) && bytes[6] === 0 && bytes[7] <= 15) return result("XZ compressed data", "application/x-xz");
|
|
228
|
+
if (bytes.length >= 6 && matches(bytes, [40, 181, 47, 253]) && (bytes[4] & 24) === 0) return result("Zstandard compressed data", "application/zstd");
|
|
229
|
+
if (bytes.length >= 30 && matches(bytes, [80, 75, 3, 4]) || bytes.length >= 22 && matches(bytes, [80, 75, 5, 6])) return result("Zip archive data", "application/zip");
|
|
230
|
+
if (bytes.length >= 32 && matches(bytes, [55, 122, 188, 175, 39, 28])) return result("7-zip archive data", "application/x-7z-compressed");
|
|
231
|
+
if (bytes.length >= 14 && matches(bytes, [82, 97, 114, 33, 26, 7, 0]) || bytes.length >= 16 && matches(bytes, [82, 97, 114, 33, 26, 7, 1, 0])) return result("RAR archive data", "application/x-rar");
|
|
232
|
+
if (bytes.length >= 512 && ascii(bytes, 257, 5) === "ustar") {
|
|
233
|
+
const checksum = ascii(bytes, 148, 8).replace(/\0.*$/u, "").trim();
|
|
234
|
+
if (/^[0-7]+$/u.test(checksum)) {
|
|
235
|
+
let total = 0;
|
|
236
|
+
for (let index = 0; index < 512; index++) total += index >= 148 && index < 156 ? 32 : bytes[index];
|
|
237
|
+
if (parseInt(checksum, 8) === total) return result("POSIX tar archive", "application/x-tar");
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (bytes.length >= 52 && matches(bytes, [127, 69, 76, 70]) && [1, 2].includes(bytes[4]) && [1, 2].includes(bytes[5]) && bytes[6] === 1 && (bytes[4] === 1 || bytes.length >= 64)) {
|
|
241
|
+
const type = view.getUint16(16, bytes[5] === 1);
|
|
242
|
+
const mime = type === 1 ? "application/x-object" : type === 2 ? "application/x-executable" : type === 3 ? "application/x-sharedlib" : "application/octet-stream";
|
|
243
|
+
return result(`ELF ${bytes[4] === 1 ? "32" : "64"}-bit ${type === 2 ? "executable" : type === 3 ? "shared object" : "object"}`, mime);
|
|
244
|
+
}
|
|
245
|
+
if (bytes.length >= 64 && ascii(bytes, 0, 2) === "MZ") {
|
|
246
|
+
const offset = view.getUint32(60, true);
|
|
247
|
+
if (offset >= 64 && offset <= bytes.length - 24 && matches(bytes, [80, 69, 0, 0], offset)) return result("PE executable", "application/vnd.microsoft.portable-executable");
|
|
248
|
+
return result("DOS executable", "application/x-dosexec");
|
|
249
|
+
}
|
|
250
|
+
if (bytes.length >= 8 && matches(bytes, [0, 97, 115, 109, 1, 0, 0, 0])) return result("WebAssembly binary module", "application/wasm");
|
|
251
|
+
if (bytes.length >= 100 && ascii(bytes, 0, 16) === "SQLite format 3\0") {
|
|
252
|
+
const size = view.getUint16(16);
|
|
253
|
+
if ((size === 1 || size >= 512 && size <= 32768 && (size & size - 1) === 0) && [1, 2].includes(bytes[18]) && [1, 2].includes(bytes[19]) && matches(bytes, [64, 32, 32], 21)) return result("SQLite 3.x database", "application/vnd.sqlite3");
|
|
254
|
+
}
|
|
255
|
+
if (bytes.length >= 512 && matches(bytes, [208, 207, 17, 224, 161, 177, 26, 225]) && matches(bytes, [254, 255], 28)) return result("OLE Compound Document", "application/x-ole-storage");
|
|
256
|
+
return void 0;
|
|
257
|
+
}
|
|
258
|
+
function classify(bytes, complete) {
|
|
259
|
+
if (!bytes.length) return complete ? result("empty", "inode/x-empty") : binary;
|
|
260
|
+
const recognized = magic(bytes);
|
|
261
|
+
if (recognized) return recognized;
|
|
262
|
+
if (matches(bytes, [255, 254, 0, 0]) || matches(bytes, [0, 0, 254, 255])) return binary;
|
|
263
|
+
let encoding = "utf-8";
|
|
264
|
+
let bom = 0;
|
|
265
|
+
if (matches(bytes, [239, 187, 191])) bom = 3;
|
|
266
|
+
else if (matches(bytes, [255, 254])) {
|
|
267
|
+
encoding = "utf-16le";
|
|
268
|
+
bom = 2;
|
|
269
|
+
} else if (matches(bytes, [254, 255])) {
|
|
270
|
+
encoding = "utf-16be";
|
|
271
|
+
bom = 2;
|
|
272
|
+
}
|
|
273
|
+
let text;
|
|
274
|
+
try {
|
|
275
|
+
text = new TextDecoder(encoding, { fatal: true }).decode(bytes, { stream: !complete });
|
|
276
|
+
} catch {
|
|
277
|
+
return binary;
|
|
278
|
+
}
|
|
279
|
+
if (!text.length && bytes.length > bom) return binary;
|
|
280
|
+
for (const character of text) {
|
|
281
|
+
const code = character.codePointAt(0);
|
|
282
|
+
if (code < 32 && ![8, 9, 10, 12, 13, 27].includes(code) || code >= 127 && code <= 159) return binary;
|
|
283
|
+
}
|
|
284
|
+
if (!bom && encoding === "utf-8" && bytes.every((value) => value < 128)) encoding = "us-ascii";
|
|
285
|
+
if (complete && /^[\x20\t\r\n]*(?:\{|\[)/u.test(text)) {
|
|
286
|
+
try {
|
|
287
|
+
JSON.parse(text);
|
|
288
|
+
return result("JSON text data", "application/json", encoding);
|
|
289
|
+
} catch {
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const description = encoding === "us-ascii" ? "ASCII text" : encoding === "utf-8" ? "Unicode text, UTF-8" : `Unicode text, ${encoding === "utf-16le" ? "UTF-16, little-endian" : "UTF-16, big-endian"}`;
|
|
293
|
+
return result(description, "text/plain", encoding);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// packages/safe-bash/src/commands/llm/mime.ts
|
|
297
|
+
var extensions = {
|
|
298
|
+
png: "image/png",
|
|
299
|
+
jpg: "image/jpeg",
|
|
300
|
+
jpeg: "image/jpeg",
|
|
301
|
+
gif: "image/gif",
|
|
302
|
+
webp: "image/webp",
|
|
303
|
+
avif: "image/avif",
|
|
304
|
+
heic: "image/heic",
|
|
305
|
+
heif: "image/heif",
|
|
306
|
+
bmp: "image/bmp",
|
|
307
|
+
tif: "image/tiff",
|
|
308
|
+
tiff: "image/tiff",
|
|
309
|
+
svg: "image/svg+xml",
|
|
310
|
+
mp3: "audio/mpeg",
|
|
311
|
+
wav: "audio/wav",
|
|
312
|
+
ogg: "audio/ogg",
|
|
313
|
+
oga: "audio/ogg",
|
|
314
|
+
opus: "audio/ogg",
|
|
315
|
+
flac: "audio/flac",
|
|
316
|
+
aac: "audio/aac",
|
|
317
|
+
m4a: "audio/mp4",
|
|
318
|
+
aiff: "audio/aiff",
|
|
319
|
+
mp4: "video/mp4",
|
|
320
|
+
m4v: "video/mp4",
|
|
321
|
+
mov: "video/quicktime",
|
|
322
|
+
webm: "video/webm",
|
|
323
|
+
mkv: "video/x-matroska",
|
|
324
|
+
avi: "video/x-msvideo",
|
|
325
|
+
ogv: "video/ogg",
|
|
326
|
+
mpeg: "video/mpeg",
|
|
327
|
+
mpg: "video/mpeg",
|
|
328
|
+
pdf: "application/pdf",
|
|
329
|
+
json: "application/json",
|
|
330
|
+
zip: "application/zip",
|
|
331
|
+
gz: "application/gzip",
|
|
332
|
+
txt: "text/plain",
|
|
333
|
+
md: "text/markdown",
|
|
334
|
+
csv: "text/csv",
|
|
335
|
+
html: "text/html",
|
|
336
|
+
xml: "application/xml"
|
|
337
|
+
};
|
|
338
|
+
function ebmlInteger(bytes, offset, retainMarker = false) {
|
|
339
|
+
const first = bytes[offset];
|
|
340
|
+
if (!first) return void 0;
|
|
341
|
+
let marker = 128, width = 1;
|
|
342
|
+
while (!(first & marker)) {
|
|
343
|
+
marker >>= 1;
|
|
344
|
+
width++;
|
|
345
|
+
}
|
|
346
|
+
if (offset + width > bytes.length) return void 0;
|
|
347
|
+
let value = retainMarker ? first : first & marker - 1;
|
|
348
|
+
for (let index = 1; index < width; index++) value = value * 256 + bytes[offset + index];
|
|
349
|
+
if (!Number.isSafeInteger(value) || !retainMarker && value === 2 ** (7 * width) - 1) return void 0;
|
|
350
|
+
return { value, width };
|
|
351
|
+
}
|
|
352
|
+
function ebmlMimeType(bytes) {
|
|
353
|
+
const header = ebmlInteger(bytes, 4);
|
|
354
|
+
if (!header) return void 0;
|
|
355
|
+
let offset = 4 + header.width;
|
|
356
|
+
const end = Math.min(bytes.length, offset + header.value, 4096);
|
|
357
|
+
while (offset < end) {
|
|
358
|
+
const identifier = ebmlInteger(bytes, offset, true);
|
|
359
|
+
if (!identifier) return void 0;
|
|
360
|
+
offset += identifier.width;
|
|
361
|
+
const size = ebmlInteger(bytes, offset);
|
|
362
|
+
if (!size) return void 0;
|
|
363
|
+
offset += size.width;
|
|
364
|
+
if (size.value > end - offset) return void 0;
|
|
365
|
+
if (identifier.value === 17026) {
|
|
366
|
+
const docType = new TextDecoder().decode(bytes.subarray(offset, offset + size.value)).split("\0", 1)[0];
|
|
367
|
+
if (docType === "webm") return "video/webm";
|
|
368
|
+
if (docType === "matroska") return "video/x-matroska";
|
|
369
|
+
return void 0;
|
|
370
|
+
}
|
|
371
|
+
offset += size.value;
|
|
372
|
+
}
|
|
373
|
+
return void 0;
|
|
374
|
+
}
|
|
375
|
+
function sniffMimeType(path, bytes) {
|
|
376
|
+
const matches2 = (signature, offset = 0) => bytes.length >= offset + signature.length && signature.every((byte, index) => bytes[offset + index] === byte);
|
|
377
|
+
const ascii2 = (offset, text) => bytes.length >= offset + text.length && [...text].every((character, index) => bytes[offset + index] === character.charCodeAt(0));
|
|
378
|
+
if (matches2([137, 80, 78, 71, 13, 10, 26, 10])) return "image/png";
|
|
379
|
+
if (matches2([255, 216, 255])) return "image/jpeg";
|
|
380
|
+
if (ascii2(0, "GIF87a") || ascii2(0, "GIF89a")) return "image/gif";
|
|
381
|
+
if (ascii2(0, "RIFF")) {
|
|
382
|
+
if (ascii2(8, "WEBP")) return "image/webp";
|
|
383
|
+
if (ascii2(8, "WAVE")) return "audio/wav";
|
|
384
|
+
if (ascii2(8, "AVI ")) return "video/x-msvideo";
|
|
385
|
+
}
|
|
386
|
+
if (ascii2(0, "fLaC")) return "audio/flac";
|
|
387
|
+
if (ascii2(0, "OggS")) {
|
|
388
|
+
const packet = 27 + (bytes[26] ?? 0);
|
|
389
|
+
if (bytes[26] && bytes[packet] === 128 && ascii2(packet + 1, "theora")) return "video/ogg";
|
|
390
|
+
return "audio/ogg";
|
|
391
|
+
}
|
|
392
|
+
if (ascii2(0, "ID3") || bytes.length >= 2 && bytes[0] === 255 && (bytes[1] & 224) === 224 && (bytes[1] & 6) !== 0) return "audio/mpeg";
|
|
393
|
+
if (bytes.length >= 2 && bytes[0] === 255 && (bytes[1] & 246) === 240) return "audio/aac";
|
|
394
|
+
if (ascii2(0, "FORM") && (ascii2(8, "AIFF") || ascii2(8, "AIFC"))) return "audio/aiff";
|
|
395
|
+
if (ascii2(4, "ftyp")) {
|
|
396
|
+
if (ascii2(8, "avif") || ascii2(8, "avis")) return "image/avif";
|
|
397
|
+
if (ascii2(8, "heic") || ascii2(8, "heix")) return "image/heic";
|
|
398
|
+
if (ascii2(8, "mif1") || ascii2(8, "msf1")) return "image/heif";
|
|
399
|
+
if (ascii2(8, "M4A ") || ascii2(8, "M4B ")) return "audio/mp4";
|
|
400
|
+
if (ascii2(8, "qt ")) return "video/quicktime";
|
|
401
|
+
return "video/mp4";
|
|
402
|
+
}
|
|
403
|
+
if (matches2([26, 69, 223, 163])) {
|
|
404
|
+
const mimeType = ebmlMimeType(bytes);
|
|
405
|
+
if (mimeType) return mimeType;
|
|
406
|
+
}
|
|
407
|
+
if (ascii2(0, "%PDF-")) return "application/pdf";
|
|
408
|
+
if (matches2([80, 75, 3, 4]) || matches2([80, 75, 5, 6])) return "application/zip";
|
|
409
|
+
if (matches2([31, 139])) return "application/gzip";
|
|
410
|
+
if (matches2([73, 73, 42, 0]) || matches2([77, 77, 0, 42])) return "image/tiff";
|
|
411
|
+
if (ascii2(0, "BM")) return "image/bmp";
|
|
412
|
+
const prefix = bytes.subarray(0, 512);
|
|
413
|
+
const classified = classify(prefix, bytes.length <= prefix.length).mime;
|
|
414
|
+
if (!["application/octet-stream", "text/plain", "inode/x-empty"].includes(classified)) return classified;
|
|
415
|
+
const filename = path.slice(path.lastIndexOf("/") + 1);
|
|
416
|
+
const dot = filename.lastIndexOf(".");
|
|
417
|
+
const extension = dot < 0 ? "" : filename.slice(dot + 1).toLowerCase();
|
|
418
|
+
return Object.hasOwn(extensions, extension) ? extensions[extension] : classified === "inode/x-empty" ? "application/octet-stream" : classified;
|
|
419
|
+
}
|
|
420
|
+
function acceptsMimeType(types, mimeType) {
|
|
421
|
+
if ([...mimeType].some((character) => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127)) return false;
|
|
422
|
+
const mime = mimeType.split(";", 1)[0].trim().toLowerCase();
|
|
423
|
+
const parts = mime.split("/");
|
|
424
|
+
if (parts.length !== 2 || parts.some((part) => !part || [...part].some((character) => !(character >= "a" && character <= "z" || character >= "0" && character <= "9" || "!#$%&'+-.^_`|~".includes(character))))) return false;
|
|
425
|
+
return types.some((type) => {
|
|
426
|
+
const accepted = type.toLowerCase();
|
|
427
|
+
return accepted === mime || accepted.endsWith("/*") && accepted.slice(0, -2) === parts[0];
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// packages/safe-bash/src/commands/llm/providers/shared.ts
|
|
432
|
+
init_platform();
|
|
433
|
+
function providerLimits(input = {}) {
|
|
434
|
+
const limits = { maxRequestBytes: 64 * 1024 * 1024, maxResponseBytes: 64 * 1024 * 1024, maxEventBytes: 1024 * 1024, maxPolls: 120, pollIntervalMs: 1e3, ...input };
|
|
435
|
+
for (const [name, value] of Object.entries(limits)) {
|
|
436
|
+
if (!Number.isSafeInteger(value) || value < (name === "maxPolls" || name === "pollIntervalMs" ? 0 : 1)) throw new RangeError(`Invalid provider limit: ${name}`);
|
|
437
|
+
}
|
|
438
|
+
return Object.freeze(limits);
|
|
439
|
+
}
|
|
440
|
+
function credential(value) {
|
|
441
|
+
if (typeof value !== "string" || value.length === 0 || [...value].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127)) throw new TypeError("Invalid provider apiKey");
|
|
442
|
+
return value;
|
|
443
|
+
}
|
|
444
|
+
function jsonBody(value, limit) {
|
|
445
|
+
const text = JSON.stringify(value);
|
|
446
|
+
if (text.length > limit) throw new RangeError("Provider request byte limit exceeded");
|
|
447
|
+
const bytes = new TextEncoder().encode(text);
|
|
448
|
+
if (bytes.length > limit) throw new RangeError("Provider request byte limit exceeded");
|
|
449
|
+
return { body: (async function* () {
|
|
450
|
+
yield bytes;
|
|
451
|
+
})(), headers: [["content-type", "application/json"]] };
|
|
452
|
+
}
|
|
453
|
+
function multipart(options, files, limit) {
|
|
454
|
+
const encoder = new TextEncoder();
|
|
455
|
+
const parts = [];
|
|
456
|
+
const entries = Object.entries(options).map(([name, value]) => {
|
|
457
|
+
if (![...name].every((char) => "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-".includes(char))) throw new TypeError("Invalid multipart option name");
|
|
458
|
+
return [name, String(value)];
|
|
459
|
+
});
|
|
460
|
+
let size = files.reduce((sum, file) => sum + file.bytes.length, 0);
|
|
461
|
+
for (const [, value] of entries) {
|
|
462
|
+
if (value.length > limit) throw new RangeError("Provider request byte limit exceeded");
|
|
463
|
+
size += encoder.encode(value).length;
|
|
464
|
+
}
|
|
465
|
+
if (size > limit) throw new RangeError("Provider request byte limit exceeded");
|
|
466
|
+
let boundary = "safe-bash-llm-boundary";
|
|
467
|
+
const contains = (bytes, needle) => {
|
|
468
|
+
outer: for (let start = 0; start <= bytes.length - needle.length; start++) {
|
|
469
|
+
for (let index = 0; index < needle.length; index++) if (bytes[start + index] !== needle[index]) continue outer;
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
472
|
+
return false;
|
|
473
|
+
};
|
|
474
|
+
let attempts = 0;
|
|
475
|
+
while (entries.some(([, value]) => value.includes(boundary)) || files.some((file) => contains(file.bytes, encoder.encode(boundary)))) {
|
|
476
|
+
if (attempts++ === 32) throw new RangeError("Provider multipart boundary collision limit exceeded");
|
|
477
|
+
boundary += "x";
|
|
478
|
+
}
|
|
479
|
+
for (const [name, value] of entries) parts.push(encoder.encode(`--${boundary}\r
|
|
480
|
+
Content-Disposition: form-data; name="${name}"\r
|
|
481
|
+
\r
|
|
482
|
+
${value}\r
|
|
483
|
+
`));
|
|
484
|
+
for (const [index, file] of files.entries()) {
|
|
485
|
+
if ([...file.mimeType].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) > 126)) throw new TypeError("Invalid attachment MIME type");
|
|
486
|
+
parts.push(encoder.encode(`--${boundary}\r
|
|
487
|
+
Content-Disposition: form-data; name="${file.field}"; filename="attachment-${index}"\r
|
|
488
|
+
Content-Type: ${file.mimeType}\r
|
|
489
|
+
\r
|
|
490
|
+
`), Uint8Array.from(file.bytes), encoder.encode("\r\n"));
|
|
491
|
+
}
|
|
492
|
+
parts.push(encoder.encode(`--${boundary}--\r
|
|
493
|
+
`));
|
|
494
|
+
if (parts.reduce((sum, part) => sum + part.length, 0) > limit) throw new RangeError("Provider request byte limit exceeded");
|
|
495
|
+
return { body: (async function* () {
|
|
496
|
+
yield* parts;
|
|
497
|
+
})(), headers: [["content-type", `multipart/form-data; boundary=${boundary}`]] };
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// packages/safe-bash/src/commands/llm/openai.ts
|
|
501
|
+
var numericOptions = {
|
|
502
|
+
chat: /* @__PURE__ */ new Map([
|
|
503
|
+
["temperature", "number"],
|
|
504
|
+
["top_p", "number"],
|
|
505
|
+
["frequency_penalty", "number"],
|
|
506
|
+
["presence_penalty", "number"],
|
|
507
|
+
["max_tokens", "integer"],
|
|
508
|
+
["max_completion_tokens", "integer"],
|
|
509
|
+
["n", "integer"],
|
|
510
|
+
["seed", "integer"],
|
|
511
|
+
["top_logprobs", "integer"]
|
|
512
|
+
]),
|
|
513
|
+
images: /* @__PURE__ */ new Map([
|
|
514
|
+
["n", "integer"],
|
|
515
|
+
["output_compression", "integer"],
|
|
516
|
+
["partial_images", "integer"]
|
|
517
|
+
])
|
|
518
|
+
};
|
|
519
|
+
function jsonOptions(options, endpoint) {
|
|
520
|
+
return Object.fromEntries(Object.entries(options).map(([key, value]) => {
|
|
521
|
+
if ((endpoint === "chat" ? ["store", "parallel_tool_calls", "logprobs"] : ["stream"]).includes(key)) {
|
|
522
|
+
if (value !== "true" && value !== "false") throw new Error(`Invalid OpenAI option ${key}: expected boolean`);
|
|
523
|
+
return [key, value === "true"];
|
|
524
|
+
}
|
|
525
|
+
const type = numericOptions[endpoint].get(key);
|
|
526
|
+
if (!type) return [key, value];
|
|
527
|
+
const number = value.trim() ? Number(value) : NaN;
|
|
528
|
+
if (!Number.isFinite(number) || type === "integer" && !Number.isSafeInteger(number)) {
|
|
529
|
+
throw new Error(`Invalid OpenAI option ${key}: expected a finite ${type === "integer" ? "safe integer" : "number"}`);
|
|
530
|
+
}
|
|
531
|
+
return [key, number];
|
|
532
|
+
}));
|
|
533
|
+
}
|
|
534
|
+
function base64(bytes) {
|
|
535
|
+
let binary2 = "";
|
|
536
|
+
for (let offset = 0; offset < bytes.length; offset += 8192) {
|
|
537
|
+
binary2 += String.fromCharCode(...bytes.subarray(offset, offset + 8192));
|
|
538
|
+
}
|
|
539
|
+
return btoa(binary2);
|
|
540
|
+
}
|
|
541
|
+
function imageBytes(value) {
|
|
542
|
+
if (typeof value !== "string" || value.length === 0) throw new Error("OpenAI image response has no b64_json");
|
|
543
|
+
let decoded;
|
|
544
|
+
try {
|
|
545
|
+
decoded = atob(value);
|
|
546
|
+
} catch {
|
|
547
|
+
throw new Error("OpenAI image response contains invalid base64");
|
|
548
|
+
}
|
|
549
|
+
if (btoa(decoded) !== value) throw new Error("OpenAI image response contains non-canonical base64");
|
|
550
|
+
return Uint8Array.from(decoded, (character) => character.charCodeAt(0));
|
|
551
|
+
}
|
|
552
|
+
function waitForPoll(signal, interval) {
|
|
553
|
+
signal.throwIfAborted();
|
|
554
|
+
return new Promise((resolve, reject) => {
|
|
555
|
+
const abort = () => {
|
|
556
|
+
clearTimeout(timer);
|
|
557
|
+
signal.removeEventListener("abort", abort);
|
|
558
|
+
reject(signal.reason);
|
|
559
|
+
};
|
|
560
|
+
const timer = setTimeout(() => {
|
|
561
|
+
signal.removeEventListener("abort", abort);
|
|
562
|
+
resolve();
|
|
563
|
+
}, interval);
|
|
564
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
565
|
+
if (signal.aborted) abort();
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
function job(value, expectedId) {
|
|
569
|
+
if (value.error != null) throw new Error(`OpenAI video failed: ${openAiError(value.error) ?? "unknown error"}`);
|
|
570
|
+
if (typeof value.id !== "string" || value.id.length === 0 || value.id === "." || value.id === ".." || expectedId !== void 0 && value.id !== expectedId) {
|
|
571
|
+
throw new Error("OpenAI video response has an invalid job id");
|
|
572
|
+
}
|
|
573
|
+
if (value.status !== "queued" && value.status !== "in_progress" && value.status !== "completed") {
|
|
574
|
+
throw new Error(`OpenAI video failed: ${typeof value.status === "string" ? value.status : "invalid status"}`);
|
|
575
|
+
}
|
|
576
|
+
return { id: value.id, status: value.status };
|
|
577
|
+
}
|
|
578
|
+
function createOpenAiProvider(options) {
|
|
579
|
+
const { transport, apiKey } = options;
|
|
580
|
+
const limits = providerLimits(options.limits);
|
|
581
|
+
if (typeof transport !== "function") throw new TypeError("OpenAI requires an injected HTTP transport");
|
|
582
|
+
if (typeof apiKey !== "string" || !apiKey.trim() || apiKey.includes("\r") || apiKey.includes("\n")) throw new TypeError("OpenAI requires a valid API key");
|
|
583
|
+
credential(apiKey);
|
|
584
|
+
const base = new URL(options.baseUrl ?? "https://api.openai.com/v1");
|
|
585
|
+
if (base.protocol !== "https:" && base.protocol !== "http:" || base.username || base.password || base.search || base.hash) {
|
|
586
|
+
throw new TypeError("OpenAI baseUrl must be an HTTP(S) URL without credentials, query or fragment");
|
|
587
|
+
}
|
|
588
|
+
const baseUrl = base.href.endsWith("/") ? base.href.slice(0, -1) : base.href;
|
|
589
|
+
const configured = options.models.map((model) => Object.freeze({
|
|
590
|
+
...model,
|
|
591
|
+
...model.aliases === void 0 ? {} : { aliases: Object.freeze([...model.aliases]) },
|
|
592
|
+
...model.attachmentTypes === void 0 ? {} : { attachmentTypes: Object.freeze([...model.attachmentTypes]) }
|
|
593
|
+
}));
|
|
594
|
+
const byId = /* @__PURE__ */ new Map();
|
|
595
|
+
for (const model of configured) {
|
|
596
|
+
if (!model.id || byId.has(model.id)) throw new TypeError(`OpenAI duplicate or empty model id: ${model.id}`);
|
|
597
|
+
if (!["chat", "images", "videos"].includes(model.endpoint)) throw new TypeError(`OpenAI invalid endpoint for model: ${model.id}`);
|
|
598
|
+
if (model.endpoint === "images" && !acceptsMimeType(["image/png", "image/jpeg", "image/webp"], model.outputType ?? "")) throw new TypeError("Image models require outputType image/png, image/jpeg or image/webp");
|
|
599
|
+
if (model.endpoint === "videos" && !acceptsMimeType(["video/mp4"], model.outputType ?? "")) throw new TypeError("Video models require outputType video/mp4");
|
|
600
|
+
if (model.endpoint === "chat" && model.outputType !== void 0 && !acceptsMimeType(["text/*"], model.outputType)) throw new TypeError("Chat models require a text outputType");
|
|
601
|
+
byId.set(model.id, model);
|
|
602
|
+
}
|
|
603
|
+
return {
|
|
604
|
+
name: "openai",
|
|
605
|
+
models: Object.freeze(configured),
|
|
606
|
+
async *complete(request) {
|
|
607
|
+
request.signal.throwIfAborted();
|
|
608
|
+
const model = byId.get(request.model);
|
|
609
|
+
if (!model) throw new Error(`Unknown model: ${request.model}`);
|
|
610
|
+
if (request.attachments.some((attachment) => !acceptsMimeType(["image/*"], attachment.mimeType))) {
|
|
611
|
+
throw new Error(`OpenAI ${model.endpoint} endpoint only supports image attachments`);
|
|
612
|
+
}
|
|
613
|
+
if (model.endpoint === "videos" && request.attachments.length > 1) throw new Error("OpenAI videos accepts only one input_reference image");
|
|
614
|
+
if (request.attachments.reduce((size, file) => size + file.bytes.byteLength, 0) > limits.maxRequestBytes) throw new RangeError("Provider request byte limit exceeded");
|
|
615
|
+
if (model.endpoint !== "chat" && request.system !== void 0) throw new TypeError("System prompts are supported only by chat models");
|
|
616
|
+
const reserved = model.endpoint === "chat" ? ["model", "messages", "stream"] : model.endpoint === "images" ? ["model", "prompt", "image", "image[]"] : ["model", "prompt", "input_reference"];
|
|
617
|
+
for (const key of Object.keys(request.options)) {
|
|
618
|
+
if (reserved.includes(key)) throw new Error(`OpenAI option ${key} is controlled by the provider`);
|
|
619
|
+
}
|
|
620
|
+
const send = (path, method, body) => openAiResponse(transport, {
|
|
621
|
+
url: `${baseUrl}${path}`,
|
|
622
|
+
method,
|
|
623
|
+
signal: request.signal,
|
|
624
|
+
...body,
|
|
625
|
+
headers: [["authorization", `Bearer ${apiKey}`], ...body?.headers ?? []]
|
|
626
|
+
});
|
|
627
|
+
if (model.endpoint === "chat") {
|
|
628
|
+
const messages = [];
|
|
629
|
+
if (request.system !== void 0) messages.push({ role: "system", content: request.system });
|
|
630
|
+
messages.push({ role: "user", content: request.attachments.length === 0 ? request.prompt : [
|
|
631
|
+
{ type: "text", text: request.prompt },
|
|
632
|
+
...request.attachments.map((attachment) => ({ type: "image_url", image_url: { url: `data:${attachment.mimeType};base64,${base64(attachment.bytes)}` } }))
|
|
633
|
+
] });
|
|
634
|
+
for await (const response of send("/chat/completions", "POST", jsonBody({ ...jsonOptions(request.options, "chat"), model: request.model, messages, stream: true }, limits.maxRequestBytes))) {
|
|
635
|
+
yield* openAiChat(response.body, request.signal, limits.maxEventBytes, limits.maxResponseBytes);
|
|
636
|
+
}
|
|
637
|
+
} else if (model.endpoint === "images") {
|
|
638
|
+
const editing = request.attachments.length > 0;
|
|
639
|
+
const outputFormat = model.outputType.split(";", 1)[0].trim().toLowerCase().slice("image/".length);
|
|
640
|
+
if (request.options.output_format !== void 0 && request.options.output_format !== outputFormat) throw new TypeError("output_format conflicts with model outputType");
|
|
641
|
+
const values = { ...jsonOptions(request.options, "images"), output_format: outputFormat, model: request.model, prompt: request.prompt };
|
|
642
|
+
if (values.stream === true) throw new TypeError("Image event streaming is not supported by this reference provider");
|
|
643
|
+
const body = editing ? multipart({ ...values, ...request.options }, request.attachments.map((file) => ({ ...file, field: "image[]" })), limits.maxRequestBytes) : jsonBody(values, limits.maxRequestBytes);
|
|
644
|
+
for await (const response of send(editing ? "/images/edits" : "/images/generations", "POST", body)) {
|
|
645
|
+
const value = await openAiJson(response, request.signal, limits.maxResponseBytes);
|
|
646
|
+
if (value.error != null) throw new Error(`OpenAI: ${openAiError(value.error) ?? "image generation failed"}`);
|
|
647
|
+
if (!Array.isArray(value.data) || value.data.length === 0) throw new Error("OpenAI image response has no images");
|
|
648
|
+
for (const item of value.data) {
|
|
649
|
+
request.signal.throwIfAborted();
|
|
650
|
+
if (!openAiRecord(item)) throw new Error("OpenAI image response contains a malformed image");
|
|
651
|
+
yield imageBytes(item.b64_json);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
} else {
|
|
655
|
+
let current;
|
|
656
|
+
for await (const response of send("/videos", "POST", multipart({ ...request.options, model: request.model, prompt: request.prompt }, request.attachments.map((file) => ({ ...file, field: "input_reference" })), limits.maxRequestBytes))) {
|
|
657
|
+
current = job(await openAiJson(response, request.signal, limits.maxResponseBytes));
|
|
658
|
+
}
|
|
659
|
+
if (!current) throw new Error("OpenAI video creation returned no job");
|
|
660
|
+
const path = `/videos/${encodeURIComponent(current.id)}`;
|
|
661
|
+
let polls = 0;
|
|
662
|
+
while (current.status !== "completed") {
|
|
663
|
+
if (polls++ >= limits.maxPolls) throw new RangeError("Provider video polling limit exceeded");
|
|
664
|
+
await waitForPoll(request.signal, limits.pollIntervalMs);
|
|
665
|
+
for await (const response of send(path, "GET")) current = job(await openAiJson(response, request.signal, limits.maxResponseBytes), current.id);
|
|
666
|
+
}
|
|
667
|
+
for await (const response of send(`${path}/content`, "GET")) {
|
|
668
|
+
const contentType = response.headers.find(([name]) => name.toLowerCase() === "content-type")?.[1].split(";")[0]?.trim().toLowerCase();
|
|
669
|
+
if (contentType && contentType !== "application/octet-stream" && !contentType.startsWith("video/")) {
|
|
670
|
+
throw new Error(`OpenAI video download has unexpected content type: ${contentType}`);
|
|
671
|
+
}
|
|
672
|
+
let received = false;
|
|
673
|
+
for await (const chunk of openAiBytes(response.body, request.signal, limits.maxResponseBytes)) {
|
|
674
|
+
if (chunk.byteLength === 0) continue;
|
|
675
|
+
received = true;
|
|
676
|
+
yield chunk;
|
|
677
|
+
}
|
|
678
|
+
if (!received) throw new Error("OpenAI video download is empty");
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// packages/safe-bash/src/commands/llm/elevenlabs.ts
|
|
686
|
+
init_platform();
|
|
687
|
+
|
|
688
|
+
// packages/safe-bash/src/commands/llm/elevenlabs-stream.ts
|
|
689
|
+
init_platform();
|
|
690
|
+
async function abortable(operation, signal) {
|
|
691
|
+
signal.throwIfAborted();
|
|
692
|
+
let onAbort;
|
|
693
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
694
|
+
onAbort = () => reject(signal.reason);
|
|
695
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
696
|
+
});
|
|
697
|
+
try {
|
|
698
|
+
return await Promise.race([Promise.resolve().then(() => {
|
|
699
|
+
signal.throwIfAborted();
|
|
700
|
+
return operation();
|
|
701
|
+
}), aborted]);
|
|
702
|
+
} finally {
|
|
703
|
+
signal.removeEventListener("abort", onAbort);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
async function* streamElevenLabs(transport, request, limit = 64 * 1024 * 1024) {
|
|
707
|
+
const { signal } = request;
|
|
708
|
+
signal.throwIfAborted();
|
|
709
|
+
let iterator;
|
|
710
|
+
let finished = false;
|
|
711
|
+
let failed = false;
|
|
712
|
+
let closed = false;
|
|
713
|
+
let size = 0;
|
|
714
|
+
const cleanups = /* @__PURE__ */ new Map();
|
|
715
|
+
const runCleanup = (entry) => {
|
|
716
|
+
return entry.pending ??= Promise.resolve().then(entry.run);
|
|
717
|
+
};
|
|
718
|
+
const register = (cleanup, receiver) => {
|
|
719
|
+
if (cleanups.has(cleanup)) return;
|
|
720
|
+
const entry = { run: () => cleanup.call(receiver) };
|
|
721
|
+
cleanups.set(cleanup, entry);
|
|
722
|
+
if (closed) void runCleanup(entry).catch(() => {
|
|
723
|
+
});
|
|
724
|
+
};
|
|
725
|
+
const dispose = async () => {
|
|
726
|
+
closed = true;
|
|
727
|
+
let seen = 0;
|
|
728
|
+
const failures = [];
|
|
729
|
+
while (seen < cleanups.size) {
|
|
730
|
+
const entries = [...cleanups.values()].slice(seen);
|
|
731
|
+
seen = cleanups.size;
|
|
732
|
+
const results = await Promise.allSettled(entries.map(runCleanup));
|
|
733
|
+
for (const result2 of results) if (result2.status === "rejected") failures.push(result2.reason);
|
|
734
|
+
}
|
|
735
|
+
if (failures.length) throw failures[0];
|
|
736
|
+
};
|
|
737
|
+
const close = async () => {
|
|
738
|
+
const failures = [];
|
|
739
|
+
try {
|
|
740
|
+
await dispose();
|
|
741
|
+
} catch (error) {
|
|
742
|
+
failures.push(error);
|
|
743
|
+
}
|
|
744
|
+
if (!finished && iterator?.return) {
|
|
745
|
+
const closing = Promise.resolve().then(() => iterator.return());
|
|
746
|
+
if (signal.aborted) void closing.catch(() => {
|
|
747
|
+
});
|
|
748
|
+
else {
|
|
749
|
+
try {
|
|
750
|
+
await closing;
|
|
751
|
+
} catch (error) {
|
|
752
|
+
failures.push(error);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
if (!failed) {
|
|
757
|
+
signal.throwIfAborted();
|
|
758
|
+
if (failures.length) throw failures[0];
|
|
759
|
+
}
|
|
760
|
+
};
|
|
761
|
+
const onAbort = () => {
|
|
762
|
+
void dispose().catch(() => {
|
|
763
|
+
});
|
|
764
|
+
};
|
|
765
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
766
|
+
try {
|
|
767
|
+
const acquired = await abortable(async () => {
|
|
768
|
+
signal.throwIfAborted();
|
|
769
|
+
const response = await transport({ ...request, registerCleanup: register });
|
|
770
|
+
register(response.dispose, response);
|
|
771
|
+
if (signal.aborted) {
|
|
772
|
+
await dispose();
|
|
773
|
+
signal.throwIfAborted();
|
|
774
|
+
}
|
|
775
|
+
return response;
|
|
776
|
+
}, signal);
|
|
777
|
+
signal.throwIfAborted();
|
|
778
|
+
if (acquired.status < 200 || acquired.status >= 300) throw new Error(`ElevenLabs HTTP ${acquired.status}: ${acquired.statusText}`);
|
|
779
|
+
iterator = acquired.body[Symbol.asyncIterator]();
|
|
780
|
+
while (true) {
|
|
781
|
+
const next = await abortable(() => iterator.next(), signal);
|
|
782
|
+
signal.throwIfAborted();
|
|
783
|
+
if (next.done) {
|
|
784
|
+
finished = true;
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
if (!(next.value instanceof Uint8Array)) throw new TypeError("ElevenLabs audio stream must yield Uint8Array chunks");
|
|
788
|
+
size += next.value.byteLength;
|
|
789
|
+
if (size > limit) throw new RangeError("Provider response byte limit exceeded");
|
|
790
|
+
yield Uint8Array.from(next.value);
|
|
791
|
+
}
|
|
792
|
+
} catch (error) {
|
|
793
|
+
failed = true;
|
|
794
|
+
throw error;
|
|
795
|
+
} finally {
|
|
796
|
+
signal.removeEventListener("abort", onAbort);
|
|
797
|
+
await close();
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// packages/safe-bash/src/commands/llm/elevenlabs.ts
|
|
802
|
+
var outputFormats = /* @__PURE__ */ new Map([
|
|
803
|
+
["audio/mpeg", "mp3_44100_128"],
|
|
804
|
+
["audio/wav", "wav_44100"],
|
|
805
|
+
["audio/x-wav", "wav_44100"],
|
|
806
|
+
["audio/pcm", "pcm_44100"],
|
|
807
|
+
["audio/basic", "ulaw_8000"],
|
|
808
|
+
["audio/opus", "opus_48000_128"],
|
|
809
|
+
["audio/ogg", "opus_48000_128"]
|
|
810
|
+
]);
|
|
811
|
+
var optionTypes = {
|
|
812
|
+
tts: /* @__PURE__ */ new Map([
|
|
813
|
+
["stability", "number"],
|
|
814
|
+
["similarity_boost", "number"],
|
|
815
|
+
["style", "number"],
|
|
816
|
+
["speed", "number"],
|
|
817
|
+
["use_speaker_boost", "boolean"]
|
|
818
|
+
]),
|
|
819
|
+
music: /* @__PURE__ */ new Map([
|
|
820
|
+
["music_length_ms", "integer"],
|
|
821
|
+
["seed", "integer"],
|
|
822
|
+
["force_instrumental", "boolean"],
|
|
823
|
+
["respect_sections_durations", "boolean"],
|
|
824
|
+
["store_for_inpainting", "boolean"],
|
|
825
|
+
["sign_with_c2pa", "boolean"]
|
|
826
|
+
])
|
|
827
|
+
};
|
|
828
|
+
function parseOptions(options, endpoint) {
|
|
829
|
+
return Object.fromEntries(Object.entries(options).map(([key, value]) => {
|
|
830
|
+
const type = optionTypes[endpoint].get(key);
|
|
831
|
+
if (!type) return [key, value];
|
|
832
|
+
if (type === "boolean") {
|
|
833
|
+
if (value === "true" || value === "false") return [key, value === "true"];
|
|
834
|
+
} else {
|
|
835
|
+
const number = value.trim() ? Number(value) : NaN;
|
|
836
|
+
if (Number.isFinite(number) && (type !== "integer" || Number.isSafeInteger(number))) return [key, number];
|
|
837
|
+
}
|
|
838
|
+
throw new Error(`Invalid ElevenLabs option ${key}: expected ${type}`);
|
|
839
|
+
}));
|
|
840
|
+
}
|
|
841
|
+
function createElevenLabsProvider(options) {
|
|
842
|
+
const transport = options.transport;
|
|
843
|
+
if (typeof transport !== "function") throw new TypeError("Provider transport is required");
|
|
844
|
+
const apiKey = credential(options.apiKey);
|
|
845
|
+
const limits = providerLimits(options.limits);
|
|
846
|
+
const baseUrl = new URL(options.baseUrl ?? "https://api.elevenlabs.io");
|
|
847
|
+
if (!["https:", "http:"].includes(baseUrl.protocol) || baseUrl.username || baseUrl.password || baseUrl.search || baseUrl.hash) {
|
|
848
|
+
throw new Error("Invalid ElevenLabs baseUrl: expected an HTTP(S) URL without credentials, query, or fragment");
|
|
849
|
+
}
|
|
850
|
+
if (!baseUrl.pathname.endsWith("/")) baseUrl.pathname += "/";
|
|
851
|
+
const models = options.models.map((model) => Object.freeze({
|
|
852
|
+
...model,
|
|
853
|
+
...model.aliases === void 0 ? {} : { aliases: Object.freeze([...model.aliases]) },
|
|
854
|
+
...model.attachmentTypes === void 0 ? {} : { attachmentTypes: Object.freeze([...model.attachmentTypes]) }
|
|
855
|
+
}));
|
|
856
|
+
const byId = /* @__PURE__ */ new Map();
|
|
857
|
+
for (const model of models) {
|
|
858
|
+
if (model.endpoint !== "tts" && model.endpoint !== "music") throw new Error(`Invalid ElevenLabs endpoint: ${model.endpoint}`);
|
|
859
|
+
if (!model.outputType) throw new TypeError("ElevenLabs models require an audio outputType");
|
|
860
|
+
const mimeType = model.outputType.split(";", 1)[0].trim().toLowerCase();
|
|
861
|
+
const format = outputFormats.get(mimeType);
|
|
862
|
+
if (!format || !acceptsMimeType([mimeType], model.outputType) || model.endpoint === "music" && format.startsWith("wav_")) {
|
|
863
|
+
throw new Error(`Unsupported ElevenLabs outputType for ${model.endpoint}: ${model.outputType}`);
|
|
864
|
+
}
|
|
865
|
+
if (model.attachmentTypes?.length) throw new Error(`ElevenLabs model ${model.id} does not accept attachments`);
|
|
866
|
+
if (!model.id || byId.has(model.id)) throw new Error(`Duplicate or empty ElevenLabs model: ${model.id}`);
|
|
867
|
+
byId.set(model.id, { model, format });
|
|
868
|
+
}
|
|
869
|
+
return {
|
|
870
|
+
name: "elevenlabs",
|
|
871
|
+
models: Object.freeze(models),
|
|
872
|
+
async *complete(request) {
|
|
873
|
+
request.signal.throwIfAborted();
|
|
874
|
+
const entry = byId.get(request.model);
|
|
875
|
+
if (!entry) throw new Error(`Unknown model: ${request.model}`);
|
|
876
|
+
const { model, format } = entry;
|
|
877
|
+
if (request.attachments.length) throw new Error(`Model ${model.id} does not accept ${request.attachments[0].mimeType} attachments`);
|
|
878
|
+
if (request.system !== void 0) throw new TypeError("ElevenLabs reference endpoints do not accept system prompts");
|
|
879
|
+
const { output_format: requestedFormat, ...requestOptions } = request.options;
|
|
880
|
+
if (requestedFormat !== void 0 && requestedFormat !== format) throw new TypeError("output_format conflicts with model outputType");
|
|
881
|
+
let path;
|
|
882
|
+
let body;
|
|
883
|
+
if (model.endpoint === "tts") {
|
|
884
|
+
const { voice_id: suppliedVoice, ...settings } = requestOptions;
|
|
885
|
+
const voice = suppliedVoice ?? model.defaultVoiceId;
|
|
886
|
+
if (!voice?.trim()) throw new Error(`ElevenLabs model ${model.id} requires voice_id or a configured defaultVoiceId`);
|
|
887
|
+
if (voice === "." || voice === "..") throw new Error("Invalid ElevenLabs voice_id: dot segments are not allowed");
|
|
888
|
+
path = `v1/text-to-speech/${encodeURIComponent(voice)}`;
|
|
889
|
+
body = { text: request.prompt, model_id: model.id, voice_settings: parseOptions(settings, model.endpoint) };
|
|
890
|
+
} else {
|
|
891
|
+
path = "v1/music";
|
|
892
|
+
body = { ...parseOptions(requestOptions, model.endpoint), prompt: request.prompt, model_id: model.id };
|
|
893
|
+
}
|
|
894
|
+
const url = new URL(path, baseUrl);
|
|
895
|
+
url.searchParams.set("output_format", format);
|
|
896
|
+
const payload = jsonBody(body, limits.maxRequestBytes);
|
|
897
|
+
yield* streamElevenLabs(transport, {
|
|
898
|
+
url: url.href,
|
|
899
|
+
method: "POST",
|
|
900
|
+
headers: [["xi-api-key", apiKey], ...payload.headers, ["accept", model.outputType]],
|
|
901
|
+
body: payload.body,
|
|
902
|
+
signal: request.signal
|
|
903
|
+
}, limits.maxResponseBytes);
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
export {
|
|
909
|
+
classify,
|
|
910
|
+
sniffMimeType,
|
|
911
|
+
acceptsMimeType,
|
|
912
|
+
createOpenAiProvider,
|
|
913
|
+
createElevenLabsProvider
|
|
914
|
+
};
|
|
915
|
+
//# sourceMappingURL=chunk-LR32SJ3D.js.map
|