@runappsai/sdk 0.0.0-stage → 0.3.1
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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 RunJobs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,275 @@
|
|
|
1
|
-
#
|
|
1
|
+
# RunApps SDK for JavaScript / TypeScript
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The client for building apps on [RunApps](https://www.runapps.ai): call any model (Claude, GPT, Gemini, DeepSeek, Qwen, MiniMax, GLM, Grok), generate images, audio and video, keep per-user files, and let the platform handle sign-in and billing. Zero runtime dependencies — native `fetch`, `FormData` and `ReadableStream` (browsers, Node 18+, Deno, Bun).
|
|
4
|
+
|
|
5
|
+
Two ways to use it:
|
|
6
|
+
|
|
7
|
+
- **In an app you publish on RunApps** — a static bundle served at `<slug>.runapps.dev`. Users sign in with their RunApps account and pay for their own usage; you never hold a key. This is what `authProvider: "runapps"` is for.
|
|
8
|
+
- **From your own code** — server, script or CLI — with a personal API key (`rk_…`).
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm i @runappsai/sdk
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Or as a script tag:
|
|
17
|
+
|
|
18
|
+
```html
|
|
19
|
+
<script src="https://cdn.jsdelivr.net/npm/@runappsai/sdk/dist/sdk.umd.js"></script>
|
|
20
|
+
<script>
|
|
21
|
+
const client = new RunApps({ authProvider: "runapps" });
|
|
22
|
+
</script>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Building an app
|
|
26
|
+
|
|
27
|
+
### 1. Create the app, then develop against it
|
|
28
|
+
|
|
29
|
+
An app gets its slug the moment you create it on the dashboard (Creator → Publish) — no zip needed yet. Pass that slug as `project` and your dev server runs against the real platform:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { RunApps } from "@runappsai/sdk";
|
|
33
|
+
|
|
34
|
+
const client = new RunApps({
|
|
35
|
+
authProvider: "runapps", // sign-in, token, refresh — all handled
|
|
36
|
+
project: "my-tool", // the draft's slug; unchanged after you upload
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const resp = await client.chat.create({
|
|
40
|
+
model: "Claude Sonnet 4.6",
|
|
41
|
+
messages: [{ role: "user", content: "Hello!" }],
|
|
42
|
+
});
|
|
43
|
+
console.log(resp.choices[0].message.content);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- Sign-in works from `http://localhost:<any port>`; the first call redirects to the RunApps grant page and back.
|
|
47
|
+
- Calls are billed to the signed-in user — while developing, that is you.
|
|
48
|
+
- Files and private prompts already use the app's real namespace, so nothing moves when you upload.
|
|
49
|
+
|
|
50
|
+
When the app is ready, build it, zip it, and upload it to the same app. Set it private (reachable by link) or submit it for review to be listed in the library.
|
|
51
|
+
|
|
52
|
+
### 2. Sign-in details
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
await client.chat.create({ ... }); // redirects to the grant page on first use
|
|
56
|
+
|
|
57
|
+
client.signIn(); // force the redirect
|
|
58
|
+
client.signOut(); // clear the cached token (sticky across reloads)
|
|
59
|
+
client.user; // { id, name } | null
|
|
60
|
+
client.auth?.onTokenChange(t => {}); // subscribe to (re)acquisition
|
|
61
|
+
client.auth?.hasFreshToken(); // avoid showing a "sign in" button needlessly
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`showIdentityBadge` (default `true`) draws a small activity badge; `badgePosition` places it.
|
|
65
|
+
|
|
66
|
+
`project` is optional on `*.runapps.dev` — the origin already says which app it is. It is required on localhost and when the client runs on a personal key (see below). Sign-in only works on the app's own `*.runapps.dev` address and on `http://localhost`. In Node, `authProvider: "runapps"` is a no-op; use `apiKey`.
|
|
67
|
+
|
|
68
|
+
### 3. Private prompts
|
|
69
|
+
|
|
70
|
+
Anything in your frontend can be read in the browser. Name your prompt files in a `bundle.json` at the root of your zip — `{"prompts": {"outline": "prompts/outline.md"}}` — or put them under `.runapps/prompts/<id>.md`. The platform never serves `bundle.json`, the files it names, or any dot folder; call the prompts by id:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
await client.chat.create({
|
|
74
|
+
model: "Claude Sonnet 4.6",
|
|
75
|
+
messages: [{ role: "user", content: "A detective in a lighthouse." }],
|
|
76
|
+
prompt: { id: "outline", vars: { genre: "mystery" } }, // {{genre}} in the file
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The gateway prepends the prompt as the system message and asks the model to keep it confidential; a reply that repeats 30+ characters of it verbatim is withheld (`finish_reason: "content_filter"`). While developing locally, before a bundle exists, send the text inline as `prompt.template` — accepted only from the app's creator — and keep it out of production builds with `import.meta.env.DEV`.
|
|
81
|
+
|
|
82
|
+
### 4. Per-user files
|
|
83
|
+
|
|
84
|
+
Every app gets a file space per signed-in user, addressed by POSIX-style paths. `FileObject.url` is a stable public address you can drop into `<img src>`.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// Write. body: Blob | File | ArrayBuffer | Uint8Array | ReadableStream | string
|
|
88
|
+
const obj = await client.files.put("assets/logo.png", pngBlob, { contentType: "image/png" });
|
|
89
|
+
await client.files.putString("notes.md", "# hello");
|
|
90
|
+
await client.files.putFromURL("cache/cat.jpg", "https://example.com/cat.jpg"); // gateway fetches it
|
|
91
|
+
|
|
92
|
+
// Read.
|
|
93
|
+
const blob = await client.files.get("assets/logo.png");
|
|
94
|
+
const meta = await client.files.stat("assets/logo.png"); // size, etag, last_modified, url
|
|
95
|
+
await client.files.exists("assets/logo.png");
|
|
96
|
+
|
|
97
|
+
// List, with an optional shell glob (*, **, ?, [abc]) matched against the whole path.
|
|
98
|
+
const page = await client.files.list({ prefix: "assets/", glob: "**/*.png", limit: 100 });
|
|
99
|
+
// page.next_cursor → pass as cursor for the next page
|
|
100
|
+
|
|
101
|
+
// Mutate.
|
|
102
|
+
await client.files.move("draft.md", "published.md");
|
|
103
|
+
await client.files.copy("template.md", "instances/today.md");
|
|
104
|
+
await client.files.del("old.log");
|
|
105
|
+
await client.files.deleteMany({ prefix: "tmp/" });
|
|
106
|
+
await client.files.deleteMany({ glob: "**/*.tmp" });
|
|
107
|
+
|
|
108
|
+
// Several ops in one round trip; each result has its own ok/error.
|
|
109
|
+
await client.files.batch([
|
|
110
|
+
{ op: "put_url", path: "a.jpg", src_url: "https://example.com/a.jpg" },
|
|
111
|
+
{ op: "copy", from: "a.jpg", to: "b.jpg" },
|
|
112
|
+
{ op: "del", path: "a.jpg" },
|
|
113
|
+
]);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Keeping app state in a file** — read, modify, write, without overwriting another tab's save. Every read carries the file's ETag; `ifMatch` makes the write fail (412) if the file changed since:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
const { value, etag } = await client.files.readJSON<{ score: number }>("state.json");
|
|
120
|
+
const next = { ...(value ?? { score: 0 }), score: (value?.score ?? 0) + 1 };
|
|
121
|
+
try {
|
|
122
|
+
await client.files.put("state.json", JSON.stringify(next), {
|
|
123
|
+
contentType: "application/json",
|
|
124
|
+
...(etag ? { ifMatch: etag } : { ifNoneMatch: true }), // first save: must not exist
|
|
125
|
+
});
|
|
126
|
+
} catch (e) {
|
|
127
|
+
if (e instanceof APIError && e.statusCode === 412) { /* re-read, merge, retry */ }
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`files.read(path)` is `get` plus the ETag for non-JSON files. `ifNoneMatch: true` refuses to overwrite (409).
|
|
132
|
+
|
|
133
|
+
## From your own code
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import { RunApps, APIError } from "@runappsai/sdk";
|
|
137
|
+
|
|
138
|
+
const client = new RunApps({ apiKey: "rk_…" }); // Dashboard → API Keys
|
|
139
|
+
|
|
140
|
+
const resp = await client.chat.create({
|
|
141
|
+
model: "Gemini 3 Flash",
|
|
142
|
+
messages: [{ role: "user", content: "Hello!" }],
|
|
143
|
+
});
|
|
144
|
+
console.log(resp.usage.total_cost); // USD
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The base URL is `https://www.runapps.ai` in both modes; `baseURL` overrides it.
|
|
148
|
+
|
|
149
|
+
**Files with a personal key** — a personal key has no app of its own, so name the app: `new RunApps({ apiKey, project: "my-tool" })`. You get your own files in that app, the same ones the app sees for you, provided you have added the app or created it.
|
|
150
|
+
|
|
151
|
+
**Any OpenAI-compatible SDK works too.** Point it at the platform with your `rk_…` key:
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from openai import OpenAI
|
|
155
|
+
client = OpenAI(api_key="rk_...", base_url="https://www.runapps.ai/v1")
|
|
156
|
+
client.chat.completions.create(model="Claude Sonnet 4.6", messages=[{"role": "user", "content": "Hello!"}])
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Browse `GET /v1/models` (no auth) for the live catalogue and per-model prices.
|
|
160
|
+
|
|
161
|
+
## Chat
|
|
162
|
+
|
|
163
|
+
OpenAI-compatible, streaming or not. The final streamed chunk carries `usage`.
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
for await (const chunk of client.chat.stream({ model: "Gemini 3 Flash", messages })) {
|
|
167
|
+
for (const c of chunk.choices) process.stdout.write(c.delta.content ?? "");
|
|
168
|
+
if (chunk.usage) console.log("\ncost", chunk.usage.total_cost);
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Images, video and audio in messages** — check the model first with `acceptsModality(model, "video")`; the gateway rejects unsupported parts with a 400 before billing:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import { userMessageParts, textPart, imagePart, videoPart, audioPart } from "@runappsai/sdk";
|
|
176
|
+
|
|
177
|
+
await client.chat.create({
|
|
178
|
+
model: "Gemini 3 Vision",
|
|
179
|
+
messages: [userMessageParts(textPart("Summarise this clip"), videoPart("https://example.com/clip.mp4"))],
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Your own tools** — pass `tools: [{ type: "function", function: { name, description, parameters } }]`, read `choices[0].message.tool_calls`, answer with `toolResultMessage(toolCallId, output)` on the next turn.
|
|
184
|
+
|
|
185
|
+
**Server tools** — the platform searches and reads the web for the model, looping until it answers; your code only sees the result. Combine freely with your own `tools`.
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
import { ServerTools } from "@runappsai/sdk";
|
|
189
|
+
|
|
190
|
+
const resp = await client.chat.create({
|
|
191
|
+
model: "Claude Sonnet 4.6",
|
|
192
|
+
messages: [{ role: "user", content: "What is the latest stable Go version?" }],
|
|
193
|
+
server_tools: [ServerTools.WebSearch, ServerTools.WebFetch],
|
|
194
|
+
max_server_iterations: 5, // default 5, max 10
|
|
195
|
+
});
|
|
196
|
+
for (const t of resp.usage.tool_costs ?? []) console.log(t.name, t.count, t.cost);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
`WebSearch` and `TwitterSearch` are priced per call, `WebFetch` is free; `usage.total_cost` includes tools and tokens. With `stream: true` the tool rounds run silently and the final answer streams.
|
|
200
|
+
|
|
201
|
+
Message builders: `userMessage`, `systemMessage`, `assistantMessage`, `toolResultMessage`, `userMessageParts`, `textPart`, `imagePart`, `videoPart`, `audioPart`.
|
|
202
|
+
|
|
203
|
+
## Models
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
const all = await client.models.list();
|
|
207
|
+
const video = await client.models.list({ capability: "video_generation" });
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Prices are in pips per million tokens (1 USD = 1,000,000 pips). Each model carries `capability_tags` (stable ids such as `t2v`, `i2v`, `voice_clone`, `timestamps` — filter with `hasCapabilityTag`), chat models carry `input_modalities` (`acceptsModality`), and every model exposes its input contract through `getOptionsSchema(model)`: accepted fields, bounds, enums, cross-field constraints, and a catalogue of voices or emotions. `validateRequest(schema, body)` returns every problem at once for a form to show.
|
|
211
|
+
|
|
212
|
+
## Image, audio, video, embeddings
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
// Image — data: URI (sync) or hosted blob URL (async); decodeMediaUrl() gives bytes.
|
|
216
|
+
const img = await client.image.generate("MiniMax Image-01", {
|
|
217
|
+
prompt: "a developer at a laptop, anime style",
|
|
218
|
+
resolution: "2K", aspect_ratio: "16:9", // or size: "WxH"
|
|
219
|
+
reference_image_urls: ["https://…"],
|
|
220
|
+
});
|
|
221
|
+
await client.image.edit("GPT Image", { image: { data: bytes, filename: "photo.png" }, prompt: "add a party hat" });
|
|
222
|
+
// generateAsync / submitGenerate + getAsyncStatus for jobs longer than ~100 s
|
|
223
|
+
|
|
224
|
+
// Speech.
|
|
225
|
+
const speech = await client.audio.speech("MiniMax Speech 2.6 HD", { input: "Hello", voice: "English_radiant_girl", emotion: "happy" });
|
|
226
|
+
const text = await client.audio.transcribe("OpenAI/Whisper", { file: { data: audioBytes, filename: "a.mp3" } });
|
|
227
|
+
// speechAsync / transcribeAsync for long jobs; voices and emotions are in getOptionsSchema(model).catalog
|
|
228
|
+
|
|
229
|
+
// Video — always async.
|
|
230
|
+
const task = await client.video.generate("MiniMax Hailuo 2.3", { prompt: "a gentle ocean wave", duration: 5 });
|
|
231
|
+
const status = await client.video.wait(task.id, { pollIntervalMs: 5000 });
|
|
232
|
+
if (status.status === "succeeded") await client.video.getContent(task.id);
|
|
233
|
+
|
|
234
|
+
// Embeddings.
|
|
235
|
+
const e = await client.embeddings.create("text-embedding-3-small", { input: ["alpha", "beta"] });
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
`VideoGenerateParams` covers keyframes (`first_frame_url` / `last_frame_url`), reference inputs, source video/image/audio drivers, `service_tier: "flex"`, and more; every `*_url` accepts `https://` or a `data:` URI (`encodeImageUrl(bytes)`).
|
|
239
|
+
|
|
240
|
+
## Errors and cancellation
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
try {
|
|
244
|
+
await client.chat.create({ ... }, { signal: controller.signal });
|
|
245
|
+
} catch (e) {
|
|
246
|
+
if (e instanceof APIError) console.error(e.statusCode, e.type, e.message);
|
|
247
|
+
else throw e; // network errors are the fetch rejection
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Every call takes `{ signal }`. In browser-auth mode a 401 is retried once after refreshing the token.
|
|
252
|
+
|
|
253
|
+
## API reference
|
|
254
|
+
|
|
255
|
+
| Service | Methods |
|
|
256
|
+
|---|---|
|
|
257
|
+
| `client.chat` | `create`, `stream` |
|
|
258
|
+
| `client.models` | `list` |
|
|
259
|
+
| `client.files` | `put`, `putString`, `putFromURL`, `get`, `read`, `readJSON`, `stat`, `exists`, `list`, `del`, `deleteMany`, `move`, `copy`, `batch` |
|
|
260
|
+
| `client.image` | `generate`, `edit`, `generateAsync`, `submitGenerate`, `getAsyncStatus` |
|
|
261
|
+
| `client.audio` | `speech`, `speechAsync`, `transcribe`, `transcribeAsync` |
|
|
262
|
+
| `client.video` | `generate`, `getStatus`, `wait`, `getContent` |
|
|
263
|
+
| `client.embeddings` | `create` |
|
|
264
|
+
|
|
265
|
+
Browser auth: `client.signIn()`, `client.signOut()`, `client.user`, `client.auth`.
|
|
266
|
+
|
|
267
|
+
Helpers: `hasCapabilityTag`, `acceptsModality`, `getOptionsSchema`, `acceptsField`, `requiresField`, `allowedValuesFor`, `validateRequest`, `supportsVoiceClone`, `supportsInstructText`, `defaultVoice`, `encodeImageUrl`, `decodeMediaUrl`, the message builders, `ServerTools`.
|
|
268
|
+
|
|
269
|
+
## Compatibility
|
|
270
|
+
|
|
271
|
+
Browsers (any modern), Node 18+, Deno, Bun. Never ship an `rk_…` key to a browser — an app published on RunApps uses `authProvider: "runapps"` instead, and the user pays for their own usage.
|
|
272
|
+
|
|
273
|
+
## License
|
|
274
|
+
|
|
275
|
+
MIT
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory session-scoped activity index — turns the raw event
|
|
3
|
+
* stream from `SDKEvents` into the snapshot the badge UI reads each
|
|
4
|
+
* frame: which calls are in flight, what just finished, what the
|
|
5
|
+
* running session cost is.
|
|
6
|
+
*
|
|
7
|
+
* Memory bounded:
|
|
8
|
+
* - `active` map shrinks on every end/error (calls don't accumulate
|
|
9
|
+
* past their settle event).
|
|
10
|
+
* - `recent` is a bounded ring of the last RECENT_MAX completions.
|
|
11
|
+
* - tokens/sec windows hold only `RATE_WINDOW_MS` worth of deltas;
|
|
12
|
+
* stale samples get dropped on every push.
|
|
13
|
+
*
|
|
14
|
+
* Snapshot pattern (vs. emitting another event per change): the
|
|
15
|
+
* badge re-renders on `requestAnimationFrame` while it's open, so
|
|
16
|
+
* a pull-snapshot API matches that cadence — pushing on every
|
|
17
|
+
* delta would over-paint and cost battery on token-heavy streams.
|
|
18
|
+
*/
|
|
19
|
+
import type { SDKCapability, SDKEvents } from "./events.js";
|
|
20
|
+
/** One LLM call currently in flight. */
|
|
21
|
+
export interface ActiveCall {
|
|
22
|
+
id: string;
|
|
23
|
+
model: string;
|
|
24
|
+
capability: SDKCapability;
|
|
25
|
+
startedAt: number;
|
|
26
|
+
/** True for stream:true. Drives the pulsing-ring animation. */
|
|
27
|
+
streaming: boolean;
|
|
28
|
+
/** Cumulative completion tokens (0 until first delta). */
|
|
29
|
+
tokensSoFar: number;
|
|
30
|
+
/** Tokens per second over the last RATE_WINDOW_MS. 0 until the
|
|
31
|
+
* window has 2+ samples. */
|
|
32
|
+
tokensPerSec: number;
|
|
33
|
+
/** Internal sample buffer — exposed for tests; UI ignores. */
|
|
34
|
+
rateSamples: Array<{
|
|
35
|
+
t: number;
|
|
36
|
+
tokens: number;
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
/** One LLM call that already finished (success OR failure). */
|
|
40
|
+
export interface CompletedCall {
|
|
41
|
+
id: string;
|
|
42
|
+
model: string;
|
|
43
|
+
capability: SDKCapability;
|
|
44
|
+
latencyMs: number;
|
|
45
|
+
totalTokens: number;
|
|
46
|
+
costUSD?: number;
|
|
47
|
+
finishReason?: string;
|
|
48
|
+
/** false for RequestErrorEvent settles. */
|
|
49
|
+
ok: boolean;
|
|
50
|
+
/** Error message when ok=false. */
|
|
51
|
+
errorMessage?: string;
|
|
52
|
+
/** Wall clock at settle — drives the "30s ago" relative display. */
|
|
53
|
+
endedAt: number;
|
|
54
|
+
}
|
|
55
|
+
/** Running session totals — shown in the popover's footer. */
|
|
56
|
+
export interface SessionStats {
|
|
57
|
+
totalCalls: number;
|
|
58
|
+
totalCostUSD: number;
|
|
59
|
+
errorCount: number;
|
|
60
|
+
/** Wall clock when the tracker first saw an event. Drives the
|
|
61
|
+
* "session lasted N minutes" string. */
|
|
62
|
+
startedAt: number;
|
|
63
|
+
}
|
|
64
|
+
/** Pull-snapshot of the tracker — taken once per badge re-render. */
|
|
65
|
+
export interface ActivitySnapshot {
|
|
66
|
+
active: ActiveCall[];
|
|
67
|
+
recent: CompletedCall[];
|
|
68
|
+
session: SessionStats;
|
|
69
|
+
/** Highest-priority status colour for the LED dot:
|
|
70
|
+
* "error" → recent failure within ERROR_HIGHLIGHT_MS
|
|
71
|
+
* "active" → at least one in-flight call
|
|
72
|
+
* "idle" → nothing happening
|
|
73
|
+
* The badge's CSS uses this as a class. */
|
|
74
|
+
status: "idle" | "active" | "error";
|
|
75
|
+
}
|
|
76
|
+
export declare class ActivityTracker {
|
|
77
|
+
private active;
|
|
78
|
+
private recent;
|
|
79
|
+
private session;
|
|
80
|
+
/** Track most recent error wall-clock so the LED stays red for
|
|
81
|
+
* ERROR_HIGHLIGHT_MS even after the error scrolls out of recent. */
|
|
82
|
+
private lastErrorAt;
|
|
83
|
+
/** "Snapshot changed" listeners. Fired AFTER every event handler
|
|
84
|
+
* runs (start / delta / end / error), letting UIs re-render in
|
|
85
|
+
* push-mode instead of polling. Without this hook, the badge's
|
|
86
|
+
* ring/LED stayed idle until the user opened the popover —
|
|
87
|
+
* events landed in the tracker but nobody told the DOM. */
|
|
88
|
+
private changeListeners;
|
|
89
|
+
/**
|
|
90
|
+
* Subscribe the tracker to a bus. Returns a single disposer that
|
|
91
|
+
* unsubscribes all four handlers — callers (BrowserAuth in our
|
|
92
|
+
* case) attach this on construction and dispose on teardown.
|
|
93
|
+
*/
|
|
94
|
+
attach(events: SDKEvents): () => void;
|
|
95
|
+
/**
|
|
96
|
+
* Subscribe to "snapshot may have changed" notifications. Fires
|
|
97
|
+
* synchronously after each event handler runs — typical consumer
|
|
98
|
+
* is the badge's `requestRedraw()` which coalesces multiple
|
|
99
|
+
* notifications into one rAF paint.
|
|
100
|
+
*
|
|
101
|
+
* Returns a disposer; call to unsubscribe.
|
|
102
|
+
*/
|
|
103
|
+
onChange(handler: () => void): () => void;
|
|
104
|
+
private notify;
|
|
105
|
+
/** O(active+recent) — small in practice, fine on every frame. */
|
|
106
|
+
snapshot(): ActivitySnapshot;
|
|
107
|
+
private onStart;
|
|
108
|
+
private onDelta;
|
|
109
|
+
private onEnd;
|
|
110
|
+
private onError;
|
|
111
|
+
private pushRecent;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=activity-tracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity-tracker.d.ts","sourceRoot":"","sources":["../src/activity-tracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAKV,aAAa,EACb,SAAS,EACV,MAAM,aAAa,CAAC;AAYrB,wCAAwC;AACxC,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,aAAa,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,SAAS,EAAE,OAAO,CAAC;IACnB,0DAA0D;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB;iCAC6B;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,8DAA8D;IAC9D,WAAW,EAAE,KAAK,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnD;AAED,+DAA+D;AAC/D,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,aAAa,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,EAAE,EAAE,OAAO,CAAC;IACZ,mCAAmC;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,8DAA8D;AAC9D,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB;6CACyC;IACzC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qEAAqE;AACrE,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,OAAO,EAAE,YAAY,CAAC;IACtB;;;;gDAI4C;IAC5C,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;CACrC;AAOD,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAiC;IAC/C,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,OAAO,CAKb;IACF;yEACqE;IACrE,OAAO,CAAC,WAAW,CAAK;IACxB;;;;gEAI4D;IAC5D,OAAO,CAAC,eAAe,CAAyB;IAEhD;;;;OAIG;IACH,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,IAAI;IAYrC;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAKzC,OAAO,CAAC,MAAM;IAWd,iEAAiE;IACjE,QAAQ,IAAI,gBAAgB;IAiB5B,OAAO,CAAC,OAAO;IAaf,OAAO,CAAC,OAAO;IAkBf,OAAO,CAAC,KAAK;IAkBb,OAAO,CAAC,OAAO;IAkBf,OAAO,CAAC,UAAU;CAKnB"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory session-scoped activity index — turns the raw event
|
|
3
|
+
* stream from `SDKEvents` into the snapshot the badge UI reads each
|
|
4
|
+
* frame: which calls are in flight, what just finished, what the
|
|
5
|
+
* running session cost is.
|
|
6
|
+
*
|
|
7
|
+
* Memory bounded:
|
|
8
|
+
* - `active` map shrinks on every end/error (calls don't accumulate
|
|
9
|
+
* past their settle event).
|
|
10
|
+
* - `recent` is a bounded ring of the last RECENT_MAX completions.
|
|
11
|
+
* - tokens/sec windows hold only `RATE_WINDOW_MS` worth of deltas;
|
|
12
|
+
* stale samples get dropped on every push.
|
|
13
|
+
*
|
|
14
|
+
* Snapshot pattern (vs. emitting another event per change): the
|
|
15
|
+
* badge re-renders on `requestAnimationFrame` while it's open, so
|
|
16
|
+
* a pull-snapshot API matches that cadence — pushing on every
|
|
17
|
+
* delta would over-paint and cost battery on token-heavy streams.
|
|
18
|
+
*/
|
|
19
|
+
/** Sliding window for tokens/sec — 1.5s gives a snappy display
|
|
20
|
+
* without jittering on every chunk arrival (most providers emit
|
|
21
|
+
* ~5-30 deltas/s). */
|
|
22
|
+
const RATE_WINDOW_MS = 1500;
|
|
23
|
+
/** Cap on the recent-completion ring buffer. The popover lists the
|
|
24
|
+
* last 5 by default but we keep a few extra so a future "show more"
|
|
25
|
+
* UI doesn't need a refactor. */
|
|
26
|
+
const RECENT_MAX = 20;
|
|
27
|
+
/** Window during which a recent error keeps the LED red. 30s is
|
|
28
|
+
* long enough to grab attention but short enough that an old
|
|
29
|
+
* failure doesn't dominate when subsequent calls succeed. */
|
|
30
|
+
const ERROR_HIGHLIGHT_MS = 30_000;
|
|
31
|
+
export class ActivityTracker {
|
|
32
|
+
active = new Map();
|
|
33
|
+
recent = [];
|
|
34
|
+
session = {
|
|
35
|
+
totalCalls: 0,
|
|
36
|
+
totalCostUSD: 0,
|
|
37
|
+
errorCount: 0,
|
|
38
|
+
startedAt: Date.now(),
|
|
39
|
+
};
|
|
40
|
+
/** Track most recent error wall-clock so the LED stays red for
|
|
41
|
+
* ERROR_HIGHLIGHT_MS even after the error scrolls out of recent. */
|
|
42
|
+
lastErrorAt = 0;
|
|
43
|
+
/** "Snapshot changed" listeners. Fired AFTER every event handler
|
|
44
|
+
* runs (start / delta / end / error), letting UIs re-render in
|
|
45
|
+
* push-mode instead of polling. Without this hook, the badge's
|
|
46
|
+
* ring/LED stayed idle until the user opened the popover —
|
|
47
|
+
* events landed in the tracker but nobody told the DOM. */
|
|
48
|
+
changeListeners = new Set();
|
|
49
|
+
/**
|
|
50
|
+
* Subscribe the tracker to a bus. Returns a single disposer that
|
|
51
|
+
* unsubscribes all four handlers — callers (BrowserAuth in our
|
|
52
|
+
* case) attach this on construction and dispose on teardown.
|
|
53
|
+
*/
|
|
54
|
+
attach(events) {
|
|
55
|
+
const offs = [
|
|
56
|
+
events.on("request:start", (e) => { this.onStart(e); this.notify(); }),
|
|
57
|
+
events.on("request:streamDelta", (e) => { this.onDelta(e); this.notify(); }),
|
|
58
|
+
events.on("request:end", (e) => { this.onEnd(e); this.notify(); }),
|
|
59
|
+
events.on("request:error", (e) => { this.onError(e); this.notify(); }),
|
|
60
|
+
];
|
|
61
|
+
return () => {
|
|
62
|
+
for (const o of offs)
|
|
63
|
+
o();
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Subscribe to "snapshot may have changed" notifications. Fires
|
|
68
|
+
* synchronously after each event handler runs — typical consumer
|
|
69
|
+
* is the badge's `requestRedraw()` which coalesces multiple
|
|
70
|
+
* notifications into one rAF paint.
|
|
71
|
+
*
|
|
72
|
+
* Returns a disposer; call to unsubscribe.
|
|
73
|
+
*/
|
|
74
|
+
onChange(handler) {
|
|
75
|
+
this.changeListeners.add(handler);
|
|
76
|
+
return () => this.changeListeners.delete(handler);
|
|
77
|
+
}
|
|
78
|
+
notify() {
|
|
79
|
+
for (const h of this.changeListeners) {
|
|
80
|
+
try {
|
|
81
|
+
h();
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
// eslint-disable-next-line no-console
|
|
85
|
+
console.error("[runjobs sdk] ActivityTracker onChange listener threw:", err);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** O(active+recent) — small in practice, fine on every frame. */
|
|
90
|
+
snapshot() {
|
|
91
|
+
const now = Date.now();
|
|
92
|
+
return {
|
|
93
|
+
active: Array.from(this.active.values()),
|
|
94
|
+
recent: this.recent.slice(),
|
|
95
|
+
session: { ...this.session },
|
|
96
|
+
status: now - this.lastErrorAt < ERROR_HIGHLIGHT_MS
|
|
97
|
+
? "error"
|
|
98
|
+
: this.active.size > 0
|
|
99
|
+
? "active"
|
|
100
|
+
: "idle",
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// ─── handlers ─────────────────────────────────────────────────
|
|
104
|
+
onStart(e) {
|
|
105
|
+
this.active.set(e.id, {
|
|
106
|
+
id: e.id,
|
|
107
|
+
model: e.model,
|
|
108
|
+
capability: e.capability,
|
|
109
|
+
startedAt: e.startedAt,
|
|
110
|
+
streaming: e.streaming,
|
|
111
|
+
tokensSoFar: 0,
|
|
112
|
+
tokensPerSec: 0,
|
|
113
|
+
rateSamples: [],
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
onDelta(e) {
|
|
117
|
+
const call = this.active.get(e.id);
|
|
118
|
+
if (!call)
|
|
119
|
+
return; // delta after end — drop
|
|
120
|
+
call.tokensSoFar = e.totalTokens;
|
|
121
|
+
const now = Date.now();
|
|
122
|
+
call.rateSamples.push({ t: now, tokens: e.deltaTokens });
|
|
123
|
+
// Drop samples older than the window.
|
|
124
|
+
const cutoff = now - RATE_WINDOW_MS;
|
|
125
|
+
while (call.rateSamples.length > 0 && call.rateSamples[0].t < cutoff) {
|
|
126
|
+
call.rateSamples.shift();
|
|
127
|
+
}
|
|
128
|
+
if (call.rateSamples.length >= 2) {
|
|
129
|
+
const sumTokens = call.rateSamples.reduce((s, x) => s + x.tokens, 0);
|
|
130
|
+
const spanMs = now - call.rateSamples[0].t;
|
|
131
|
+
call.tokensPerSec = spanMs > 0 ? (sumTokens * 1000) / spanMs : 0;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
onEnd(e) {
|
|
135
|
+
const call = this.active.get(e.id);
|
|
136
|
+
this.active.delete(e.id);
|
|
137
|
+
this.pushRecent({
|
|
138
|
+
id: e.id,
|
|
139
|
+
model: e.model,
|
|
140
|
+
capability: e.capability,
|
|
141
|
+
latencyMs: e.latencyMs,
|
|
142
|
+
totalTokens: e.totalTokens || call?.tokensSoFar || 0,
|
|
143
|
+
...(e.costUSD !== undefined && { costUSD: e.costUSD }),
|
|
144
|
+
...(e.finishReason !== undefined && { finishReason: e.finishReason }),
|
|
145
|
+
ok: true,
|
|
146
|
+
endedAt: Date.now(),
|
|
147
|
+
});
|
|
148
|
+
this.session.totalCalls += 1;
|
|
149
|
+
if (e.costUSD !== undefined)
|
|
150
|
+
this.session.totalCostUSD += e.costUSD;
|
|
151
|
+
}
|
|
152
|
+
onError(e) {
|
|
153
|
+
const call = this.active.get(e.id);
|
|
154
|
+
this.active.delete(e.id);
|
|
155
|
+
this.pushRecent({
|
|
156
|
+
id: e.id,
|
|
157
|
+
model: e.model,
|
|
158
|
+
capability: e.capability,
|
|
159
|
+
latencyMs: e.latencyMs,
|
|
160
|
+
totalTokens: call?.tokensSoFar || 0,
|
|
161
|
+
ok: false,
|
|
162
|
+
errorMessage: e.error.message,
|
|
163
|
+
endedAt: Date.now(),
|
|
164
|
+
});
|
|
165
|
+
this.session.totalCalls += 1;
|
|
166
|
+
this.session.errorCount += 1;
|
|
167
|
+
this.lastErrorAt = Date.now();
|
|
168
|
+
}
|
|
169
|
+
pushRecent(c) {
|
|
170
|
+
// Newest first — popover renders top-to-bottom newest→oldest.
|
|
171
|
+
this.recent.unshift(c);
|
|
172
|
+
if (this.recent.length > RECENT_MAX)
|
|
173
|
+
this.recent.length = RECENT_MAX;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=activity-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity-tracker.js","sourceRoot":"","sources":["../src/activity-tracker.ts"],"names":[],"mappings":"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"}
|