@assistant-ui/react 0.7.81 → 0.7.83
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +81 -14
- package/dist/cloud/AssistantCloudAPI.d.ts +4 -1
- package/dist/cloud/AssistantCloudAPI.d.ts.map +1 -1
- package/dist/cloud/AssistantCloudAPI.js +8 -1
- package/dist/cloud/AssistantCloudAPI.js.map +1 -1
- package/dist/cloud/AssistantCloudAPI.mjs +10 -2
- package/dist/cloud/AssistantCloudAPI.mjs.map +1 -1
- package/dist/cloud/AssistantCloudAuthStrategy.d.ts +10 -3
- package/dist/cloud/AssistantCloudAuthStrategy.d.ts.map +1 -1
- package/dist/cloud/AssistantCloudAuthStrategy.js +91 -31
- package/dist/cloud/AssistantCloudAuthStrategy.js.map +1 -1
- package/dist/cloud/AssistantCloudAuthStrategy.mjs +90 -31
- package/dist/cloud/AssistantCloudAuthStrategy.mjs.map +1 -1
- package/dist/runtimes/remote-thread-list/adapter/cloud.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/adapter/cloud.js +17 -10
- package/dist/runtimes/remote-thread-list/adapter/cloud.js.map +1 -1
- package/dist/runtimes/remote-thread-list/adapter/cloud.mjs +17 -10
- package/dist/runtimes/remote-thread-list/adapter/cloud.mjs.map +1 -1
- package/package.json +3 -3
- package/src/cloud/AssistantCloudAPI.tsx +14 -2
- package/src/cloud/AssistantCloudAuthStrategy.tsx +128 -49
- package/src/runtimes/remote-thread-list/adapter/cloud.tsx +25 -15
package/README.md
CHANGED
@@ -10,27 +10,94 @@
|
|
10
10
|
<a href="https://cal.com/simon-farshid/assistant-ui">Contact Sales</a>
|
11
11
|
</p>
|
12
12
|
|
13
|
-
|
13
|
+
[](https://app.workweave.ai/reports/repository/org_GhSIrtWo37b5B3Mv0At3wQ1Q/722184017)
|
14
|
+

|
14
15
|
|
15
|
-
|
16
|
+
- [⭐️ Star us on GitHub](https://github.com/assistant-ui/assistant-ui)
|
16
17
|
|
17
|
-
|
18
|
+
## The UX of ChatGPT in your React app 💬🚀
|
19
|
+
|
20
|
+
**assistant-ui** is an open source Typescript/React library for AI chat.
|
21
|
+
|
22
|
+
The library handles essential chat features such as auto-scrolling, accessibility, and real-time updates, while providing easy integration with LangGraph, AI SDK and custom backends.
|
23
|
+
|
24
|
+
The API of assistant-ui is inspired by libraries like shadcn/ui and cmdk. Instead of a single monolithic chat component, developers get primitive components that can be fully customized.
|
25
|
+
|
26
|
+
We have wide model provider support (OpenAI, Anthropic, Mistral, Perplexity, AWS Bedrock, Azure, Google Gemini, Hugging Face, Fireworks, Cohere, Replicate, Ollama) out of the box and the ability to integrate custom APIs.
|
18
27
|
|
19
|
-
##
|
28
|
+
## Getting Started
|
29
|
+
|
30
|
+
You can get started by running `npx assistant-ui create` (new project) or `npx assistant-ui init` (existing project) in your terminal.
|
20
31
|
|
21
32
|
[](https://youtu.be/k6Dc8URmLjk)
|
22
33
|
|
23
|
-
|
34
|
+
## Features
|
35
|
+
|
36
|
+
- shadcn/ui
|
37
|
+
- Radix UI-inspired primitives for AI Chat
|
38
|
+
- Beautiful shadcn/ui theme to get you started
|
39
|
+
- Chat UI
|
40
|
+
- Streaming, Auto-scrolling, Markdown, Code Highlighting, File Attachments, and more
|
41
|
+
- Keyboard shortcuts and accessibility features
|
42
|
+
- Generative UI
|
43
|
+
- Map LLM tool calls and JSONs to custom UI components
|
44
|
+
- Frontend tool calls
|
45
|
+
- Let LLMs take action in your frontend application
|
46
|
+
- Human tool calls
|
47
|
+
- Human approvals and input collection
|
48
|
+
- Chat history and analytics
|
49
|
+
- Sign up for assistant-cloud and configure by simply setting an environment variable
|
50
|
+
|
51
|
+
## Choose your backend
|
52
|
+
|
53
|
+
- AI SDK
|
54
|
+
- First class integration into AI SDK by Vercel. Connect to any LLM provider supported by AI SDK.
|
55
|
+
- LangGraph
|
56
|
+
- First class integration into LangGraph and LangGraph Cloud. Connect to any LLM provider supported by LangChain.
|
57
|
+
- Custom
|
58
|
+
- Use assistant-ui as the visualization layer on top your own backend/streaming protocols.
|
59
|
+
|
60
|
+
## Customization
|
61
|
+
|
62
|
+
The API of assistant-ui is inspired by libraries like Radix UI and cmdk. Instead of a single monolithic chat component, we give you composable primitives and a great starter configuration. You have full control over the look and feel of every pixel while leaving auto-scrolling, LLM streaming and accessibility to us.
|
63
|
+
|
64
|
+

|
65
|
+
|
66
|
+
Sample customization to make a perplexity lookalike:
|
67
|
+
|
68
|
+

|
69
|
+
|
70
|
+
## **Demo Video**
|
71
|
+
|
72
|
+
[](https://youtu.be/ZW56UHlqTCQ)
|
73
|
+
|
74
|
+
[](https://youtu.be/9eLKs9AM4tU)
|
75
|
+
|
76
|
+
## Traction
|
77
|
+
|
78
|
+
Hundreds of projects use assistant-ui to build in-app AI assistants, including companies like LangChain, AthenaIntelligence, Browser Use, and more.
|
79
|
+
|
80
|
+
With >50k+ monthly downloads, assistant-ui is the most popular UI library for AI chat.
|
81
|
+
|
82
|
+
<img src="https://raw.githubusercontent.com/assistant-ui/assistant-ui/main/.github/assets/growth.png" alt="Growth" style="max-width: 400px;">
|
24
83
|
|
25
|
-
|
26
|
-
npx assistant-ui@latest create # new project
|
27
|
-
npx assistant-ui@latest init # existing project
|
28
|
-
```
|
84
|
+
## 2025 Q1 Roadmap
|
29
85
|
|
30
|
-
|
86
|
+
- [x] Assistant Cloud
|
87
|
+
- [x] Chat Persistence
|
88
|
+
- [x] React 19, Tailwind v4, NextJS 19 support
|
89
|
+
- [x] Improved Markdown rendering performance
|
90
|
+
- [x] LangGraph `interrupt()` support
|
91
|
+
- [x] Open in v0 support
|
92
|
+
- [ ] Improved documentation (work in progress)
|
93
|
+
- [ ] OpenAI Realtime Voice (work in progress)
|
94
|
+
- [ ] Resume interrupted LLM calls (work in progress)
|
95
|
+
- [ ] Native PDF attachment support
|
96
|
+
- [ ] Follow-up suggestions
|
31
97
|
|
32
|
-
|
98
|
+
## Next Steps
|
33
99
|
|
34
|
-
|
35
|
-
|
36
|
-
|
100
|
+
- [Check out example demos](https://www.assistant-ui.com/)
|
101
|
+
- [Read our docs](https://www.assistant-ui.com/docs/)
|
102
|
+
- [Join our Discord](https://discord.com/invite/S9dwgCNEFs)
|
103
|
+
- [Book a sales call](https://cal.com/simon-farshid/assistant-ui)
|
@@ -1,10 +1,13 @@
|
|
1
1
|
export type AssistantCloudConfig = {
|
2
2
|
baseUrl: string;
|
3
|
-
authToken: (
|
3
|
+
authToken: () => Promise<string | null>;
|
4
4
|
} | {
|
5
5
|
apiKey: string;
|
6
6
|
userId: string;
|
7
7
|
workspaceId: string;
|
8
|
+
} | {
|
9
|
+
baseUrl: string;
|
10
|
+
anonymous: true;
|
8
11
|
};
|
9
12
|
type MakeRequestOptions = {
|
10
13
|
method?: "POST" | "PUT" | "DELETE" | undefined;
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"AssistantCloudAPI.d.ts","sourceRoot":"","sources":["../../src/cloud/AssistantCloudAPI.tsx"],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"AssistantCloudAPI.d.ts","sourceRoot":"","sources":["../../src/cloud/AssistantCloudAPI.tsx"],"names":[],"mappings":"AAOA,MAAM,MAAM,oBAAoB,GAC5B;IACE,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACzC,GACD;IACE,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB,GACD;IACE,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB,CAAC;AASN,KAAK,kBAAkB,GAAG;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,SAAS,CAAC;IAC9D,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B,CAAC;AAEF,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,QAAQ,CAAC;gBAEL,MAAM,EAAE,oBAAoB;IAqB3B,cAAc;IAId,cAAc,CACzB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,kBAAuB;IAiDrB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB;CAI5E"}
|
@@ -37,13 +37,20 @@ var AssistantCloudAPI = class {
|
|
37
37
|
if ("authToken" in config) {
|
38
38
|
this._baseUrl = config.baseUrl;
|
39
39
|
this._auth = new import_AssistantCloudAuthStrategy.AssistantCloudJWTAuthStrategy(config.authToken);
|
40
|
-
} else {
|
40
|
+
} else if ("apiKey" in config) {
|
41
41
|
this._baseUrl = "https://backend.assistant-api.com";
|
42
42
|
this._auth = new import_AssistantCloudAuthStrategy.AssistantCloudAPIKeyAuthStrategy(
|
43
43
|
config.apiKey,
|
44
44
|
config.userId,
|
45
45
|
config.workspaceId
|
46
46
|
);
|
47
|
+
} else if ("anonymous" in config) {
|
48
|
+
this._baseUrl = config.baseUrl;
|
49
|
+
this._auth = new import_AssistantCloudAuthStrategy.AssistantCloudAnonymousAuthStrategy(config.baseUrl);
|
50
|
+
} else {
|
51
|
+
throw new Error(
|
52
|
+
"Invalid configuration: Must provide authToken, apiKey, or anonymous configuration"
|
53
|
+
);
|
47
54
|
}
|
48
55
|
}
|
49
56
|
async initializeAuth() {
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../../src/cloud/AssistantCloudAPI.tsx"],"sourcesContent":["import {\n AssistantCloudAuthStrategy,\n AssistantCloudJWTAuthStrategy,\n AssistantCloudAPIKeyAuthStrategy,\n} from \"./AssistantCloudAuthStrategy\";\n\nexport type AssistantCloudConfig =\n | {\n baseUrl: string;\n authToken: (
|
1
|
+
{"version":3,"sources":["../../src/cloud/AssistantCloudAPI.tsx"],"sourcesContent":["import {\n AssistantCloudAuthStrategy,\n AssistantCloudJWTAuthStrategy,\n AssistantCloudAPIKeyAuthStrategy,\n AssistantCloudAnonymousAuthStrategy,\n} from \"./AssistantCloudAuthStrategy\";\n\nexport type AssistantCloudConfig =\n | {\n baseUrl: string;\n authToken: () => Promise<string | null>;\n }\n | {\n apiKey: string;\n userId: string;\n workspaceId: string;\n }\n | {\n baseUrl: string;\n anonymous: true;\n };\n\nclass CloudAPIError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"APIError\";\n }\n}\n\ntype MakeRequestOptions = {\n method?: \"POST\" | \"PUT\" | \"DELETE\" | undefined;\n headers?: Record<string, string> | undefined;\n query?: Record<string, string | number | boolean> | undefined;\n body?: object | undefined;\n};\n\nexport class AssistantCloudAPI {\n private _auth: AssistantCloudAuthStrategy;\n private _baseUrl;\n\n constructor(config: AssistantCloudConfig) {\n if (\"authToken\" in config) {\n this._baseUrl = config.baseUrl;\n this._auth = new AssistantCloudJWTAuthStrategy(config.authToken);\n } else if (\"apiKey\" in config) {\n this._baseUrl = \"https://backend.assistant-api.com\";\n this._auth = new AssistantCloudAPIKeyAuthStrategy(\n config.apiKey,\n config.userId,\n config.workspaceId,\n );\n } else if (\"anonymous\" in config) {\n this._baseUrl = config.baseUrl;\n this._auth = new AssistantCloudAnonymousAuthStrategy(config.baseUrl);\n } else {\n throw new Error(\n \"Invalid configuration: Must provide authToken, apiKey, or anonymous configuration\",\n );\n }\n }\n\n public async initializeAuth() {\n return !!this._auth.getAuthHeaders();\n }\n\n public async makeRawRequest(\n endpoint: string,\n options: MakeRequestOptions = {},\n ) {\n const authHeaders = await this._auth.getAuthHeaders();\n if (!authHeaders) throw new Error(\"Authronization failed\");\n\n const headers = {\n ...authHeaders,\n ...options.headers,\n \"Content-Type\": \"application/json\",\n };\n\n const queryParams = new URLSearchParams();\n if (options.query) {\n for (const [key, value] of Object.entries(options.query)) {\n if (value === false) continue;\n if (value === true) {\n queryParams.set(key, \"true\");\n } else {\n queryParams.set(key, value.toString());\n }\n }\n }\n\n const url = new URL(`${this._baseUrl}/v1${endpoint}`);\n url.search = queryParams.toString();\n\n const response = await fetch(url, {\n method: options.method ?? \"GET\",\n headers,\n body: options.body ? JSON.stringify(options.body) : null,\n });\n\n this._auth.readAuthHeaders(response.headers);\n\n if (!response.ok) {\n const text = await response.text();\n try {\n const body = JSON.parse(text);\n throw new CloudAPIError(body.message);\n } catch {\n throw new Error(\n `Request failed with status ${response.status}, ${text}`,\n );\n }\n }\n\n return response;\n }\n\n public async makeRequest(endpoint: string, options: MakeRequestOptions = {}) {\n const response = await this.makeRawRequest(endpoint, options);\n return response.json();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wCAKO;AAiBP,IAAM,gBAAN,cAA4B,MAAM;AAAA,EAChC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AASO,IAAM,oBAAN,MAAwB;AAAA,EACrB;AAAA,EACA;AAAA,EAER,YAAY,QAA8B;AACxC,QAAI,eAAe,QAAQ;AACzB,WAAK,WAAW,OAAO;AACvB,WAAK,QAAQ,IAAI,gEAA8B,OAAO,SAAS;AAAA,IACjE,WAAW,YAAY,QAAQ;AAC7B,WAAK,WAAW;AAChB,WAAK,QAAQ,IAAI;AAAA,QACf,OAAO;AAAA,QACP,OAAO;AAAA,QACP,OAAO;AAAA,MACT;AAAA,IACF,WAAW,eAAe,QAAQ;AAChC,WAAK,WAAW,OAAO;AACvB,WAAK,QAAQ,IAAI,sEAAoC,OAAO,OAAO;AAAA,IACrE,OAAO;AACL,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAa,iBAAiB;AAC5B,WAAO,CAAC,CAAC,KAAK,MAAM,eAAe;AAAA,EACrC;AAAA,EAEA,MAAa,eACX,UACA,UAA8B,CAAC,GAC/B;AACA,UAAM,cAAc,MAAM,KAAK,MAAM,eAAe;AACpD,QAAI,CAAC,YAAa,OAAM,IAAI,MAAM,uBAAuB;AAEzD,UAAM,UAAU;AAAA,MACd,GAAG;AAAA,MACH,GAAG,QAAQ;AAAA,MACX,gBAAgB;AAAA,IAClB;AAEA,UAAM,cAAc,IAAI,gBAAgB;AACxC,QAAI,QAAQ,OAAO;AACjB,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,KAAK,GAAG;AACxD,YAAI,UAAU,MAAO;AACrB,YAAI,UAAU,MAAM;AAClB,sBAAY,IAAI,KAAK,MAAM;AAAA,QAC7B,OAAO;AACL,sBAAY,IAAI,KAAK,MAAM,SAAS,CAAC;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AAEA,UAAM,MAAM,IAAI,IAAI,GAAG,KAAK,QAAQ,MAAM,QAAQ,EAAE;AACpD,QAAI,SAAS,YAAY,SAAS;AAElC,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,QAAQ,QAAQ,UAAU;AAAA,MAC1B;AAAA,MACA,MAAM,QAAQ,OAAO,KAAK,UAAU,QAAQ,IAAI,IAAI;AAAA,IACtD,CAAC;AAED,SAAK,MAAM,gBAAgB,SAAS,OAAO;AAE3C,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,UAAI;AACF,cAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,cAAM,IAAI,cAAc,KAAK,OAAO;AAAA,MACtC,QAAQ;AACN,cAAM,IAAI;AAAA,UACR,8BAA8B,SAAS,MAAM,KAAK,IAAI;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAa,YAAY,UAAkB,UAA8B,CAAC,GAAG;AAC3E,UAAM,WAAW,MAAM,KAAK,eAAe,UAAU,OAAO;AAC5D,WAAO,SAAS,KAAK;AAAA,EACvB;AACF;","names":[]}
|
@@ -1,7 +1,8 @@
|
|
1
1
|
// src/cloud/AssistantCloudAPI.tsx
|
2
2
|
import {
|
3
3
|
AssistantCloudJWTAuthStrategy,
|
4
|
-
AssistantCloudAPIKeyAuthStrategy
|
4
|
+
AssistantCloudAPIKeyAuthStrategy,
|
5
|
+
AssistantCloudAnonymousAuthStrategy
|
5
6
|
} from "./AssistantCloudAuthStrategy.mjs";
|
6
7
|
var CloudAPIError = class extends Error {
|
7
8
|
constructor(message) {
|
@@ -16,13 +17,20 @@ var AssistantCloudAPI = class {
|
|
16
17
|
if ("authToken" in config) {
|
17
18
|
this._baseUrl = config.baseUrl;
|
18
19
|
this._auth = new AssistantCloudJWTAuthStrategy(config.authToken);
|
19
|
-
} else {
|
20
|
+
} else if ("apiKey" in config) {
|
20
21
|
this._baseUrl = "https://backend.assistant-api.com";
|
21
22
|
this._auth = new AssistantCloudAPIKeyAuthStrategy(
|
22
23
|
config.apiKey,
|
23
24
|
config.userId,
|
24
25
|
config.workspaceId
|
25
26
|
);
|
27
|
+
} else if ("anonymous" in config) {
|
28
|
+
this._baseUrl = config.baseUrl;
|
29
|
+
this._auth = new AssistantCloudAnonymousAuthStrategy(config.baseUrl);
|
30
|
+
} else {
|
31
|
+
throw new Error(
|
32
|
+
"Invalid configuration: Must provide authToken, apiKey, or anonymous configuration"
|
33
|
+
);
|
26
34
|
}
|
27
35
|
}
|
28
36
|
async initializeAuth() {
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../../src/cloud/AssistantCloudAPI.tsx"],"sourcesContent":["import {\n AssistantCloudAuthStrategy,\n AssistantCloudJWTAuthStrategy,\n AssistantCloudAPIKeyAuthStrategy,\n} from \"./AssistantCloudAuthStrategy\";\n\nexport type AssistantCloudConfig =\n | {\n baseUrl: string;\n authToken: (
|
1
|
+
{"version":3,"sources":["../../src/cloud/AssistantCloudAPI.tsx"],"sourcesContent":["import {\n AssistantCloudAuthStrategy,\n AssistantCloudJWTAuthStrategy,\n AssistantCloudAPIKeyAuthStrategy,\n AssistantCloudAnonymousAuthStrategy,\n} from \"./AssistantCloudAuthStrategy\";\n\nexport type AssistantCloudConfig =\n | {\n baseUrl: string;\n authToken: () => Promise<string | null>;\n }\n | {\n apiKey: string;\n userId: string;\n workspaceId: string;\n }\n | {\n baseUrl: string;\n anonymous: true;\n };\n\nclass CloudAPIError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"APIError\";\n }\n}\n\ntype MakeRequestOptions = {\n method?: \"POST\" | \"PUT\" | \"DELETE\" | undefined;\n headers?: Record<string, string> | undefined;\n query?: Record<string, string | number | boolean> | undefined;\n body?: object | undefined;\n};\n\nexport class AssistantCloudAPI {\n private _auth: AssistantCloudAuthStrategy;\n private _baseUrl;\n\n constructor(config: AssistantCloudConfig) {\n if (\"authToken\" in config) {\n this._baseUrl = config.baseUrl;\n this._auth = new AssistantCloudJWTAuthStrategy(config.authToken);\n } else if (\"apiKey\" in config) {\n this._baseUrl = \"https://backend.assistant-api.com\";\n this._auth = new AssistantCloudAPIKeyAuthStrategy(\n config.apiKey,\n config.userId,\n config.workspaceId,\n );\n } else if (\"anonymous\" in config) {\n this._baseUrl = config.baseUrl;\n this._auth = new AssistantCloudAnonymousAuthStrategy(config.baseUrl);\n } else {\n throw new Error(\n \"Invalid configuration: Must provide authToken, apiKey, or anonymous configuration\",\n );\n }\n }\n\n public async initializeAuth() {\n return !!this._auth.getAuthHeaders();\n }\n\n public async makeRawRequest(\n endpoint: string,\n options: MakeRequestOptions = {},\n ) {\n const authHeaders = await this._auth.getAuthHeaders();\n if (!authHeaders) throw new Error(\"Authronization failed\");\n\n const headers = {\n ...authHeaders,\n ...options.headers,\n \"Content-Type\": \"application/json\",\n };\n\n const queryParams = new URLSearchParams();\n if (options.query) {\n for (const [key, value] of Object.entries(options.query)) {\n if (value === false) continue;\n if (value === true) {\n queryParams.set(key, \"true\");\n } else {\n queryParams.set(key, value.toString());\n }\n }\n }\n\n const url = new URL(`${this._baseUrl}/v1${endpoint}`);\n url.search = queryParams.toString();\n\n const response = await fetch(url, {\n method: options.method ?? \"GET\",\n headers,\n body: options.body ? JSON.stringify(options.body) : null,\n });\n\n this._auth.readAuthHeaders(response.headers);\n\n if (!response.ok) {\n const text = await response.text();\n try {\n const body = JSON.parse(text);\n throw new CloudAPIError(body.message);\n } catch {\n throw new Error(\n `Request failed with status ${response.status}, ${text}`,\n );\n }\n }\n\n return response;\n }\n\n public async makeRequest(endpoint: string, options: MakeRequestOptions = {}) {\n const response = await this.makeRawRequest(endpoint, options);\n return response.json();\n }\n}\n"],"mappings":";AAAA;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAiBP,IAAM,gBAAN,cAA4B,MAAM;AAAA,EAChC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AASO,IAAM,oBAAN,MAAwB;AAAA,EACrB;AAAA,EACA;AAAA,EAER,YAAY,QAA8B;AACxC,QAAI,eAAe,QAAQ;AACzB,WAAK,WAAW,OAAO;AACvB,WAAK,QAAQ,IAAI,8BAA8B,OAAO,SAAS;AAAA,IACjE,WAAW,YAAY,QAAQ;AAC7B,WAAK,WAAW;AAChB,WAAK,QAAQ,IAAI;AAAA,QACf,OAAO;AAAA,QACP,OAAO;AAAA,QACP,OAAO;AAAA,MACT;AAAA,IACF,WAAW,eAAe,QAAQ;AAChC,WAAK,WAAW,OAAO;AACvB,WAAK,QAAQ,IAAI,oCAAoC,OAAO,OAAO;AAAA,IACrE,OAAO;AACL,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAa,iBAAiB;AAC5B,WAAO,CAAC,CAAC,KAAK,MAAM,eAAe;AAAA,EACrC;AAAA,EAEA,MAAa,eACX,UACA,UAA8B,CAAC,GAC/B;AACA,UAAM,cAAc,MAAM,KAAK,MAAM,eAAe;AACpD,QAAI,CAAC,YAAa,OAAM,IAAI,MAAM,uBAAuB;AAEzD,UAAM,UAAU;AAAA,MACd,GAAG;AAAA,MACH,GAAG,QAAQ;AAAA,MACX,gBAAgB;AAAA,IAClB;AAEA,UAAM,cAAc,IAAI,gBAAgB;AACxC,QAAI,QAAQ,OAAO;AACjB,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,KAAK,GAAG;AACxD,YAAI,UAAU,MAAO;AACrB,YAAI,UAAU,MAAM;AAClB,sBAAY,IAAI,KAAK,MAAM;AAAA,QAC7B,OAAO;AACL,sBAAY,IAAI,KAAK,MAAM,SAAS,CAAC;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AAEA,UAAM,MAAM,IAAI,IAAI,GAAG,KAAK,QAAQ,MAAM,QAAQ,EAAE;AACpD,QAAI,SAAS,YAAY,SAAS;AAElC,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,QAAQ,QAAQ,UAAU;AAAA,MAC1B;AAAA,MACA,MAAM,QAAQ,OAAO,KAAK,UAAU,QAAQ,IAAI,IAAI;AAAA,IACtD,CAAC;AAED,SAAK,MAAM,gBAAgB,SAAS,OAAO;AAE3C,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,UAAI;AACF,cAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,cAAM,IAAI,cAAc,KAAK,OAAO;AAAA,MACtC,QAAQ;AACN,cAAM,IAAI;AAAA,UACR,8BAA8B,SAAS,MAAM,KAAK,IAAI;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAa,YAAY,UAAkB,UAA8B,CAAC,GAAG;AAC3E,UAAM,WAAW,MAAM,KAAK,eAAe,UAAU,OAAO;AAC5D,WAAO,SAAS,KAAK;AAAA,EACvB;AACF;","names":[]}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
export type AssistantCloudAuthStrategy = {
|
2
|
-
readonly strategy: "jwt" | "api-key";
|
2
|
+
readonly strategy: "anon" | "jwt" | "api-key";
|
3
3
|
getAuthHeaders(): Promise<Record<string, string> | false>;
|
4
4
|
readAuthHeaders(headers: Headers): void;
|
5
5
|
};
|
@@ -8,8 +8,7 @@ export declare class AssistantCloudJWTAuthStrategy implements AssistantCloudAuth
|
|
8
8
|
readonly strategy = "jwt";
|
9
9
|
private cachedToken;
|
10
10
|
private tokenExpiry;
|
11
|
-
constructor(authTokenCallback: (
|
12
|
-
private getJwtExpiry;
|
11
|
+
constructor(authTokenCallback: () => Promise<string | null>);
|
13
12
|
getAuthHeaders(): Promise<Record<string, string> | false>;
|
14
13
|
readAuthHeaders(headers: Headers): void;
|
15
14
|
}
|
@@ -20,4 +19,12 @@ export declare class AssistantCloudAPIKeyAuthStrategy implements AssistantCloudA
|
|
20
19
|
getAuthHeaders(): Promise<Record<string, string>>;
|
21
20
|
readAuthHeaders(): void;
|
22
21
|
}
|
22
|
+
export declare class AssistantCloudAnonymousAuthStrategy implements AssistantCloudAuthStrategy {
|
23
|
+
readonly strategy = "anon";
|
24
|
+
private baseUrl;
|
25
|
+
private jwtStrategy;
|
26
|
+
constructor(baseUrl: string);
|
27
|
+
getAuthHeaders(): Promise<Record<string, string> | false>;
|
28
|
+
readAuthHeaders(headers: Headers): void;
|
29
|
+
}
|
23
30
|
//# sourceMappingURL=AssistantCloudAuthStrategy.d.ts.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"AssistantCloudAuthStrategy.d.ts","sourceRoot":"","sources":["../../src/cloud/AssistantCloudAuthStrategy.tsx"],"names":[],"mappings":"AAAA,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,CAAC,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAC;
|
1
|
+
{"version":3,"file":"AssistantCloudAuthStrategy.d.ts","sourceRoot":"","sources":["../../src/cloud/AssistantCloudAuthStrategy.tsx"],"names":[],"mappings":"AAAA,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;IAC9C,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC;IAC1D,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CACzC,CAAC;AAgCF,qBAAa,6BACX,YAAW,0BAA0B;;IAErC,SAAgB,QAAQ,SAAS;IAEjC,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,WAAW,CAAuB;gBAG9B,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAI9C,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC;IAsB/D,eAAe,CAAC,OAAO,EAAE,OAAO;CAYxC;AAED,qBAAa,gCACX,YAAW,0BAA0B;;IAErC,SAAgB,QAAQ,aAAa;gBAMzB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;IAMlD,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAQvD,eAAe;CAGvB;AAID,qBAAa,mCACX,YAAW,0BAA0B;IAErC,SAAgB,QAAQ,UAAU;IAElC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAgC;gBAEvC,OAAO,EAAE,MAAM;IA8Dd,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC;IAI/D,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;CAG/C"}
|
@@ -21,9 +21,32 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
21
21
|
var AssistantCloudAuthStrategy_exports = {};
|
22
22
|
__export(AssistantCloudAuthStrategy_exports, {
|
23
23
|
AssistantCloudAPIKeyAuthStrategy: () => AssistantCloudAPIKeyAuthStrategy,
|
24
|
+
AssistantCloudAnonymousAuthStrategy: () => AssistantCloudAnonymousAuthStrategy,
|
24
25
|
AssistantCloudJWTAuthStrategy: () => AssistantCloudJWTAuthStrategy
|
25
26
|
});
|
26
27
|
module.exports = __toCommonJS(AssistantCloudAuthStrategy_exports);
|
28
|
+
var getJwtExpiry = (jwt) => {
|
29
|
+
try {
|
30
|
+
const parts = jwt.split(".");
|
31
|
+
const bodyPart = parts[1];
|
32
|
+
if (!bodyPart) {
|
33
|
+
throw new Error("Invalid JWT format");
|
34
|
+
}
|
35
|
+
let base64 = bodyPart.replace(/-/g, "+").replace(/_/g, "/");
|
36
|
+
while (base64.length % 4 !== 0) {
|
37
|
+
base64 += "=";
|
38
|
+
}
|
39
|
+
const payload = atob(base64);
|
40
|
+
const payloadObj = JSON.parse(payload);
|
41
|
+
const exp = payloadObj.exp;
|
42
|
+
if (!exp || typeof exp !== "number") {
|
43
|
+
throw new Error('JWT does not contain a valid "exp" field');
|
44
|
+
}
|
45
|
+
return exp * 1e3;
|
46
|
+
} catch (error) {
|
47
|
+
throw new Error("Unable to determine the token expiry: " + error);
|
48
|
+
}
|
49
|
+
};
|
27
50
|
var AssistantCloudJWTAuthStrategy = class {
|
28
51
|
strategy = "jwt";
|
29
52
|
cachedToken = null;
|
@@ -32,50 +55,26 @@ var AssistantCloudJWTAuthStrategy = class {
|
|
32
55
|
constructor(authTokenCallback) {
|
33
56
|
this.#authTokenCallback = authTokenCallback;
|
34
57
|
}
|
35
|
-
getJwtExpiry(jwt) {
|
36
|
-
try {
|
37
|
-
const bodyPart = jwt.split(".").at(1);
|
38
|
-
if (!bodyPart) {
|
39
|
-
throw new Error("Invalid JWT format");
|
40
|
-
}
|
41
|
-
const payload = Buffer.from(
|
42
|
-
bodyPart.replace(/-/g, "+").replace(/_/g, "/"),
|
43
|
-
"base64"
|
44
|
-
).toString();
|
45
|
-
const payloadObj = JSON.parse(payload);
|
46
|
-
const exp = payloadObj.exp;
|
47
|
-
if (!exp || typeof exp !== "number") {
|
48
|
-
throw new Error('JWT does not contain a valid "exp" field');
|
49
|
-
}
|
50
|
-
return exp * 1e3;
|
51
|
-
} catch (error) {
|
52
|
-
throw new Error("Unable to determine the token expiry " + error);
|
53
|
-
}
|
54
|
-
}
|
55
58
|
async getAuthHeaders() {
|
56
59
|
const currentTime = Date.now();
|
57
60
|
if (this.cachedToken && this.tokenExpiry && this.tokenExpiry - currentTime > 30 * 1e3) {
|
58
|
-
return {
|
59
|
-
Authorization: `Bearer ${this.cachedToken}`
|
60
|
-
};
|
61
|
+
return { Authorization: `Bearer ${this.cachedToken}` };
|
61
62
|
}
|
62
|
-
const newToken = await this.#authTokenCallback
|
63
|
+
const newToken = await this.#authTokenCallback();
|
63
64
|
if (!newToken) return false;
|
64
|
-
const expiry = this.getJwtExpiry(newToken);
|
65
65
|
this.cachedToken = newToken;
|
66
|
-
this.tokenExpiry =
|
67
|
-
return {
|
68
|
-
Authorization: `Bearer ${newToken}`
|
69
|
-
};
|
66
|
+
this.tokenExpiry = getJwtExpiry(newToken);
|
67
|
+
return { Authorization: `Bearer ${newToken}` };
|
70
68
|
}
|
71
69
|
readAuthHeaders(headers) {
|
72
70
|
const authHeader = headers.get("Authorization");
|
73
71
|
if (!authHeader) return;
|
74
72
|
const [scheme, token] = authHeader.split(" ");
|
75
|
-
if (scheme !== "Bearer" || !token)
|
73
|
+
if (scheme !== "Bearer" || !token) {
|
76
74
|
throw new Error("Invalid auth header received");
|
75
|
+
}
|
77
76
|
this.cachedToken = token;
|
78
|
-
this.tokenExpiry =
|
77
|
+
this.tokenExpiry = getJwtExpiry(token);
|
79
78
|
}
|
80
79
|
};
|
81
80
|
var AssistantCloudAPIKeyAuthStrategy = class {
|
@@ -98,9 +97,70 @@ var AssistantCloudAPIKeyAuthStrategy = class {
|
|
98
97
|
readAuthHeaders() {
|
99
98
|
}
|
100
99
|
};
|
100
|
+
var AUI_REFRESH_TOKEN_NAME = "aui:refresh_token";
|
101
|
+
var AssistantCloudAnonymousAuthStrategy = class {
|
102
|
+
strategy = "anon";
|
103
|
+
baseUrl;
|
104
|
+
jwtStrategy;
|
105
|
+
constructor(baseUrl) {
|
106
|
+
this.baseUrl = baseUrl;
|
107
|
+
this.jwtStrategy = new AssistantCloudJWTAuthStrategy(async () => {
|
108
|
+
const currentTime = Date.now();
|
109
|
+
const storedRefreshTokenJson = localStorage.getItem(
|
110
|
+
AUI_REFRESH_TOKEN_NAME
|
111
|
+
);
|
112
|
+
const storedRefreshToken = storedRefreshTokenJson ? JSON.parse(storedRefreshTokenJson) : void 0;
|
113
|
+
if (storedRefreshToken) {
|
114
|
+
const refreshExpiry = new Date(storedRefreshToken.expires_at).getTime();
|
115
|
+
if (refreshExpiry - currentTime > 30 * 1e3) {
|
116
|
+
const response2 = await fetch(
|
117
|
+
`${this.baseUrl}/v1/auth/tokens/refresh`,
|
118
|
+
{
|
119
|
+
method: "POST",
|
120
|
+
headers: { "Content-Type": "application/json" },
|
121
|
+
body: JSON.stringify({ refresh_token: storedRefreshToken.token })
|
122
|
+
}
|
123
|
+
);
|
124
|
+
if (response2.ok) {
|
125
|
+
const data2 = await response2.json();
|
126
|
+
const { access_token: access_token2, refresh_token: refresh_token2 } = data2;
|
127
|
+
if (refresh_token2) {
|
128
|
+
localStorage.setItem(
|
129
|
+
AUI_REFRESH_TOKEN_NAME,
|
130
|
+
JSON.stringify(refresh_token2)
|
131
|
+
);
|
132
|
+
}
|
133
|
+
return access_token2;
|
134
|
+
}
|
135
|
+
} else {
|
136
|
+
localStorage.removeItem(AUI_REFRESH_TOKEN_NAME);
|
137
|
+
}
|
138
|
+
}
|
139
|
+
const response = await fetch(`${this.baseUrl}/v1/auth/tokens/anonymous`, {
|
140
|
+
method: "POST"
|
141
|
+
});
|
142
|
+
if (!response.ok) return null;
|
143
|
+
const data = await response.json();
|
144
|
+
const { access_token, refresh_token } = data;
|
145
|
+
if (!access_token || !refresh_token) return null;
|
146
|
+
localStorage.setItem(
|
147
|
+
AUI_REFRESH_TOKEN_NAME,
|
148
|
+
JSON.stringify(refresh_token)
|
149
|
+
);
|
150
|
+
return access_token;
|
151
|
+
});
|
152
|
+
}
|
153
|
+
async getAuthHeaders() {
|
154
|
+
return this.jwtStrategy.getAuthHeaders();
|
155
|
+
}
|
156
|
+
readAuthHeaders(headers) {
|
157
|
+
this.jwtStrategy.readAuthHeaders(headers);
|
158
|
+
}
|
159
|
+
};
|
101
160
|
// Annotate the CommonJS export names for ESM import in node:
|
102
161
|
0 && (module.exports = {
|
103
162
|
AssistantCloudAPIKeyAuthStrategy,
|
163
|
+
AssistantCloudAnonymousAuthStrategy,
|
104
164
|
AssistantCloudJWTAuthStrategy
|
105
165
|
});
|
106
166
|
//# sourceMappingURL=AssistantCloudAuthStrategy.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../../src/cloud/AssistantCloudAuthStrategy.tsx"],"sourcesContent":["export type AssistantCloudAuthStrategy = {\n readonly strategy: \"jwt\" | \"api-key\";\n getAuthHeaders(): Promise<Record<string, string> | false>;\n readAuthHeaders(headers: Headers): void;\n};\n\
|
1
|
+
{"version":3,"sources":["../../src/cloud/AssistantCloudAuthStrategy.tsx"],"sourcesContent":["export type AssistantCloudAuthStrategy = {\n readonly strategy: \"anon\" | \"jwt\" | \"api-key\";\n getAuthHeaders(): Promise<Record<string, string> | false>;\n readAuthHeaders(headers: Headers): void;\n};\n\nconst getJwtExpiry = (jwt: string): number => {\n try {\n const parts = jwt.split(\".\");\n const bodyPart = parts[1];\n if (!bodyPart) {\n throw new Error(\"Invalid JWT format\");\n }\n\n // Convert from Base64Url to Base64 and add padding if necessary\n let base64 = bodyPart.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (base64.length % 4 !== 0) {\n base64 += \"=\";\n }\n\n // Decode the Base64 string and parse the payload\n const payload = atob(base64);\n const payloadObj = JSON.parse(payload);\n const exp = payloadObj.exp;\n\n if (!exp || typeof exp !== \"number\") {\n throw new Error('JWT does not contain a valid \"exp\" field');\n }\n\n // Convert expiration time to milliseconds\n return exp * 1000;\n } catch (error) {\n throw new Error(\"Unable to determine the token expiry: \" + error);\n }\n};\n\nexport class AssistantCloudJWTAuthStrategy\n implements AssistantCloudAuthStrategy\n{\n public readonly strategy = \"jwt\";\n\n private cachedToken: string | null = null;\n private tokenExpiry: number | null = null;\n #authTokenCallback: () => Promise<string | null>;\n\n constructor(authTokenCallback: () => Promise<string | null>) {\n this.#authTokenCallback = authTokenCallback;\n }\n\n public async getAuthHeaders(): Promise<Record<string, string> | false> {\n const currentTime = Date.now();\n\n // Use cached token if it's valid for at least 30 more seconds\n if (\n this.cachedToken &&\n this.tokenExpiry &&\n this.tokenExpiry - currentTime > 30 * 1000\n ) {\n return { Authorization: `Bearer ${this.cachedToken}` };\n }\n\n // Fetch a new token via the callback\n const newToken = await this.#authTokenCallback();\n if (!newToken) return false;\n\n this.cachedToken = newToken;\n this.tokenExpiry = getJwtExpiry(newToken);\n\n return { Authorization: `Bearer ${newToken}` };\n }\n\n public readAuthHeaders(headers: Headers) {\n const authHeader = headers.get(\"Authorization\");\n if (!authHeader) return;\n\n const [scheme, token] = authHeader.split(\" \");\n if (scheme !== \"Bearer\" || !token) {\n throw new Error(\"Invalid auth header received\");\n }\n\n this.cachedToken = token;\n this.tokenExpiry = getJwtExpiry(token);\n }\n}\n\nexport class AssistantCloudAPIKeyAuthStrategy\n implements AssistantCloudAuthStrategy\n{\n public readonly strategy = \"api-key\";\n\n #apiKey: string;\n #userId: string;\n #workspaceId: string;\n\n constructor(apiKey: string, userId: string, workspaceId: string) {\n this.#apiKey = apiKey;\n this.#userId = userId;\n this.#workspaceId = workspaceId;\n }\n\n public async getAuthHeaders(): Promise<Record<string, string>> {\n return {\n Authorization: `Bearer ${this.#apiKey}`,\n \"Aui-User-Id\": this.#userId,\n \"Aui-Workspace-Id\": this.#workspaceId,\n };\n }\n\n public readAuthHeaders() {\n // No operation needed for API key auth\n }\n}\n\nconst AUI_REFRESH_TOKEN_NAME = \"aui:refresh_token\";\n\nexport class AssistantCloudAnonymousAuthStrategy\n implements AssistantCloudAuthStrategy\n{\n public readonly strategy = \"anon\";\n\n private baseUrl: string;\n private jwtStrategy: AssistantCloudJWTAuthStrategy;\n\n constructor(baseUrl: string) {\n this.baseUrl = baseUrl;\n this.jwtStrategy = new AssistantCloudJWTAuthStrategy(async () => {\n const currentTime = Date.now();\n const storedRefreshTokenJson = localStorage.getItem(\n AUI_REFRESH_TOKEN_NAME,\n );\n const storedRefreshToken = storedRefreshTokenJson\n ? (JSON.parse(storedRefreshTokenJson) as {\n token: string;\n expires_at: string;\n })\n : undefined;\n\n if (storedRefreshToken) {\n const refreshExpiry = new Date(storedRefreshToken.expires_at).getTime();\n if (refreshExpiry - currentTime > 30 * 1000) {\n const response = await fetch(\n `${this.baseUrl}/v1/auth/tokens/refresh`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refresh_token: storedRefreshToken.token }),\n },\n );\n\n if (response.ok) {\n const data = await response.json();\n const { access_token, refresh_token } = data;\n if (refresh_token) {\n localStorage.setItem(\n AUI_REFRESH_TOKEN_NAME,\n JSON.stringify(refresh_token),\n );\n }\n return access_token;\n }\n } else {\n localStorage.removeItem(AUI_REFRESH_TOKEN_NAME);\n }\n }\n\n // No valid refresh token; request a new anonymous token\n const response = await fetch(`${this.baseUrl}/v1/auth/tokens/anonymous`, {\n method: \"POST\",\n });\n\n if (!response.ok) return null;\n\n const data = await response.json();\n const { access_token, refresh_token } = data;\n\n if (!access_token || !refresh_token) return null;\n\n localStorage.setItem(\n AUI_REFRESH_TOKEN_NAME,\n JSON.stringify(refresh_token),\n );\n return access_token;\n });\n }\n\n public async getAuthHeaders(): Promise<Record<string, string> | false> {\n return this.jwtStrategy.getAuthHeaders();\n }\n\n public readAuthHeaders(headers: Headers): void {\n this.jwtStrategy.readAuthHeaders(headers);\n }\n}\n"],"mappings":"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","names":["response","data","access_token","refresh_token"]}
|
@@ -1,4 +1,26 @@
|
|
1
1
|
// src/cloud/AssistantCloudAuthStrategy.tsx
|
2
|
+
var getJwtExpiry = (jwt) => {
|
3
|
+
try {
|
4
|
+
const parts = jwt.split(".");
|
5
|
+
const bodyPart = parts[1];
|
6
|
+
if (!bodyPart) {
|
7
|
+
throw new Error("Invalid JWT format");
|
8
|
+
}
|
9
|
+
let base64 = bodyPart.replace(/-/g, "+").replace(/_/g, "/");
|
10
|
+
while (base64.length % 4 !== 0) {
|
11
|
+
base64 += "=";
|
12
|
+
}
|
13
|
+
const payload = atob(base64);
|
14
|
+
const payloadObj = JSON.parse(payload);
|
15
|
+
const exp = payloadObj.exp;
|
16
|
+
if (!exp || typeof exp !== "number") {
|
17
|
+
throw new Error('JWT does not contain a valid "exp" field');
|
18
|
+
}
|
19
|
+
return exp * 1e3;
|
20
|
+
} catch (error) {
|
21
|
+
throw new Error("Unable to determine the token expiry: " + error);
|
22
|
+
}
|
23
|
+
};
|
2
24
|
var AssistantCloudJWTAuthStrategy = class {
|
3
25
|
strategy = "jwt";
|
4
26
|
cachedToken = null;
|
@@ -7,50 +29,26 @@ var AssistantCloudJWTAuthStrategy = class {
|
|
7
29
|
constructor(authTokenCallback) {
|
8
30
|
this.#authTokenCallback = authTokenCallback;
|
9
31
|
}
|
10
|
-
getJwtExpiry(jwt) {
|
11
|
-
try {
|
12
|
-
const bodyPart = jwt.split(".").at(1);
|
13
|
-
if (!bodyPart) {
|
14
|
-
throw new Error("Invalid JWT format");
|
15
|
-
}
|
16
|
-
const payload = Buffer.from(
|
17
|
-
bodyPart.replace(/-/g, "+").replace(/_/g, "/"),
|
18
|
-
"base64"
|
19
|
-
).toString();
|
20
|
-
const payloadObj = JSON.parse(payload);
|
21
|
-
const exp = payloadObj.exp;
|
22
|
-
if (!exp || typeof exp !== "number") {
|
23
|
-
throw new Error('JWT does not contain a valid "exp" field');
|
24
|
-
}
|
25
|
-
return exp * 1e3;
|
26
|
-
} catch (error) {
|
27
|
-
throw new Error("Unable to determine the token expiry " + error);
|
28
|
-
}
|
29
|
-
}
|
30
32
|
async getAuthHeaders() {
|
31
33
|
const currentTime = Date.now();
|
32
34
|
if (this.cachedToken && this.tokenExpiry && this.tokenExpiry - currentTime > 30 * 1e3) {
|
33
|
-
return {
|
34
|
-
Authorization: `Bearer ${this.cachedToken}`
|
35
|
-
};
|
35
|
+
return { Authorization: `Bearer ${this.cachedToken}` };
|
36
36
|
}
|
37
|
-
const newToken = await this.#authTokenCallback
|
37
|
+
const newToken = await this.#authTokenCallback();
|
38
38
|
if (!newToken) return false;
|
39
|
-
const expiry = this.getJwtExpiry(newToken);
|
40
39
|
this.cachedToken = newToken;
|
41
|
-
this.tokenExpiry =
|
42
|
-
return {
|
43
|
-
Authorization: `Bearer ${newToken}`
|
44
|
-
};
|
40
|
+
this.tokenExpiry = getJwtExpiry(newToken);
|
41
|
+
return { Authorization: `Bearer ${newToken}` };
|
45
42
|
}
|
46
43
|
readAuthHeaders(headers) {
|
47
44
|
const authHeader = headers.get("Authorization");
|
48
45
|
if (!authHeader) return;
|
49
46
|
const [scheme, token] = authHeader.split(" ");
|
50
|
-
if (scheme !== "Bearer" || !token)
|
47
|
+
if (scheme !== "Bearer" || !token) {
|
51
48
|
throw new Error("Invalid auth header received");
|
49
|
+
}
|
52
50
|
this.cachedToken = token;
|
53
|
-
this.tokenExpiry =
|
51
|
+
this.tokenExpiry = getJwtExpiry(token);
|
54
52
|
}
|
55
53
|
};
|
56
54
|
var AssistantCloudAPIKeyAuthStrategy = class {
|
@@ -73,8 +71,69 @@ var AssistantCloudAPIKeyAuthStrategy = class {
|
|
73
71
|
readAuthHeaders() {
|
74
72
|
}
|
75
73
|
};
|
74
|
+
var AUI_REFRESH_TOKEN_NAME = "aui:refresh_token";
|
75
|
+
var AssistantCloudAnonymousAuthStrategy = class {
|
76
|
+
strategy = "anon";
|
77
|
+
baseUrl;
|
78
|
+
jwtStrategy;
|
79
|
+
constructor(baseUrl) {
|
80
|
+
this.baseUrl = baseUrl;
|
81
|
+
this.jwtStrategy = new AssistantCloudJWTAuthStrategy(async () => {
|
82
|
+
const currentTime = Date.now();
|
83
|
+
const storedRefreshTokenJson = localStorage.getItem(
|
84
|
+
AUI_REFRESH_TOKEN_NAME
|
85
|
+
);
|
86
|
+
const storedRefreshToken = storedRefreshTokenJson ? JSON.parse(storedRefreshTokenJson) : void 0;
|
87
|
+
if (storedRefreshToken) {
|
88
|
+
const refreshExpiry = new Date(storedRefreshToken.expires_at).getTime();
|
89
|
+
if (refreshExpiry - currentTime > 30 * 1e3) {
|
90
|
+
const response2 = await fetch(
|
91
|
+
`${this.baseUrl}/v1/auth/tokens/refresh`,
|
92
|
+
{
|
93
|
+
method: "POST",
|
94
|
+
headers: { "Content-Type": "application/json" },
|
95
|
+
body: JSON.stringify({ refresh_token: storedRefreshToken.token })
|
96
|
+
}
|
97
|
+
);
|
98
|
+
if (response2.ok) {
|
99
|
+
const data2 = await response2.json();
|
100
|
+
const { access_token: access_token2, refresh_token: refresh_token2 } = data2;
|
101
|
+
if (refresh_token2) {
|
102
|
+
localStorage.setItem(
|
103
|
+
AUI_REFRESH_TOKEN_NAME,
|
104
|
+
JSON.stringify(refresh_token2)
|
105
|
+
);
|
106
|
+
}
|
107
|
+
return access_token2;
|
108
|
+
}
|
109
|
+
} else {
|
110
|
+
localStorage.removeItem(AUI_REFRESH_TOKEN_NAME);
|
111
|
+
}
|
112
|
+
}
|
113
|
+
const response = await fetch(`${this.baseUrl}/v1/auth/tokens/anonymous`, {
|
114
|
+
method: "POST"
|
115
|
+
});
|
116
|
+
if (!response.ok) return null;
|
117
|
+
const data = await response.json();
|
118
|
+
const { access_token, refresh_token } = data;
|
119
|
+
if (!access_token || !refresh_token) return null;
|
120
|
+
localStorage.setItem(
|
121
|
+
AUI_REFRESH_TOKEN_NAME,
|
122
|
+
JSON.stringify(refresh_token)
|
123
|
+
);
|
124
|
+
return access_token;
|
125
|
+
});
|
126
|
+
}
|
127
|
+
async getAuthHeaders() {
|
128
|
+
return this.jwtStrategy.getAuthHeaders();
|
129
|
+
}
|
130
|
+
readAuthHeaders(headers) {
|
131
|
+
this.jwtStrategy.readAuthHeaders(headers);
|
132
|
+
}
|
133
|
+
};
|
76
134
|
export {
|
77
135
|
AssistantCloudAPIKeyAuthStrategy,
|
136
|
+
AssistantCloudAnonymousAuthStrategy,
|
78
137
|
AssistantCloudJWTAuthStrategy
|
79
138
|
};
|
80
139
|
//# sourceMappingURL=AssistantCloudAuthStrategy.mjs.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../../src/cloud/AssistantCloudAuthStrategy.tsx"],"sourcesContent":["export type AssistantCloudAuthStrategy = {\n readonly strategy: \"jwt\" | \"api-key\";\n getAuthHeaders(): Promise<Record<string, string> | false>;\n readAuthHeaders(headers: Headers): void;\n};\n\
|
1
|
+
{"version":3,"sources":["../../src/cloud/AssistantCloudAuthStrategy.tsx"],"sourcesContent":["export type AssistantCloudAuthStrategy = {\n readonly strategy: \"anon\" | \"jwt\" | \"api-key\";\n getAuthHeaders(): Promise<Record<string, string> | false>;\n readAuthHeaders(headers: Headers): void;\n};\n\nconst getJwtExpiry = (jwt: string): number => {\n try {\n const parts = jwt.split(\".\");\n const bodyPart = parts[1];\n if (!bodyPart) {\n throw new Error(\"Invalid JWT format\");\n }\n\n // Convert from Base64Url to Base64 and add padding if necessary\n let base64 = bodyPart.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (base64.length % 4 !== 0) {\n base64 += \"=\";\n }\n\n // Decode the Base64 string and parse the payload\n const payload = atob(base64);\n const payloadObj = JSON.parse(payload);\n const exp = payloadObj.exp;\n\n if (!exp || typeof exp !== \"number\") {\n throw new Error('JWT does not contain a valid \"exp\" field');\n }\n\n // Convert expiration time to milliseconds\n return exp * 1000;\n } catch (error) {\n throw new Error(\"Unable to determine the token expiry: \" + error);\n }\n};\n\nexport class AssistantCloudJWTAuthStrategy\n implements AssistantCloudAuthStrategy\n{\n public readonly strategy = \"jwt\";\n\n private cachedToken: string | null = null;\n private tokenExpiry: number | null = null;\n #authTokenCallback: () => Promise<string | null>;\n\n constructor(authTokenCallback: () => Promise<string | null>) {\n this.#authTokenCallback = authTokenCallback;\n }\n\n public async getAuthHeaders(): Promise<Record<string, string> | false> {\n const currentTime = Date.now();\n\n // Use cached token if it's valid for at least 30 more seconds\n if (\n this.cachedToken &&\n this.tokenExpiry &&\n this.tokenExpiry - currentTime > 30 * 1000\n ) {\n return { Authorization: `Bearer ${this.cachedToken}` };\n }\n\n // Fetch a new token via the callback\n const newToken = await this.#authTokenCallback();\n if (!newToken) return false;\n\n this.cachedToken = newToken;\n this.tokenExpiry = getJwtExpiry(newToken);\n\n return { Authorization: `Bearer ${newToken}` };\n }\n\n public readAuthHeaders(headers: Headers) {\n const authHeader = headers.get(\"Authorization\");\n if (!authHeader) return;\n\n const [scheme, token] = authHeader.split(\" \");\n if (scheme !== \"Bearer\" || !token) {\n throw new Error(\"Invalid auth header received\");\n }\n\n this.cachedToken = token;\n this.tokenExpiry = getJwtExpiry(token);\n }\n}\n\nexport class AssistantCloudAPIKeyAuthStrategy\n implements AssistantCloudAuthStrategy\n{\n public readonly strategy = \"api-key\";\n\n #apiKey: string;\n #userId: string;\n #workspaceId: string;\n\n constructor(apiKey: string, userId: string, workspaceId: string) {\n this.#apiKey = apiKey;\n this.#userId = userId;\n this.#workspaceId = workspaceId;\n }\n\n public async getAuthHeaders(): Promise<Record<string, string>> {\n return {\n Authorization: `Bearer ${this.#apiKey}`,\n \"Aui-User-Id\": this.#userId,\n \"Aui-Workspace-Id\": this.#workspaceId,\n };\n }\n\n public readAuthHeaders() {\n // No operation needed for API key auth\n }\n}\n\nconst AUI_REFRESH_TOKEN_NAME = \"aui:refresh_token\";\n\nexport class AssistantCloudAnonymousAuthStrategy\n implements AssistantCloudAuthStrategy\n{\n public readonly strategy = \"anon\";\n\n private baseUrl: string;\n private jwtStrategy: AssistantCloudJWTAuthStrategy;\n\n constructor(baseUrl: string) {\n this.baseUrl = baseUrl;\n this.jwtStrategy = new AssistantCloudJWTAuthStrategy(async () => {\n const currentTime = Date.now();\n const storedRefreshTokenJson = localStorage.getItem(\n AUI_REFRESH_TOKEN_NAME,\n );\n const storedRefreshToken = storedRefreshTokenJson\n ? (JSON.parse(storedRefreshTokenJson) as {\n token: string;\n expires_at: string;\n })\n : undefined;\n\n if (storedRefreshToken) {\n const refreshExpiry = new Date(storedRefreshToken.expires_at).getTime();\n if (refreshExpiry - currentTime > 30 * 1000) {\n const response = await fetch(\n `${this.baseUrl}/v1/auth/tokens/refresh`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ refresh_token: storedRefreshToken.token }),\n },\n );\n\n if (response.ok) {\n const data = await response.json();\n const { access_token, refresh_token } = data;\n if (refresh_token) {\n localStorage.setItem(\n AUI_REFRESH_TOKEN_NAME,\n JSON.stringify(refresh_token),\n );\n }\n return access_token;\n }\n } else {\n localStorage.removeItem(AUI_REFRESH_TOKEN_NAME);\n }\n }\n\n // No valid refresh token; request a new anonymous token\n const response = await fetch(`${this.baseUrl}/v1/auth/tokens/anonymous`, {\n method: \"POST\",\n });\n\n if (!response.ok) return null;\n\n const data = await response.json();\n const { access_token, refresh_token } = data;\n\n if (!access_token || !refresh_token) return null;\n\n localStorage.setItem(\n AUI_REFRESH_TOKEN_NAME,\n JSON.stringify(refresh_token),\n );\n return access_token;\n });\n }\n\n public async getAuthHeaders(): Promise<Record<string, string> | false> {\n return this.jwtStrategy.getAuthHeaders();\n }\n\n public readAuthHeaders(headers: Headers): void {\n this.jwtStrategy.readAuthHeaders(headers);\n }\n}\n"],"mappings":"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","names":["response","data","access_token","refresh_token"]}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"cloud.d.ts","sourceRoot":"","sources":["../../../../src/runtimes/remote-thread-list/adapter/cloud.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAMnD,KAAK,UAAU,GAAG;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AACF,KAAK,6BAA6B,GAAG;IACnC,KAAK,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;IAEnC,MAAM,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/B,MAAM,CAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C,CAAC;
|
1
|
+
{"version":3,"file":"cloud.d.ts","sourceRoot":"","sources":["../../../../src/runtimes/remote-thread-list/adapter/cloud.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAMnD,KAAK,UAAU,GAAG;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AACF,KAAK,6BAA6B,GAAG;IACnC,KAAK,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;IAEnC,MAAM,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/B,MAAM,CAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C,CAAC;AAOF,eAAO,MAAM,yBAAyB,YAC3B,6BAA6B,KACrC,uBA4EF,CAAC"}
|
@@ -24,17 +24,32 @@ __export(cloud_exports, {
|
|
24
24
|
});
|
25
25
|
module.exports = __toCommonJS(cloud_exports);
|
26
26
|
var import_react = require("react");
|
27
|
+
var import_cloud = require("../../../cloud/index.js");
|
27
28
|
var import_AssistantCloudThreadHistoryAdapter = require("../../../cloud/AssistantCloudThreadHistoryAdapter.js");
|
28
29
|
var import_RuntimeAdapterProvider = require("../../adapters/RuntimeAdapterProvider.js");
|
29
30
|
var import_edge = require("../../edge/index.js");
|
30
31
|
var import_in_memory = require("./in-memory.js");
|
31
32
|
var import_jsx_runtime = require("react/jsx-runtime");
|
33
|
+
var baseUrl = process.env["NEXT_PUBLIC_ASSISTANT_BASE_URL"];
|
34
|
+
var autoCloud = baseUrl ? new import_cloud.AssistantCloud({ baseUrl, anonymous: true }) : void 0;
|
32
35
|
var useCloudThreadListAdapter = (adapter) => {
|
33
36
|
const adapterRef = (0, import_react.useRef)(adapter);
|
34
37
|
(0, import_react.useEffect)(() => {
|
35
38
|
adapterRef.current = adapter;
|
36
39
|
}, [adapter]);
|
37
|
-
const
|
40
|
+
const unstable_Provider = (0, import_react.useCallback)(
|
41
|
+
({ children }) => {
|
42
|
+
const history = (0, import_AssistantCloudThreadHistoryAdapter.useAssistantCloudThreadHistoryAdapter)({
|
43
|
+
get current() {
|
44
|
+
return adapterRef.current.cloud ?? autoCloud;
|
45
|
+
}
|
46
|
+
});
|
47
|
+
const adapters = (0, import_react.useMemo)(() => ({ history }), [history]);
|
48
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_RuntimeAdapterProvider.RuntimeAdapterProvider, { adapters, children });
|
49
|
+
},
|
50
|
+
[]
|
51
|
+
);
|
52
|
+
const cloud = adapter.cloud ?? autoCloud;
|
38
53
|
if (!cloud) return new import_in_memory.InMemoryThreadListAdapter();
|
39
54
|
return {
|
40
55
|
list: async () => {
|
@@ -78,15 +93,7 @@ var useCloudThreadListAdapter = (adapter) => {
|
|
78
93
|
messages: (0, import_edge.toCoreMessages)(messages)
|
79
94
|
});
|
80
95
|
},
|
81
|
-
unstable_Provider
|
82
|
-
const history = (0, import_AssistantCloudThreadHistoryAdapter.useAssistantCloudThreadHistoryAdapter)({
|
83
|
-
get current() {
|
84
|
-
return adapterRef.current.cloud;
|
85
|
-
}
|
86
|
-
});
|
87
|
-
const adapters = (0, import_react.useMemo)(() => ({ history }), [history]);
|
88
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_RuntimeAdapterProvider.RuntimeAdapterProvider, { adapters, children });
|
89
|
-
}, [])
|
96
|
+
unstable_Provider
|
90
97
|
};
|
91
98
|
};
|
92
99
|
// Annotate the CommonJS export names for ESM import in node:
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../../../../src/runtimes/remote-thread-list/adapter/cloud.tsx"],"sourcesContent":["import {\n FC,\n PropsWithChildren,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n} from \"react\";\nimport { AssistantCloud } from \"../../../cloud\";\nimport { RemoteThreadListAdapter } from \"../types\";\nimport { useAssistantCloudThreadHistoryAdapter } from \"../../../cloud/AssistantCloudThreadHistoryAdapter\";\nimport { RuntimeAdapterProvider } from \"../../adapters/RuntimeAdapterProvider\";\nimport { toCoreMessages } from \"../../edge\";\nimport { InMemoryThreadListAdapter } from \"./in-memory\";\n\ntype ThreadData = {\n externalId: string;\n};\ntype CloudThreadListAdapterOptions = {\n cloud?: AssistantCloud | undefined;\n\n create?(): Promise<ThreadData>;\n delete?(threadId: string): Promise<void>;\n};\n\nexport const useCloudThreadListAdapter = (\n adapter: CloudThreadListAdapterOptions,\n): RemoteThreadListAdapter => {\n const adapterRef = useRef(adapter);\n useEffect(() => {\n adapterRef.current = adapter;\n }, [adapter]);\n\n const cloud = adapter.cloud;\n if (!cloud) return new InMemoryThreadListAdapter();\n\n return {\n list: async () => {\n const { threads } = await cloud.threads.list();\n return {\n threads: threads.map((t) => ({\n status: t.is_archived ? \"archived\" : \"regular\",\n remoteId: t.id,\n title: t.title,\n externalId: t.external_id ?? undefined,\n })),\n };\n },\n\n initialize: async () => {\n const createTask = adapter.create?.() ?? Promise.resolve();\n const t = await createTask;\n const external_id = t ? t.externalId : undefined;\n const { thread_id: remoteId } = await cloud.threads.create({\n last_message_at: new Date(),\n external_id,\n });\n\n return { externalId: external_id, remoteId: remoteId };\n },\n\n rename: async (threadId, newTitle) => {\n return cloud.threads.update(threadId, { title: newTitle });\n },\n archive: async (threadId) => {\n return cloud.threads.update(threadId, { is_archived: true });\n },\n unarchive: async (threadId) => {\n return cloud.threads.update(threadId, { is_archived: false });\n },\n delete: async (threadId) => {\n await adapter.delete?.(threadId);\n return cloud.threads.delete(threadId);\n },\n\n generateTitle: async (threadId, messages) => {\n return cloud.runs.stream({\n thread_id: threadId,\n assistant_id: \"system/thread_title\",\n messages: toCoreMessages(messages),\n });\n },\n\n unstable_Provider
|
1
|
+
{"version":3,"sources":["../../../../src/runtimes/remote-thread-list/adapter/cloud.tsx"],"sourcesContent":["import {\n FC,\n PropsWithChildren,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n} from \"react\";\nimport { AssistantCloud } from \"../../../cloud\";\nimport { RemoteThreadListAdapter } from \"../types\";\nimport { useAssistantCloudThreadHistoryAdapter } from \"../../../cloud/AssistantCloudThreadHistoryAdapter\";\nimport { RuntimeAdapterProvider } from \"../../adapters/RuntimeAdapterProvider\";\nimport { toCoreMessages } from \"../../edge\";\nimport { InMemoryThreadListAdapter } from \"./in-memory\";\n\ntype ThreadData = {\n externalId: string;\n};\ntype CloudThreadListAdapterOptions = {\n cloud?: AssistantCloud | undefined;\n\n create?(): Promise<ThreadData>;\n delete?(threadId: string): Promise<void>;\n};\n\nconst baseUrl = process.env[\"NEXT_PUBLIC_ASSISTANT_BASE_URL\"];\nconst autoCloud = baseUrl\n ? new AssistantCloud({ baseUrl, anonymous: true })\n : undefined;\n\nexport const useCloudThreadListAdapter = (\n adapter: CloudThreadListAdapterOptions,\n): RemoteThreadListAdapter => {\n const adapterRef = useRef(adapter);\n useEffect(() => {\n adapterRef.current = adapter;\n }, [adapter]);\n\n const unstable_Provider = useCallback<FC<PropsWithChildren>>(\n ({ children }) => {\n const history = useAssistantCloudThreadHistoryAdapter({\n get current() {\n return adapterRef.current.cloud ?? autoCloud!;\n },\n });\n const adapters = useMemo(() => ({ history }), [history]);\n\n return (\n <RuntimeAdapterProvider adapters={adapters}>\n {children}\n </RuntimeAdapterProvider>\n );\n },\n [],\n );\n\n const cloud = adapter.cloud ?? autoCloud;\n if (!cloud) return new InMemoryThreadListAdapter();\n\n return {\n list: async () => {\n const { threads } = await cloud.threads.list();\n return {\n threads: threads.map((t) => ({\n status: t.is_archived ? \"archived\" : \"regular\",\n remoteId: t.id,\n title: t.title,\n externalId: t.external_id ?? undefined,\n })),\n };\n },\n\n initialize: async () => {\n const createTask = adapter.create?.() ?? Promise.resolve();\n const t = await createTask;\n const external_id = t ? t.externalId : undefined;\n const { thread_id: remoteId } = await cloud.threads.create({\n last_message_at: new Date(),\n external_id,\n });\n\n return { externalId: external_id, remoteId: remoteId };\n },\n\n rename: async (threadId, newTitle) => {\n return cloud.threads.update(threadId, { title: newTitle });\n },\n archive: async (threadId) => {\n return cloud.threads.update(threadId, { is_archived: true });\n },\n unarchive: async (threadId) => {\n return cloud.threads.update(threadId, { is_archived: false });\n },\n delete: async (threadId) => {\n await adapter.delete?.(threadId);\n return cloud.threads.delete(threadId);\n },\n\n generateTitle: async (threadId, messages) => {\n return cloud.runs.stream({\n thread_id: threadId,\n assistant_id: \"system/thread_title\",\n messages: toCoreMessages(messages),\n });\n },\n\n unstable_Provider,\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAOO;AACP,mBAA+B;AAE/B,gDAAsD;AACtD,oCAAuC;AACvC,kBAA+B;AAC/B,uBAA0C;AAmClC;AAvBR,IAAM,UAAU,QAAQ,IAAI,gCAAgC;AAC5D,IAAM,YAAY,UACd,IAAI,4BAAe,EAAE,SAAS,WAAW,KAAK,CAAC,IAC/C;AAEG,IAAM,4BAA4B,CACvC,YAC4B;AAC5B,QAAM,iBAAa,qBAAO,OAAO;AACjC,8BAAU,MAAM;AACd,eAAW,UAAU;AAAA,EACvB,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,wBAAoB;AAAA,IACxB,CAAC,EAAE,SAAS,MAAM;AAChB,YAAM,cAAU,iFAAsC;AAAA,QACpD,IAAI,UAAU;AACZ,iBAAO,WAAW,QAAQ,SAAS;AAAA,QACrC;AAAA,MACF,CAAC;AACD,YAAM,eAAW,sBAAQ,OAAO,EAAE,QAAQ,IAAI,CAAC,OAAO,CAAC;AAEvD,aACE,4CAAC,wDAAuB,UACrB,UACH;AAAA,IAEJ;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,QAAQ,QAAQ,SAAS;AAC/B,MAAI,CAAC,MAAO,QAAO,IAAI,2CAA0B;AAEjD,SAAO;AAAA,IACL,MAAM,YAAY;AAChB,YAAM,EAAE,QAAQ,IAAI,MAAM,MAAM,QAAQ,KAAK;AAC7C,aAAO;AAAA,QACL,SAAS,QAAQ,IAAI,CAAC,OAAO;AAAA,UAC3B,QAAQ,EAAE,cAAc,aAAa;AAAA,UACrC,UAAU,EAAE;AAAA,UACZ,OAAO,EAAE;AAAA,UACT,YAAY,EAAE,eAAe;AAAA,QAC/B,EAAE;AAAA,MACJ;AAAA,IACF;AAAA,IAEA,YAAY,YAAY;AACtB,YAAM,aAAa,QAAQ,SAAS,KAAK,QAAQ,QAAQ;AACzD,YAAM,IAAI,MAAM;AAChB,YAAM,cAAc,IAAI,EAAE,aAAa;AACvC,YAAM,EAAE,WAAW,SAAS,IAAI,MAAM,MAAM,QAAQ,OAAO;AAAA,QACzD,iBAAiB,oBAAI,KAAK;AAAA,QAC1B;AAAA,MACF,CAAC;AAED,aAAO,EAAE,YAAY,aAAa,SAAmB;AAAA,IACvD;AAAA,IAEA,QAAQ,OAAO,UAAU,aAAa;AACpC,aAAO,MAAM,QAAQ,OAAO,UAAU,EAAE,OAAO,SAAS,CAAC;AAAA,IAC3D;AAAA,IACA,SAAS,OAAO,aAAa;AAC3B,aAAO,MAAM,QAAQ,OAAO,UAAU,EAAE,aAAa,KAAK,CAAC;AAAA,IAC7D;AAAA,IACA,WAAW,OAAO,aAAa;AAC7B,aAAO,MAAM,QAAQ,OAAO,UAAU,EAAE,aAAa,MAAM,CAAC;AAAA,IAC9D;AAAA,IACA,QAAQ,OAAO,aAAa;AAC1B,YAAM,QAAQ,SAAS,QAAQ;AAC/B,aAAO,MAAM,QAAQ,OAAO,QAAQ;AAAA,IACtC;AAAA,IAEA,eAAe,OAAO,UAAU,aAAa;AAC3C,aAAO,MAAM,KAAK,OAAO;AAAA,QACvB,WAAW;AAAA,QACX,cAAc;AAAA,QACd,cAAU,4BAAe,QAAQ;AAAA,MACnC,CAAC;AAAA,IACH;AAAA,IAEA;AAAA,EACF;AACF;","names":[]}
|
@@ -5,17 +5,32 @@ import {
|
|
5
5
|
useMemo,
|
6
6
|
useRef
|
7
7
|
} from "react";
|
8
|
+
import { AssistantCloud } from "../../../cloud/index.mjs";
|
8
9
|
import { useAssistantCloudThreadHistoryAdapter } from "../../../cloud/AssistantCloudThreadHistoryAdapter.mjs";
|
9
10
|
import { RuntimeAdapterProvider } from "../../adapters/RuntimeAdapterProvider.mjs";
|
10
11
|
import { toCoreMessages } from "../../edge/index.mjs";
|
11
12
|
import { InMemoryThreadListAdapter } from "./in-memory.mjs";
|
12
13
|
import { jsx } from "react/jsx-runtime";
|
14
|
+
var baseUrl = process.env["NEXT_PUBLIC_ASSISTANT_BASE_URL"];
|
15
|
+
var autoCloud = baseUrl ? new AssistantCloud({ baseUrl, anonymous: true }) : void 0;
|
13
16
|
var useCloudThreadListAdapter = (adapter) => {
|
14
17
|
const adapterRef = useRef(adapter);
|
15
18
|
useEffect(() => {
|
16
19
|
adapterRef.current = adapter;
|
17
20
|
}, [adapter]);
|
18
|
-
const
|
21
|
+
const unstable_Provider = useCallback(
|
22
|
+
({ children }) => {
|
23
|
+
const history = useAssistantCloudThreadHistoryAdapter({
|
24
|
+
get current() {
|
25
|
+
return adapterRef.current.cloud ?? autoCloud;
|
26
|
+
}
|
27
|
+
});
|
28
|
+
const adapters = useMemo(() => ({ history }), [history]);
|
29
|
+
return /* @__PURE__ */ jsx(RuntimeAdapterProvider, { adapters, children });
|
30
|
+
},
|
31
|
+
[]
|
32
|
+
);
|
33
|
+
const cloud = adapter.cloud ?? autoCloud;
|
19
34
|
if (!cloud) return new InMemoryThreadListAdapter();
|
20
35
|
return {
|
21
36
|
list: async () => {
|
@@ -59,15 +74,7 @@ var useCloudThreadListAdapter = (adapter) => {
|
|
59
74
|
messages: toCoreMessages(messages)
|
60
75
|
});
|
61
76
|
},
|
62
|
-
unstable_Provider
|
63
|
-
const history = useAssistantCloudThreadHistoryAdapter({
|
64
|
-
get current() {
|
65
|
-
return adapterRef.current.cloud;
|
66
|
-
}
|
67
|
-
});
|
68
|
-
const adapters = useMemo(() => ({ history }), [history]);
|
69
|
-
return /* @__PURE__ */ jsx(RuntimeAdapterProvider, { adapters, children });
|
70
|
-
}, [])
|
77
|
+
unstable_Provider
|
71
78
|
};
|
72
79
|
};
|
73
80
|
export {
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../../../../src/runtimes/remote-thread-list/adapter/cloud.tsx"],"sourcesContent":["import {\n FC,\n PropsWithChildren,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n} from \"react\";\nimport { AssistantCloud } from \"../../../cloud\";\nimport { RemoteThreadListAdapter } from \"../types\";\nimport { useAssistantCloudThreadHistoryAdapter } from \"../../../cloud/AssistantCloudThreadHistoryAdapter\";\nimport { RuntimeAdapterProvider } from \"../../adapters/RuntimeAdapterProvider\";\nimport { toCoreMessages } from \"../../edge\";\nimport { InMemoryThreadListAdapter } from \"./in-memory\";\n\ntype ThreadData = {\n externalId: string;\n};\ntype CloudThreadListAdapterOptions = {\n cloud?: AssistantCloud | undefined;\n\n create?(): Promise<ThreadData>;\n delete?(threadId: string): Promise<void>;\n};\n\nexport const useCloudThreadListAdapter = (\n adapter: CloudThreadListAdapterOptions,\n): RemoteThreadListAdapter => {\n const adapterRef = useRef(adapter);\n useEffect(() => {\n adapterRef.current = adapter;\n }, [adapter]);\n\n const cloud = adapter.cloud;\n if (!cloud) return new InMemoryThreadListAdapter();\n\n return {\n list: async () => {\n const { threads } = await cloud.threads.list();\n return {\n threads: threads.map((t) => ({\n status: t.is_archived ? \"archived\" : \"regular\",\n remoteId: t.id,\n title: t.title,\n externalId: t.external_id ?? undefined,\n })),\n };\n },\n\n initialize: async () => {\n const createTask = adapter.create?.() ?? Promise.resolve();\n const t = await createTask;\n const external_id = t ? t.externalId : undefined;\n const { thread_id: remoteId } = await cloud.threads.create({\n last_message_at: new Date(),\n external_id,\n });\n\n return { externalId: external_id, remoteId: remoteId };\n },\n\n rename: async (threadId, newTitle) => {\n return cloud.threads.update(threadId, { title: newTitle });\n },\n archive: async (threadId) => {\n return cloud.threads.update(threadId, { is_archived: true });\n },\n unarchive: async (threadId) => {\n return cloud.threads.update(threadId, { is_archived: false });\n },\n delete: async (threadId) => {\n await adapter.delete?.(threadId);\n return cloud.threads.delete(threadId);\n },\n\n generateTitle: async (threadId, messages) => {\n return cloud.runs.stream({\n thread_id: threadId,\n assistant_id: \"system/thread_title\",\n messages: toCoreMessages(messages),\n });\n },\n\n unstable_Provider
|
1
|
+
{"version":3,"sources":["../../../../src/runtimes/remote-thread-list/adapter/cloud.tsx"],"sourcesContent":["import {\n FC,\n PropsWithChildren,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n} from \"react\";\nimport { AssistantCloud } from \"../../../cloud\";\nimport { RemoteThreadListAdapter } from \"../types\";\nimport { useAssistantCloudThreadHistoryAdapter } from \"../../../cloud/AssistantCloudThreadHistoryAdapter\";\nimport { RuntimeAdapterProvider } from \"../../adapters/RuntimeAdapterProvider\";\nimport { toCoreMessages } from \"../../edge\";\nimport { InMemoryThreadListAdapter } from \"./in-memory\";\n\ntype ThreadData = {\n externalId: string;\n};\ntype CloudThreadListAdapterOptions = {\n cloud?: AssistantCloud | undefined;\n\n create?(): Promise<ThreadData>;\n delete?(threadId: string): Promise<void>;\n};\n\nconst baseUrl = process.env[\"NEXT_PUBLIC_ASSISTANT_BASE_URL\"];\nconst autoCloud = baseUrl\n ? new AssistantCloud({ baseUrl, anonymous: true })\n : undefined;\n\nexport const useCloudThreadListAdapter = (\n adapter: CloudThreadListAdapterOptions,\n): RemoteThreadListAdapter => {\n const adapterRef = useRef(adapter);\n useEffect(() => {\n adapterRef.current = adapter;\n }, [adapter]);\n\n const unstable_Provider = useCallback<FC<PropsWithChildren>>(\n ({ children }) => {\n const history = useAssistantCloudThreadHistoryAdapter({\n get current() {\n return adapterRef.current.cloud ?? autoCloud!;\n },\n });\n const adapters = useMemo(() => ({ history }), [history]);\n\n return (\n <RuntimeAdapterProvider adapters={adapters}>\n {children}\n </RuntimeAdapterProvider>\n );\n },\n [],\n );\n\n const cloud = adapter.cloud ?? autoCloud;\n if (!cloud) return new InMemoryThreadListAdapter();\n\n return {\n list: async () => {\n const { threads } = await cloud.threads.list();\n return {\n threads: threads.map((t) => ({\n status: t.is_archived ? \"archived\" : \"regular\",\n remoteId: t.id,\n title: t.title,\n externalId: t.external_id ?? undefined,\n })),\n };\n },\n\n initialize: async () => {\n const createTask = adapter.create?.() ?? Promise.resolve();\n const t = await createTask;\n const external_id = t ? t.externalId : undefined;\n const { thread_id: remoteId } = await cloud.threads.create({\n last_message_at: new Date(),\n external_id,\n });\n\n return { externalId: external_id, remoteId: remoteId };\n },\n\n rename: async (threadId, newTitle) => {\n return cloud.threads.update(threadId, { title: newTitle });\n },\n archive: async (threadId) => {\n return cloud.threads.update(threadId, { is_archived: true });\n },\n unarchive: async (threadId) => {\n return cloud.threads.update(threadId, { is_archived: false });\n },\n delete: async (threadId) => {\n await adapter.delete?.(threadId);\n return cloud.threads.delete(threadId);\n },\n\n generateTitle: async (threadId, messages) => {\n return cloud.runs.stream({\n thread_id: threadId,\n assistant_id: \"system/thread_title\",\n messages: toCoreMessages(messages),\n });\n },\n\n unstable_Provider,\n };\n};\n"],"mappings":";AAAA;AAAA,EAGE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,sBAAsB;AAE/B,SAAS,6CAA6C;AACtD,SAAS,8BAA8B;AACvC,SAAS,sBAAsB;AAC/B,SAAS,iCAAiC;AAmClC;AAvBR,IAAM,UAAU,QAAQ,IAAI,gCAAgC;AAC5D,IAAM,YAAY,UACd,IAAI,eAAe,EAAE,SAAS,WAAW,KAAK,CAAC,IAC/C;AAEG,IAAM,4BAA4B,CACvC,YAC4B;AAC5B,QAAM,aAAa,OAAO,OAAO;AACjC,YAAU,MAAM;AACd,eAAW,UAAU;AAAA,EACvB,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,oBAAoB;AAAA,IACxB,CAAC,EAAE,SAAS,MAAM;AAChB,YAAM,UAAU,sCAAsC;AAAA,QACpD,IAAI,UAAU;AACZ,iBAAO,WAAW,QAAQ,SAAS;AAAA,QACrC;AAAA,MACF,CAAC;AACD,YAAM,WAAW,QAAQ,OAAO,EAAE,QAAQ,IAAI,CAAC,OAAO,CAAC;AAEvD,aACE,oBAAC,0BAAuB,UACrB,UACH;AAAA,IAEJ;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,QAAQ,QAAQ,SAAS;AAC/B,MAAI,CAAC,MAAO,QAAO,IAAI,0BAA0B;AAEjD,SAAO;AAAA,IACL,MAAM,YAAY;AAChB,YAAM,EAAE,QAAQ,IAAI,MAAM,MAAM,QAAQ,KAAK;AAC7C,aAAO;AAAA,QACL,SAAS,QAAQ,IAAI,CAAC,OAAO;AAAA,UAC3B,QAAQ,EAAE,cAAc,aAAa;AAAA,UACrC,UAAU,EAAE;AAAA,UACZ,OAAO,EAAE;AAAA,UACT,YAAY,EAAE,eAAe;AAAA,QAC/B,EAAE;AAAA,MACJ;AAAA,IACF;AAAA,IAEA,YAAY,YAAY;AACtB,YAAM,aAAa,QAAQ,SAAS,KAAK,QAAQ,QAAQ;AACzD,YAAM,IAAI,MAAM;AAChB,YAAM,cAAc,IAAI,EAAE,aAAa;AACvC,YAAM,EAAE,WAAW,SAAS,IAAI,MAAM,MAAM,QAAQ,OAAO;AAAA,QACzD,iBAAiB,oBAAI,KAAK;AAAA,QAC1B;AAAA,MACF,CAAC;AAED,aAAO,EAAE,YAAY,aAAa,SAAmB;AAAA,IACvD;AAAA,IAEA,QAAQ,OAAO,UAAU,aAAa;AACpC,aAAO,MAAM,QAAQ,OAAO,UAAU,EAAE,OAAO,SAAS,CAAC;AAAA,IAC3D;AAAA,IACA,SAAS,OAAO,aAAa;AAC3B,aAAO,MAAM,QAAQ,OAAO,UAAU,EAAE,aAAa,KAAK,CAAC;AAAA,IAC7D;AAAA,IACA,WAAW,OAAO,aAAa;AAC7B,aAAO,MAAM,QAAQ,OAAO,UAAU,EAAE,aAAa,MAAM,CAAC;AAAA,IAC9D;AAAA,IACA,QAAQ,OAAO,aAAa;AAC1B,YAAM,QAAQ,SAAS,QAAQ;AAC/B,aAAO,MAAM,QAAQ,OAAO,QAAQ;AAAA,IACtC;AAAA,IAEA,eAAe,OAAO,UAAU,aAAa;AAC3C,aAAO,MAAM,KAAK,OAAO;AAAA,QACvB,WAAW;AAAA,QACX,cAAc;AAAA,QACd,UAAU,eAAe,QAAQ;AAAA,MACnC,CAAC;AAAA,IACH;AAAA,IAEA;AAAA,EACF;AACF;","names":[]}
|
package/package.json
CHANGED
@@ -29,7 +29,7 @@
|
|
29
29
|
"conversational-ui",
|
30
30
|
"conversational-ai"
|
31
31
|
],
|
32
|
-
"version": "0.7.
|
32
|
+
"version": "0.7.83",
|
33
33
|
"license": "MIT",
|
34
34
|
"exports": {
|
35
35
|
".": {
|
@@ -113,9 +113,9 @@
|
|
113
113
|
"tailwindcss": "^3.4.17",
|
114
114
|
"tailwindcss-animate": "^1.0.7",
|
115
115
|
"tsx": "^4.19.2",
|
116
|
+
"@assistant-ui/tsconfig": "0.0.0",
|
116
117
|
"@assistant-ui/tailwindcss-transformer": "0.1.0",
|
117
|
-
"@assistant-ui/tsbuildutils": "^0.0.1"
|
118
|
-
"@assistant-ui/tsconfig": "0.0.0"
|
118
|
+
"@assistant-ui/tsbuildutils": "^0.0.1"
|
119
119
|
},
|
120
120
|
"publishConfig": {
|
121
121
|
"access": "public",
|
@@ -2,17 +2,22 @@ import {
|
|
2
2
|
AssistantCloudAuthStrategy,
|
3
3
|
AssistantCloudJWTAuthStrategy,
|
4
4
|
AssistantCloudAPIKeyAuthStrategy,
|
5
|
+
AssistantCloudAnonymousAuthStrategy,
|
5
6
|
} from "./AssistantCloudAuthStrategy";
|
6
7
|
|
7
8
|
export type AssistantCloudConfig =
|
8
9
|
| {
|
9
10
|
baseUrl: string;
|
10
|
-
authToken: (
|
11
|
+
authToken: () => Promise<string | null>;
|
11
12
|
}
|
12
13
|
| {
|
13
14
|
apiKey: string;
|
14
15
|
userId: string;
|
15
16
|
workspaceId: string;
|
17
|
+
}
|
18
|
+
| {
|
19
|
+
baseUrl: string;
|
20
|
+
anonymous: true;
|
16
21
|
};
|
17
22
|
|
18
23
|
class CloudAPIError extends Error {
|
@@ -37,13 +42,20 @@ export class AssistantCloudAPI {
|
|
37
42
|
if ("authToken" in config) {
|
38
43
|
this._baseUrl = config.baseUrl;
|
39
44
|
this._auth = new AssistantCloudJWTAuthStrategy(config.authToken);
|
40
|
-
} else {
|
45
|
+
} else if ("apiKey" in config) {
|
41
46
|
this._baseUrl = "https://backend.assistant-api.com";
|
42
47
|
this._auth = new AssistantCloudAPIKeyAuthStrategy(
|
43
48
|
config.apiKey,
|
44
49
|
config.userId,
|
45
50
|
config.workspaceId,
|
46
51
|
);
|
52
|
+
} else if ("anonymous" in config) {
|
53
|
+
this._baseUrl = config.baseUrl;
|
54
|
+
this._auth = new AssistantCloudAnonymousAuthStrategy(config.baseUrl);
|
55
|
+
} else {
|
56
|
+
throw new Error(
|
57
|
+
"Invalid configuration: Must provide authToken, apiKey, or anonymous configuration",
|
58
|
+
);
|
47
59
|
}
|
48
60
|
}
|
49
61
|
|
@@ -1,9 +1,39 @@
|
|
1
1
|
export type AssistantCloudAuthStrategy = {
|
2
|
-
readonly strategy: "jwt" | "api-key";
|
2
|
+
readonly strategy: "anon" | "jwt" | "api-key";
|
3
3
|
getAuthHeaders(): Promise<Record<string, string> | false>;
|
4
4
|
readAuthHeaders(headers: Headers): void;
|
5
5
|
};
|
6
6
|
|
7
|
+
const getJwtExpiry = (jwt: string): number => {
|
8
|
+
try {
|
9
|
+
const parts = jwt.split(".");
|
10
|
+
const bodyPart = parts[1];
|
11
|
+
if (!bodyPart) {
|
12
|
+
throw new Error("Invalid JWT format");
|
13
|
+
}
|
14
|
+
|
15
|
+
// Convert from Base64Url to Base64 and add padding if necessary
|
16
|
+
let base64 = bodyPart.replace(/-/g, "+").replace(/_/g, "/");
|
17
|
+
while (base64.length % 4 !== 0) {
|
18
|
+
base64 += "=";
|
19
|
+
}
|
20
|
+
|
21
|
+
// Decode the Base64 string and parse the payload
|
22
|
+
const payload = atob(base64);
|
23
|
+
const payloadObj = JSON.parse(payload);
|
24
|
+
const exp = payloadObj.exp;
|
25
|
+
|
26
|
+
if (!exp || typeof exp !== "number") {
|
27
|
+
throw new Error('JWT does not contain a valid "exp" field');
|
28
|
+
}
|
29
|
+
|
30
|
+
// Convert expiration time to milliseconds
|
31
|
+
return exp * 1000;
|
32
|
+
} catch (error) {
|
33
|
+
throw new Error("Unable to determine the token expiry: " + error);
|
34
|
+
}
|
35
|
+
};
|
36
|
+
|
7
37
|
export class AssistantCloudJWTAuthStrategy
|
8
38
|
implements AssistantCloudAuthStrategy
|
9
39
|
{
|
@@ -11,65 +41,32 @@ export class AssistantCloudJWTAuthStrategy
|
|
11
41
|
|
12
42
|
private cachedToken: string | null = null;
|
13
43
|
private tokenExpiry: number | null = null;
|
44
|
+
#authTokenCallback: () => Promise<string | null>;
|
14
45
|
|
15
|
-
|
16
|
-
|
17
|
-
constructor(authTokenCallback: (() => Promise<string | null>) | undefined) {
|
46
|
+
constructor(authTokenCallback: () => Promise<string | null>) {
|
18
47
|
this.#authTokenCallback = authTokenCallback;
|
19
48
|
}
|
20
49
|
|
21
|
-
private getJwtExpiry(jwt: string): number {
|
22
|
-
try {
|
23
|
-
const bodyPart = jwt.split(".").at(1);
|
24
|
-
if (!bodyPart) {
|
25
|
-
throw new Error("Invalid JWT format");
|
26
|
-
}
|
27
|
-
|
28
|
-
// Base64Url decode
|
29
|
-
const payload = Buffer.from(
|
30
|
-
bodyPart.replace(/-/g, "+").replace(/_/g, "/"),
|
31
|
-
"base64",
|
32
|
-
).toString();
|
33
|
-
const payloadObj = JSON.parse(payload);
|
34
|
-
|
35
|
-
const exp = payloadObj.exp;
|
36
|
-
if (!exp || typeof exp !== "number") {
|
37
|
-
throw new Error('JWT does not contain a valid "exp" field');
|
38
|
-
}
|
39
|
-
|
40
|
-
// Convert exp to milliseconds
|
41
|
-
return exp * 1000;
|
42
|
-
} catch (error) {
|
43
|
-
throw new Error("Unable to determine the token expiry " + error);
|
44
|
-
}
|
45
|
-
}
|
46
|
-
|
47
50
|
public async getAuthHeaders(): Promise<Record<string, string> | false> {
|
48
51
|
const currentTime = Date.now();
|
49
52
|
|
50
|
-
//
|
53
|
+
// Use cached token if it's valid for at least 30 more seconds
|
51
54
|
if (
|
52
55
|
this.cachedToken &&
|
53
56
|
this.tokenExpiry &&
|
54
|
-
this.tokenExpiry - currentTime > 30 * 1000
|
57
|
+
this.tokenExpiry - currentTime > 30 * 1000
|
55
58
|
) {
|
56
|
-
return {
|
57
|
-
Authorization: `Bearer ${this.cachedToken}`,
|
58
|
-
};
|
59
|
+
return { Authorization: `Bearer ${this.cachedToken}` };
|
59
60
|
}
|
60
61
|
|
61
|
-
// Fetch a new token
|
62
|
-
const newToken = await this.#authTokenCallback
|
62
|
+
// Fetch a new token via the callback
|
63
|
+
const newToken = await this.#authTokenCallback();
|
63
64
|
if (!newToken) return false;
|
64
65
|
|
65
|
-
const expiry = this.getJwtExpiry(newToken);
|
66
|
-
|
67
66
|
this.cachedToken = newToken;
|
68
|
-
this.tokenExpiry =
|
67
|
+
this.tokenExpiry = getJwtExpiry(newToken);
|
69
68
|
|
70
|
-
return {
|
71
|
-
Authorization: `Bearer ${newToken}`,
|
72
|
-
};
|
69
|
+
return { Authorization: `Bearer ${newToken}` };
|
73
70
|
}
|
74
71
|
|
75
72
|
public readAuthHeaders(headers: Headers) {
|
@@ -77,11 +74,12 @@ export class AssistantCloudJWTAuthStrategy
|
|
77
74
|
if (!authHeader) return;
|
78
75
|
|
79
76
|
const [scheme, token] = authHeader.split(" ");
|
80
|
-
if (scheme !== "Bearer" || !token)
|
77
|
+
if (scheme !== "Bearer" || !token) {
|
81
78
|
throw new Error("Invalid auth header received");
|
79
|
+
}
|
82
80
|
|
83
81
|
this.cachedToken = token;
|
84
|
-
this.tokenExpiry =
|
82
|
+
this.tokenExpiry = getJwtExpiry(token);
|
85
83
|
}
|
86
84
|
}
|
87
85
|
|
@@ -90,9 +88,9 @@ export class AssistantCloudAPIKeyAuthStrategy
|
|
90
88
|
{
|
91
89
|
public readonly strategy = "api-key";
|
92
90
|
|
93
|
-
#apiKey;
|
94
|
-
#userId;
|
95
|
-
#workspaceId;
|
91
|
+
#apiKey: string;
|
92
|
+
#userId: string;
|
93
|
+
#workspaceId: string;
|
96
94
|
|
97
95
|
constructor(apiKey: string, userId: string, workspaceId: string) {
|
98
96
|
this.#apiKey = apiKey;
|
@@ -109,6 +107,87 @@ export class AssistantCloudAPIKeyAuthStrategy
|
|
109
107
|
}
|
110
108
|
|
111
109
|
public readAuthHeaders() {
|
112
|
-
//
|
110
|
+
// No operation needed for API key auth
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
const AUI_REFRESH_TOKEN_NAME = "aui:refresh_token";
|
115
|
+
|
116
|
+
export class AssistantCloudAnonymousAuthStrategy
|
117
|
+
implements AssistantCloudAuthStrategy
|
118
|
+
{
|
119
|
+
public readonly strategy = "anon";
|
120
|
+
|
121
|
+
private baseUrl: string;
|
122
|
+
private jwtStrategy: AssistantCloudJWTAuthStrategy;
|
123
|
+
|
124
|
+
constructor(baseUrl: string) {
|
125
|
+
this.baseUrl = baseUrl;
|
126
|
+
this.jwtStrategy = new AssistantCloudJWTAuthStrategy(async () => {
|
127
|
+
const currentTime = Date.now();
|
128
|
+
const storedRefreshTokenJson = localStorage.getItem(
|
129
|
+
AUI_REFRESH_TOKEN_NAME,
|
130
|
+
);
|
131
|
+
const storedRefreshToken = storedRefreshTokenJson
|
132
|
+
? (JSON.parse(storedRefreshTokenJson) as {
|
133
|
+
token: string;
|
134
|
+
expires_at: string;
|
135
|
+
})
|
136
|
+
: undefined;
|
137
|
+
|
138
|
+
if (storedRefreshToken) {
|
139
|
+
const refreshExpiry = new Date(storedRefreshToken.expires_at).getTime();
|
140
|
+
if (refreshExpiry - currentTime > 30 * 1000) {
|
141
|
+
const response = await fetch(
|
142
|
+
`${this.baseUrl}/v1/auth/tokens/refresh`,
|
143
|
+
{
|
144
|
+
method: "POST",
|
145
|
+
headers: { "Content-Type": "application/json" },
|
146
|
+
body: JSON.stringify({ refresh_token: storedRefreshToken.token }),
|
147
|
+
},
|
148
|
+
);
|
149
|
+
|
150
|
+
if (response.ok) {
|
151
|
+
const data = await response.json();
|
152
|
+
const { access_token, refresh_token } = data;
|
153
|
+
if (refresh_token) {
|
154
|
+
localStorage.setItem(
|
155
|
+
AUI_REFRESH_TOKEN_NAME,
|
156
|
+
JSON.stringify(refresh_token),
|
157
|
+
);
|
158
|
+
}
|
159
|
+
return access_token;
|
160
|
+
}
|
161
|
+
} else {
|
162
|
+
localStorage.removeItem(AUI_REFRESH_TOKEN_NAME);
|
163
|
+
}
|
164
|
+
}
|
165
|
+
|
166
|
+
// No valid refresh token; request a new anonymous token
|
167
|
+
const response = await fetch(`${this.baseUrl}/v1/auth/tokens/anonymous`, {
|
168
|
+
method: "POST",
|
169
|
+
});
|
170
|
+
|
171
|
+
if (!response.ok) return null;
|
172
|
+
|
173
|
+
const data = await response.json();
|
174
|
+
const { access_token, refresh_token } = data;
|
175
|
+
|
176
|
+
if (!access_token || !refresh_token) return null;
|
177
|
+
|
178
|
+
localStorage.setItem(
|
179
|
+
AUI_REFRESH_TOKEN_NAME,
|
180
|
+
JSON.stringify(refresh_token),
|
181
|
+
);
|
182
|
+
return access_token;
|
183
|
+
});
|
184
|
+
}
|
185
|
+
|
186
|
+
public async getAuthHeaders(): Promise<Record<string, string> | false> {
|
187
|
+
return this.jwtStrategy.getAuthHeaders();
|
188
|
+
}
|
189
|
+
|
190
|
+
public readAuthHeaders(headers: Headers): void {
|
191
|
+
this.jwtStrategy.readAuthHeaders(headers);
|
113
192
|
}
|
114
193
|
}
|
@@ -23,6 +23,11 @@ type CloudThreadListAdapterOptions = {
|
|
23
23
|
delete?(threadId: string): Promise<void>;
|
24
24
|
};
|
25
25
|
|
26
|
+
const baseUrl = process.env["NEXT_PUBLIC_ASSISTANT_BASE_URL"];
|
27
|
+
const autoCloud = baseUrl
|
28
|
+
? new AssistantCloud({ baseUrl, anonymous: true })
|
29
|
+
: undefined;
|
30
|
+
|
26
31
|
export const useCloudThreadListAdapter = (
|
27
32
|
adapter: CloudThreadListAdapterOptions,
|
28
33
|
): RemoteThreadListAdapter => {
|
@@ -31,7 +36,25 @@ export const useCloudThreadListAdapter = (
|
|
31
36
|
adapterRef.current = adapter;
|
32
37
|
}, [adapter]);
|
33
38
|
|
34
|
-
const
|
39
|
+
const unstable_Provider = useCallback<FC<PropsWithChildren>>(
|
40
|
+
({ children }) => {
|
41
|
+
const history = useAssistantCloudThreadHistoryAdapter({
|
42
|
+
get current() {
|
43
|
+
return adapterRef.current.cloud ?? autoCloud!;
|
44
|
+
},
|
45
|
+
});
|
46
|
+
const adapters = useMemo(() => ({ history }), [history]);
|
47
|
+
|
48
|
+
return (
|
49
|
+
<RuntimeAdapterProvider adapters={adapters}>
|
50
|
+
{children}
|
51
|
+
</RuntimeAdapterProvider>
|
52
|
+
);
|
53
|
+
},
|
54
|
+
[],
|
55
|
+
);
|
56
|
+
|
57
|
+
const cloud = adapter.cloud ?? autoCloud;
|
35
58
|
if (!cloud) return new InMemoryThreadListAdapter();
|
36
59
|
|
37
60
|
return {
|
@@ -81,19 +104,6 @@ export const useCloudThreadListAdapter = (
|
|
81
104
|
});
|
82
105
|
},
|
83
106
|
|
84
|
-
unstable_Provider
|
85
|
-
const history = useAssistantCloudThreadHistoryAdapter({
|
86
|
-
get current() {
|
87
|
-
return adapterRef.current.cloud!;
|
88
|
-
},
|
89
|
-
});
|
90
|
-
const adapters = useMemo(() => ({ history }), [history]);
|
91
|
-
|
92
|
-
return (
|
93
|
-
<RuntimeAdapterProvider adapters={adapters}>
|
94
|
-
{children}
|
95
|
-
</RuntimeAdapterProvider>
|
96
|
-
);
|
97
|
-
}, []),
|
107
|
+
unstable_Provider,
|
98
108
|
};
|
99
109
|
};
|