@ascending-inc/jarvis-embed 0.1.2

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/README.md ADDED
@@ -0,0 +1,296 @@
1
+ # Jarvis Embed SDK
2
+
3
+ Embed the Jarvis AI Assistant in any web application.
4
+
5
+ ---
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @ascending-inc/jarvis-embed
11
+ ```
12
+
13
+ ### Browser (no bundler)
14
+
15
+ A pre-built UMD bundle is included in the package at `dist/index.global.js`. Load it with a `<script>` tag and access the class via `window.JarvisSDK`:
16
+
17
+ ```html
18
+ <script src="node_modules/@ascending-inc/jarvis-embed/dist/index.global.js"></script>
19
+ <script>
20
+ const { JarvisEmbed } = window.JarvisSDK;
21
+
22
+ const jarvis = new JarvisEmbed({
23
+ provider: 'google',
24
+ token: googleIdToken,
25
+ containerId: 'chat-container',
26
+ });
27
+ </script>
28
+ ```
29
+
30
+ ---
31
+
32
+ ## Usage
33
+
34
+ ```ts
35
+ import { JarvisEmbed } from '@ascending-inc/jarvis-embed';
36
+
37
+ const jarvis = new JarvisEmbed({
38
+ provider: 'google',
39
+ token: googleIdToken,
40
+ containerId: 'chat-container',
41
+ model: 'my-spec',
42
+ onReady: (jarvisToken) => jarvis.setMcpServers(['my-mcp-server']),
43
+ });
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Options
49
+
50
+ | Option | Type | Default | Description |
51
+ |--------|------|---------|-------------|
52
+ | `provider` | `AuthProvider` | required | Auth provider — see [Authentication](#authentication). |
53
+ | `token` | `string` | required* | OAuth / JWT token. *Not used for `hmac`. |
54
+ | `containerId` | `string` | — | ID of the DOM element to mount the iframe into. |
55
+ | `container` | `HTMLElement` | — | Direct element reference (alternative to `containerId`). |
56
+ | `width` | `string` | `'100%'` | CSS width of the iframe. |
57
+ | `height` | `string` | `'600px'` | CSS height of the iframe. |
58
+ | `apiUrl` | `string` | `https://jarvis.ascendingdc.com` | Override for self-hosted deployments. |
59
+ | `model` | `string` | — | Spec identifier to use for the conversation (sent as `?spec=` to the API). Retrieve available values from `GET {apiUrl}/api/config`. |
60
+ | `debug` | `boolean` | `false` | Log SDK activity to the console. |
61
+ | `onReady` | `(jarvisToken: string) => void` | — | Fires when the iframe is authenticated and ready. Receives the Jarvis session token — use it to call Jarvis APIs (e.g. `GET {apiUrl}/api/mcp/servers`) on behalf of the user. |
62
+ | `onError` | `(err: Error) => void` | — | Fires on failure. |
63
+ | `onMessage` | `(data: unknown) => void` | — | Fires when the iframe posts a message to the host page. |
64
+
65
+ If neither `containerId` nor `container` is provided the iframe appends to `document.body`.
66
+
67
+ ### Getting a spec
68
+
69
+ Available specs can be retrieved from the Jarvis config endpoint:
70
+
71
+ ```
72
+ GET https://jarvis-demo.ascendingdc.com/api/config
73
+ ```
74
+
75
+ ---
76
+
77
+ ## Authentication
78
+
79
+ Calls `POST {apiUrl}/api/auth/exchange` with your auth payload and receives a Jarvis session token back.
80
+
81
+ ### `google` / `s_jwt` / `a_jwt`
82
+
83
+ | Provider | Token |
84
+ |----------|-------|
85
+ | `google` | Google `id_token` from OAuth2 |
86
+ | `s_jwt` | JWT signed with a shared secret (HS256) |
87
+ | `a_jwt` | JWT signed with a private key (RS256 / ES256) |
88
+
89
+ ### `direct`
90
+
91
+ Pass a Jarvis session token you already hold — the SDK skips the `/api/auth/exchange` call entirely and uses the token as-is for `SDK_AUTH`.
92
+
93
+ ```ts
94
+ new JarvisEmbed({
95
+ provider: 'direct',
96
+ token: existingJarvisToken,
97
+ containerId: 'chat-container',
98
+ });
99
+ ```
100
+
101
+ ### `hmac`
102
+
103
+ ```ts
104
+ new JarvisEmbed({
105
+ provider: 'hmac',
106
+ userId: 'user_123',
107
+ timestamp: Math.floor(Date.now() / 1000),
108
+ signature: hmacHex, // HMAC-SHA256(userId + timestamp)
109
+ containerId: 'chat-container',
110
+ });
111
+ ```
112
+
113
+ Requests older than 5 minutes are rejected server-side.
114
+
115
+ ---
116
+
117
+ ## Methods
118
+
119
+ | Method | Signature | Description |
120
+ |--------|-----------|-------------|
121
+ | `destroy` | `() => void` | Removes the iframe and cleans up the `window` message listener. Call this on unmount — essential for React. |
122
+ | `setMcpServers` | `(servers: string[]) => void` | Activates one or more [MCP](https://modelcontextprotocol.io) servers by name. Safe to call before the iframe is ready — servers are queued and flushed automatically on `SDK_READY`. |
123
+
124
+ ---
125
+
126
+ ## MCP (Model Context Protocol)
127
+
128
+ Pass one or more MCP server names to give Jarvis access to external tools and data sources during a session.
129
+
130
+ ### Discovering available servers
131
+
132
+ Call `GET {apiUrl}/api/mcp/servers` with the Jarvis token as a Bearer header to retrieve the names of all servers available to the authenticated user. The response is an object keyed by server name:
133
+
134
+ ```ts
135
+ const jarvis = new JarvisEmbed({
136
+ provider: 'google',
137
+ token: googleIdToken,
138
+ containerId: 'chat-container',
139
+ onReady: async (jarvisToken) => {
140
+ const res = await fetch(`https://jarvis.ascendingdc.com/api/mcp/servers`, {
141
+ headers: { Authorization: `Bearer ${jarvisToken}` },
142
+ });
143
+ const servers = await res.json(); // { "posthog": {...}, "github": {...}, ... }
144
+ const names = Object.keys(servers);
145
+
146
+ // Activate all of them, or let the user pick from `names`
147
+ jarvis.setMcpServers(names);
148
+ },
149
+ });
150
+ ```
151
+
152
+ ### Activating servers
153
+
154
+ The safest place to call `setMcpServers` is inside `onReady`, which fires once the iframe has authenticated and is listening:
155
+
156
+ ```ts
157
+ const jarvis = new JarvisEmbed({
158
+ provider: 'google',
159
+ token: googleIdToken,
160
+ containerId: 'chat-container',
161
+ onReady: () => {
162
+ jarvis.setMcpServers(['posthog', 'aws-knowledge']);
163
+ },
164
+ });
165
+ ```
166
+
167
+ You can also call it at any time after instantiation — if the iframe isn't ready yet the servers are queued internally and sent as soon as `SDK_READY` fires:
168
+
169
+ ```ts
170
+ const jarvis = new JarvisEmbed({
171
+ provider: 's_jwt',
172
+ token: myJwt,
173
+ containerId: 'chat-container',
174
+ });
175
+
176
+ // Called immediately — queued until SDK_READY
177
+ jarvis.setMcpServers(['github', 'jira']);
178
+ ```
179
+
180
+ To swap the active server set later (e.g. after a user action), call `setMcpServers` again with the new list:
181
+
182
+ ```ts
183
+ document.getElementById('enable-analytics')?.addEventListener('click', () => {
184
+ jarvis.setMcpServers(['posthog']);
185
+ });
186
+ ```
187
+
188
+ ---
189
+
190
+ ## React
191
+
192
+ `useJarvis` is not exported from the package — copy `examples/react/src/useJarvis.ts` into your project. It wraps `JarvisEmbed` in a `useEffect` and calls `destroy()` on unmount automatically:
193
+
194
+ ```ts
195
+ import { useEffect, useRef } from 'react';
196
+ import { JarvisEmbed } from '@ascending-inc/jarvis-embed';
197
+ import type { JarvisConfig } from '@ascending-inc/jarvis-embed';
198
+
199
+ export function useJarvis(config: JarvisConfig | null) {
200
+ const jarvisRef = useRef<JarvisEmbed | null>(null);
201
+
202
+ useEffect(() => {
203
+ if (!config) return;
204
+ jarvisRef.current = new JarvisEmbed(config);
205
+ return () => {
206
+ jarvisRef.current?.destroy();
207
+ jarvisRef.current = null;
208
+ };
209
+ }, [config]);
210
+
211
+ return jarvisRef;
212
+ }
213
+ ```
214
+
215
+ Pass `null` to defer initialization until the user is authenticated. The hook calls `destroy()` automatically on unmount, so there are no memory leaks or stale event listeners.
216
+
217
+ ### Using the `container` prop in React
218
+
219
+ When mounting into a React-managed DOM node, use a **callback ref** so initialization only happens once the element actually exists. Wrap config in `useMemo` with the container as a dependency — this ensures the SDK sees a real `HTMLElement`, not `null`:
220
+
221
+ ```tsx
222
+ import { useCallback, useMemo, useState } from 'react';
223
+ import { useJarvis } from './useJarvis';
224
+
225
+ function ChatWidget({ googleToken }: { googleToken: string }) {
226
+ const [container, setContainer] = useState<HTMLDivElement | null>(null);
227
+
228
+ const config = useMemo(() => {
229
+ if (!container || !googleToken) return null;
230
+ return {
231
+ provider: 'google' as const,
232
+ token: googleToken,
233
+ container,
234
+ width: '100%',
235
+ height: '100%',
236
+ onReady: (jarvisToken: string) => {
237
+ // fetch available servers and activate them
238
+ },
239
+ };
240
+ }, [container, googleToken]);
241
+
242
+ const jarvisRef = useJarvis(config);
243
+
244
+ return <div ref={setContainer} style={{ flex: 1 }} />;
245
+ }
246
+ ```
247
+
248
+ Using `containerId` instead avoids this entirely — the SDK does the `getElementById` lookup itself after the iframe loads — but the `container` prop approach above is required when the element is managed by React state.
249
+
250
+ ---
251
+
252
+ ## Examples
253
+
254
+ Both examples demonstrate Google OAuth, MCP tool selection, and embedding the chat widget. They share the same Express backend for token exchange.
255
+
256
+ ### 1. Clone and set up
257
+
258
+ ```bash
259
+ git clone https://github.com/ascending-llc/jarvis-embed.git
260
+ cd jarvis-embed
261
+ npm run setup
262
+ ```
263
+
264
+ `setup` installs root dependencies, builds the SDK, and installs dependencies for both examples.
265
+
266
+ ### 2. Configure environment variables
267
+
268
+ Each example has its own `.env`. Copy and fill in both:
269
+
270
+ ```bash
271
+ cp examples/vanilla/.env.example examples/vanilla/.env
272
+ cp examples/react/.env.example examples/react/.env
273
+ ```
274
+
275
+ | Variable | Description |
276
+ |---|---|
277
+ | `GOOGLE_CLIENT_ID` | OAuth client ID from [Google Cloud Console](https://console.cloud.google.com/apis/credentials) |
278
+ | `GOOGLE_CLIENT_SECRET` | OAuth client secret (never sent to the browser) |
279
+ | `REDIRECT_URI` | Must match what's registered in Google Cloud Console |
280
+ | `JARVIS_URL` | `https://jarvis-demo.ascendingdc.com` or `http://localhost:3080` for local Jarvis |
281
+ | `JARVIS_MODEL` | Optional spec override |
282
+ | `PORT` | Express port (default `5500`) |
283
+
284
+ ### 3. Run an example
285
+
286
+ **Vanilla JS** — floating chat widget, served at `http://localhost:5500`
287
+
288
+ ```bash
289
+ npm run example:vanilla
290
+ ```
291
+
292
+ **React** — `useJarvis` hook demo with proper cleanup, served at `http://localhost:5501`
293
+
294
+ ```bash
295
+ npm run example:react
296
+ ```
@@ -0,0 +1,50 @@
1
+ /** Auth fields vary by provider — TypeScript will enforce the right shape. */
2
+ type AuthPayload = {
3
+ provider: 'google';
4
+ token: string;
5
+ } | {
6
+ provider: 's_jwt';
7
+ token: string;
8
+ } | {
9
+ provider: 'a_jwt';
10
+ token: string;
11
+ } | {
12
+ provider: 'direct';
13
+ token: string;
14
+ } | {
15
+ provider: 'hmac';
16
+ userId: string;
17
+ timestamp: number;
18
+ signature: string;
19
+ };
20
+ type BaseConfig = {
21
+ containerId?: string;
22
+ container?: HTMLElement;
23
+ width?: string;
24
+ height?: string;
25
+ apiUrl?: string;
26
+ model?: string;
27
+ debug?: boolean;
28
+ onReady?: (jarvisToken: string) => void;
29
+ onError?: (error: Error) => void;
30
+ onMessage?: (data: unknown) => void;
31
+ };
32
+ type JarvisConfig = BaseConfig & AuthPayload;
33
+
34
+ declare class JarvisEmbed {
35
+ private readonly config;
36
+ private readonly apiUrl;
37
+ private iframe;
38
+ private messageHandler;
39
+ private sdkReady;
40
+ private pendingMcpServers;
41
+ private destroyed;
42
+ constructor(config: JarvisConfig);
43
+ setMcpServers(servers: string[]): void;
44
+ destroy(): void;
45
+ private start;
46
+ private resolveContainer;
47
+ private exchangeToken;
48
+ }
49
+
50
+ export { type JarvisConfig, JarvisEmbed };
@@ -0,0 +1,50 @@
1
+ /** Auth fields vary by provider — TypeScript will enforce the right shape. */
2
+ type AuthPayload = {
3
+ provider: 'google';
4
+ token: string;
5
+ } | {
6
+ provider: 's_jwt';
7
+ token: string;
8
+ } | {
9
+ provider: 'a_jwt';
10
+ token: string;
11
+ } | {
12
+ provider: 'direct';
13
+ token: string;
14
+ } | {
15
+ provider: 'hmac';
16
+ userId: string;
17
+ timestamp: number;
18
+ signature: string;
19
+ };
20
+ type BaseConfig = {
21
+ containerId?: string;
22
+ container?: HTMLElement;
23
+ width?: string;
24
+ height?: string;
25
+ apiUrl?: string;
26
+ model?: string;
27
+ debug?: boolean;
28
+ onReady?: (jarvisToken: string) => void;
29
+ onError?: (error: Error) => void;
30
+ onMessage?: (data: unknown) => void;
31
+ };
32
+ type JarvisConfig = BaseConfig & AuthPayload;
33
+
34
+ declare class JarvisEmbed {
35
+ private readonly config;
36
+ private readonly apiUrl;
37
+ private iframe;
38
+ private messageHandler;
39
+ private sdkReady;
40
+ private pendingMcpServers;
41
+ private destroyed;
42
+ constructor(config: JarvisConfig);
43
+ setMcpServers(servers: string[]): void;
44
+ destroy(): void;
45
+ private start;
46
+ private resolveContainer;
47
+ private exchangeToken;
48
+ }
49
+
50
+ export { type JarvisConfig, JarvisEmbed };
@@ -0,0 +1,124 @@
1
+ var JarvisSDK = (function (exports) {
2
+ 'use strict';
3
+
4
+ // src/index.ts
5
+ var JarvisEmbed = class {
6
+ constructor(config) {
7
+ this.iframe = null;
8
+ this.messageHandler = null;
9
+ this.sdkReady = false;
10
+ this.pendingMcpServers = null;
11
+ this.destroyed = false;
12
+ var _a, _b;
13
+ this.config = config;
14
+ this.apiUrl = (_b = (_a = config.apiUrl) == null ? void 0 : _a.replace(/\/$/, "")) != null ? _b : "https://jarvis.ascendingdc.com";
15
+ this.start();
16
+ }
17
+ setMcpServers(servers) {
18
+ var _a;
19
+ const isReady = this.sdkReady && ((_a = this.iframe) == null ? void 0 : _a.contentWindow) != null;
20
+ if (!isReady) {
21
+ this.pendingMcpServers = servers;
22
+ return;
23
+ }
24
+ const chatOrigin = new URL(this.apiUrl).origin;
25
+ this.iframe.contentWindow.postMessage({ type: "SDK_MCP", servers }, chatOrigin);
26
+ }
27
+ destroy() {
28
+ var _a;
29
+ this.destroyed = true;
30
+ if (this.messageHandler) {
31
+ window.removeEventListener("message", this.messageHandler);
32
+ this.messageHandler = null;
33
+ }
34
+ (_a = this.iframe) == null ? void 0 : _a.remove();
35
+ this.iframe = null;
36
+ this.sdkReady = false;
37
+ this.pendingMcpServers = null;
38
+ }
39
+ async start() {
40
+ var _a, _b, _c, _d;
41
+ let token;
42
+ try {
43
+ token = this.config.provider === "direct" ? this.config.token : await this.exchangeToken(this.config);
44
+ } catch (err) {
45
+ const error = err instanceof Error ? err : new Error(String(err));
46
+ (_b = (_a = this.config).onError) == null ? void 0 : _b.call(_a, error);
47
+ return;
48
+ }
49
+ if (this.destroyed) return;
50
+ const container = this.resolveContainer();
51
+ if (!container) return;
52
+ const chatOrigin = new URL(this.apiUrl).origin;
53
+ const iframe = document.createElement("iframe");
54
+ const chatUrl = new URL(`${this.apiUrl}/v1/chat`);
55
+ if (this.config.model) chatUrl.searchParams.set("spec", this.config.model);
56
+ iframe.src = chatUrl.toString();
57
+ iframe.title = "Jarvis AI Assistant";
58
+ iframe.style.cssText = `width:${(_c = this.config.width) != null ? _c : "100%"};height:${(_d = this.config.height) != null ? _d : "600px"};border:none;display:block;`;
59
+ iframe.addEventListener("load", () => {
60
+ var _a2;
61
+ (_a2 = iframe.contentWindow) == null ? void 0 : _a2.postMessage({ type: "SDK_AUTH", token }, chatOrigin);
62
+ });
63
+ this.messageHandler = (e) => {
64
+ var _a2, _b2, _c2, _d2, _e;
65
+ const isCorrectOrigin = e.origin === chatOrigin;
66
+ if (!isCorrectOrigin) return;
67
+ const isSdkReady = ((_a2 = e.data) == null ? void 0 : _a2.type) === "SDK_READY";
68
+ if (!isSdkReady) {
69
+ (_c2 = (_b2 = this.config).onMessage) == null ? void 0 : _c2.call(_b2, e.data);
70
+ return;
71
+ }
72
+ if (this.sdkReady) return;
73
+ this.sdkReady = true;
74
+ (_e = (_d2 = this.config).onReady) == null ? void 0 : _e.call(_d2, token);
75
+ const hasPendingServers = this.pendingMcpServers != null && iframe.contentWindow != null;
76
+ if (hasPendingServers) {
77
+ iframe.contentWindow.postMessage({ type: "SDK_MCP", servers: this.pendingMcpServers }, chatOrigin);
78
+ this.pendingMcpServers = null;
79
+ }
80
+ };
81
+ window.addEventListener("message", this.messageHandler);
82
+ container.appendChild(iframe);
83
+ this.iframe = iframe;
84
+ }
85
+ resolveContainer() {
86
+ var _a, _b;
87
+ if (this.config.container) return this.config.container;
88
+ if (this.config.containerId) {
89
+ const el = document.getElementById(this.config.containerId);
90
+ if (el) return el;
91
+ (_b = (_a = this.config).onError) == null ? void 0 : _b.call(_a, new Error(`Container element with id "${this.config.containerId}" not found`));
92
+ return null;
93
+ }
94
+ return document.body;
95
+ }
96
+ async exchangeToken(auth) {
97
+ if (this.config.debug) console.log("[JarvisEmbed] Exchanging token, provider:", auth.provider);
98
+ const body = auth.provider === "hmac" ? { provider: "hmac", userId: auth.userId, timestamp: auth.timestamp, signature: auth.signature } : { provider: auth.provider, token: auth.token };
99
+ const controller = new AbortController();
100
+ const timeoutId = setTimeout(() => controller.abort(), 15e3);
101
+ let res;
102
+ try {
103
+ res = await fetch(`${this.apiUrl}/api/auth/exchange`, {
104
+ method: "POST",
105
+ headers: { "Content-Type": "application/json" },
106
+ body: JSON.stringify(body),
107
+ signal: controller.signal
108
+ });
109
+ } finally {
110
+ clearTimeout(timeoutId);
111
+ }
112
+ if (!res.ok) throw new Error(`Token exchange failed (HTTP ${res.status})`);
113
+ const data = await res.json();
114
+ return data.token;
115
+ }
116
+ };
117
+
118
+ exports.JarvisEmbed = JarvisEmbed;
119
+
120
+ return exports;
121
+
122
+ })({});
123
+ //# sourceMappingURL=index.global.js.map
124
+ //# sourceMappingURL=index.global.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["_a","_b","_c","_d"],"mappings":"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","file":"index.global.js","sourcesContent":["import type { AuthPayload, JarvisConfig } from './types';\n\nexport type { JarvisConfig };\n\nexport class JarvisEmbed {\n private readonly config: JarvisConfig;\n private readonly apiUrl: string;\n\n private iframe: HTMLIFrameElement | null = null;\n private messageHandler: ((e: MessageEvent) => void) | null = null;\n private sdkReady = false;\n private pendingMcpServers: string[] | null = null;\n private destroyed = false;\n\n constructor(config: JarvisConfig) {\n this.config = config;\n this.apiUrl = config.apiUrl?.replace(/\\/$/, '') ?? 'https://jarvis.ascendingdc.com';\n this.start();\n }\n\n setMcpServers(servers: string[]): void {\n const isReady = this.sdkReady && this.iframe?.contentWindow != null;\n\n if (!isReady) {\n this.pendingMcpServers = servers;\n return;\n }\n\n const chatOrigin = new URL(this.apiUrl).origin;\n this.iframe!.contentWindow!.postMessage({ type: 'SDK_MCP', servers }, chatOrigin);\n }\n\n destroy(): void {\n this.destroyed = true;\n if (this.messageHandler) {\n window.removeEventListener('message', this.messageHandler);\n this.messageHandler = null;\n }\n this.iframe?.remove();\n this.iframe = null;\n this.sdkReady = false;\n this.pendingMcpServers = null;\n }\n\n private async start(): Promise<void> {\n let token: string;\n try {\n token = this.config.provider === 'direct'\n ? this.config.token\n : await this.exchangeToken(this.config);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.config.onError?.(error);\n return;\n }\n\n if (this.destroyed) return;\n\n const container = this.resolveContainer();\n if (!container) return;\n const chatOrigin = new URL(this.apiUrl).origin;\n\n const iframe = document.createElement('iframe');\n const chatUrl = new URL(`${this.apiUrl}/v1/chat`);\n if (this.config.model) chatUrl.searchParams.set('spec', this.config.model);\n iframe.src = chatUrl.toString();\n iframe.title = 'Jarvis AI Assistant';\n iframe.style.cssText = `width:${this.config.width ?? '100%'};height:${this.config.height ?? '600px'};border:none;display:block;`;\n\n iframe.addEventListener('load', () => {\n iframe.contentWindow?.postMessage({ type: 'SDK_AUTH', token }, chatOrigin);\n });\n\n this.messageHandler = (e: MessageEvent) => {\n const isCorrectOrigin = e.origin === chatOrigin;\n if (!isCorrectOrigin) return;\n\n const isSdkReady = e.data?.type === 'SDK_READY';\n if (!isSdkReady) {\n this.config.onMessage?.(e.data);\n return;\n }\n\n if (this.sdkReady) return;\n this.sdkReady = true;\n this.config.onReady?.(token);\n\n const hasPendingServers = this.pendingMcpServers != null && iframe.contentWindow != null;\n if (hasPendingServers) {\n iframe.contentWindow!.postMessage({ type: 'SDK_MCP', servers: this.pendingMcpServers }, chatOrigin);\n this.pendingMcpServers = null;\n }\n };\n window.addEventListener('message', this.messageHandler);\n\n container.appendChild(iframe);\n this.iframe = iframe;\n }\n\n private resolveContainer(): HTMLElement | null {\n if (this.config.container) return this.config.container;\n\n if (this.config.containerId) {\n const el = document.getElementById(this.config.containerId);\n if (el) return el;\n this.config.onError?.(new Error(`Container element with id \"${this.config.containerId}\" not found`));\n return null;\n }\n\n return document.body;\n }\n\n private async exchangeToken(auth: AuthPayload): Promise<string> {\n if (this.config.debug) console.log('[JarvisEmbed] Exchanging token, provider:', auth.provider);\n\n const body: AuthPayload = auth.provider === 'hmac'\n ? { provider: 'hmac', userId: auth.userId, timestamp: auth.timestamp, signature: auth.signature }\n : { provider: auth.provider, token: auth.token };\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 15_000);\n\n let res: Response;\n try {\n res = await fetch(`${this.apiUrl}/api/auth/exchange`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timeoutId);\n }\n\n if (!res.ok) throw new Error(`Token exchange failed (HTTP ${res.status})`);\n\n const data = await res.json() as { token: string };\n return data.token;\n }\n}\n"]}
package/dist/index.js ADDED
@@ -0,0 +1,119 @@
1
+ 'use strict';
2
+
3
+ // src/index.ts
4
+ var JarvisEmbed = class {
5
+ constructor(config) {
6
+ this.iframe = null;
7
+ this.messageHandler = null;
8
+ this.sdkReady = false;
9
+ this.pendingMcpServers = null;
10
+ this.destroyed = false;
11
+ var _a, _b;
12
+ this.config = config;
13
+ this.apiUrl = (_b = (_a = config.apiUrl) == null ? void 0 : _a.replace(/\/$/, "")) != null ? _b : "https://jarvis.ascendingdc.com";
14
+ this.start();
15
+ }
16
+ setMcpServers(servers) {
17
+ var _a;
18
+ const isReady = this.sdkReady && ((_a = this.iframe) == null ? void 0 : _a.contentWindow) != null;
19
+ if (!isReady) {
20
+ this.pendingMcpServers = servers;
21
+ return;
22
+ }
23
+ const chatOrigin = new URL(this.apiUrl).origin;
24
+ this.iframe.contentWindow.postMessage({ type: "SDK_MCP", servers }, chatOrigin);
25
+ }
26
+ destroy() {
27
+ var _a;
28
+ this.destroyed = true;
29
+ if (this.messageHandler) {
30
+ window.removeEventListener("message", this.messageHandler);
31
+ this.messageHandler = null;
32
+ }
33
+ (_a = this.iframe) == null ? void 0 : _a.remove();
34
+ this.iframe = null;
35
+ this.sdkReady = false;
36
+ this.pendingMcpServers = null;
37
+ }
38
+ async start() {
39
+ var _a, _b, _c, _d;
40
+ let token;
41
+ try {
42
+ token = this.config.provider === "direct" ? this.config.token : await this.exchangeToken(this.config);
43
+ } catch (err) {
44
+ const error = err instanceof Error ? err : new Error(String(err));
45
+ (_b = (_a = this.config).onError) == null ? void 0 : _b.call(_a, error);
46
+ return;
47
+ }
48
+ if (this.destroyed) return;
49
+ const container = this.resolveContainer();
50
+ if (!container) return;
51
+ const chatOrigin = new URL(this.apiUrl).origin;
52
+ const iframe = document.createElement("iframe");
53
+ const chatUrl = new URL(`${this.apiUrl}/v1/chat`);
54
+ if (this.config.model) chatUrl.searchParams.set("spec", this.config.model);
55
+ iframe.src = chatUrl.toString();
56
+ iframe.title = "Jarvis AI Assistant";
57
+ iframe.style.cssText = `width:${(_c = this.config.width) != null ? _c : "100%"};height:${(_d = this.config.height) != null ? _d : "600px"};border:none;display:block;`;
58
+ iframe.addEventListener("load", () => {
59
+ var _a2;
60
+ (_a2 = iframe.contentWindow) == null ? void 0 : _a2.postMessage({ type: "SDK_AUTH", token }, chatOrigin);
61
+ });
62
+ this.messageHandler = (e) => {
63
+ var _a2, _b2, _c2, _d2, _e;
64
+ const isCorrectOrigin = e.origin === chatOrigin;
65
+ if (!isCorrectOrigin) return;
66
+ const isSdkReady = ((_a2 = e.data) == null ? void 0 : _a2.type) === "SDK_READY";
67
+ if (!isSdkReady) {
68
+ (_c2 = (_b2 = this.config).onMessage) == null ? void 0 : _c2.call(_b2, e.data);
69
+ return;
70
+ }
71
+ if (this.sdkReady) return;
72
+ this.sdkReady = true;
73
+ (_e = (_d2 = this.config).onReady) == null ? void 0 : _e.call(_d2, token);
74
+ const hasPendingServers = this.pendingMcpServers != null && iframe.contentWindow != null;
75
+ if (hasPendingServers) {
76
+ iframe.contentWindow.postMessage({ type: "SDK_MCP", servers: this.pendingMcpServers }, chatOrigin);
77
+ this.pendingMcpServers = null;
78
+ }
79
+ };
80
+ window.addEventListener("message", this.messageHandler);
81
+ container.appendChild(iframe);
82
+ this.iframe = iframe;
83
+ }
84
+ resolveContainer() {
85
+ var _a, _b;
86
+ if (this.config.container) return this.config.container;
87
+ if (this.config.containerId) {
88
+ const el = document.getElementById(this.config.containerId);
89
+ if (el) return el;
90
+ (_b = (_a = this.config).onError) == null ? void 0 : _b.call(_a, new Error(`Container element with id "${this.config.containerId}" not found`));
91
+ return null;
92
+ }
93
+ return document.body;
94
+ }
95
+ async exchangeToken(auth) {
96
+ if (this.config.debug) console.log("[JarvisEmbed] Exchanging token, provider:", auth.provider);
97
+ const body = auth.provider === "hmac" ? { provider: "hmac", userId: auth.userId, timestamp: auth.timestamp, signature: auth.signature } : { provider: auth.provider, token: auth.token };
98
+ const controller = new AbortController();
99
+ const timeoutId = setTimeout(() => controller.abort(), 15e3);
100
+ let res;
101
+ try {
102
+ res = await fetch(`${this.apiUrl}/api/auth/exchange`, {
103
+ method: "POST",
104
+ headers: { "Content-Type": "application/json" },
105
+ body: JSON.stringify(body),
106
+ signal: controller.signal
107
+ });
108
+ } finally {
109
+ clearTimeout(timeoutId);
110
+ }
111
+ if (!res.ok) throw new Error(`Token exchange failed (HTTP ${res.status})`);
112
+ const data = await res.json();
113
+ return data.token;
114
+ }
115
+ };
116
+
117
+ exports.JarvisEmbed = JarvisEmbed;
118
+ //# sourceMappingURL=index.js.map
119
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["_a","_b","_c","_d"],"mappings":"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","file":"index.js","sourcesContent":["import type { AuthPayload, JarvisConfig } from './types';\n\nexport type { JarvisConfig };\n\nexport class JarvisEmbed {\n private readonly config: JarvisConfig;\n private readonly apiUrl: string;\n\n private iframe: HTMLIFrameElement | null = null;\n private messageHandler: ((e: MessageEvent) => void) | null = null;\n private sdkReady = false;\n private pendingMcpServers: string[] | null = null;\n private destroyed = false;\n\n constructor(config: JarvisConfig) {\n this.config = config;\n this.apiUrl = config.apiUrl?.replace(/\\/$/, '') ?? 'https://jarvis.ascendingdc.com';\n this.start();\n }\n\n setMcpServers(servers: string[]): void {\n const isReady = this.sdkReady && this.iframe?.contentWindow != null;\n\n if (!isReady) {\n this.pendingMcpServers = servers;\n return;\n }\n\n const chatOrigin = new URL(this.apiUrl).origin;\n this.iframe!.contentWindow!.postMessage({ type: 'SDK_MCP', servers }, chatOrigin);\n }\n\n destroy(): void {\n this.destroyed = true;\n if (this.messageHandler) {\n window.removeEventListener('message', this.messageHandler);\n this.messageHandler = null;\n }\n this.iframe?.remove();\n this.iframe = null;\n this.sdkReady = false;\n this.pendingMcpServers = null;\n }\n\n private async start(): Promise<void> {\n let token: string;\n try {\n token = this.config.provider === 'direct'\n ? this.config.token\n : await this.exchangeToken(this.config);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.config.onError?.(error);\n return;\n }\n\n if (this.destroyed) return;\n\n const container = this.resolveContainer();\n if (!container) return;\n const chatOrigin = new URL(this.apiUrl).origin;\n\n const iframe = document.createElement('iframe');\n const chatUrl = new URL(`${this.apiUrl}/v1/chat`);\n if (this.config.model) chatUrl.searchParams.set('spec', this.config.model);\n iframe.src = chatUrl.toString();\n iframe.title = 'Jarvis AI Assistant';\n iframe.style.cssText = `width:${this.config.width ?? '100%'};height:${this.config.height ?? '600px'};border:none;display:block;`;\n\n iframe.addEventListener('load', () => {\n iframe.contentWindow?.postMessage({ type: 'SDK_AUTH', token }, chatOrigin);\n });\n\n this.messageHandler = (e: MessageEvent) => {\n const isCorrectOrigin = e.origin === chatOrigin;\n if (!isCorrectOrigin) return;\n\n const isSdkReady = e.data?.type === 'SDK_READY';\n if (!isSdkReady) {\n this.config.onMessage?.(e.data);\n return;\n }\n\n if (this.sdkReady) return;\n this.sdkReady = true;\n this.config.onReady?.(token);\n\n const hasPendingServers = this.pendingMcpServers != null && iframe.contentWindow != null;\n if (hasPendingServers) {\n iframe.contentWindow!.postMessage({ type: 'SDK_MCP', servers: this.pendingMcpServers }, chatOrigin);\n this.pendingMcpServers = null;\n }\n };\n window.addEventListener('message', this.messageHandler);\n\n container.appendChild(iframe);\n this.iframe = iframe;\n }\n\n private resolveContainer(): HTMLElement | null {\n if (this.config.container) return this.config.container;\n\n if (this.config.containerId) {\n const el = document.getElementById(this.config.containerId);\n if (el) return el;\n this.config.onError?.(new Error(`Container element with id \"${this.config.containerId}\" not found`));\n return null;\n }\n\n return document.body;\n }\n\n private async exchangeToken(auth: AuthPayload): Promise<string> {\n if (this.config.debug) console.log('[JarvisEmbed] Exchanging token, provider:', auth.provider);\n\n const body: AuthPayload = auth.provider === 'hmac'\n ? { provider: 'hmac', userId: auth.userId, timestamp: auth.timestamp, signature: auth.signature }\n : { provider: auth.provider, token: auth.token };\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 15_000);\n\n let res: Response;\n try {\n res = await fetch(`${this.apiUrl}/api/auth/exchange`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timeoutId);\n }\n\n if (!res.ok) throw new Error(`Token exchange failed (HTTP ${res.status})`);\n\n const data = await res.json() as { token: string };\n return data.token;\n }\n}\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,117 @@
1
+ // src/index.ts
2
+ var JarvisEmbed = class {
3
+ constructor(config) {
4
+ this.iframe = null;
5
+ this.messageHandler = null;
6
+ this.sdkReady = false;
7
+ this.pendingMcpServers = null;
8
+ this.destroyed = false;
9
+ var _a, _b;
10
+ this.config = config;
11
+ this.apiUrl = (_b = (_a = config.apiUrl) == null ? void 0 : _a.replace(/\/$/, "")) != null ? _b : "https://jarvis.ascendingdc.com";
12
+ this.start();
13
+ }
14
+ setMcpServers(servers) {
15
+ var _a;
16
+ const isReady = this.sdkReady && ((_a = this.iframe) == null ? void 0 : _a.contentWindow) != null;
17
+ if (!isReady) {
18
+ this.pendingMcpServers = servers;
19
+ return;
20
+ }
21
+ const chatOrigin = new URL(this.apiUrl).origin;
22
+ this.iframe.contentWindow.postMessage({ type: "SDK_MCP", servers }, chatOrigin);
23
+ }
24
+ destroy() {
25
+ var _a;
26
+ this.destroyed = true;
27
+ if (this.messageHandler) {
28
+ window.removeEventListener("message", this.messageHandler);
29
+ this.messageHandler = null;
30
+ }
31
+ (_a = this.iframe) == null ? void 0 : _a.remove();
32
+ this.iframe = null;
33
+ this.sdkReady = false;
34
+ this.pendingMcpServers = null;
35
+ }
36
+ async start() {
37
+ var _a, _b, _c, _d;
38
+ let token;
39
+ try {
40
+ token = this.config.provider === "direct" ? this.config.token : await this.exchangeToken(this.config);
41
+ } catch (err) {
42
+ const error = err instanceof Error ? err : new Error(String(err));
43
+ (_b = (_a = this.config).onError) == null ? void 0 : _b.call(_a, error);
44
+ return;
45
+ }
46
+ if (this.destroyed) return;
47
+ const container = this.resolveContainer();
48
+ if (!container) return;
49
+ const chatOrigin = new URL(this.apiUrl).origin;
50
+ const iframe = document.createElement("iframe");
51
+ const chatUrl = new URL(`${this.apiUrl}/v1/chat`);
52
+ if (this.config.model) chatUrl.searchParams.set("spec", this.config.model);
53
+ iframe.src = chatUrl.toString();
54
+ iframe.title = "Jarvis AI Assistant";
55
+ iframe.style.cssText = `width:${(_c = this.config.width) != null ? _c : "100%"};height:${(_d = this.config.height) != null ? _d : "600px"};border:none;display:block;`;
56
+ iframe.addEventListener("load", () => {
57
+ var _a2;
58
+ (_a2 = iframe.contentWindow) == null ? void 0 : _a2.postMessage({ type: "SDK_AUTH", token }, chatOrigin);
59
+ });
60
+ this.messageHandler = (e) => {
61
+ var _a2, _b2, _c2, _d2, _e;
62
+ const isCorrectOrigin = e.origin === chatOrigin;
63
+ if (!isCorrectOrigin) return;
64
+ const isSdkReady = ((_a2 = e.data) == null ? void 0 : _a2.type) === "SDK_READY";
65
+ if (!isSdkReady) {
66
+ (_c2 = (_b2 = this.config).onMessage) == null ? void 0 : _c2.call(_b2, e.data);
67
+ return;
68
+ }
69
+ if (this.sdkReady) return;
70
+ this.sdkReady = true;
71
+ (_e = (_d2 = this.config).onReady) == null ? void 0 : _e.call(_d2, token);
72
+ const hasPendingServers = this.pendingMcpServers != null && iframe.contentWindow != null;
73
+ if (hasPendingServers) {
74
+ iframe.contentWindow.postMessage({ type: "SDK_MCP", servers: this.pendingMcpServers }, chatOrigin);
75
+ this.pendingMcpServers = null;
76
+ }
77
+ };
78
+ window.addEventListener("message", this.messageHandler);
79
+ container.appendChild(iframe);
80
+ this.iframe = iframe;
81
+ }
82
+ resolveContainer() {
83
+ var _a, _b;
84
+ if (this.config.container) return this.config.container;
85
+ if (this.config.containerId) {
86
+ const el = document.getElementById(this.config.containerId);
87
+ if (el) return el;
88
+ (_b = (_a = this.config).onError) == null ? void 0 : _b.call(_a, new Error(`Container element with id "${this.config.containerId}" not found`));
89
+ return null;
90
+ }
91
+ return document.body;
92
+ }
93
+ async exchangeToken(auth) {
94
+ if (this.config.debug) console.log("[JarvisEmbed] Exchanging token, provider:", auth.provider);
95
+ const body = auth.provider === "hmac" ? { provider: "hmac", userId: auth.userId, timestamp: auth.timestamp, signature: auth.signature } : { provider: auth.provider, token: auth.token };
96
+ const controller = new AbortController();
97
+ const timeoutId = setTimeout(() => controller.abort(), 15e3);
98
+ let res;
99
+ try {
100
+ res = await fetch(`${this.apiUrl}/api/auth/exchange`, {
101
+ method: "POST",
102
+ headers: { "Content-Type": "application/json" },
103
+ body: JSON.stringify(body),
104
+ signal: controller.signal
105
+ });
106
+ } finally {
107
+ clearTimeout(timeoutId);
108
+ }
109
+ if (!res.ok) throw new Error(`Token exchange failed (HTTP ${res.status})`);
110
+ const data = await res.json();
111
+ return data.token;
112
+ }
113
+ };
114
+
115
+ export { JarvisEmbed };
116
+ //# sourceMappingURL=index.mjs.map
117
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["_a","_b","_c","_d"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { AuthPayload, JarvisConfig } from './types';\n\nexport type { JarvisConfig };\n\nexport class JarvisEmbed {\n private readonly config: JarvisConfig;\n private readonly apiUrl: string;\n\n private iframe: HTMLIFrameElement | null = null;\n private messageHandler: ((e: MessageEvent) => void) | null = null;\n private sdkReady = false;\n private pendingMcpServers: string[] | null = null;\n private destroyed = false;\n\n constructor(config: JarvisConfig) {\n this.config = config;\n this.apiUrl = config.apiUrl?.replace(/\\/$/, '') ?? 'https://jarvis.ascendingdc.com';\n this.start();\n }\n\n setMcpServers(servers: string[]): void {\n const isReady = this.sdkReady && this.iframe?.contentWindow != null;\n\n if (!isReady) {\n this.pendingMcpServers = servers;\n return;\n }\n\n const chatOrigin = new URL(this.apiUrl).origin;\n this.iframe!.contentWindow!.postMessage({ type: 'SDK_MCP', servers }, chatOrigin);\n }\n\n destroy(): void {\n this.destroyed = true;\n if (this.messageHandler) {\n window.removeEventListener('message', this.messageHandler);\n this.messageHandler = null;\n }\n this.iframe?.remove();\n this.iframe = null;\n this.sdkReady = false;\n this.pendingMcpServers = null;\n }\n\n private async start(): Promise<void> {\n let token: string;\n try {\n token = this.config.provider === 'direct'\n ? this.config.token\n : await this.exchangeToken(this.config);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.config.onError?.(error);\n return;\n }\n\n if (this.destroyed) return;\n\n const container = this.resolveContainer();\n if (!container) return;\n const chatOrigin = new URL(this.apiUrl).origin;\n\n const iframe = document.createElement('iframe');\n const chatUrl = new URL(`${this.apiUrl}/v1/chat`);\n if (this.config.model) chatUrl.searchParams.set('spec', this.config.model);\n iframe.src = chatUrl.toString();\n iframe.title = 'Jarvis AI Assistant';\n iframe.style.cssText = `width:${this.config.width ?? '100%'};height:${this.config.height ?? '600px'};border:none;display:block;`;\n\n iframe.addEventListener('load', () => {\n iframe.contentWindow?.postMessage({ type: 'SDK_AUTH', token }, chatOrigin);\n });\n\n this.messageHandler = (e: MessageEvent) => {\n const isCorrectOrigin = e.origin === chatOrigin;\n if (!isCorrectOrigin) return;\n\n const isSdkReady = e.data?.type === 'SDK_READY';\n if (!isSdkReady) {\n this.config.onMessage?.(e.data);\n return;\n }\n\n if (this.sdkReady) return;\n this.sdkReady = true;\n this.config.onReady?.(token);\n\n const hasPendingServers = this.pendingMcpServers != null && iframe.contentWindow != null;\n if (hasPendingServers) {\n iframe.contentWindow!.postMessage({ type: 'SDK_MCP', servers: this.pendingMcpServers }, chatOrigin);\n this.pendingMcpServers = null;\n }\n };\n window.addEventListener('message', this.messageHandler);\n\n container.appendChild(iframe);\n this.iframe = iframe;\n }\n\n private resolveContainer(): HTMLElement | null {\n if (this.config.container) return this.config.container;\n\n if (this.config.containerId) {\n const el = document.getElementById(this.config.containerId);\n if (el) return el;\n this.config.onError?.(new Error(`Container element with id \"${this.config.containerId}\" not found`));\n return null;\n }\n\n return document.body;\n }\n\n private async exchangeToken(auth: AuthPayload): Promise<string> {\n if (this.config.debug) console.log('[JarvisEmbed] Exchanging token, provider:', auth.provider);\n\n const body: AuthPayload = auth.provider === 'hmac'\n ? { provider: 'hmac', userId: auth.userId, timestamp: auth.timestamp, signature: auth.signature }\n : { provider: auth.provider, token: auth.token };\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 15_000);\n\n let res: Response;\n try {\n res = await fetch(`${this.apiUrl}/api/auth/exchange`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timeoutId);\n }\n\n if (!res.ok) throw new Error(`Token exchange failed (HTTP ${res.status})`);\n\n const data = await res.json() as { token: string };\n return data.token;\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@ascending-inc/jarvis-embed",
3
+ "version": "0.1.2",
4
+ "description": "Official SDK for embedding the Jarvis AI Assistant in any web application",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.mjs",
18
+ "require": "./dist/index.js"
19
+ }
20
+ },
21
+ "scripts": {
22
+ "build": "tsup",
23
+ "dev": "tsup --watch",
24
+ "type-check": "tsc --noEmit",
25
+ "docs": "typedoc",
26
+ "setup": "npm install && npm run build && npm install --prefix examples/vanilla && npm install --prefix examples/react",
27
+ "example:vanilla": "npm start --prefix examples/vanilla",
28
+ "example:react": "npm start --prefix examples/react"
29
+ },
30
+ "devDependencies": {
31
+ "tsup": "^8.3.0",
32
+ "typescript": "^5.7.0",
33
+ "typedoc": "^0.27.0"
34
+ }
35
+ }