totalum-sdk 0.1.0-dev.10

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.
Files changed (53) hide show
  1. package/README.md +18 -0
  2. package/dist/_types/billing.d-BbSZh1wY.d.ts +38 -0
  3. package/dist/_types/coerce.d-C0KIW76L.d.ts +6 -0
  4. package/dist/_types/errors.d-Cu3q_E_r.d.ts +3093 -0
  5. package/dist/_types/integrations.d-BNGV70e3.d.ts +793 -0
  6. package/dist/_types/ops.d-C9375KIG.d.ts +117 -0
  7. package/dist/ai/index.d.ts +424 -0
  8. package/dist/ai/index.js +185 -0
  9. package/dist/analytics/index.d.ts +35 -0
  10. package/dist/analytics/index.js +15 -0
  11. package/dist/browser/index.d.ts +1595 -0
  12. package/dist/browser/index.js +168 -0
  13. package/dist/cron/index.d.ts +180 -0
  14. package/dist/cron/index.js +61 -0
  15. package/dist/d1/errors.js +31 -0
  16. package/dist/d1/https.js +103 -0
  17. package/dist/d1/index.d.ts +107 -0
  18. package/dist/d1/index.js +73 -0
  19. package/dist/d1/lazy.js +112 -0
  20. package/dist/d1/libsql.js +125 -0
  21. package/dist/d1/session.js +38 -0
  22. package/dist/d1/sql.js +87 -0
  23. package/dist/d1/types.js +1 -0
  24. package/dist/email/index.d.ts +31 -0
  25. package/dist/email/index.js +30 -0
  26. package/dist/errors.js +35 -0
  27. package/dist/files/index.d.ts +119 -0
  28. package/dist/files/index.js +63 -0
  29. package/dist/http.js +85 -0
  30. package/dist/index.d.ts +73 -0
  31. package/dist/index.js +68 -0
  32. package/dist/logs/index.d.ts +51 -0
  33. package/dist/logs/index.js +18 -0
  34. package/dist/payments/index.d.ts +47 -0
  35. package/dist/payments/index.js +20 -0
  36. package/dist/pdf/index.d.ts +42 -0
  37. package/dist/pdf/index.js +17 -0
  38. package/dist/react/index.d.ts +91 -0
  39. package/dist/react/index.js +178 -0
  40. package/dist/realtime/index.d.ts +103 -0
  41. package/dist/realtime/index.js +30 -0
  42. package/dist/scan/index.d.ts +46 -0
  43. package/dist/scan/index.js +12 -0
  44. package/dist/seo/index.d.ts +23 -0
  45. package/dist/seo/index.js +12 -0
  46. package/dist/speech/index.d.ts +41 -0
  47. package/dist/speech/index.js +22 -0
  48. package/dist/web/index.d.ts +170 -0
  49. package/dist/web/index.js +31 -0
  50. package/dist/webhooks/index.d.ts +48 -0
  51. package/dist/webhooks/index.js +61 -0
  52. package/package.json +146 -0
  53. package/totalum-sdk.md +1088 -0
