anva-sdk 0.1.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.
Files changed (4) hide show
  1. package/README.md +26 -0
  2. package/index.d.ts +81 -0
  3. package/index.js +181 -0
  4. package/package.json +38 -0
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # anva — JavaScript / TypeScript SDK
2
+
3
+ Official JS/TS SDK for [Anva](https://anva.ai) — live AI avatars.
4
+ ESM, zero dependencies, typed. Node 18+ and modern browsers (keep your API
5
+ key server-side; hand only the `embed_url` to clients).
6
+
7
+ ```bash
8
+ npm install anva-sdk
9
+ ```
10
+
11
+ ```js
12
+ import { Anva } from "anva-sdk";
13
+
14
+ const client = new Anva(process.env.ANVA_KEY);
15
+
16
+ const session = await client.createSession({ characterId: "char_..." });
17
+ console.log(session.embed_url);
18
+
19
+ await client.sendMessage(session.session_id, "Welcome!");
20
+
21
+ for await (const event of client.events(session.session_id)) {
22
+ console.log(event);
23
+ }
24
+ ```
25
+
26
+ Errors throw `AnvaError` with `.status` and `.code`. MIT © Penguin Robotics
package/index.d.ts ADDED
@@ -0,0 +1,81 @@
1
+ /** Official JavaScript/TypeScript SDK for Anva (https://anva.ai). */
2
+
3
+ export interface AnvaOptions {
4
+ /** Override for self-hosted / testing setups. Default: https://anva.ai */
5
+ baseUrl?: string;
6
+ }
7
+
8
+ export interface CreateSessionParams {
9
+ characterId: string;
10
+ /** Omit for the built-in conversation engine, or "external" to drive the LLM yourself. */
11
+ llmMode?: "external";
12
+ webhookUrl?: string;
13
+ webhookSecret?: string;
14
+ }
15
+
16
+ export interface Session {
17
+ session_id: string;
18
+ session_token: string;
19
+ character_id: string;
20
+ llm_mode?: string;
21
+ expires_at: string;
22
+ /** Ready to drop into an <iframe allow="camera; microphone; autoplay">. */
23
+ embed_url: string;
24
+ events_ws_url: string;
25
+ [key: string]: unknown;
26
+ }
27
+
28
+ export interface Character {
29
+ id: string;
30
+ name: string;
31
+ visual_character_id: string;
32
+ system_prompt: string;
33
+ voice_id: string;
34
+ language_code?: string;
35
+ disabled: boolean;
36
+ active: boolean;
37
+ [key: string]: unknown;
38
+ }
39
+
40
+ export interface SessionEvent {
41
+ type: string;
42
+ [key: string]: unknown;
43
+ }
44
+
45
+ export interface EventsOptions {
46
+ /** A WebSocket-compatible constructor (e.g. `ws` in older Node versions). */
47
+ WebSocketImpl?: new (url: string) => WebSocket;
48
+ }
49
+
50
+ export declare class AnvaError extends Error {
51
+ status: number;
52
+ code: string;
53
+ }
54
+
55
+ export declare class Anva {
56
+ constructor(apiKey: string, opts?: AnvaOptions);
57
+ apiKey: string;
58
+ baseUrl: string;
59
+
60
+ createSession(params: CreateSessionParams): Promise<Session>;
61
+ getSession(sessionId: string): Promise<Record<string, unknown>>;
62
+ endSession(sessionId: string): Promise<Record<string, unknown>>;
63
+ sendMessage(sessionId: string, text: string): Promise<{ status: string }>;
64
+ interrupt(sessionId: string): Promise<{ status: string }>;
65
+ triggerAction(sessionId: string, name: string): Promise<{ status: string }>;
66
+ eventsUrl(sessionId: string): string;
67
+ events(sessionId: string, opts?: EventsOptions): AsyncGenerator<SessionEvent, void, void>;
68
+
69
+ listCharacters(): Promise<{ characters: Character[] }>;
70
+ createCharacter(params: {
71
+ name: string;
72
+ visualCharacterId?: string;
73
+ systemPrompt?: string;
74
+ voiceId?: string;
75
+ languageCode?: string;
76
+ }): Promise<Character>;
77
+ getCharacter(characterId: string): Promise<Character>;
78
+ deleteCharacter(characterId: string): Promise<{ id: string; status: string }>;
79
+ }
80
+
81
+ export default Anva;
package/index.js ADDED
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Official JavaScript/TypeScript SDK for Anva (https://anva.ai).
3
+ *
4
+ * import { Anva } from "anva";
5
+ * const client = new Anva(process.env.ANVA_KEY);
6
+ * const session = await client.createSession({ characterId: "char_..." });
7
+ * // put session.embed_url in an <iframe allow="camera; microphone; autoplay">
8
+ *
9
+ * Works in Node 18+ (global fetch/WebSocket) and modern browsers — but keep
10
+ * your API key server-side; mint sessions on your backend and hand the
11
+ * embed_url to the client.
12
+ */
13
+
14
+ const DEFAULT_BASE_URL = "https://anva.ai";
15
+
16
+ export class AnvaError extends Error {
17
+ constructor(status, code, message) {
18
+ super(`${code}: ${message} (HTTP ${status})`);
19
+ this.name = "AnvaError";
20
+ this.status = status;
21
+ this.code = code;
22
+ }
23
+ }
24
+
25
+ export class Anva {
26
+ /**
27
+ * @param {string} apiKey an API key from the dashboard (anva_key_...)
28
+ * @param {{baseUrl?: string}} [opts]
29
+ */
30
+ constructor(apiKey, opts = {}) {
31
+ if (!apiKey || !apiKey.trim()) throw new Error("apiKey is required");
32
+ this.apiKey = apiKey.trim();
33
+ this.baseUrl = (opts.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
34
+ }
35
+
36
+ // -- sessions -------------------------------------------------------------
37
+
38
+ /**
39
+ * Create a live session. Returns { session_id, session_token, embed_url,
40
+ * events_ws_url, expires_at, ... }.
41
+ */
42
+ createSession({ characterId, llmMode, webhookUrl, webhookSecret }) {
43
+ const body = { character_id: characterId };
44
+ if (llmMode) body.llm_mode = llmMode;
45
+ if (webhookUrl) body.webhook_url = webhookUrl;
46
+ if (webhookSecret) body.webhook_secret = webhookSecret;
47
+ return this._request("POST", "/api/v1/sessions", body);
48
+ }
49
+
50
+ getSession(sessionId) {
51
+ return this._request("GET", `/api/v1/sessions/${enc(sessionId)}`);
52
+ }
53
+
54
+ endSession(sessionId) {
55
+ return this._request("DELETE", `/api/v1/sessions/${enc(sessionId)}`);
56
+ }
57
+
58
+ /** Have the avatar speak `text` to the user. */
59
+ sendMessage(sessionId, text) {
60
+ return this._request("POST", `/api/v1/sessions/${enc(sessionId)}/messages`, { text });
61
+ }
62
+
63
+ /** Stop the avatar mid-sentence. */
64
+ interrupt(sessionId) {
65
+ return this._request("POST", `/api/v1/sessions/${enc(sessionId)}/interrupt`, {});
66
+ }
67
+
68
+ triggerAction(sessionId, name) {
69
+ return this._request("POST", `/api/v1/sessions/${enc(sessionId)}/actions`, { name });
70
+ }
71
+
72
+ /** The authenticated WebSocket URL for the session's event stream. */
73
+ eventsUrl(sessionId) {
74
+ const ws = this.baseUrl.replace(/^http/, "ws");
75
+ return `${ws}/api/v1/sessions/${enc(sessionId)}/events?api_key=${encodeURIComponent(this.apiKey)}`;
76
+ }
77
+
78
+ /**
79
+ * Async-iterate live events (transcripts, state changes):
80
+ *
81
+ * for await (const event of client.events(sessionId)) { ... }
82
+ */
83
+ async *events(sessionId, { WebSocketImpl } = {}) {
84
+ const WS = WebSocketImpl || globalThis.WebSocket;
85
+ if (!WS) throw new Error("no WebSocket implementation available — pass { WebSocketImpl }");
86
+ const ws = new WS(this.eventsUrl(sessionId));
87
+ const queue = [];
88
+ let notify = null;
89
+ let done = false;
90
+ let failure = null;
91
+ ws.onmessage = (m) => {
92
+ try {
93
+ queue.push(JSON.parse(m.data));
94
+ } catch {
95
+ /* non-JSON frame */
96
+ }
97
+ if (notify) notify();
98
+ };
99
+ ws.onclose = () => {
100
+ done = true;
101
+ if (notify) notify();
102
+ };
103
+ ws.onerror = () => {
104
+ failure = new Error("events socket error");
105
+ done = true;
106
+ if (notify) notify();
107
+ };
108
+ try {
109
+ while (!done || queue.length) {
110
+ if (!queue.length) {
111
+ await new Promise((resolve) => (notify = resolve));
112
+ notify = null;
113
+ continue;
114
+ }
115
+ yield queue.shift();
116
+ }
117
+ if (failure) throw failure;
118
+ } finally {
119
+ try {
120
+ ws.close();
121
+ } catch {
122
+ /* already closed */
123
+ }
124
+ }
125
+ }
126
+
127
+ // -- characters -----------------------------------------------------------
128
+
129
+ listCharacters() {
130
+ return this._request("GET", "/api/v1/characters");
131
+ }
132
+
133
+ createCharacter({ name, visualCharacterId, systemPrompt, voiceId, languageCode }) {
134
+ const body = { name };
135
+ if (visualCharacterId) body.visual_character_id = visualCharacterId;
136
+ if (systemPrompt) body.system_prompt = systemPrompt;
137
+ if (voiceId) body.voice_id = voiceId;
138
+ if (languageCode) body.language_code = languageCode;
139
+ return this._request("POST", "/api/v1/characters", body);
140
+ }
141
+
142
+ getCharacter(characterId) {
143
+ return this._request("GET", `/api/v1/characters/${enc(characterId)}`);
144
+ }
145
+
146
+ deleteCharacter(characterId) {
147
+ return this._request("DELETE", `/api/v1/characters/${enc(characterId)}`);
148
+ }
149
+
150
+ // -- plumbing -------------------------------------------------------------
151
+
152
+ async _request(method, path, body) {
153
+ const res = await fetch(this.baseUrl + path, {
154
+ method,
155
+ headers: {
156
+ Authorization: `Bearer ${this.apiKey}`,
157
+ "Content-Type": "application/json",
158
+ "User-Agent": "anva-js/0.1.0",
159
+ },
160
+ body: body === undefined ? undefined : JSON.stringify(body),
161
+ });
162
+ const raw = await res.text();
163
+ let payload = {};
164
+ try {
165
+ payload = raw ? JSON.parse(raw) : {};
166
+ } catch {
167
+ payload = { message: raw.slice(0, 300) };
168
+ }
169
+ if (!res.ok) {
170
+ const err = payload.error || payload;
171
+ throw new AnvaError(res.status, err.code || "request_failed", err.message || "request failed");
172
+ }
173
+ return payload;
174
+ }
175
+ }
176
+
177
+ function enc(part) {
178
+ return encodeURIComponent(String(part));
179
+ }
180
+
181
+ export default Anva;
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "anva-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Official JavaScript/TypeScript SDK for Anva \u2014 live AI avatars for your product.",
5
+ "keywords": [
6
+ "avatar",
7
+ "ai",
8
+ "webrtc",
9
+ "voice",
10
+ "anva"
11
+ ],
12
+ "homepage": "https://anva.ai",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/Anva-avatars/anva-sdk.git",
16
+ "directory": "js"
17
+ },
18
+ "license": "MIT",
19
+ "author": "Anva <anva.ai.2026@gmail.com>",
20
+ "type": "module",
21
+ "module": "./index.js",
22
+ "types": "./index.d.ts",
23
+ "exports": {
24
+ ".": {
25
+ "types": "./index.d.ts",
26
+ "import": "./index.js",
27
+ "default": "./index.js"
28
+ }
29
+ },
30
+ "files": [
31
+ "index.js",
32
+ "index.d.ts",
33
+ "README.md"
34
+ ],
35
+ "engines": {
36
+ "node": ">=18"
37
+ }
38
+ }