@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 +296 -0
- package/dist/index.d.mts +50 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.global.js +124 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +119 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +117 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +35 -0
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
|
+
```
|
package/dist/index.d.mts
ADDED
|
@@ -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 };
|
package/dist/index.d.ts
ADDED
|
@@ -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":";;;;AAIO,MAAM,cAAN,MAAkB;EAAA,EAUvB,YAAY,MAAA,EAAsB;EANlC,IAAA,IAAA,CAAQ,MAAA,GAAmC,IAAA;EAC3C,IAAA,IAAA,CAAQ,cAAA,GAAqD,IAAA;EAC7D,IAAA,IAAA,CAAQ,QAAA,GAAW,KAAA;EACnB,IAAA,IAAA,CAAQ,iBAAA,GAAqC,IAAA;EAC7C,IAAA,IAAA,CAAQ,SAAA,GAAY,KAAA;EAZtB,IAAA,IAAA,EAAA,EAAA,EAAA;EAeI,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;EACd,IAAA,IAAA,CAAK,UAAS,EAAA,GAAA,CAAA,EAAA,GAAA,MAAA,CAAO,MAAA,KAAP,mBAAe,OAAA,CAAQ,KAAA,EAAO,QAA9B,IAAA,GAAA,EAAA,GAAqC,gCAAA;EACnD,IAAA,IAAA,CAAK,KAAA,EAAM;EAAA,EACb;EAAA,EAEA,cAAc,OAAA,EAAyB;EApBzC,IAAA,IAAA,EAAA;EAqBI,IAAA,MAAM,UAAU,IAAA,CAAK,QAAA,IAAA,CAAA,CAAY,EAAA,GAAA,IAAA,CAAK,MAAA,KAAL,mBAAa,aAAA,KAAiB,IAAA;EAE/D,IAAA,IAAI,CAAC,OAAA,EAAS;EACZ,MAAA,IAAA,CAAK,iBAAA,GAAoB,OAAA;EACzB,MAAA;EAAA,IACF;EAEA,IAAA,MAAM,UAAA,GAAa,IAAI,GAAA,CAAI,IAAA,CAAK,MAAM,CAAA,CAAE,MAAA;EACxC,IAAA,IAAA,CAAK,MAAA,CAAQ,cAAe,WAAA,CAAY,EAAE,MAAM,SAAA,EAAW,OAAA,IAAW,UAAU,CAAA;EAAA,EAClF;EAAA,EAEA,OAAA,GAAgB;EAhClB,IAAA,IAAA,EAAA;EAiCI,IAAA,IAAA,CAAK,SAAA,GAAY,IAAA;EACjB,IAAA,IAAI,KAAK,cAAA,EAAgB;EACvB,MAAA,MAAA,CAAO,mBAAA,CAAoB,SAAA,EAAW,IAAA,CAAK,cAAc,CAAA;EACzD,MAAA,IAAA,CAAK,cAAA,GAAiB,IAAA;EAAA,IACxB;EACA,IAAA,CAAA,EAAA,GAAA,IAAA,CAAK,WAAL,IAAA,GAAA,MAAA,GAAA,EAAA,CAAa,MAAA,EAAA;EACb,IAAA,IAAA,CAAK,MAAA,GAAS,IAAA;EACd,IAAA,IAAA,CAAK,QAAA,GAAW,KAAA;EAChB,IAAA,IAAA,CAAK,iBAAA,GAAoB,IAAA;EAAA,EAC3B;EAAA,EAEA,MAAc,KAAA,GAAuB;EA5CvC,IAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;EA6CI,IAAA,IAAI,KAAA;EACJ,IAAA,IAAI;EACF,MAAA,KAAA,GAAQ,IAAA,CAAK,MAAA,CAAO,QAAA,KAAa,QAAA,GAC7B,IAAA,CAAK,MAAA,CAAO,KAAA,GACZ,MAAM,IAAA,CAAK,aAAA,CAAc,IAAA,CAAK,MAAM,CAAA;EAAA,IAC1C,SAAS,GAAA,EAAK;EACZ,MAAA,MAAM,KAAA,GAAQ,eAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,KAAA,CAAM,MAAA,CAAO,GAAG,CAAC,CAAA;EAChE,MAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,IAAA,CAAK,MAAA,EAAO,YAAZ,IAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,EAAA,EAAsB,KAAA,CAAA;EACtB,MAAA;EAAA,IACF;EAEA,IAAA,IAAI,KAAK,SAAA,EAAW;EAEpB,IAAA,MAAM,SAAA,GAAY,KAAK,gBAAA,EAAiB;EACxC,IAAA,IAAI,CAAC,SAAA,EAAW;EAChB,IAAA,MAAM,UAAA,GAAa,IAAI,GAAA,CAAI,IAAA,CAAK,MAAM,CAAA,CAAE,MAAA;EAExC,IAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;EAC9C,IAAA,MAAM,UAAU,IAAI,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,QAAA,CAAU,CAAA;EAChD,IAAA,IAAI,IAAA,CAAK,OAAO,KAAA,EAAO,OAAA,CAAQ,aAAa,GAAA,CAAI,MAAA,EAAQ,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA;EACzE,IAAA,MAAA,CAAO,GAAA,GAAM,QAAQ,QAAA,EAAS;EAC9B,IAAA,MAAA,CAAO,KAAA,GAAQ,qBAAA;EACf,IAAA,MAAA,CAAO,KAAA,CAAM,OAAA,GAAU,CAAA,MAAA,EAAA,CAAS,EAAA,GAAA,IAAA,CAAK,MAAA,CAAO,KAAA,KAAZ,IAAA,GAAA,EAAA,GAAqB,MAAM,CAAA,QAAA,EAAA,CAAW,EAAA,GAAA,IAAA,CAAK,MAAA,CAAO,MAAA,KAAZ,YAAsB,OAAO,CAAA,2BAAA,CAAA;EAEnG,IAAA,MAAA,CAAO,gBAAA,CAAiB,QAAQ,MAAM;EArE1C,MAAA,IAAAA,GAAAA;EAsEM,MAAA,CAAAA,GAAAA,GAAA,MAAA,CAAO,aAAA,KAAP,IAAA,GAAA,MAAA,GAAAA,GAAAA,CAAsB,YAAY,EAAE,IAAA,EAAM,UAAA,EAAY,KAAA,EAAM,EAAG,UAAA,CAAA;EAAA,IACjE,CAAC,CAAA;EAED,IAAA,IAAA,CAAK,cAAA,GAAiB,CAAC,CAAA,KAAoB;EAzE/C,MAAA,IAAAA,GAAAA,EAAAC,GAAAA,EAAAC,GAAAA,EAAAC,GAAAA,EAAA,EAAA;EA0EM,MAAA,MAAM,eAAA,GAAkB,EAAE,MAAA,KAAW,UAAA;EACrC,MAAA,IAAI,CAAC,eAAA,EAAiB;EAEtB,MAAA,MAAM,eAAaH,GAAAA,GAAA,CAAA,CAAE,IAAA,KAAF,IAAA,GAAA,MAAA,GAAAA,IAAQ,IAAA,MAAS,WAAA;EACpC,MAAA,IAAI,CAAC,UAAA,EAAY;EACf,QAAA,CAAAE,GAAAA,GAAAA,CAAAD,MAAA,IAAA,CAAK,MAAA,EAAO,cAAZ,IAAA,GAAA,MAAA,GAAAC,GAAAA,CAAA,IAAA,CAAAD,GAAAA,EAAwB,CAAA,CAAE,IAAA,CAAA;EAC1B,QAAA;EAAA,MACF;EAEA,MAAA,IAAI,KAAK,QAAA,EAAU;EACnB,MAAA,IAAA,CAAK,QAAA,GAAW,IAAA;EAChB,MAAA,CAAA,EAAA,GAAA,CAAAE,GAAAA,GAAA,IAAA,CAAK,MAAA,EAAO,OAAA,KAAZ,wBAAAA,GAAAA,EAAsB,KAAA,CAAA;EAEtB,MAAA,MAAM,iBAAA,GAAoB,IAAA,CAAK,iBAAA,IAAqB,IAAA,IAAQ,OAAO,aAAA,IAAiB,IAAA;EACpF,MAAA,IAAI,iBAAA,EAAmB;EACrB,QAAA,MAAA,CAAO,aAAA,CAAe,YAAY,EAAE,IAAA,EAAM,WAAW,OAAA,EAAS,IAAA,CAAK,iBAAA,EAAkB,EAAG,UAAU,CAAA;EAClG,QAAA,IAAA,CAAK,iBAAA,GAAoB,IAAA;EAAA,MAC3B;EAAA,IACF,CAAA;EACA,IAAA,MAAA,CAAO,gBAAA,CAAiB,SAAA,EAAW,IAAA,CAAK,cAAc,CAAA;EAEtD,IAAA,SAAA,CAAU,YAAY,MAAM,CAAA;EAC5B,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;EAAA,EAChB;EAAA,EAEQ,gBAAA,GAAuC;EAnGjD,IAAA,IAAA,EAAA,EAAA,EAAA;EAoGI,IAAA,IAAI,IAAA,CAAK,MAAA,CAAO,SAAA,EAAW,OAAO,KAAK,MAAA,CAAO,SAAA;EAE9C,IAAA,IAAI,IAAA,CAAK,OAAO,WAAA,EAAa;EAC3B,MAAA,MAAM,EAAA,GAAK,QAAA,CAAS,cAAA,CAAe,IAAA,CAAK,OAAO,WAAW,CAAA;EAC1D,MAAA,IAAI,IAAI,OAAO,EAAA;EACf,MAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,IAAA,CAAK,MAAA,EAAO,YAAZ,IAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,EAAA,EAAsB,IAAI,MAAM,CAAA,2BAAA,EAA8B,IAAA,CAAK,MAAA,CAAO,WAAW,CAAA,WAAA,CAAa,CAAA,CAAA;EAClG,MAAA,OAAO,IAAA;EAAA,IACT;EAEA,IAAA,OAAO,QAAA,CAAS,IAAA;EAAA,EAClB;EAAA,EAEA,MAAc,cAAc,IAAA,EAAoC;EAC9D,IAAA,IAAI,KAAK,MAAA,CAAO,KAAA,UAAe,GAAA,CAAI,2CAAA,EAA6C,KAAK,QAAQ,CAAA;EAE7F,IAAA,MAAM,IAAA,GAAoB,KAAK,QAAA,KAAa,MAAA,GACxC,EAAE,QAAA,EAAU,MAAA,EAAQ,MAAA,EAAQ,IAAA,CAAK,MAAA,EAAQ,SAAA,EAAW,KAAK,SAAA,EAAW,SAAA,EAAW,IAAA,CAAK,SAAA,EAAU,GAC9F,EAAE,UAAU,IAAA,CAAK,QAAA,EAAU,KAAA,EAAO,IAAA,CAAK,KAAA,EAAM;EAEjD,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;EACvC,IAAA,MAAM,YAAY,UAAA,CAAW,MAAM,UAAA,CAAW,KAAA,IAAS,IAAM,CAAA;EAE7D,IAAA,IAAI,GAAA;EACJ,IAAA,IAAI;EACF,MAAA,GAAA,GAAM,MAAM,KAAA,CAAM,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,kBAAA,CAAA,EAAsB;EAAA,QACpD,MAAA,EAAQ,MAAA;EAAA,QACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;EAAA,QAC9C,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA;EAAA,QACzB,QAAQ,UAAA,CAAW;EAAA,OACpB,CAAA;EAAA,IACH,CAAA,SAAE;EACA,MAAA,YAAA,CAAa,SAAS,CAAA;EAAA,IACxB;EAEA,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI,MAAM,IAAI,KAAA,CAAM,CAAA,4BAAA,EAA+B,GAAA,CAAI,MAAM,CAAA,CAAA,CAAG,CAAA;EAEzE,IAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;EAC5B,IAAA,OAAO,IAAA,CAAK,KAAA;EAAA,EACd;EACF","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
|
+
}
|