@@ -0,0 +1,178 @@
1
+ import { useEffect, useMemo, useSyncExternalStore } from 'react';
2
+ const HEARTBEAT_MS = 30_000;
3
+ const STALE_MS = 75_000;
4
+ const PRESENCE = 'presence:';
5
+ /** One room, one socket; framework-free (the hook below is a thin wrapper). */
6
+ export class RealtimeChannel {
7
+ room;
8
+ options;
9
+ ws;
10
+ stopped = true;
11
+ attempt = 0;
12
+ timer;
13
+ heartbeat;
14
+ peers = new Map();
15
+ listeners = new Set();
16
+ connectionId = crypto.randomUUID();
17
+ snapshot = { status: 'closed', messages: [], others: [] };
18
+ constructor(room, options = {}) {
19
+ this.room = room;
20
+ this.options = options;
21
+ }
22
+ subscribe = (listener) => {
23
+ this.listeners.add(listener);
24
+ return () => this.listeners.delete(listener);
25
+ };
26
+ getSnapshot = () => this.snapshot;
27
+ connect() {
28
+ if (!this.stopped)
29
+ return;
30
+ this.stopped = false;
31
+ this.set({ status: 'connecting' });
32
+ void this.open();
33
+ if (this.options.presence)
34
+ this.heartbeat = setInterval(() => {
35
+ this.send(`${PRESENCE}here`, this.options.presence);
36
+ this.expire();
37
+ }, HEARTBEAT_MS);
38
+ }
39
+ /** Sends `data` to everyone in the room (you included); `false` when the socket is not open. */
40
+ publish = (data, event = 'message') => this.send(event, data);
41
+ close() {
42
+ if (this.options.presence)
43
+ this.send(`${PRESENCE}leave`, null);
44
+ this.stopped = true;
45
+ clearTimeout(this.timer);
46
+ clearInterval(this.heartbeat);
47
+ this.ws?.close(1000, 'closed');
48
+ this.ws = undefined;
49
+ this.peers.clear();
50
+ this.set({ status: 'closed', others: [] });
51
+ }
52
+ async open() {
53
+ try {
54
+ const { url } = await this.ticket();
55
+ if (this.stopped)
56
+ return;
57
+ const ws = new (this.options.WebSocket ?? WebSocket)(url);
58
+ this.ws = ws;
59
+ ws.addEventListener('open', () => {
60
+ this.attempt = 0;
61
+ this.set({ status: 'open' });
62
+ if (this.options.presence)
63
+ this.send(`${PRESENCE}join`, this.options.presence);
64
+ });
65
+ ws.addEventListener('message', (e) => {
66
+ this.receive(String(e.data));
67
+ });
68
+ ws.addEventListener('close', () => {
69
+ if (this.ws === ws)
70
+ this.retry();
71
+ });
72
+ }
73
+ catch {
74
+ this.retry();
75
+ }
76
+ }
77
+ async ticket() {
78
+ if (this.options.getTicket)
79
+ return this.options.getTicket(this.room);
80
+ const res = await fetch(this.options.ticketUrl ?? '/api/realtime/ticket', {
81
+ method: 'POST',
82
+ headers: { 'content-type': 'application/json' },
83
+ body: JSON.stringify({ room: this.room }),
84
+ });
85
+ if (!res.ok)
86
+ throw new Error(`ticket ${String(res.status)}`);
87
+ const body = await res.json();
88
+ const { url, data } = body;
89
+ return { url: url ?? data?.url ?? '' };
90
+ }
91
+ /** A drop is normal (every deploy drops every socket): re-ticket and reconnect, 0.5 s … 30 s apart. */
92
+ retry() {
93
+ this.ws = undefined;
94
+ if (this.stopped)
95
+ return;
96
+ this.peers.clear();
97
+ this.set({ status: 'reconnecting', others: [] });
98
+ const delay = Math.min(30_000, 500 * 2 ** this.attempt++) * (0.75 + Math.random() / 2);
99
+ this.timer = setTimeout(() => void this.open(), delay);
100
+ }
101
+ send(event, data) {
102
+ if (this.ws?.readyState !== 1)
103
+ return false;
104
+ const payload = event.startsWith(PRESENCE) ? { connectionId: this.connectionId, meta: data } : data;
105
+ this.ws.send(JSON.stringify({ type: 'publish', event, data: payload }));
106
+ return true;
107
+ }
108
+ receive(raw) {
109
+ let frame;
110
+ try {
111
+ frame = JSON.parse(raw);
112
+ }
113
+ catch {
114
+ return;
115
+ }
116
+ if (frame.type !== 'message' || !frame.event || !frame.identity)
117
+ return;
118
+ if (!frame.event.startsWith(PRESENCE)) {
119
+ const keep = this.options.keep ?? 100;
120
+ this.set({ messages: [...this.snapshot.messages, frame].slice(-keep) });
121
+ return;
122
+ }
123
+ const { connectionId, meta } = (frame.data ?? {});
124
+ if (!this.options.presence || !connectionId || connectionId === this.connectionId)
125
+ return;
126
+ if (frame.event === `${PRESENCE}leave`)
127
+ this.peers.delete(connectionId);
128
+ else {
129
+ // a newcomer learns who is already here
130
+ if (frame.event === `${PRESENCE}join`)
131
+ this.send(`${PRESENCE}here`, this.options.presence);
132
+ this.peers.set(connectionId, {
133
+ connectionId,
134
+ identity: frame.identity,
135
+ meta: meta,
136
+ seen: Date.now(),
137
+ });
138
+ }
139
+ this.expire();
140
+ }
141
+ /** Peers whose socket dropped without a goodbye disappear after 75 s of silence. */
142
+ expire() {
143
+ for (const [id, peer] of this.peers)
144
+ if (Date.now() - peer.seen > STALE_MS)
145
+ this.peers.delete(id);
146
+ const others = [...this.peers.values()].map(({ connectionId, identity, meta }) => ({
147
+ connectionId,
148
+ identity,
149
+ meta,
150
+ }));
151
+ this.set({ others });
152
+ }
153
+ set(patch) {
154
+ this.snapshot = { ...this.snapshot, ...patch };
155
+ for (const listener of this.listeners)
156
+ listener();
157
+ }
158
+ }
159
+ const SERVER = { status: 'connecting', messages: [], others: [] };
160
+ /**
161
+ * A room in a client component: `status`, the `messages` received since it opened, `others` (with `presence`), and
162
+ * `publish(data, event?)`. Render `status === 'reconnecting'` as an ordinary state, not an error.
163
+ *
164
+ * @example
165
+ * const { messages, others, publish, status } = useRealtimeChannel(`chat:${roomId}`, { presence: { name } });
166
+ */
167
+ export function useRealtimeChannel(room, options = {}) {
168
+ // one channel per room; the options of its first render are the ones it keeps
169
+ const channel = useMemo(() => new RealtimeChannel(room, options), [room]);
170
+ useEffect(() => {
171
+ channel.connect();
172
+ return () => {
173
+ channel.close();
174
+ };
175
+ }, [channel]);
176
+ const snapshot = useSyncExternalStore(channel.subscribe, channel.getSnapshot, () => SERVER);
177
+ return { ...snapshot, publish: channel.publish };
178
+ }
@@ -0,0 +1,103 @@
1
+ import { Z as ZodObject, c as ZodOptional, d as ZodString, g as ZodUnknown, m as $strip, o as output, t as ZodISODateTime, l as ZodInt, h as ZodRecord, j as ZodBoolean, T as TotalumClientOptions } from '../_types/errors.d-Cu3q_E_r.js';
2
+ export { a as TotalumError, i as isTotalumError } from '../_types/errors.d-Cu3q_E_r.js';
3
+
4
+ /** `POST /v1/realtime/tickets` (scope `realtime`). `ttl` is the join window in seconds, not the session length. */
5
+ declare const RealtimeTicketInput: ZodObject<{
6
+ room: ZodString;
7
+ identity: ZodObject<{
8
+ id: ZodString;
9
+ name: ZodOptional<ZodString>;
10
+ metadata: ZodOptional<ZodRecord<ZodString, ZodUnknown>>;
11
+ }, $strip>;
12
+ permissions: ZodOptional<ZodObject<{
13
+ subscribe: ZodOptional<ZodBoolean>;
14
+ publish: ZodOptional<ZodBoolean>;
15
+ }, $strip>>;
16
+ ttl: ZodOptional<ZodInt>;
17
+ }, $strip>;
18
+ type RealtimeTicketInput = output<typeof RealtimeTicketInput>;
19
+ declare const RealtimeTicketOutput: ZodObject<{
20
+ ticket: ZodString;
21
+ url: ZodString;
22
+ room: ZodString;
23
+ identity: ZodObject<{
24
+ id: ZodString;
25
+ name: ZodOptional<ZodString>;
26
+ metadata: ZodOptional<ZodRecord<ZodString, ZodUnknown>>;
27
+ }, $strip>;
28
+ permissions: ZodObject<{
29
+ subscribe: ZodBoolean;
30
+ publish: ZodBoolean;
31
+ }, $strip>;
32
+ expiresAt: ZodISODateTime;
33
+ }, $strip>;
34
+ type RealtimeTicketOutput = output<typeof RealtimeTicketOutput>;
35
+ /**
36
+ * `POST /v1/realtime/rooms/:room/publish` (scope `realtime`; plan 04 §5.16): server-side publish over HTTPS from the
37
+ * app's own routes or a cron job. The room is resolved inside the key's project (A8.6); `identity` defaults to
38
+ * `{id: 'server'}`. Answers `202`.
39
+ */
40
+ declare const RealtimePublishInput: ZodObject<{
41
+ event: ZodOptional<ZodString>;
42
+ data: ZodUnknown;
43
+ identity: ZodOptional<ZodObject<{
44
+ id: ZodString;
45
+ name: ZodOptional<ZodString>;
46
+ }, $strip>>;
47
+ }, $strip>;
48
+ type RealtimePublishInput = output<typeof RealtimePublishInput>;
49
+ /** The delivery confirmation: the message id and the number of sockets it was fanned out to. */
50
+ declare const RealtimePublishOutput: ZodObject<{
51
+ messageId: ZodString;
52
+ at: ZodISODateTime;
53
+ connections: ZodInt;
54
+ }, $strip>;
55
+ type RealtimePublishOutput = output<typeof RealtimePublishOutput>;
56
+
57
+ /**
58
+ * `totalum.realtime` server side (plan 05 §6.14.1) over SDK-API `/v1/realtime/*` (scope `realtime`). The app's own
59
+ * server mints a short-lived ticket after its own auth check and hands `{ticket, url}` to the page; the page opens the
60
+ * `wss://` `url` and never holds a key. A room costs awake room-minutes × 4, never connections or messages.
61
+ */
62
+ declare function totalumRealtime(options?: TotalumClientOptions): {
63
+ /**
64
+ * A ticket for one room, one identity and one permission set (default subscribe only), valid to join for `ttl`
65
+ * seconds (5–300, default 60).
66
+ *
67
+ * @example
68
+ * const { url } = await realtime.ticket({ room: `chat:${orgId}`, identity: { id: user.id }, permissions: { publish: true } });
69
+ */
70
+ ticket: (input: RealtimeTicketInput) => Promise<{
71
+ ticket: string;
72
+ url: string;
73
+ room: string;
74
+ identity: {
75
+ id: string;
76
+ name?: string | undefined;
77
+ metadata?: Record<string, unknown> | undefined;
78
+ };
79
+ permissions: {
80
+ subscribe: boolean;
81
+ publish: boolean;
82
+ };
83
+ expiresAt: string;
84
+ }>;
85
+ /**
86
+ * Publishes one message to every socket subscribed to `room`, from the app's own server (a route handler, a cron
87
+ * job): no socket needed. Subscribers receive the same `message` frame a socket publish sends, with `event`
88
+ * (default `message`) and `identity` (default `{ id: 'server' }`). Resolves with the message id, its timestamp
89
+ * and the number of sockets it was sent to (`0` when nobody is connected — it is not queued).
90
+ *
91
+ * @example
92
+ * const { connections } = await realtime.publish(`orders:${shopId}`, { event: 'order.created', data: { orderId } });
93
+ */
94
+ publish: (room: string, input: RealtimePublishInput) => Promise<{
95
+ messageId: string;
96
+ at: string;
97
+ connections: number;
98
+ }>;
99
+ };
100
+ type TotalumRealtime = ReturnType<typeof totalumRealtime>;
101
+
102
+ export { RealtimePublishInput, RealtimePublishOutput, RealtimeTicketInput, RealtimeTicketOutput, TotalumClientOptions, totalumRealtime };
103
+ export type { TotalumRealtime };
@@ -0,0 +1,30 @@
1
+ import { jsonClient } from '../http.js';
2
+ export { TotalumError, isTotalumError } from '../errors.js';
3
+ /**
4
+ * `totalum.realtime` server side (plan 05 §6.14.1) over SDK-API `/v1/realtime/*` (scope `realtime`). The app's own
5
+ * server mints a short-lived ticket after its own auth check and hands `{ticket, url}` to the page; the page opens the
6
+ * `wss://` `url` and never holds a key. A room costs awake room-minutes × 4, never connections or messages.
7
+ */
8
+ export function totalumRealtime(options = {}) {
9
+ const call = jsonClient(options);
10
+ return {
11
+ /**
12
+ * A ticket for one room, one identity and one permission set (default subscribe only), valid to join for `ttl`
13
+ * seconds (5–300, default 60).
14
+ *
15
+ * @example
16
+ * const { url } = await realtime.ticket({ room: `chat:${orgId}`, identity: { id: user.id }, permissions: { publish: true } });
17
+ */
18
+ ticket: (input) => call('POST', '/v1/realtime/tickets', input),
19
+ /**
20
+ * Publishes one message to every socket subscribed to `room`, from the app's own server (a route handler, a cron
21
+ * job): no socket needed. Subscribers receive the same `message` frame a socket publish sends, with `event`
22
+ * (default `message`) and `identity` (default `{ id: 'server' }`). Resolves with the message id, its timestamp
23
+ * and the number of sockets it was sent to (`0` when nobody is connected — it is not queued).
24
+ *
25
+ * @example
26
+ * const { connections } = await realtime.publish(`orders:${shopId}`, { event: 'order.created', data: { orderId } });
27
+ */
28
+ publish: (room, input) => call('POST', `/v1/realtime/rooms/${encodeURIComponent(room)}/publish`, input),
29
+ };
30
+ }
@@ -0,0 +1,46 @@
1
+ import { S as ScanDocumentInput, s as ScanOcrInput } from '../_types/integrations.d-BNGV70e3.js';
2
+ export { t as ScanOcrOutput } from '../_types/integrations.d-BNGV70e3.js';
3
+ import { T as TotalumClientOptions } from '../_types/errors.d-Cu3q_E_r.js';
4
+ export { a as TotalumError, i as isTotalumError } from '../_types/errors.d-Cu3q_E_r.js';
5
+ import '../_types/coerce.d-C0KIW76L.js';
6
+
7
+ /** The v1 scan result: the extracted fields (one set per page with `processEveryPdfPageAsDifferentScan`) + `metadata`. */
8
+ interface ScanDocumentResult {
9
+ data: Record<string, unknown> | Record<string, unknown>[];
10
+ metadata: {
11
+ usageUnits: number;
12
+ exactUsageUnits: number;
13
+ pages: number;
14
+ provider: string;
15
+ credits: number;
16
+ };
17
+ }
18
+ /** `totalum.scan` (plan 05 §6.7) over `/v1/scan/*` (scope `scan`): a vision model through Totalum's AI gateway. */
19
+ declare function totalumScan(options?: TotalumClientOptions): {
20
+ /** Extracts the fields described by `properties` from a document or image (one set per page on request). */
21
+ document: (input: ScanDocumentInput) => Promise<ScanDocumentResult>;
22
+ /** The text of a document or image, per page as Markdown. */
23
+ ocr: (input: ScanOcrInput) => Promise<{
24
+ text: string;
25
+ pages: {
26
+ markdown: string;
27
+ width: number;
28
+ height: number;
29
+ words?: {
30
+ text: string;
31
+ bbox: number[];
32
+ }[] | undefined;
33
+ }[];
34
+ fullDetails?: unknown;
35
+ } & {
36
+ usage: {
37
+ vendor?: string | undefined;
38
+ units?: number | undefined;
39
+ credits: number;
40
+ };
41
+ }>;
42
+ };
43
+ type TotalumScan = ReturnType<typeof totalumScan>;
44
+
45
+ export { ScanDocumentInput, ScanOcrInput, TotalumClientOptions, totalumScan };
46
+ export type { ScanDocumentResult, TotalumScan };
@@ -0,0 +1,12 @@
1
+ import { envelopeClient, withUsage } from '../http.js';
2
+ export { TotalumError, isTotalumError } from '../errors.js';
3
+ /** `totalum.scan` (plan 05 §6.7) over `/v1/scan/*` (scope `scan`): a vision model through Totalum's AI gateway. */
4
+ export function totalumScan(options = {}) {
5
+ const call = envelopeClient(options);
6
+ return {
7
+ /** Extracts the fields described by `properties` from a document or image (one set per page on request). */
8
+ document: async (input) => (await call('POST', '/v1/scan/document', input)),
9
+ /** The text of a document or image, per page as Markdown. */
10
+ ocr: async (input) => withUsage(await call('POST', '/v1/scan/ocr', input)),
11
+ };
12
+ }
@@ -0,0 +1,23 @@
1
+ export { w as SeoIndexNowKey, x as SeoNotifyOutput } from '../_types/integrations.d-BNGV70e3.js';
2
+ import { T as TotalumClientOptions } from '../_types/errors.d-Cu3q_E_r.js';
3
+ export { a as TotalumError, i as isTotalumError } from '../_types/errors.d-Cu3q_E_r.js';
4
+ import '../_types/coerce.d-C0KIW76L.js';
5
+
6
+ /** `totalum.seo` (plan 05 §6.10) over SDK-API `/v1/seo/*` (scope `seo`): IndexNow for verified custom domains, free. */
7
+ declare function totalumSeo(options?: TotalumClientOptions): {
8
+ /** Tells search engines (IndexNow) that these URLs of the project's custom domains changed. */
9
+ notifyChanged: (urls: string[]) => Promise<{
10
+ accepted: number;
11
+ indexNow: "error" | "sent" | "skipped_no_custom_domain";
12
+ sitemapsPinged: boolean;
13
+ }>;
14
+ /** The key the template serves at `/<key>.txt`. */
15
+ indexNowKey: () => Promise<{
16
+ key: string;
17
+ keyLocation: string;
18
+ }>;
19
+ };
20
+ type TotalumSeo = ReturnType<typeof totalumSeo>;
21
+
22
+ export { TotalumClientOptions, totalumSeo };
23
+ export type { TotalumSeo };
@@ -0,0 +1,12 @@
1
+ import { jsonClient } from '../http.js';
2
+ export { TotalumError, isTotalumError } from '../errors.js';
3
+ /** `totalum.seo` (plan 05 §6.10) over SDK-API `/v1/seo/*` (scope `seo`): IndexNow for verified custom domains, free. */
4
+ export function totalumSeo(options = {}) {
5
+ const call = jsonClient(options);
6
+ return {
7
+ /** Tells search engines (IndexNow) that these URLs of the project's custom domains changed. */
8
+ notifyChanged: (urls) => call('POST', '/v1/seo/notify-changed', { urls }),
9
+ /** The key the template serves at `/<key>.txt`. */
10
+ indexNowKey: () => call('GET', '/v1/seo/indexnow-key'),
11
+ };
12
+ }
@@ -0,0 +1,41 @@
1
+ import { T as TranscribeInput, y as SpeakInput, z as StoredMedia } from '../_types/integrations.d-BNGV70e3.js';
2
+ export { A as TranscribeOutput } from '../_types/integrations.d-BNGV70e3.js';
3
+ import { T as TotalumClientOptions } from '../_types/errors.d-Cu3q_E_r.js';
4
+ export { a as TotalumError, i as isTotalumError } from '../_types/errors.d-Cu3q_E_r.js';
5
+ import '../_types/coerce.d-C0KIW76L.js';
6
+
7
+ /** `totalum.speech` (plan 05 §6.8) over `/v1/speech/*` (scope `speech`): OpenRouter speech through Totalum. */
8
+ declare function totalumSpeech(options?: TotalumClientOptions): {
9
+ /** Speech to text from `audioBase64` or a `url`; optional language, word timestamps and speakers. */
10
+ transcribe: (input: TranscribeInput) => Promise<{
11
+ text: string;
12
+ durationSec: number;
13
+ usage: {
14
+ credits: number;
15
+ vendor?: string | undefined;
16
+ units?: number | undefined;
17
+ vendorCostUsd?: number | undefined;
18
+ };
19
+ language?: string | undefined;
20
+ segments?: {
21
+ start: number;
22
+ end: number;
23
+ text: string;
24
+ }[] | undefined;
25
+ words?: {
26
+ word: string;
27
+ start: number;
28
+ end: number;
29
+ }[] | undefined;
30
+ }>;
31
+ /** The audio bytes, or with `store: true` the stored file `{key, url, usage}`; `credits` is the call's hold. */
32
+ synthesize: (input: SpeakInput) => Promise<StoredMedia | {
33
+ audio: ArrayBuffer;
34
+ contentType: string;
35
+ credits: number;
36
+ }>;
37
+ };
38
+ type TotalumSpeech = ReturnType<typeof totalumSpeech>;
39
+
40
+ export { SpeakInput, StoredMedia, TotalumClientOptions, TranscribeInput, totalumSpeech };
41
+ export type { TotalumSpeech };
@@ -0,0 +1,22 @@
1
+ import { jsonClient, rawClient } from '../http.js';
2
+ export { TotalumError, isTotalumError } from '../errors.js';
3
+ /** `totalum.speech` (plan 05 §6.8) over `/v1/speech/*` (scope `speech`): OpenRouter speech through Totalum. */
4
+ export function totalumSpeech(options = {}) {
5
+ const call = jsonClient(options);
6
+ const raw = rawClient(options);
7
+ return {
8
+ /** Speech to text from `audioBase64` or a `url`; optional language, word timestamps and speakers. */
9
+ transcribe: (input) => call('POST', '/v1/speech/transcribe', input),
10
+ /** The audio bytes, or with `store: true` the stored file `{key, url, usage}`; `credits` is the call's hold. */
11
+ synthesize: async (input) => {
12
+ if (input.store)
13
+ return call('POST', '/v1/speech/speak', input);
14
+ const res = await raw('/v1/speech/speak', input);
15
+ return {
16
+ audio: await res.arrayBuffer(),
17
+ contentType: res.headers.get('content-type') ?? 'audio/mpeg',
18
+ credits: Number(res.headers.get('x-totalum-credits') ?? 0),
19
+ };
20
+ },
21
+ };
22
+ }
@@ -0,0 +1,170 @@
1
+ import { G as WebScrapeInput, H as WebSearchInput, I as WebMapInput, K as WebCrawlInput, u as ScreenshotInput } from '../_types/integrations.d-BNGV70e3.js';
2
+ export { J as JobCreated, v as ScreenshotOutput, W as WebJob, B as WebScrapeOutput, D as WebSearchOutput } from '../_types/integrations.d-BNGV70e3.js';
3
+ import { T as TotalumClientOptions } from '../_types/errors.d-Cu3q_E_r.js';
4
+ export { a as TotalumError, i as isTotalumError } from '../_types/errors.d-Cu3q_E_r.js';
5
+ import '../_types/coerce.d-C0KIW76L.js';
6
+
7
+ /**
8
+ * `totalum.web` (plan 05 §6.2) over SDK-API `/v1/web/*` (scope `web`): Firecrawl scrape/search/map/crawl and Browser
9
+ * Run screenshots, charged provider cost × 28 — every result carries `usage.credits`.
10
+ */
11
+ declare function totalumWeb(options?: TotalumClientOptions): {
12
+ /**
13
+ * One page as `markdown`, `html`, `links`, … (Firecrawl).
14
+ *
15
+ * @example
16
+ * const { markdown } = await web.scrape('https://example.com', { formats: ['markdown'] });
17
+ */
18
+ scrape: (url: string, opts?: Omit<WebScrapeInput, "url">) => Promise<{
19
+ url: string;
20
+ statusCode: number;
21
+ metadata: {
22
+ sourceURL: string;
23
+ title?: string | undefined;
24
+ description?: string | undefined;
25
+ language?: string | undefined;
26
+ };
27
+ usage: {
28
+ credits: number;
29
+ vendor?: string | undefined;
30
+ units?: number | undefined;
31
+ vendorCostUsd?: number | undefined;
32
+ };
33
+ markdown?: string | undefined;
34
+ html?: string | undefined;
35
+ rawHtml?: string | undefined;
36
+ links?: string[] | undefined;
37
+ screenshotUrl?: string | undefined;
38
+ json?: unknown;
39
+ summary?: string | undefined;
40
+ contentUrl?: string | undefined;
41
+ }>;
42
+ /** A web search; with `scrapeOptions` each result also carries its page content. */
43
+ search: (query: string, opts?: Omit<WebSearchInput, "query">) => Promise<{
44
+ web: {
45
+ title: string;
46
+ url: string;
47
+ position: number;
48
+ snippet?: string | undefined;
49
+ }[];
50
+ news?: {
51
+ title: string;
52
+ url: string;
53
+ position: number;
54
+ snippet?: string | undefined;
55
+ }[] | undefined;
56
+ images?: Record<string, unknown>[] | undefined;
57
+ scraped?: {
58
+ url: string;
59
+ statusCode: number;
60
+ metadata: {
61
+ sourceURL: string;
62
+ title?: string | undefined;
63
+ description?: string | undefined;
64
+ language?: string | undefined;
65
+ };
66
+ markdown?: string | undefined;
67
+ html?: string | undefined;
68
+ rawHtml?: string | undefined;
69
+ links?: string[] | undefined;
70
+ screenshotUrl?: string | undefined;
71
+ json?: unknown;
72
+ summary?: string | undefined;
73
+ contentUrl?: string | undefined;
74
+ }[] | undefined;
75
+ } & {
76
+ usage: {
77
+ vendor?: string | undefined;
78
+ units?: number | undefined;
79
+ credits: number;
80
+ };
81
+ }>;
82
+ /** The URLs of a site (sitemap and links), without fetching the pages. */
83
+ map: (url: string, opts?: Omit<WebMapInput, "url">) => Promise<{
84
+ links: string[];
85
+ } & {
86
+ usage: {
87
+ vendor?: string | undefined;
88
+ units?: number | undefined;
89
+ credits: number;
90
+ };
91
+ }>;
92
+ /** `202 {jobId}`: poll `crawlStatus` until the job leaves `running`; that read settles its charge. */
93
+ crawl: (url: string, opts?: Omit<WebCrawlInput, "url">) => Promise<{
94
+ jobId: string;
95
+ estimatedCredits?: number | undefined;
96
+ }>;
97
+ /** The crawl job: `status`, pages so far and their content. */
98
+ crawlStatus: (jobId: string) => Promise<{
99
+ jobId: string;
100
+ status: "queued" | "running" | "completed" | "failed" | "cancelled" | "expired";
101
+ total: number;
102
+ completed: number;
103
+ creditsUsed: number;
104
+ data: {
105
+ url: string;
106
+ statusCode: number;
107
+ metadata: {
108
+ sourceURL: string;
109
+ title?: string | undefined;
110
+ description?: string | undefined;
111
+ language?: string | undefined;
112
+ };
113
+ markdown?: string | undefined;
114
+ html?: string | undefined;
115
+ rawHtml?: string | undefined;
116
+ links?: string[] | undefined;
117
+ screenshotUrl?: string | undefined;
118
+ json?: unknown;
119
+ summary?: string | undefined;
120
+ contentUrl?: string | undefined;
121
+ }[];
122
+ next?: string | undefined;
123
+ resultUrl?: string | undefined;
124
+ }>;
125
+ /** Stops a running crawl; pages already crawled are charged. */
126
+ cancelCrawl: (jobId: string) => Promise<{
127
+ jobId: string;
128
+ status: "queued" | "running" | "completed" | "failed" | "cancelled" | "expired";
129
+ total: number;
130
+ completed: number;
131
+ creditsUsed: number;
132
+ data: {
133
+ url: string;
134
+ statusCode: number;
135
+ metadata: {
136
+ sourceURL: string;
137
+ title?: string | undefined;
138
+ description?: string | undefined;
139
+ language?: string | undefined;
140
+ };
141
+ markdown?: string | undefined;
142
+ html?: string | undefined;
143
+ rawHtml?: string | undefined;
144
+ links?: string[] | undefined;
145
+ screenshotUrl?: string | undefined;
146
+ json?: unknown;
147
+ summary?: string | undefined;
148
+ contentUrl?: string | undefined;
149
+ }[];
150
+ next?: string | undefined;
151
+ resultUrl?: string | undefined;
152
+ }>;
153
+ /** A screenshot of a `url` or of inline `html`, stored as a project file (`key`, `url`). */
154
+ screenshot: (input: ScreenshotInput) => Promise<{
155
+ key: string;
156
+ url: string;
157
+ format: "png" | "jpeg" | "webp";
158
+ base64?: string | undefined;
159
+ } & {
160
+ usage: {
161
+ vendor?: string | undefined;
162
+ units?: number | undefined;
163
+ credits: number;
164
+ };
165
+ }>;
166
+ };
167
+ type TotalumWeb = ReturnType<typeof totalumWeb>;
168
+
169
+ export { ScreenshotInput, TotalumClientOptions, totalumWeb };
170
+ export type { TotalumWeb };
@@ -0,0 +1,31 @@
1
+ import { envelopeClient, withUsage } from '../http.js';
2
+ export { TotalumError, isTotalumError } from '../errors.js';
3
+ /**
4
+ * `totalum.web` (plan 05 §6.2) over SDK-API `/v1/web/*` (scope `web`): Firecrawl scrape/search/map/crawl and Browser
5
+ * Run screenshots, charged provider cost × 28 — every result carries `usage.credits`.
6
+ */
7
+ export function totalumWeb(options = {}) {
8
+ const call = envelopeClient(options);
9
+ const job = (jobId) => `/v1/web/jobs/${encodeURIComponent(jobId)}`;
10
+ return {
11
+ /**
12
+ * One page as `markdown`, `html`, `links`, … (Firecrawl).
13
+ *
14
+ * @example
15
+ * const { markdown } = await web.scrape('https://example.com', { formats: ['markdown'] });
16
+ */
17
+ scrape: async (url, opts = {}) => (await call('POST', '/v1/web/scrape', { ...opts, url })).data,
18
+ /** A web search; with `scrapeOptions` each result also carries its page content. */
19
+ search: async (query, opts = {}) => withUsage(await call('POST', '/v1/web/search', { ...opts, query })),
20
+ /** The URLs of a site (sitemap and links), without fetching the pages. */
21
+ map: async (url, opts = {}) => withUsage(await call('POST', '/v1/web/map', { ...opts, url })),
22
+ /** `202 {jobId}`: poll `crawlStatus` until the job leaves `running`; that read settles its charge. */
23
+ crawl: async (url, opts = {}) => (await call('POST', '/v1/web/crawl', { ...opts, url })).data,
24
+ /** The crawl job: `status`, pages so far and their content. */
25
+ crawlStatus: async (jobId) => (await call('GET', job(jobId))).data,
26
+ /** Stops a running crawl; pages already crawled are charged. */
27
+ cancelCrawl: async (jobId) => (await call('DELETE', job(jobId))).data,
28
+ /** A screenshot of a `url` or of inline `html`, stored as a project file (`key`, `url`). */
29
+ screenshot: async (input) => withUsage(await call('POST', '/v1/web/screenshot', input)),
30
+ };
31
+ }