@runappsai/sdk 0.0.0-stage → 0.3.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/LICENSE +21 -0
- package/README.md +274 -2
- package/dist/activity-tracker.d.ts +113 -0
- package/dist/activity-tracker.d.ts.map +1 -0
- package/dist/activity-tracker.js +176 -0
- package/dist/activity-tracker.js.map +1 -0
- package/dist/audio.d.ts +129 -0
- package/dist/audio.d.ts.map +1 -0
- package/dist/audio.js +267 -0
- package/dist/audio.js.map +1 -0
- package/dist/browser-auth.d.ts +179 -0
- package/dist/browser-auth.d.ts.map +1 -0
- package/dist/browser-auth.js +1361 -0
- package/dist/browser-auth.js.map +1 -0
- package/dist/chat.d.ts +221 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +155 -0
- package/dist/chat.js.map +1 -0
- package/dist/client.d.ts +155 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +157 -0
- package/dist/client.js.map +1 -0
- package/dist/computer.d.ts +57 -0
- package/dist/computer.d.ts.map +1 -0
- package/dist/computer.js +21 -0
- package/dist/computer.js.map +1 -0
- package/dist/embeddings.d.ts +64 -0
- package/dist/embeddings.d.ts.map +1 -0
- package/dist/embeddings.js +30 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/errors.d.ts +24 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +29 -0
- package/dist/errors.js.map +1 -0
- package/dist/event-wrap.d.ts +59 -0
- package/dist/event-wrap.d.ts.map +1 -0
- package/dist/event-wrap.js +142 -0
- package/dist/event-wrap.js.map +1 -0
- package/dist/events.d.ts +119 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +87 -0
- package/dist/events.js.map +1 -0
- package/dist/files.d.ts +218 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +212 -0
- package/dist/files.js.map +1 -0
- package/dist/image.d.ts +185 -0
- package/dist/image.d.ts.map +1 -0
- package/dist/image.js +148 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/media.d.ts +56 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +133 -0
- package/dist/media.js.map +1 -0
- package/dist/model_options.d.ts +165 -0
- package/dist/model_options.d.ts.map +1 -0
- package/dist/model_options.js +59 -0
- package/dist/model_options.js.map +1 -0
- package/dist/models.d.ts +88 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +55 -0
- package/dist/models.js.map +1 -0
- package/dist/sdk.umd.js +4 -0
- package/dist/sdk.umd.js.map +1 -0
- package/dist/transport.d.ts +120 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +214 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +31 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.d.ts +16 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +329 -0
- package/dist/validate.js.map +1 -0
- package/dist/video.d.ts +134 -0
- package/dist/video.d.ts.map +1 -0
- package/dist/video.js +60 -0
- package/dist/video.js.map +1 -0
- package/package.json +54 -4
- package/src/activity-tracker.ts +251 -0
- package/src/audio.ts +440 -0
- package/src/browser-auth.ts +1529 -0
- package/src/chat.ts +368 -0
- package/src/client.ts +250 -0
- package/src/computer.ts +80 -0
- package/src/embeddings.ts +97 -0
- package/src/errors.ts +29 -0
- package/src/event-wrap.ts +168 -0
- package/src/events.ts +172 -0
- package/src/files.ts +347 -0
- package/src/image.ts +339 -0
- package/src/index.ts +151 -0
- package/src/media.ts +147 -0
- package/src/model_options.ts +220 -0
- package/src/models.ts +121 -0
- package/src/transport.ts +292 -0
- package/src/types.ts +31 -0
- package/src/validate.ts +364 -0
- package/src/video.ts +215 -0
package/dist/audio.d.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { Transport } from "./transport.js";
|
|
2
|
+
import type { Usage } from "./types.js";
|
|
3
|
+
import type { SDKEvents } from "./events.js";
|
|
4
|
+
export interface SpeechParams {
|
|
5
|
+
input: string;
|
|
6
|
+
/** Named voice id (`alloy`, `nova`, …; provider-dependent). Omit
|
|
7
|
+
* when supplying `reference_audio_url` for zero-shot voice
|
|
8
|
+
* cloning — the cloned timbre overrides any named voice. */
|
|
9
|
+
voice?: string;
|
|
10
|
+
response_format?: string;
|
|
11
|
+
speed?: number;
|
|
12
|
+
/** Provider-specific. Inspect the chosen TTS model's
|
|
13
|
+
* `options.supported_emotions` (via `client.models.get(name)`) for
|
|
14
|
+
* the legal enum values. */
|
|
15
|
+
emotion?: string;
|
|
16
|
+
/** -12 … 12 semitones (provider-dependent). */
|
|
17
|
+
pitch?: number;
|
|
18
|
+
/** 0.1 … 10.0 — 1.0 = normal. */
|
|
19
|
+
volume?: number;
|
|
20
|
+
/** -12 … 12 — voice timbre shift (provider-dependent). */
|
|
21
|
+
timber?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Free-form natural-language directive for voiceclone (CosyVoice).
|
|
24
|
+
* e.g. "用四川话快速地说"
|
|
25
|
+
*/
|
|
26
|
+
instruct_text?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Public URL of a reference clip whose timbre to match (zero-shot
|
|
29
|
+
* voice cloning). When set, voiceclone-capable models ignore `voice`
|
|
30
|
+
* and synthesize matching the referenced audio.
|
|
31
|
+
*/
|
|
32
|
+
reference_audio_url?: string;
|
|
33
|
+
/** Transcript of the reference clip — improves CosyVoice quality. */
|
|
34
|
+
reference_text?: string;
|
|
35
|
+
user?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Vendor-specific top-level fields the canonical params don't model.
|
|
38
|
+
* Used for music-generation models on the TTS bucket — ACE-Step needs
|
|
39
|
+
* `tags` (genre, required), `duration`, `seed`, `scheduler`, etc.
|
|
40
|
+
* The SDK spreads these at the request body's TOP LEVEL (not nested
|
|
41
|
+
* under "extra") to match the gateway's extractSpeechExtra contract.
|
|
42
|
+
* Canonical fields above always win over extra keys with the same name.
|
|
43
|
+
*/
|
|
44
|
+
extra?: Record<string, unknown>;
|
|
45
|
+
}
|
|
46
|
+
export interface SpeechResponse {
|
|
47
|
+
/** Decoded audio bytes. */
|
|
48
|
+
data: Uint8Array;
|
|
49
|
+
/** MIME type reported by the gateway (e.g. "audio/mpeg"). */
|
|
50
|
+
contentType: string;
|
|
51
|
+
usage: Usage;
|
|
52
|
+
}
|
|
53
|
+
/** Same shape as image.ImageFileInput — kept independent so callers can import either. */
|
|
54
|
+
export interface AudioFileInput {
|
|
55
|
+
data: Blob | ArrayBuffer | Uint8Array | string;
|
|
56
|
+
filename: string;
|
|
57
|
+
contentType?: string;
|
|
58
|
+
}
|
|
59
|
+
export interface TranscribeParams {
|
|
60
|
+
file: AudioFileInput;
|
|
61
|
+
language?: string;
|
|
62
|
+
prompt?: string;
|
|
63
|
+
response_format?: string;
|
|
64
|
+
timestamp_granularities?: ("segment" | "word")[];
|
|
65
|
+
user?: string;
|
|
66
|
+
}
|
|
67
|
+
export interface TranscribeResponse {
|
|
68
|
+
text: string;
|
|
69
|
+
usage: Usage;
|
|
70
|
+
/** Raw upstream payload — segment timings, etc., when present. */
|
|
71
|
+
raw?: Record<string, unknown>;
|
|
72
|
+
}
|
|
73
|
+
export declare class AudioService {
|
|
74
|
+
private readonly transport;
|
|
75
|
+
private readonly events;
|
|
76
|
+
constructor(transport: Transport, events: SDKEvents);
|
|
77
|
+
/**
|
|
78
|
+
* Generate speech from text. Output bytes are base64-decoded by the SDK
|
|
79
|
+
* — `response.data` is a `Uint8Array` you can write directly to disk:
|
|
80
|
+
*
|
|
81
|
+
* ```ts
|
|
82
|
+
* await fs.writeFile("out.mp3", await client.audio.speech("OpenAI/TTS", {
|
|
83
|
+
* input: "Hello!",
|
|
84
|
+
* voice: "nova",
|
|
85
|
+
* }).then(r => r.data));
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
speech(model: string, params: SpeechParams, init?: {
|
|
89
|
+
signal?: AbortSignal;
|
|
90
|
+
}): Promise<SpeechResponse>;
|
|
91
|
+
/**
|
|
92
|
+
* Async equivalent of `speech()`. Submits the job to the gateway's
|
|
93
|
+
* async TTS endpoint, polls every ~3s until terminal, then decodes
|
|
94
|
+
* the result audio_url. Use this when a request may exceed the
|
|
95
|
+
* ~100s origin timeout — ACE-Step music generation at high quality,
|
|
96
|
+
* large CosyVoice batches, etc. The sync `speech()` method is the
|
|
97
|
+
* right choice for short-clip TTS that fits inside Cloudflare's
|
|
98
|
+
* timeout (skips the submit + poll round-trips).
|
|
99
|
+
*
|
|
100
|
+
* Returns the same `SpeechResponse` shape as `speech()` — `data` +
|
|
101
|
+
* `usage` — so callers can swap the two methods with no other change.
|
|
102
|
+
*
|
|
103
|
+
* Caller's `signal` deadline bounds the poll wait. Without one,
|
|
104
|
+
* an internal 10-minute cap applies.
|
|
105
|
+
*/
|
|
106
|
+
speechAsync(model: string, params: SpeechParams, init?: {
|
|
107
|
+
signal?: AbortSignal;
|
|
108
|
+
pollIntervalMs?: number;
|
|
109
|
+
}): Promise<SpeechResponse>;
|
|
110
|
+
/** Transcribe audio to text via multipart upload. */
|
|
111
|
+
transcribe(model: string, params: TranscribeParams, init?: {
|
|
112
|
+
signal?: AbortSignal;
|
|
113
|
+
}): Promise<TranscribeResponse>;
|
|
114
|
+
/**
|
|
115
|
+
* Async equivalent of `transcribe()`. Submits the upload, polls
|
|
116
|
+
* every ~3s until terminal, returns the same `TranscribeResponse`.
|
|
117
|
+
* Use this for long audio (lectures, podcasts, multi-hour
|
|
118
|
+
* recordings) where Whisper can take minutes — well past
|
|
119
|
+
* Cloudflare's ~100s sync ceiling.
|
|
120
|
+
*
|
|
121
|
+
* Caller's `signal` deadline bounds the poll wait. Without one,
|
|
122
|
+
* an internal 10-minute cap applies.
|
|
123
|
+
*/
|
|
124
|
+
transcribeAsync(model: string, params: TranscribeParams, init?: {
|
|
125
|
+
signal?: AbortSignal;
|
|
126
|
+
pollIntervalMs?: number;
|
|
127
|
+
}): Promise<TranscribeResponse>;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=audio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio.d.ts","sourceRoot":"","sources":["../src/audio.ts"],"names":[],"mappings":"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"}
|
package/dist/audio.js
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { wrapEvents } from "./event-wrap.js";
|
|
2
|
+
import { decodeMediaUrl } from "./media.js";
|
|
3
|
+
/* ------------------------------------------------------------------ */
|
|
4
|
+
/* Service */
|
|
5
|
+
/* ------------------------------------------------------------------ */
|
|
6
|
+
export class AudioService {
|
|
7
|
+
transport;
|
|
8
|
+
events;
|
|
9
|
+
constructor(transport, events) {
|
|
10
|
+
this.transport = transport;
|
|
11
|
+
this.events = events;
|
|
12
|
+
}
|
|
13
|
+
// (Removed: listVoices — fetch voice metadata via client.models.get /
|
|
14
|
+
// client.models.list and read model.options.voices /
|
|
15
|
+
// model.options.supported_emotions.)
|
|
16
|
+
/**
|
|
17
|
+
* Generate speech from text. Output bytes are base64-decoded by the SDK
|
|
18
|
+
* — `response.data` is a `Uint8Array` you can write directly to disk:
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* await fs.writeFile("out.mp3", await client.audio.speech("OpenAI/TTS", {
|
|
22
|
+
* input: "Hello!",
|
|
23
|
+
* voice: "nova",
|
|
24
|
+
* }).then(r => r.data));
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
async speech(model, params, init) {
|
|
28
|
+
return wrapEvents(this.events, { model, capability: "text_to_speech" }, async () => {
|
|
29
|
+
// Wire shape: {audio_url: "data:<mime>;base64,...", usage: ...}.
|
|
30
|
+
const raw = await this.transport.postJSON("/v1/audio/speech", buildSpeechBody(model, params), init);
|
|
31
|
+
const { bytes, contentType } = await decodeMediaUrl(raw.audio_url);
|
|
32
|
+
return { data: bytes, contentType, usage: raw.usage };
|
|
33
|
+
}, (r) => ({ costUSD: r.usage?.total_cost }));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Async equivalent of `speech()`. Submits the job to the gateway's
|
|
37
|
+
* async TTS endpoint, polls every ~3s until terminal, then decodes
|
|
38
|
+
* the result audio_url. Use this when a request may exceed the
|
|
39
|
+
* ~100s origin timeout — ACE-Step music generation at high quality,
|
|
40
|
+
* large CosyVoice batches, etc. The sync `speech()` method is the
|
|
41
|
+
* right choice for short-clip TTS that fits inside Cloudflare's
|
|
42
|
+
* timeout (skips the submit + poll round-trips).
|
|
43
|
+
*
|
|
44
|
+
* Returns the same `SpeechResponse` shape as `speech()` — `data` +
|
|
45
|
+
* `usage` — so callers can swap the two methods with no other change.
|
|
46
|
+
*
|
|
47
|
+
* Caller's `signal` deadline bounds the poll wait. Without one,
|
|
48
|
+
* an internal 10-minute cap applies.
|
|
49
|
+
*/
|
|
50
|
+
async speechAsync(model, params, init) {
|
|
51
|
+
return wrapEvents(this.events, { model, capability: "text_to_speech" }, async () => {
|
|
52
|
+
const submit = await this.transport.postJSON("/v1/async/audio/speech", buildSpeechBody(model, params), init);
|
|
53
|
+
if (!submit.id) {
|
|
54
|
+
throw new Error("runapps: speech submit response missing job id");
|
|
55
|
+
}
|
|
56
|
+
return waitSpeechJob(this.transport, submit.id, init);
|
|
57
|
+
}, (r) => ({ costUSD: r.usage?.total_cost }));
|
|
58
|
+
}
|
|
59
|
+
/** Transcribe audio to text via multipart upload. */
|
|
60
|
+
async transcribe(model, params, init) {
|
|
61
|
+
const form = buildTranscribeForm(model, params);
|
|
62
|
+
return wrapEvents(this.events, { model, capability: "speech_to_text" }, () => this.transport.postMultipart("/v1/audio/transcriptions", form, init), (r) => ({ costUSD: r.usage?.total_cost }));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Async equivalent of `transcribe()`. Submits the upload, polls
|
|
66
|
+
* every ~3s until terminal, returns the same `TranscribeResponse`.
|
|
67
|
+
* Use this for long audio (lectures, podcasts, multi-hour
|
|
68
|
+
* recordings) where Whisper can take minutes — well past
|
|
69
|
+
* Cloudflare's ~100s sync ceiling.
|
|
70
|
+
*
|
|
71
|
+
* Caller's `signal` deadline bounds the poll wait. Without one,
|
|
72
|
+
* an internal 10-minute cap applies.
|
|
73
|
+
*/
|
|
74
|
+
async transcribeAsync(model, params, init) {
|
|
75
|
+
return wrapEvents(this.events, { model, capability: "speech_to_text" }, async () => {
|
|
76
|
+
const form = buildTranscribeForm(model, params);
|
|
77
|
+
const submit = await this.transport.postMultipart("/v1/async/audio/transcriptions", form, init);
|
|
78
|
+
if (!submit.id) {
|
|
79
|
+
throw new Error("runapps: transcribe submit response missing job id");
|
|
80
|
+
}
|
|
81
|
+
return waitTranscribeJob(this.transport, submit.id, init);
|
|
82
|
+
}, (r) => ({ costUSD: r.usage?.total_cost }));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/* ------------------------------------------------------------------ */
|
|
86
|
+
/* Helpers */
|
|
87
|
+
/* ------------------------------------------------------------------ */
|
|
88
|
+
/**
|
|
89
|
+
* Polls the gateway's async speech-status endpoint until the job
|
|
90
|
+
* reaches a terminal state, then decodes the result `audio_url`
|
|
91
|
+
* data: URI into raw bytes + mime. Used by `AudioService.speechAsync`.
|
|
92
|
+
*
|
|
93
|
+
* Default poll cadence: first poll 2s after submit, then every 3s.
|
|
94
|
+
* Caller can override via `init.pollIntervalMs`. `init.signal`
|
|
95
|
+
* cancels the poll loop.
|
|
96
|
+
*/
|
|
97
|
+
async function waitSpeechJob(transport, jobId, init) {
|
|
98
|
+
const path = `/v1/async/audio/speech/${encodeURIComponent(jobId)}`;
|
|
99
|
+
const interval = init?.pollIntervalMs ?? 3000;
|
|
100
|
+
// First poll happens fast — typical TTS already done in 2-5s.
|
|
101
|
+
await sleep(2000, init?.signal);
|
|
102
|
+
// 10-minute hard cap if caller didn't supply a signal with a
|
|
103
|
+
// narrower deadline. Long enough for the slowest ACE-Step jobs.
|
|
104
|
+
const startedAt = Date.now();
|
|
105
|
+
const internalDeadline = startedAt + 10 * 60 * 1000;
|
|
106
|
+
for (;;) {
|
|
107
|
+
if (init?.signal?.aborted) {
|
|
108
|
+
throw new DOMException("speech poll aborted", "AbortError");
|
|
109
|
+
}
|
|
110
|
+
if (!init?.signal && Date.now() > internalDeadline) {
|
|
111
|
+
throw new Error("runapps: speech job timed out (10 min internal cap)");
|
|
112
|
+
}
|
|
113
|
+
const status = await transport.getJSON(path, init);
|
|
114
|
+
if (status.status === "succeeded") {
|
|
115
|
+
if (!status.audio_url) {
|
|
116
|
+
throw new Error("runapps: speech job succeeded but no audio_url returned");
|
|
117
|
+
}
|
|
118
|
+
const { bytes, contentType } = await decodeMediaUrl(status.audio_url);
|
|
119
|
+
return {
|
|
120
|
+
data: bytes,
|
|
121
|
+
contentType,
|
|
122
|
+
usage: status.usage ?? { total_cost: 0 },
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (status.status === "failed") {
|
|
126
|
+
throw new Error(status.error || "runapps: speech job failed");
|
|
127
|
+
}
|
|
128
|
+
// queued | running → wait + retry.
|
|
129
|
+
await sleep(interval, init?.signal);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Polls the gateway's async transcribe-status endpoint until terminal,
|
|
134
|
+
* then assembles a TranscribeResponse from the final payload. Used by
|
|
135
|
+
* `AudioService.transcribeAsync`.
|
|
136
|
+
*/
|
|
137
|
+
async function waitTranscribeJob(transport, jobId, init) {
|
|
138
|
+
const path = `/v1/async/audio/transcriptions/${encodeURIComponent(jobId)}`;
|
|
139
|
+
const interval = init?.pollIntervalMs ?? 3000;
|
|
140
|
+
await sleep(2000, init?.signal);
|
|
141
|
+
const internalDeadline = Date.now() + 10 * 60 * 1000;
|
|
142
|
+
for (;;) {
|
|
143
|
+
if (init?.signal?.aborted) {
|
|
144
|
+
throw new DOMException("transcribe poll aborted", "AbortError");
|
|
145
|
+
}
|
|
146
|
+
if (!init?.signal && Date.now() > internalDeadline) {
|
|
147
|
+
throw new Error("runapps: transcribe job timed out (10 min internal cap)");
|
|
148
|
+
}
|
|
149
|
+
const status = await transport.getJSON(path, init);
|
|
150
|
+
switch (status.status) {
|
|
151
|
+
case "succeeded":
|
|
152
|
+
return assembleTranscribeResponse(status);
|
|
153
|
+
case "failed":
|
|
154
|
+
throw new Error(status.error || "runapps: transcribe job failed");
|
|
155
|
+
case "queued":
|
|
156
|
+
case "running":
|
|
157
|
+
await sleep(interval, init?.signal);
|
|
158
|
+
break;
|
|
159
|
+
default:
|
|
160
|
+
throw new Error(`runapps: unknown transcribe job status ${JSON.stringify(status.status)}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Pulls canonical text + usage out of a succeeded transcribe-status
|
|
166
|
+
* payload, leaves the rest under .raw — matches the sync response
|
|
167
|
+
* shape produced by `transcribe()`.
|
|
168
|
+
*/
|
|
169
|
+
function assembleTranscribeResponse(payload) {
|
|
170
|
+
const out = {
|
|
171
|
+
text: payload.text ?? "",
|
|
172
|
+
usage: payload.usage ?? { total_cost: 0 },
|
|
173
|
+
};
|
|
174
|
+
const raw = {};
|
|
175
|
+
for (const [k, v] of Object.entries(payload)) {
|
|
176
|
+
if (k === "text" || k === "usage" || k === "id" || k === "status")
|
|
177
|
+
continue;
|
|
178
|
+
raw[k] = v;
|
|
179
|
+
}
|
|
180
|
+
if (Object.keys(raw).length > 0) {
|
|
181
|
+
out.raw = raw;
|
|
182
|
+
}
|
|
183
|
+
return out;
|
|
184
|
+
}
|
|
185
|
+
function sleep(ms, signal) {
|
|
186
|
+
return new Promise((resolve, reject) => {
|
|
187
|
+
if (signal?.aborted) {
|
|
188
|
+
reject(new DOMException("aborted", "AbortError"));
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const timer = setTimeout(() => {
|
|
192
|
+
signal?.removeEventListener("abort", onAbort);
|
|
193
|
+
resolve();
|
|
194
|
+
}, ms);
|
|
195
|
+
const onAbort = () => {
|
|
196
|
+
clearTimeout(timer);
|
|
197
|
+
reject(new DOMException("aborted", "AbortError"));
|
|
198
|
+
};
|
|
199
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* buildSpeechBody assembles the wire body for /v1/audio/speech (and
|
|
204
|
+
* its async sibling): `{model, ...canonical, ...extra}` flattened at
|
|
205
|
+
* the top level. The gateway's extractSpeechExtra peels non-canonical
|
|
206
|
+
* keys back into req.Extra, so the round-trip is symmetric. Canonical
|
|
207
|
+
* fields always win over `extra` keys of the same name — never let
|
|
208
|
+
* Extra silently override a typed field.
|
|
209
|
+
*/
|
|
210
|
+
function buildSpeechBody(model, params) {
|
|
211
|
+
const { extra, ...canonical } = params;
|
|
212
|
+
const body = { model, ...canonical };
|
|
213
|
+
if (extra) {
|
|
214
|
+
for (const [k, v] of Object.entries(extra)) {
|
|
215
|
+
if (k in body)
|
|
216
|
+
continue;
|
|
217
|
+
body[k] = v;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return body;
|
|
221
|
+
}
|
|
222
|
+
function buildTranscribeForm(model, params) {
|
|
223
|
+
const form = new FormData();
|
|
224
|
+
form.set("model", model);
|
|
225
|
+
if (params.language)
|
|
226
|
+
form.set("language", params.language);
|
|
227
|
+
if (params.prompt)
|
|
228
|
+
form.set("prompt", params.prompt);
|
|
229
|
+
if (params.response_format)
|
|
230
|
+
form.set("response_format", params.response_format);
|
|
231
|
+
if (params.timestamp_granularities) {
|
|
232
|
+
for (const g of params.timestamp_granularities) {
|
|
233
|
+
form.append("timestamp_granularities[]", g);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (params.user)
|
|
237
|
+
form.set("user", params.user);
|
|
238
|
+
const ct = params.file.contentType ?? guessAudioMime(params.file.filename);
|
|
239
|
+
const blob = params.file.data instanceof Blob
|
|
240
|
+
? params.file.data
|
|
241
|
+
: new Blob([params.file.data], { type: ct });
|
|
242
|
+
form.set("file", blob, params.file.filename);
|
|
243
|
+
return form;
|
|
244
|
+
}
|
|
245
|
+
function guessAudioMime(filename) {
|
|
246
|
+
const ext = filename.toLowerCase().split(".").pop() ?? "";
|
|
247
|
+
switch (ext) {
|
|
248
|
+
case "mp3":
|
|
249
|
+
return "audio/mpeg";
|
|
250
|
+
case "wav":
|
|
251
|
+
return "audio/wav";
|
|
252
|
+
case "m4a":
|
|
253
|
+
return "audio/mp4";
|
|
254
|
+
case "flac":
|
|
255
|
+
return "audio/flac";
|
|
256
|
+
case "ogg":
|
|
257
|
+
return "audio/ogg";
|
|
258
|
+
case "webm":
|
|
259
|
+
return "audio/webm";
|
|
260
|
+
default:
|
|
261
|
+
return "application/octet-stream";
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// (base64ToBytes was here — promoted to media.ts as part of
|
|
265
|
+
// decodeMediaUrl. The audio path now goes through that single helper
|
|
266
|
+
// so image and audio decode through one implementation.)
|
|
267
|
+
//# sourceMappingURL=audio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio.js","sourceRoot":"","sources":["../src/audio.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import type { SDKEvents } from "./events.js";
|
|
2
|
+
export interface BrowserAuthOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Origin where the RunApps gateway lives — also the page that issues
|
|
5
|
+
* tokens at /api/sdk/grant. Default `https://www.runapps.ai`.
|
|
6
|
+
*/
|
|
7
|
+
origin?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Suppress the floating identity badge. Default `false` at this
|
|
10
|
+
* layer. The wrapping `RunApps` client passes `hideBadge: false`
|
|
11
|
+
* by default (badge SHOWN — it's now a live activity indicator,
|
|
12
|
+
* see `activity-tracker.ts`); callers wanting their own status UI
|
|
13
|
+
* opt out via `RunApps({ showIdentityBadge: false })`. Set this
|
|
14
|
+
* directly only when constructing BrowserAuth without going
|
|
15
|
+
* through `RunApps`.
|
|
16
|
+
*/
|
|
17
|
+
hideBadge?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Pin the grant flow to a named runapps.ai project. When set, the
|
|
20
|
+
* SDK passes `project_id=<value>` to `/api/sdk/grant`, asking the
|
|
21
|
+
* gateway to mint a project-bound `rrt_*` resource token for THAT
|
|
22
|
+
* project regardless of which (origin, app) pair the bundle is
|
|
23
|
+
* served from.
|
|
24
|
+
*
|
|
25
|
+
* Use cases:
|
|
26
|
+
* - **Local dev** for a 1:1 bundle/project app: the localhost
|
|
27
|
+
* origin isn't registered with any project, so without this
|
|
28
|
+
* option the grant flow can't pick one and `client.files` 403s
|
|
29
|
+
* on every call. Pin the project explicitly and `pnpm dev`
|
|
30
|
+
* hits the same files namespace as production.
|
|
31
|
+
* - **Multi-tenant bundles** that let the user pick a project at
|
|
32
|
+
* runtime — pass the chosen project id, then call `signOut()` +
|
|
33
|
+
* `signIn()` (or reconstruct the client) on switch.
|
|
34
|
+
*
|
|
35
|
+
* Omit to keep the default behaviour: the gateway derives the
|
|
36
|
+
* project from the registered (origin, app) pair.
|
|
37
|
+
*/
|
|
38
|
+
project?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Optional event bus from the parent RunApps client. When wired,
|
|
41
|
+
* the identity badge subscribes to call telemetry events and
|
|
42
|
+
* renders a real-time activity ring + LED dot + click-through
|
|
43
|
+
* popover with active / recent / session stats — turning the
|
|
44
|
+
* static "signed in" badge into a desktop-ball-style live
|
|
45
|
+
* indicator (similar to a tray-app gauge).
|
|
46
|
+
*
|
|
47
|
+
* Optional and back-compat: when omitted (or when `hideBadge` is
|
|
48
|
+
* true), the badge falls back to its original static behaviour.
|
|
49
|
+
*/
|
|
50
|
+
events?: SDKEvents;
|
|
51
|
+
/**
|
|
52
|
+
* Which corner the badge floats in. See `RunApps.badgePosition`
|
|
53
|
+
* for the user-facing flavour. Defaults to `"bottom-right"`.
|
|
54
|
+
*/
|
|
55
|
+
badgePosition?: BadgePosition;
|
|
56
|
+
}
|
|
57
|
+
/** Four-corner anchor for the floating activity badge. */
|
|
58
|
+
export type BadgePosition = "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
59
|
+
export interface BrowserUser {
|
|
60
|
+
id?: string;
|
|
61
|
+
name?: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* BrowserAuth encapsulates the browser-side auth state for a RunApps
|
|
65
|
+
* client. Exposes:
|
|
66
|
+
*
|
|
67
|
+
* - getToken() fresh bearer (auto-refreshes / signs in)
|
|
68
|
+
* - signIn() force a redirect to the grant page
|
|
69
|
+
* - user { id, name } — null until signed in
|
|
70
|
+
* - onTokenChange(fn) subscribe to token (re)acquisition
|
|
71
|
+
*
|
|
72
|
+
* Construct once per page; the SDK passes its `getToken` as the
|
|
73
|
+
* transport's `apiKeyResolver`.
|
|
74
|
+
*/
|
|
75
|
+
export declare class BrowserAuth {
|
|
76
|
+
private readonly origin;
|
|
77
|
+
private readonly hideBadge;
|
|
78
|
+
private readonly project;
|
|
79
|
+
private readonly events;
|
|
80
|
+
/** Lazily-constructed when events are present — drives the badge's
|
|
81
|
+
* activity ring / LED / popover. nullable for tests / non-browser. */
|
|
82
|
+
private readonly tracker;
|
|
83
|
+
private readonly badgePosition;
|
|
84
|
+
private token;
|
|
85
|
+
private expiresAt;
|
|
86
|
+
private userInfo;
|
|
87
|
+
private listeners;
|
|
88
|
+
private parentHandshake;
|
|
89
|
+
private signingIn;
|
|
90
|
+
constructor(opts?: BrowserAuthOptions);
|
|
91
|
+
/** Public token-fetcher; pass to RunApps as `apiKeyResolver`. */
|
|
92
|
+
getToken: () => Promise<string>;
|
|
93
|
+
/** Subscribe to (re)acquisition events. Returns an unsubscribe fn. */
|
|
94
|
+
onTokenChange(handler: (token: string) => void): () => void;
|
|
95
|
+
/** Currently-authenticated user, or null. */
|
|
96
|
+
get user(): BrowserUser | null;
|
|
97
|
+
/**
|
|
98
|
+
* True iff a cached bearer token is present AND not within the
|
|
99
|
+
* 60-second pre-expiry refresh margin. Use this — not `user !==
|
|
100
|
+
* null` — to decide whether to show "please sign in" UI: the user
|
|
101
|
+
* metadata is only populated when the SDK was fed fresh user info
|
|
102
|
+
* (post-redirect fragment carries it; iframe handshake carries it;
|
|
103
|
+
* an older localStorage save format may have persisted only the
|
|
104
|
+
* token). A token without metadata is still a fully-usable
|
|
105
|
+
* session — every gateway call will succeed — and prompting the
|
|
106
|
+
* user to sign in there causes a redirect that loses session
|
|
107
|
+
* continuity for no reason.
|
|
108
|
+
*/
|
|
109
|
+
hasFreshToken(): boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Clear the cached token / user / badge and the persisted copy in
|
|
112
|
+
* localStorage. Next gateway call will trigger a redirect to the
|
|
113
|
+
* grant page — i.e. the standard sign-in flow. Wire this to a
|
|
114
|
+
* "Sign out" button in the bundle's settings UI.
|
|
115
|
+
*
|
|
116
|
+
* Does NOT redirect on its own; the bundle decides whether to
|
|
117
|
+
* also navigate the user (e.g. to a logged-out splash page) or
|
|
118
|
+
* just refresh state and let the next API call re-prompt.
|
|
119
|
+
*/
|
|
120
|
+
signOut(): void;
|
|
121
|
+
/**
|
|
122
|
+
* Drop the in-memory + persisted token without setting the sticky
|
|
123
|
+
* "signed out" flag. Used by the transport layer when the gateway
|
|
124
|
+
* returns 401 (token revoked server-side, e.g. user unsubscribed in
|
|
125
|
+
* another tab). The next `getToken()` will trigger the standard
|
|
126
|
+
* `signIn()` redirect-grant flow and the user re-auths transparently.
|
|
127
|
+
*/
|
|
128
|
+
invalidate(): void;
|
|
129
|
+
/** Force a redirect to the grant page. */
|
|
130
|
+
signIn(): void;
|
|
131
|
+
/**
|
|
132
|
+
* Build the `/api/sdk/grant?…` URL the user is redirected to. Split
|
|
133
|
+
* out so tests can assert URL formation without a `window` context;
|
|
134
|
+
* `signIn` is the only production caller.
|
|
135
|
+
*
|
|
136
|
+
* Public surface, but namespaced under `_buildGrantUrlForTest` so it
|
|
137
|
+
* doesn't show up in IntelliSense as a normal API.
|
|
138
|
+
*/
|
|
139
|
+
_buildGrantUrlForTest(args: {
|
|
140
|
+
pageOrigin: string;
|
|
141
|
+
app: string;
|
|
142
|
+
redirectTo: string;
|
|
143
|
+
scheme: "light" | "dark";
|
|
144
|
+
}): string;
|
|
145
|
+
/**
|
|
146
|
+
* Return the localStorage keys this instance reads / writes for its
|
|
147
|
+
* persisted token + sign-out flag. Exposed for tests so we can
|
|
148
|
+
* assert per-project namespacing without a real DOM; not part of
|
|
149
|
+
* the public API surface (the leading underscore + `ForTest` suffix
|
|
150
|
+
* is the SDK's convention for this).
|
|
151
|
+
*/
|
|
152
|
+
_storageKeysForTest(): {
|
|
153
|
+
auth: string;
|
|
154
|
+
signedOut: string;
|
|
155
|
+
};
|
|
156
|
+
private buildGrantUrl;
|
|
157
|
+
private nowSec;
|
|
158
|
+
private tokenIsFresh;
|
|
159
|
+
private inIframe;
|
|
160
|
+
private setToken;
|
|
161
|
+
private savePersisted;
|
|
162
|
+
private loadPersisted;
|
|
163
|
+
private clearPersisted;
|
|
164
|
+
private isSignedOut;
|
|
165
|
+
private markSignedOut;
|
|
166
|
+
private clearSignedOut;
|
|
167
|
+
private removeBadge;
|
|
168
|
+
/** Parse the #runjobs_token=… fragment, install, then strip it. */
|
|
169
|
+
private consumeFragment;
|
|
170
|
+
/** postMessage handshake with the dashboard parent (iframe path). */
|
|
171
|
+
private requestTokenFromParent;
|
|
172
|
+
/** Floating identity badge — bottom-right pill showing the user.
|
|
173
|
+
* Click opens the runapps.ai dashboard in a new tab so the user
|
|
174
|
+
* can manage their account / billing without losing the bundle's
|
|
175
|
+
* in-flight state. Uses `noopener,noreferrer` so the new tab
|
|
176
|
+
* can't reach back into the bundle window via `window.opener`. */
|
|
177
|
+
private renderBadge;
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=browser-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-auth.d.ts","sourceRoot":"","sources":["../src/browser-auth.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB;;;OAGG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,0DAA0D;AAC1D,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,UAAU,CAAC;AAEtF,MAAM,WAAW,WAAW;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AA8CD;;;;;;;;;;;GAWG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C;2EACuE;IACvE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,eAAe,CAA8B;IACrD,OAAO,CAAC,SAAS,CAAS;gBAEd,IAAI,GAAE,kBAAuB;IA+CzC,iEAAiE;IACjE,QAAQ,QAAa,OAAO,CAAC,MAAM,CAAC,CA0DlC;IAEF,sEAAsE;IACtE,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;IAQ3D,6CAA6C;IAC7C,IAAI,IAAI,IAAI,WAAW,GAAG,IAAI,CAE7B;IAED;;;;;;;;;;;OAWG;IACH,aAAa,IAAI,OAAO;IAIxB;;;;;;;;;OASG;IACH,OAAO,IAAI,IAAI;IAWf;;;;;;OAMG;IACH,UAAU,IAAI,IAAI;IASlB,0CAA0C;IAC1C,MAAM,IAAI,IAAI;IAmBd;;;;;;;OAOG;IACH,qBAAqB,CAAC,IAAI,EAAE;QAC1B,UAAU,EAAE,MAAM,CAAC;QACnB,GAAG,EAAE,MAAM,CAAC;QACZ,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;KAC1B,GAAG,MAAM;IAIV;;;;;;OAMG;IACH,mBAAmB,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IAO1D,OAAO,CAAC,aAAa;IAiBrB,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,QAAQ;IAQhB,OAAO,CAAC,QAAQ;IAiBhB,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,aAAa;IAyBrB,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,WAAW;IAKnB,mEAAmE;IACnE,OAAO,CAAC,eAAe;IAkCvB,qEAAqE;IACrE,OAAO,CAAC,sBAAsB;IAoD9B;;;;uEAImE;IACnE,OAAO,CAAC,WAAW;CAqCpB"}
|