@mindstudio-ai/interface 0.1.0 → 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 +114 -20
- package/dist/index.d.ts +194 -78
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Frontend SDK for [MindStudio](https://mindstudio.ai) v2 app web interfaces.
|
|
4
4
|
|
|
5
|
-
Typed RPC to backend methods, file uploads,
|
|
5
|
+
Typed RPC to backend methods, file uploads, authentication, and agent chat — all from the browser. Zero dependencies.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -13,27 +13,36 @@ npm install @mindstudio-ai/interface
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
|
15
15
|
```tsx
|
|
16
|
-
import { createClient, platform, auth } from '@mindstudio-ai/interface';
|
|
16
|
+
import { createClient, platform, auth, type AppUser } from '@mindstudio-ai/interface';
|
|
17
17
|
|
|
18
18
|
const api = createClient();
|
|
19
19
|
|
|
20
|
+
// Reactive auth state — re-renders on login/logout
|
|
21
|
+
function useAuth() {
|
|
22
|
+
const [user, setUser] = useState<AppUser | null>(null);
|
|
23
|
+
useEffect(() => auth.onAuthStateChanged(setUser), []);
|
|
24
|
+
return user;
|
|
25
|
+
}
|
|
26
|
+
|
|
20
27
|
function App() {
|
|
21
|
-
const
|
|
28
|
+
const user = useAuth();
|
|
29
|
+
|
|
30
|
+
if (!user) return <LoginPage />;
|
|
31
|
+
|
|
32
|
+
return <Dashboard user={user} />;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function Dashboard({ user }: { user: AppUser }) {
|
|
36
|
+
const [data, setData] = useState(null);
|
|
22
37
|
|
|
23
38
|
useEffect(() => {
|
|
24
|
-
api.getDashboard().then(
|
|
39
|
+
api.getDashboard().then(setData);
|
|
25
40
|
}, []);
|
|
26
41
|
|
|
27
|
-
const handleUpload = async (file: File) => {
|
|
28
|
-
const url = await platform.uploadFile(file);
|
|
29
|
-
// use the CDN url...
|
|
30
|
-
};
|
|
31
|
-
|
|
32
42
|
return (
|
|
33
43
|
<div>
|
|
34
|
-
<p>Welcome, {
|
|
35
|
-
<
|
|
36
|
-
<input type="file" onChange={(e) => handleUpload(e.target.files[0])} />
|
|
44
|
+
<p>Welcome, {user.email}</p>
|
|
45
|
+
<button onClick={() => auth.logout()}>Log out</button>
|
|
37
46
|
</div>
|
|
38
47
|
);
|
|
39
48
|
}
|
|
@@ -75,16 +84,99 @@ const url = await platform.uploadFile(file);
|
|
|
75
84
|
|
|
76
85
|
### `auth`
|
|
77
86
|
|
|
78
|
-
|
|
87
|
+
Authentication flows, user state, and validation helpers. The platform handles verification code delivery, cookie management, and user storage — you build the login UI.
|
|
88
|
+
|
|
89
|
+
#### Login flow
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
import { auth } from '@mindstudio-ai/interface';
|
|
93
|
+
|
|
94
|
+
// Send a verification code
|
|
95
|
+
const { verificationId } = await auth.sendEmailCode('user@example.com');
|
|
96
|
+
|
|
97
|
+
// User enters the code in your UI...
|
|
98
|
+
const user = await auth.verifyEmailCode(verificationId, code);
|
|
99
|
+
// Session is now active — all SDK calls use the authenticated token
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
SMS works the same way — use `auth.sendSmsCode(phone)` and `auth.verifySmsCode(verificationId, code)`. Phone numbers must be E.164 format.
|
|
103
|
+
|
|
104
|
+
#### User state
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
auth.getCurrentUser() // { id, email, phone, roles, createdAt } or null
|
|
108
|
+
auth.isAuthenticated() // boolean
|
|
109
|
+
await auth.logout() // clears session
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### Phone helpers
|
|
113
|
+
|
|
114
|
+
Utilities for building a phone input with country code picker:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
auth.phone.countries // [{ code: 'US', dialCode: '+1', name: 'United States', flag: '🇺🇸' }, ...]
|
|
118
|
+
auth.phone.detectCountry() // 'US' — guessed from timezone
|
|
119
|
+
auth.phone.toE164('5551234567', 'US') // '+15551234567'
|
|
120
|
+
auth.phone.format('+15551234567') // '+1 (555) 123-4567'
|
|
121
|
+
auth.phone.isValid('+15551234567') // true
|
|
122
|
+
auth.email.isValid('user@example.com') // true
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### Email/phone change
|
|
126
|
+
|
|
127
|
+
Authenticated users can change their email or phone through a verification flow:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
await auth.requestEmailChange('new@example.com');
|
|
131
|
+
await auth.confirmEmailChange('new@example.com', code);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
#### Reactive auth state
|
|
135
|
+
|
|
136
|
+
`onAuthStateChanged` fires immediately with the current user, then again on every auth transition. Use it to build reactive UIs:
|
|
79
137
|
|
|
80
138
|
```ts
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
139
|
+
// React hook
|
|
140
|
+
function useAuth() {
|
|
141
|
+
const [user, setUser] = useState<AppUser | null>(null);
|
|
142
|
+
useEffect(() => auth.onAuthStateChanged(setUser), []);
|
|
143
|
+
return user;
|
|
144
|
+
}
|
|
85
145
|
```
|
|
86
146
|
|
|
87
|
-
|
|
147
|
+
You can also read the current user synchronously via `auth.currentUser`.
|
|
148
|
+
|
|
149
|
+
#### Auth error codes
|
|
150
|
+
|
|
151
|
+
Auth methods throw `MindStudioInterfaceError`. Handle specific cases via `err.code`:
|
|
152
|
+
|
|
153
|
+
| Code | Status | Meaning |
|
|
154
|
+
|------|--------|---------|
|
|
155
|
+
| `rate_limited` | 429 | Too many code requests (max 5 per 15 min) |
|
|
156
|
+
| `invalid_code` | 400 | Wrong verification code |
|
|
157
|
+
| `verification_expired` | 400 | Code expired (10 min TTL) |
|
|
158
|
+
| `max_attempts_exceeded` | 400 | Too many incorrect attempts (max 3) |
|
|
159
|
+
| `not_authenticated` | 401 | No auth session (change/logout endpoints) |
|
|
160
|
+
| `invalid_session` | 401 | Session expired or invalid |
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
try {
|
|
164
|
+
await auth.verifyEmailCode(verificationId, code);
|
|
165
|
+
} catch (err) {
|
|
166
|
+
if (err instanceof MindStudioInterfaceError) {
|
|
167
|
+
if (err.code === 'invalid_code') {
|
|
168
|
+
showError('Wrong code, try again');
|
|
169
|
+
} else if (err.code === 'verification_expired') {
|
|
170
|
+
showError('Code expired — sending a new one');
|
|
171
|
+
await auth.sendEmailCode(email);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
#### Session management
|
|
178
|
+
|
|
179
|
+
Verify, confirm, and logout methods update the SDK's internal session in-place. All downstream calls (method invocation, agent chat, uploads) immediately use the new authenticated (or unauthenticated) session. No page refresh needed.
|
|
88
180
|
|
|
89
181
|
### `createAgentChatClient()`
|
|
90
182
|
|
|
@@ -220,9 +312,11 @@ try {
|
|
|
220
312
|
|
|
221
313
|
## How it works
|
|
222
314
|
|
|
223
|
-
The MindStudio platform injects `window.__MINDSTUDIO__` into the page before your code runs. This contains the session token, user
|
|
315
|
+
The MindStudio platform injects `window.__MINDSTUDIO__` into the page before your code runs. This contains the session token, authenticated user (or `null`), and method registry. The SDK reads this automatically — no configuration needed.
|
|
316
|
+
|
|
317
|
+
All API calls use same-origin `/_/` paths (e.g. `/_/methods/{id}/invoke`, `/_/agent/threads`, `/_/auth/email/send`). The platform proxy resolves the app from the subdomain — no cross-origin requests or app IDs in URLs. This works identically in production and local dev.
|
|
224
318
|
|
|
225
|
-
|
|
319
|
+
Authentication is cookie-based (`HttpOnly`, `Secure`, `SameSite=Lax`). The SDK never touches the cookie directly — it's set by the server on verify and cleared on logout. Auth state transitions (login, logout, email/phone change) return a fresh session token which the SDK applies in-place, so all subsequent API calls use the new session without a page refresh.
|
|
226
320
|
|
|
227
321
|
## License
|
|
228
322
|
|
package/dist/index.d.ts
CHANGED
|
@@ -549,73 +549,30 @@ declare const platform: {
|
|
|
549
549
|
};
|
|
550
550
|
|
|
551
551
|
/**
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
* Provides synchronous access to the current user's name, email, and
|
|
555
|
-
* profile picture. Hydrated from the bootstrap globals injected by the
|
|
556
|
-
* platform — no HTTP call needed.
|
|
557
|
-
*
|
|
558
|
-
* This is for **display only**. Role checking, permission enforcement,
|
|
559
|
-
* and user lookups for other users are backend concerns — the frontend
|
|
560
|
-
* calls backend methods that handle those via `@mindstudio-ai/agent`'s
|
|
561
|
-
* `auth` namespace.
|
|
562
|
-
*
|
|
563
|
-
* @example
|
|
564
|
-
* ```ts
|
|
565
|
-
* import { auth } from '@mindstudio-ai/interface';
|
|
566
|
-
*
|
|
567
|
-
* return (
|
|
568
|
-
* <div>
|
|
569
|
-
* <p>Welcome, {auth.name}</p>
|
|
570
|
-
* <img src={auth.profilePictureUrl} />
|
|
571
|
-
* </div>
|
|
572
|
-
* );
|
|
573
|
-
* ```
|
|
574
|
-
*/
|
|
575
|
-
/**
|
|
576
|
-
* User identity context. All properties are read-only and synchronous.
|
|
577
|
-
*/
|
|
578
|
-
interface AuthContext {
|
|
579
|
-
/** Current user's ID (UUID). */
|
|
580
|
-
readonly userId: string;
|
|
581
|
-
/** Current user's display name. */
|
|
582
|
-
readonly name: string;
|
|
583
|
-
/** Current user's email address. */
|
|
584
|
-
readonly email: string;
|
|
585
|
-
/** Current user's profile picture URL, or null if not set. */
|
|
586
|
-
readonly profilePictureUrl: string | null;
|
|
587
|
-
}
|
|
588
|
-
/**
|
|
589
|
-
* Lazy auth proxy — reads from bootstrap globals on first property access.
|
|
552
|
+
* Type definitions for the @mindstudio-ai/interface SDK.
|
|
590
553
|
*
|
|
591
|
-
*
|
|
592
|
-
*
|
|
593
|
-
* This avoids throwing during import if the page hasn't finished loading
|
|
594
|
-
* the bootstrap script yet.
|
|
554
|
+
* These types describe the bootstrap globals injected by the platform,
|
|
555
|
+
* the authenticated user, and options for platform actions.
|
|
595
556
|
*/
|
|
596
|
-
declare const auth: AuthContext;
|
|
597
|
-
|
|
598
557
|
/**
|
|
599
|
-
*
|
|
558
|
+
* An authenticated app user. This is the same shape in
|
|
559
|
+
* `window.__MINDSTUDIO__.user`, auth API responses, and
|
|
560
|
+
* `auth.getCurrentUser()`.
|
|
600
561
|
*
|
|
601
|
-
*
|
|
602
|
-
* actions on failures, and by the config reader when bootstrap globals
|
|
603
|
-
* are missing.
|
|
562
|
+
* `null` means unauthenticated (app has auth enabled but no session).
|
|
604
563
|
*/
|
|
605
|
-
|
|
606
|
-
/**
|
|
607
|
-
|
|
608
|
-
/**
|
|
609
|
-
|
|
610
|
-
|
|
564
|
+
interface AppUser {
|
|
565
|
+
/** User ID (UUID) — matches the row ID in the app's user table. */
|
|
566
|
+
id: string;
|
|
567
|
+
/** Email address, or null if the user signed up via phone. */
|
|
568
|
+
email: string | null;
|
|
569
|
+
/** Phone number in E.164 format, or null if the user signed up via email. */
|
|
570
|
+
phone: string | null;
|
|
571
|
+
/** Role IDs assigned to this user. */
|
|
572
|
+
roles: string[];
|
|
573
|
+
/** ISO 8601 timestamp of when the user was created. */
|
|
574
|
+
createdAt: string;
|
|
611
575
|
}
|
|
612
|
-
|
|
613
|
-
/**
|
|
614
|
-
* Type definitions for the @mindstudio-ai/interface SDK.
|
|
615
|
-
*
|
|
616
|
-
* These types describe the bootstrap globals injected by the platform,
|
|
617
|
-
* the user context, and options for platform actions.
|
|
618
|
-
*/
|
|
619
576
|
/**
|
|
620
577
|
* Bootstrap configuration injected into the page by the MindStudio
|
|
621
578
|
* platform before the app's JavaScript runs.
|
|
@@ -623,10 +580,6 @@ declare class MindStudioInterfaceError extends Error {
|
|
|
623
580
|
* Available at `window.__MINDSTUDIO__`. The SDK reads this automatically —
|
|
624
581
|
* app code should use `createClient()`, `auth`, and `platform` instead
|
|
625
582
|
* of accessing this directly.
|
|
626
|
-
*
|
|
627
|
-
* Injection mechanism: The Cloudflare Worker on `*.static.mscdn.ai`
|
|
628
|
-
* intercepts requests for `index.html`, injects a `<script>` tag with
|
|
629
|
-
* the session context, and returns the modified HTML.
|
|
630
583
|
*/
|
|
631
584
|
interface BootstrapConfig {
|
|
632
585
|
/** Short-lived session token scoped to this app + user. */
|
|
@@ -637,8 +590,13 @@ interface BootstrapConfig {
|
|
|
637
590
|
releaseId: string;
|
|
638
591
|
/** API base URL (optional — SDK uses same-origin `/_/` paths). */
|
|
639
592
|
apiBaseUrl?: string;
|
|
640
|
-
/**
|
|
641
|
-
|
|
593
|
+
/**
|
|
594
|
+
* Authenticated user, or `null` if unauthenticated.
|
|
595
|
+
*
|
|
596
|
+
* For apps with auth enabled: `null` until the user verifies.
|
|
597
|
+
* For apps without auth: populated with a guest identity.
|
|
598
|
+
*/
|
|
599
|
+
user: AppUser | null;
|
|
642
600
|
/**
|
|
643
601
|
* Method registry mapping export names to method IDs.
|
|
644
602
|
* Embedded at injection time from the release manifest.
|
|
@@ -650,19 +608,177 @@ interface BootstrapConfig {
|
|
|
650
608
|
*/
|
|
651
609
|
methods: Record<string, string>;
|
|
652
610
|
}
|
|
611
|
+
|
|
653
612
|
/**
|
|
654
|
-
*
|
|
655
|
-
*
|
|
613
|
+
* Phone number utilities for auth flows.
|
|
614
|
+
*
|
|
615
|
+
* Provides country data, E.164 conversion, formatting, and validation.
|
|
616
|
+
* No external dependencies — designed for login/signup UIs that need
|
|
617
|
+
* a country code picker and phone number formatting.
|
|
656
618
|
*/
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
619
|
+
/** A country with its dial code and flag emoji. */
|
|
620
|
+
interface Country {
|
|
621
|
+
/** ISO 3166-1 alpha-2 code (e.g. "US"). */
|
|
622
|
+
code: string;
|
|
623
|
+
/** International dial code with + prefix (e.g. "+1"). */
|
|
624
|
+
dialCode: string;
|
|
625
|
+
/** Country name in English. */
|
|
661
626
|
name: string;
|
|
662
|
-
/**
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
627
|
+
/** Flag emoji. */
|
|
628
|
+
flag: string;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Auth — app-managed authentication for MindStudio v2 apps.
|
|
633
|
+
*
|
|
634
|
+
* Provides verification code flows (email + SMS), session state
|
|
635
|
+
* management, and phone/email validation helpers. The platform
|
|
636
|
+
* handles code delivery, cookie management, and user storage —
|
|
637
|
+
* developers build their own login UI using these methods.
|
|
638
|
+
*
|
|
639
|
+
* ## How it works
|
|
640
|
+
*
|
|
641
|
+
* ```
|
|
642
|
+
* // 1. Send a verification code
|
|
643
|
+
* const { verificationId } = await auth.sendEmailCode('user@example.com');
|
|
644
|
+
*
|
|
645
|
+
* // 2. User enters the code in your UI
|
|
646
|
+
* const user = await auth.verifyEmailCode(verificationId, '123456');
|
|
647
|
+
*
|
|
648
|
+
* // 3. Session is now active — all SDK calls use the authenticated token
|
|
649
|
+
* const result = await api.getDashboard(); // uses authenticated session
|
|
650
|
+
* auth.getCurrentUser(); // { id, email, phone, roles, createdAt }
|
|
651
|
+
* ```
|
|
652
|
+
*
|
|
653
|
+
* Verify, confirm, and logout methods update `window.__MINDSTUDIO__`
|
|
654
|
+
* in-place so all downstream calls (method invocation, agent chat,
|
|
655
|
+
* uploads) immediately use the new session. No page refresh needed.
|
|
656
|
+
*
|
|
657
|
+
* @example
|
|
658
|
+
* ```tsx
|
|
659
|
+
* import { auth } from '@mindstudio-ai/interface';
|
|
660
|
+
*
|
|
661
|
+
* function LoginPage() {
|
|
662
|
+
* const [email, setEmail] = useState('');
|
|
663
|
+
* const [verificationId, setVerificationId] = useState('');
|
|
664
|
+
* const [code, setCode] = useState('');
|
|
665
|
+
*
|
|
666
|
+
* const handleSend = async () => {
|
|
667
|
+
* const { verificationId } = await auth.sendEmailCode(email);
|
|
668
|
+
* setVerificationId(verificationId);
|
|
669
|
+
* };
|
|
670
|
+
*
|
|
671
|
+
* const handleVerify = async () => {
|
|
672
|
+
* await auth.verifyEmailCode(verificationId, code);
|
|
673
|
+
* // Session updated in-place — navigate to your app
|
|
674
|
+
* window.location.href = '/dashboard';
|
|
675
|
+
* };
|
|
676
|
+
* }
|
|
677
|
+
* ```
|
|
678
|
+
*/
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* The auth namespace — authentication flows, state, and helpers.
|
|
682
|
+
*
|
|
683
|
+
* Auth methods throw {@link MindStudioInterfaceError} on failure.
|
|
684
|
+
* Common error codes:
|
|
685
|
+
*
|
|
686
|
+
* | Code | Status | Meaning |
|
|
687
|
+
* |------|--------|---------|
|
|
688
|
+
* | `rate_limited` | 429 | Too many code requests (max 5 per 15 min per identifier) |
|
|
689
|
+
* | `invalid_code` | 400 | Wrong verification code |
|
|
690
|
+
* | `verification_expired` | 400 | Code expired (10 min TTL) — request a new one |
|
|
691
|
+
* | `max_attempts_exceeded` | 400 | Too many incorrect attempts (max 3) — request a new code |
|
|
692
|
+
* | `not_authenticated` | 401 | Auth cookie missing (change/logout endpoints) |
|
|
693
|
+
* | `invalid_session` | 401 | Auth cookie expired or invalid |
|
|
694
|
+
*/
|
|
695
|
+
interface Auth {
|
|
696
|
+
/** The current authenticated user, or `null` if not authenticated. */
|
|
697
|
+
readonly currentUser: AppUser | null;
|
|
698
|
+
/** Get the current authenticated user, or `null` if not authenticated. */
|
|
699
|
+
getCurrentUser(): AppUser | null;
|
|
700
|
+
/** Whether the current session is authenticated. */
|
|
701
|
+
isAuthenticated(): boolean;
|
|
702
|
+
/** Send a 6-digit verification code to an email address. */
|
|
703
|
+
sendEmailCode(email: string): Promise<{
|
|
704
|
+
verificationId: string;
|
|
705
|
+
}>;
|
|
706
|
+
/**
|
|
707
|
+
* Verify an email code. On success, updates the session in-place
|
|
708
|
+
* and returns the authenticated user.
|
|
709
|
+
*/
|
|
710
|
+
verifyEmailCode(verificationId: string, code: string): Promise<AppUser>;
|
|
711
|
+
/** Send a 6-digit verification code via SMS. Phone must be E.164. */
|
|
712
|
+
sendSmsCode(phone: string): Promise<{
|
|
713
|
+
verificationId: string;
|
|
714
|
+
}>;
|
|
715
|
+
/**
|
|
716
|
+
* Verify an SMS code. On success, updates the session in-place
|
|
717
|
+
* and returns the authenticated user.
|
|
718
|
+
*/
|
|
719
|
+
verifySmsCode(verificationId: string, code: string): Promise<AppUser>;
|
|
720
|
+
/** Request an email change. Sends a code to the new email. */
|
|
721
|
+
requestEmailChange(newEmail: string): Promise<void>;
|
|
722
|
+
/** Confirm an email change with the verification code. */
|
|
723
|
+
confirmEmailChange(newEmail: string, code: string): Promise<AppUser>;
|
|
724
|
+
/** Request a phone change. Sends a code to the new phone (E.164). */
|
|
725
|
+
requestPhoneChange(newPhone: string): Promise<void>;
|
|
726
|
+
/** Confirm a phone change with the verification code. */
|
|
727
|
+
confirmPhoneChange(newPhone: string, code: string): Promise<AppUser>;
|
|
728
|
+
/** Log out. Clears the cookie and updates the session to unauthenticated. */
|
|
729
|
+
logout(): Promise<void>;
|
|
730
|
+
/**
|
|
731
|
+
* Subscribe to auth state changes. Fires immediately with the
|
|
732
|
+
* current state, then again whenever verify, confirm, or logout
|
|
733
|
+
* updates the session.
|
|
734
|
+
*
|
|
735
|
+
* @returns An unsubscribe function.
|
|
736
|
+
*
|
|
737
|
+
* @example
|
|
738
|
+
* ```ts
|
|
739
|
+
* // React hook
|
|
740
|
+
* function useAuth() {
|
|
741
|
+
* const [user, setUser] = useState<AppUser | null>(null);
|
|
742
|
+
* useEffect(() => auth.onAuthStateChanged(setUser), []);
|
|
743
|
+
* return user;
|
|
744
|
+
* }
|
|
745
|
+
* ```
|
|
746
|
+
*/
|
|
747
|
+
onAuthStateChanged(callback: (user: AppUser | null) => void): () => void;
|
|
748
|
+
/** Phone number utilities — countries, formatting, validation. */
|
|
749
|
+
phone: {
|
|
750
|
+
/** All countries with dial codes, sorted alphabetically. */
|
|
751
|
+
countries: readonly Country[];
|
|
752
|
+
/** Detect the user's country from their timezone. Falls back to `'US'`. */
|
|
753
|
+
detectCountry(): string;
|
|
754
|
+
/** Format an E.164 number for display (e.g. `+1 (555) 123-4567`). */
|
|
755
|
+
format(e164: string): string;
|
|
756
|
+
/** Convert a national number to E.164 (e.g. `('5551234567', 'US') → '+15551234567'`). */
|
|
757
|
+
toE164(national: string, countryCode: string): string;
|
|
758
|
+
/** Check if a string is a valid E.164 phone number. */
|
|
759
|
+
isValid(phone: string): boolean;
|
|
760
|
+
};
|
|
761
|
+
/** Email validation. */
|
|
762
|
+
email: {
|
|
763
|
+
/** Basic email format check. */
|
|
764
|
+
isValid(email: string): boolean;
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
declare const auth: Auth;
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Error class for the @mindstudio-ai/interface SDK.
|
|
771
|
+
*
|
|
772
|
+
* Thrown by `createClient()` method calls on HTTP errors, by `platform`
|
|
773
|
+
* actions on failures, and by the config reader when bootstrap globals
|
|
774
|
+
* are missing.
|
|
775
|
+
*/
|
|
776
|
+
declare class MindStudioInterfaceError extends Error {
|
|
777
|
+
/** Machine-readable error code (e.g. 'method_not_found', 'not_initialized'). */
|
|
778
|
+
readonly code: string;
|
|
779
|
+
/** HTTP status code, when the error originated from an API response. */
|
|
780
|
+
readonly status?: number;
|
|
781
|
+
constructor(message: string, code: string, status?: number);
|
|
666
782
|
}
|
|
667
783
|
|
|
668
|
-
export { type AbortablePromise, type AgentChatClient, type AgentChatEvent, type
|
|
784
|
+
export { type AbortablePromise, type AgentChatClient, type AgentChatEvent, type AppUser, type Auth, type BootstrapConfig, type Country, type InvokeOptions, type Message, MindStudioInterfaceError, type SendMessageCallbacks, type SendMessageOptions, type SendMessageResult, type Thread, type ThreadListPage, type ThreadSummary, type UploadFileOptions, auth, createAgentChatClient, createClient, platform };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
2
|
-
`);
|
|
3
|
-
`);p=m.pop()??"";for(let c of m){if(!c.startsWith("data: "))continue;let E=c.slice(6);try{let C=JSON.parse(E);C.type==="done"&&(T={stopReason:C.stopReason,usage:C.usage}),A(C,o)}catch{}}}if(!T)throw new s("Stream ended without a done event","stream_incomplete");return T})();return l.abort=()=>a.abort(),l}}}var M={async uploadFile(e,t){let n=h(),{onProgress:i,signal:a}=t??{};a?.throwIfAborted();let r=await fetch("/_/generate-upload-request",{method:"POST",headers:{Authorization:`Bearer ${n.token}`,"Content-Type":"application/json"},body:JSON.stringify({filename:e.name,contentType:e.type||"application/octet-stream"}),signal:a});if(!r.ok)throw new s(`Upload presign failed: ${r.status} ${r.statusText}`,"upload_error",r.status);let{uploadUrl:l,uploadFields:d,publicUrl:w}=await r.json(),u=new FormData;for(let[y,p]of Object.entries(d))u.append(y,p);if(u.append("file",e),i)return await P(l,u,i,a),w;let f=await fetch(l,{method:"POST",body:u,signal:a});if(!f.ok)throw new s(`File upload failed: ${f.status} ${f.statusText}`,"upload_error",f.status);return w}};function P(e,t,n,i){return new Promise((a,o)=>{let r=new XMLHttpRequest;if(r.upload.addEventListener("progress",l=>{l.lengthComputable&&n(l.loaded/l.total)}),r.addEventListener("load",()=>{r.status>=200&&r.status<300?(n(1),a()):o(new s(`File upload failed: ${r.status} ${r.statusText}`,"upload_error",r.status))}),r.addEventListener("error",()=>{o(new s("File upload failed: network error","upload_error"))}),r.addEventListener("abort",()=>{o(new DOMException("Upload aborted","AbortError"))}),i){if(i.aborted){o(new DOMException("Upload aborted","AbortError"));return}i.addEventListener("abort",()=>r.abort(),{once:!0})}r.open("POST",e),r.send(t)})}var O=new Proxy({},{get(e,t){let{user:n}=h();switch(t){case"userId":return n.id;case"name":return n.name;case"email":return n.email;case"profilePictureUrl":return n.profilePictureUrl??null;default:return}}});export{s as MindStudioInterfaceError,O as auth,_ as createAgentChatClient,v as createClient,M as platform};
|
|
1
|
+
var d=class extends Error{code;status;constructor(a,o,i){super(a),this.name="MindStudioInterfaceError",this.code=o,this.status=i}};var M;function c(){if(M)return M;let e=globalThis.__MINDSTUDIO__;if(!e)throw new d("@mindstudio-ai/interface requires the MindStudio platform context. This page must be loaded inside MindStudio (window.__MINDSTUDIO__ is missing).","not_initialized");if(!e.token||!e.releaseId||!e.methods){let a=["token","releaseId","methods"].filter(o=>!e[o]).join(", ");throw new d(`MindStudio platform context is incomplete. Missing: ${a}`,"invalid_config")}return M=e,M}function x(e){let a=c();e.token!==void 0&&(a.token=e.token),e.user!==void 0&&(a.user=e.user),e.methods!==void 0&&(a.methods=e.methods)}function G(){return new Proxy({},{get(e,a){return async(o,i)=>{let t=c(),n=t.methods[a];if(!n)throw new d(`Method "${a}" not found. Available methods: ${Object.keys(t.methods).join(", ")||"(none)"}`,"method_not_found");let r=`/_/methods/${n}/invoke`,l=i?.stream===!0,s=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.token}`},body:JSON.stringify({input:o??{},...l&&{stream:!0}})});if(!s.ok){let u=`Method "${a}" failed: ${s.status} ${s.statusText}`,A="method_error";try{let m=await s.json();m.error&&(u=m.error),m.code&&(A=m.code)}catch{}throw new d(u,A,s.status)}if(!l)return(await s.json()).output;let E=s.body.getReader(),g=new TextDecoder,C="",S;for(;;){let{done:u,value:A}=await E.read();if(u)break;C+=g.decode(A,{stream:!0});let m=C.split(`
|
|
2
|
+
`);C=m.pop()??"";for(let T of m){if(!T.startsWith("data: "))continue;let p=T.slice(6);try{let f=JSON.parse(p);f.type==="token"&&i?.onToken&&f.text?i.onToken(f.text):f.type==="error"&&f.error?i?.onStreamError&&i.onStreamError(f.error):f.type==="done"&&(S=f.output)}catch{}}}if(S===void 0)throw new d("Stream ended without a done event","stream_incomplete");return S}}})}var U="/_/agent";async function y(e,a,o){let t={Authorization:`Bearer ${c().token}`};o!==void 0&&(t["Content-Type"]="application/json");let n=await fetch(`${U}${e}`,{method:a,headers:t,...o!==void 0&&{body:JSON.stringify(o)}});if(!n.ok){let r=`Agent chat request failed: ${n.status} ${n.statusText}`,l="agent_chat_error";try{let s=await n.json();s.error&&(r=s.error),s.code&&(l=s.code)}catch{}throw new d(r,l,n.status)}return await n.json()}function N(e,a){switch(a.onEvent?.(e),e.type){case"text":a.onText?.(e.text);break;case"thinking":a.onThinking?.(e.text);break;case"thinking_complete":a.onThinkingComplete?.(e.thinking,e.signature);break;case"tool_call_start":a.onToolCallStart?.(e.id,e.name);break;case"tool_call_result":a.onToolCallResult?.(e.id,e.output);break;case"error":a.onError?.(e.error);break}}function D(){return{createThread(){return y("/threads","POST")},listThreads(e){let a=e?`?cursor=${encodeURIComponent(e)}`:"";return y(`/threads${a}`,"GET")},getThread(e){return y(`/threads/${e}`,"GET")},async updateThread(e,a){await y(`/threads/${e}`,"PATCH",{title:a})},async deleteThread(e){await y(`/threads/${e}`,"DELETE")},sendMessage(e,a,o,i){let t=new AbortController,n=o??{};n.signal&&(n.signal.aborted?t.abort():n.signal.addEventListener("abort",()=>t.abort(),{once:!0}));let l=(async()=>{let s=c(),E=`${U}/threads/${e}/messages`,g=await fetch(E,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s.token}`},body:JSON.stringify({content:a,...i?.attachments?.length&&{attachments:i.attachments}}),signal:t.signal});if(!g.ok){let m=`Send message failed: ${g.status} ${g.statusText}`,T="agent_chat_error";try{let p=await g.json();p.error&&(m=p.error),p.code&&(T=p.code)}catch{}throw new d(m,T,g.status)}let C=g.body.getReader(),S=new TextDecoder,u="",A;for(;;){let{done:m,value:T}=await C.read();if(m)break;u+=S.decode(T,{stream:!0});let p=u.split(`
|
|
3
|
+
`);u=p.pop()??"";for(let f of p){if(!f.startsWith("data: "))continue;let O=f.slice(6);try{let w=JSON.parse(O);w.type==="done"&&(A={stopReason:w.stopReason,usage:w.usage}),N(w,n)}catch{}}}if(!A)throw new d("Stream ended without a done event","stream_incomplete");return A})();return l.abort=()=>t.abort(),l}}}var H={async uploadFile(e,a){let o=c(),{onProgress:i,signal:t}=a??{};t?.throwIfAborted();let r=await fetch("/_/generate-upload-request",{method:"POST",headers:{Authorization:`Bearer ${o.token}`,"Content-Type":"application/json"},body:JSON.stringify({filename:e.name,contentType:e.type||"application/octet-stream"}),signal:t});if(!r.ok)throw new d(`Upload presign failed: ${r.status} ${r.statusText}`,"upload_error",r.status);let{uploadUrl:l,uploadFields:s,publicUrl:E}=await r.json(),g=new FormData;for(let[S,u]of Object.entries(s))g.append(S,u);if(g.append("file",e),i)return await K(l,g,i,t),E;let C=await fetch(l,{method:"POST",body:g,signal:t});if(!C.ok)throw new d(`File upload failed: ${C.status} ${C.statusText}`,"upload_error",C.status);return E}};function K(e,a,o,i){return new Promise((t,n)=>{let r=new XMLHttpRequest;if(r.upload.addEventListener("progress",l=>{l.lengthComputable&&o(l.loaded/l.total)}),r.addEventListener("load",()=>{r.status>=200&&r.status<300?(o(1),t()):n(new d(`File upload failed: ${r.status} ${r.statusText}`,"upload_error",r.status))}),r.addEventListener("error",()=>{n(new d("File upload failed: network error","upload_error"))}),r.addEventListener("abort",()=>{n(new DOMException("Upload aborted","AbortError"))}),i){if(i.aborted){n(new DOMException("Upload aborted","AbortError"));return}i.addEventListener("abort",()=>r.abort(),{once:!0})}r.open("POST",e),r.send(a)})}var P=[{code:"AF",dialCode:"+93",name:"Afghanistan",flag:"\u{1F1E6}\u{1F1EB}"},{code:"AL",dialCode:"+355",name:"Albania",flag:"\u{1F1E6}\u{1F1F1}"},{code:"DZ",dialCode:"+213",name:"Algeria",flag:"\u{1F1E9}\u{1F1FF}"},{code:"AD",dialCode:"+376",name:"Andorra",flag:"\u{1F1E6}\u{1F1E9}"},{code:"AO",dialCode:"+244",name:"Angola",flag:"\u{1F1E6}\u{1F1F4}"},{code:"AG",dialCode:"+1",name:"Antigua and Barbuda",flag:"\u{1F1E6}\u{1F1EC}"},{code:"AR",dialCode:"+54",name:"Argentina",flag:"\u{1F1E6}\u{1F1F7}"},{code:"AM",dialCode:"+374",name:"Armenia",flag:"\u{1F1E6}\u{1F1F2}"},{code:"AU",dialCode:"+61",name:"Australia",flag:"\u{1F1E6}\u{1F1FA}"},{code:"AT",dialCode:"+43",name:"Austria",flag:"\u{1F1E6}\u{1F1F9}"},{code:"AZ",dialCode:"+994",name:"Azerbaijan",flag:"\u{1F1E6}\u{1F1FF}"},{code:"BS",dialCode:"+1",name:"Bahamas",flag:"\u{1F1E7}\u{1F1F8}"},{code:"BH",dialCode:"+973",name:"Bahrain",flag:"\u{1F1E7}\u{1F1ED}"},{code:"BD",dialCode:"+880",name:"Bangladesh",flag:"\u{1F1E7}\u{1F1E9}"},{code:"BB",dialCode:"+1",name:"Barbados",flag:"\u{1F1E7}\u{1F1E7}"},{code:"BY",dialCode:"+375",name:"Belarus",flag:"\u{1F1E7}\u{1F1FE}"},{code:"BE",dialCode:"+32",name:"Belgium",flag:"\u{1F1E7}\u{1F1EA}"},{code:"BZ",dialCode:"+501",name:"Belize",flag:"\u{1F1E7}\u{1F1FF}"},{code:"BJ",dialCode:"+229",name:"Benin",flag:"\u{1F1E7}\u{1F1EF}"},{code:"BT",dialCode:"+975",name:"Bhutan",flag:"\u{1F1E7}\u{1F1F9}"},{code:"BO",dialCode:"+591",name:"Bolivia",flag:"\u{1F1E7}\u{1F1F4}"},{code:"BA",dialCode:"+387",name:"Bosnia and Herzegovina",flag:"\u{1F1E7}\u{1F1E6}"},{code:"BW",dialCode:"+267",name:"Botswana",flag:"\u{1F1E7}\u{1F1FC}"},{code:"BR",dialCode:"+55",name:"Brazil",flag:"\u{1F1E7}\u{1F1F7}"},{code:"BN",dialCode:"+673",name:"Brunei",flag:"\u{1F1E7}\u{1F1F3}"},{code:"BG",dialCode:"+359",name:"Bulgaria",flag:"\u{1F1E7}\u{1F1EC}"},{code:"BF",dialCode:"+226",name:"Burkina Faso",flag:"\u{1F1E7}\u{1F1EB}"},{code:"BI",dialCode:"+257",name:"Burundi",flag:"\u{1F1E7}\u{1F1EE}"},{code:"CV",dialCode:"+238",name:"Cabo Verde",flag:"\u{1F1E8}\u{1F1FB}"},{code:"KH",dialCode:"+855",name:"Cambodia",flag:"\u{1F1F0}\u{1F1ED}"},{code:"CM",dialCode:"+237",name:"Cameroon",flag:"\u{1F1E8}\u{1F1F2}"},{code:"CA",dialCode:"+1",name:"Canada",flag:"\u{1F1E8}\u{1F1E6}"},{code:"CF",dialCode:"+236",name:"Central African Republic",flag:"\u{1F1E8}\u{1F1EB}"},{code:"TD",dialCode:"+235",name:"Chad",flag:"\u{1F1F9}\u{1F1E9}"},{code:"CL",dialCode:"+56",name:"Chile",flag:"\u{1F1E8}\u{1F1F1}"},{code:"CN",dialCode:"+86",name:"China",flag:"\u{1F1E8}\u{1F1F3}"},{code:"CO",dialCode:"+57",name:"Colombia",flag:"\u{1F1E8}\u{1F1F4}"},{code:"KM",dialCode:"+269",name:"Comoros",flag:"\u{1F1F0}\u{1F1F2}"},{code:"CG",dialCode:"+242",name:"Congo",flag:"\u{1F1E8}\u{1F1EC}"},{code:"CD",dialCode:"+243",name:"Congo (DRC)",flag:"\u{1F1E8}\u{1F1E9}"},{code:"CR",dialCode:"+506",name:"Costa Rica",flag:"\u{1F1E8}\u{1F1F7}"},{code:"HR",dialCode:"+385",name:"Croatia",flag:"\u{1F1ED}\u{1F1F7}"},{code:"CU",dialCode:"+53",name:"Cuba",flag:"\u{1F1E8}\u{1F1FA}"},{code:"CY",dialCode:"+357",name:"Cyprus",flag:"\u{1F1E8}\u{1F1FE}"},{code:"CZ",dialCode:"+420",name:"Czechia",flag:"\u{1F1E8}\u{1F1FF}"},{code:"CI",dialCode:"+225",name:"Cote d'Ivoire",flag:"\u{1F1E8}\u{1F1EE}"},{code:"DK",dialCode:"+45",name:"Denmark",flag:"\u{1F1E9}\u{1F1F0}"},{code:"DJ",dialCode:"+253",name:"Djibouti",flag:"\u{1F1E9}\u{1F1EF}"},{code:"DM",dialCode:"+1",name:"Dominica",flag:"\u{1F1E9}\u{1F1F2}"},{code:"DO",dialCode:"+1",name:"Dominican Republic",flag:"\u{1F1E9}\u{1F1F4}"},{code:"EC",dialCode:"+593",name:"Ecuador",flag:"\u{1F1EA}\u{1F1E8}"},{code:"EG",dialCode:"+20",name:"Egypt",flag:"\u{1F1EA}\u{1F1EC}"},{code:"SV",dialCode:"+503",name:"El Salvador",flag:"\u{1F1F8}\u{1F1FB}"},{code:"GQ",dialCode:"+240",name:"Equatorial Guinea",flag:"\u{1F1EC}\u{1F1F6}"},{code:"ER",dialCode:"+291",name:"Eritrea",flag:"\u{1F1EA}\u{1F1F7}"},{code:"EE",dialCode:"+372",name:"Estonia",flag:"\u{1F1EA}\u{1F1EA}"},{code:"SZ",dialCode:"+268",name:"Eswatini",flag:"\u{1F1F8}\u{1F1FF}"},{code:"ET",dialCode:"+251",name:"Ethiopia",flag:"\u{1F1EA}\u{1F1F9}"},{code:"FJ",dialCode:"+679",name:"Fiji",flag:"\u{1F1EB}\u{1F1EF}"},{code:"FI",dialCode:"+358",name:"Finland",flag:"\u{1F1EB}\u{1F1EE}"},{code:"FR",dialCode:"+33",name:"France",flag:"\u{1F1EB}\u{1F1F7}"},{code:"GA",dialCode:"+241",name:"Gabon",flag:"\u{1F1EC}\u{1F1E6}"},{code:"GM",dialCode:"+220",name:"Gambia",flag:"\u{1F1EC}\u{1F1F2}"},{code:"GE",dialCode:"+995",name:"Georgia",flag:"\u{1F1EC}\u{1F1EA}"},{code:"DE",dialCode:"+49",name:"Germany",flag:"\u{1F1E9}\u{1F1EA}"},{code:"GH",dialCode:"+233",name:"Ghana",flag:"\u{1F1EC}\u{1F1ED}"},{code:"GR",dialCode:"+30",name:"Greece",flag:"\u{1F1EC}\u{1F1F7}"},{code:"GD",dialCode:"+1",name:"Grenada",flag:"\u{1F1EC}\u{1F1E9}"},{code:"GT",dialCode:"+502",name:"Guatemala",flag:"\u{1F1EC}\u{1F1F9}"},{code:"GN",dialCode:"+224",name:"Guinea",flag:"\u{1F1EC}\u{1F1F3}"},{code:"GW",dialCode:"+245",name:"Guinea-Bissau",flag:"\u{1F1EC}\u{1F1FC}"},{code:"GY",dialCode:"+592",name:"Guyana",flag:"\u{1F1EC}\u{1F1FE}"},{code:"HT",dialCode:"+509",name:"Haiti",flag:"\u{1F1ED}\u{1F1F9}"},{code:"HN",dialCode:"+504",name:"Honduras",flag:"\u{1F1ED}\u{1F1F3}"},{code:"HK",dialCode:"+852",name:"Hong Kong",flag:"\u{1F1ED}\u{1F1F0}"},{code:"HU",dialCode:"+36",name:"Hungary",flag:"\u{1F1ED}\u{1F1FA}"},{code:"IS",dialCode:"+354",name:"Iceland",flag:"\u{1F1EE}\u{1F1F8}"},{code:"IN",dialCode:"+91",name:"India",flag:"\u{1F1EE}\u{1F1F3}"},{code:"ID",dialCode:"+62",name:"Indonesia",flag:"\u{1F1EE}\u{1F1E9}"},{code:"IR",dialCode:"+98",name:"Iran",flag:"\u{1F1EE}\u{1F1F7}"},{code:"IQ",dialCode:"+964",name:"Iraq",flag:"\u{1F1EE}\u{1F1F6}"},{code:"IE",dialCode:"+353",name:"Ireland",flag:"\u{1F1EE}\u{1F1EA}"},{code:"IL",dialCode:"+972",name:"Israel",flag:"\u{1F1EE}\u{1F1F1}"},{code:"IT",dialCode:"+39",name:"Italy",flag:"\u{1F1EE}\u{1F1F9}"},{code:"JM",dialCode:"+1",name:"Jamaica",flag:"\u{1F1EF}\u{1F1F2}"},{code:"JP",dialCode:"+81",name:"Japan",flag:"\u{1F1EF}\u{1F1F5}"},{code:"JO",dialCode:"+962",name:"Jordan",flag:"\u{1F1EF}\u{1F1F4}"},{code:"KZ",dialCode:"+7",name:"Kazakhstan",flag:"\u{1F1F0}\u{1F1FF}"},{code:"KE",dialCode:"+254",name:"Kenya",flag:"\u{1F1F0}\u{1F1EA}"},{code:"KI",dialCode:"+686",name:"Kiribati",flag:"\u{1F1F0}\u{1F1EE}"},{code:"KW",dialCode:"+965",name:"Kuwait",flag:"\u{1F1F0}\u{1F1FC}"},{code:"KG",dialCode:"+996",name:"Kyrgyzstan",flag:"\u{1F1F0}\u{1F1EC}"},{code:"LA",dialCode:"+856",name:"Laos",flag:"\u{1F1F1}\u{1F1E6}"},{code:"LV",dialCode:"+371",name:"Latvia",flag:"\u{1F1F1}\u{1F1FB}"},{code:"LB",dialCode:"+961",name:"Lebanon",flag:"\u{1F1F1}\u{1F1E7}"},{code:"LS",dialCode:"+266",name:"Lesotho",flag:"\u{1F1F1}\u{1F1F8}"},{code:"LR",dialCode:"+231",name:"Liberia",flag:"\u{1F1F1}\u{1F1F7}"},{code:"LY",dialCode:"+218",name:"Libya",flag:"\u{1F1F1}\u{1F1FE}"},{code:"LI",dialCode:"+423",name:"Liechtenstein",flag:"\u{1F1F1}\u{1F1EE}"},{code:"LT",dialCode:"+370",name:"Lithuania",flag:"\u{1F1F1}\u{1F1F9}"},{code:"LU",dialCode:"+352",name:"Luxembourg",flag:"\u{1F1F1}\u{1F1FA}"},{code:"MO",dialCode:"+853",name:"Macao",flag:"\u{1F1F2}\u{1F1F4}"},{code:"MG",dialCode:"+261",name:"Madagascar",flag:"\u{1F1F2}\u{1F1EC}"},{code:"MW",dialCode:"+265",name:"Malawi",flag:"\u{1F1F2}\u{1F1FC}"},{code:"MY",dialCode:"+60",name:"Malaysia",flag:"\u{1F1F2}\u{1F1FE}"},{code:"MV",dialCode:"+960",name:"Maldives",flag:"\u{1F1F2}\u{1F1FB}"},{code:"ML",dialCode:"+223",name:"Mali",flag:"\u{1F1F2}\u{1F1F1}"},{code:"MT",dialCode:"+356",name:"Malta",flag:"\u{1F1F2}\u{1F1F9}"},{code:"MH",dialCode:"+692",name:"Marshall Islands",flag:"\u{1F1F2}\u{1F1ED}"},{code:"MR",dialCode:"+222",name:"Mauritania",flag:"\u{1F1F2}\u{1F1F7}"},{code:"MU",dialCode:"+230",name:"Mauritius",flag:"\u{1F1F2}\u{1F1FA}"},{code:"MX",dialCode:"+52",name:"Mexico",flag:"\u{1F1F2}\u{1F1FD}"},{code:"FM",dialCode:"+691",name:"Micronesia",flag:"\u{1F1EB}\u{1F1F2}"},{code:"MD",dialCode:"+373",name:"Moldova",flag:"\u{1F1F2}\u{1F1E9}"},{code:"MC",dialCode:"+377",name:"Monaco",flag:"\u{1F1F2}\u{1F1E8}"},{code:"MN",dialCode:"+976",name:"Mongolia",flag:"\u{1F1F2}\u{1F1F3}"},{code:"ME",dialCode:"+382",name:"Montenegro",flag:"\u{1F1F2}\u{1F1EA}"},{code:"MA",dialCode:"+212",name:"Morocco",flag:"\u{1F1F2}\u{1F1E6}"},{code:"MZ",dialCode:"+258",name:"Mozambique",flag:"\u{1F1F2}\u{1F1FF}"},{code:"MM",dialCode:"+95",name:"Myanmar",flag:"\u{1F1F2}\u{1F1F2}"},{code:"NA",dialCode:"+264",name:"Namibia",flag:"\u{1F1F3}\u{1F1E6}"},{code:"NR",dialCode:"+674",name:"Nauru",flag:"\u{1F1F3}\u{1F1F7}"},{code:"NP",dialCode:"+977",name:"Nepal",flag:"\u{1F1F3}\u{1F1F5}"},{code:"NL",dialCode:"+31",name:"Netherlands",flag:"\u{1F1F3}\u{1F1F1}"},{code:"NZ",dialCode:"+64",name:"New Zealand",flag:"\u{1F1F3}\u{1F1FF}"},{code:"NI",dialCode:"+505",name:"Nicaragua",flag:"\u{1F1F3}\u{1F1EE}"},{code:"NE",dialCode:"+227",name:"Niger",flag:"\u{1F1F3}\u{1F1EA}"},{code:"NG",dialCode:"+234",name:"Nigeria",flag:"\u{1F1F3}\u{1F1EC}"},{code:"KP",dialCode:"+850",name:"North Korea",flag:"\u{1F1F0}\u{1F1F5}"},{code:"MK",dialCode:"+389",name:"North Macedonia",flag:"\u{1F1F2}\u{1F1F0}"},{code:"NO",dialCode:"+47",name:"Norway",flag:"\u{1F1F3}\u{1F1F4}"},{code:"OM",dialCode:"+968",name:"Oman",flag:"\u{1F1F4}\u{1F1F2}"},{code:"PK",dialCode:"+92",name:"Pakistan",flag:"\u{1F1F5}\u{1F1F0}"},{code:"PW",dialCode:"+680",name:"Palau",flag:"\u{1F1F5}\u{1F1FC}"},{code:"PS",dialCode:"+970",name:"Palestine",flag:"\u{1F1F5}\u{1F1F8}"},{code:"PA",dialCode:"+507",name:"Panama",flag:"\u{1F1F5}\u{1F1E6}"},{code:"PG",dialCode:"+675",name:"Papua New Guinea",flag:"\u{1F1F5}\u{1F1EC}"},{code:"PY",dialCode:"+595",name:"Paraguay",flag:"\u{1F1F5}\u{1F1FE}"},{code:"PE",dialCode:"+51",name:"Peru",flag:"\u{1F1F5}\u{1F1EA}"},{code:"PH",dialCode:"+63",name:"Philippines",flag:"\u{1F1F5}\u{1F1ED}"},{code:"PL",dialCode:"+48",name:"Poland",flag:"\u{1F1F5}\u{1F1F1}"},{code:"PT",dialCode:"+351",name:"Portugal",flag:"\u{1F1F5}\u{1F1F9}"},{code:"PR",dialCode:"+1",name:"Puerto Rico",flag:"\u{1F1F5}\u{1F1F7}"},{code:"QA",dialCode:"+974",name:"Qatar",flag:"\u{1F1F6}\u{1F1E6}"},{code:"RO",dialCode:"+40",name:"Romania",flag:"\u{1F1F7}\u{1F1F4}"},{code:"RU",dialCode:"+7",name:"Russia",flag:"\u{1F1F7}\u{1F1FA}"},{code:"RW",dialCode:"+250",name:"Rwanda",flag:"\u{1F1F7}\u{1F1FC}"},{code:"KN",dialCode:"+1",name:"Saint Kitts and Nevis",flag:"\u{1F1F0}\u{1F1F3}"},{code:"LC",dialCode:"+1",name:"Saint Lucia",flag:"\u{1F1F1}\u{1F1E8}"},{code:"VC",dialCode:"+1",name:"Saint Vincent and the Grenadines",flag:"\u{1F1FB}\u{1F1E8}"},{code:"WS",dialCode:"+685",name:"Samoa",flag:"\u{1F1FC}\u{1F1F8}"},{code:"SM",dialCode:"+378",name:"San Marino",flag:"\u{1F1F8}\u{1F1F2}"},{code:"ST",dialCode:"+239",name:"Sao Tome and Principe",flag:"\u{1F1F8}\u{1F1F9}"},{code:"SA",dialCode:"+966",name:"Saudi Arabia",flag:"\u{1F1F8}\u{1F1E6}"},{code:"SN",dialCode:"+221",name:"Senegal",flag:"\u{1F1F8}\u{1F1F3}"},{code:"RS",dialCode:"+381",name:"Serbia",flag:"\u{1F1F7}\u{1F1F8}"},{code:"SC",dialCode:"+248",name:"Seychelles",flag:"\u{1F1F8}\u{1F1E8}"},{code:"SL",dialCode:"+232",name:"Sierra Leone",flag:"\u{1F1F8}\u{1F1F1}"},{code:"SG",dialCode:"+65",name:"Singapore",flag:"\u{1F1F8}\u{1F1EC}"},{code:"SK",dialCode:"+421",name:"Slovakia",flag:"\u{1F1F8}\u{1F1F0}"},{code:"SI",dialCode:"+386",name:"Slovenia",flag:"\u{1F1F8}\u{1F1EE}"},{code:"SB",dialCode:"+677",name:"Solomon Islands",flag:"\u{1F1F8}\u{1F1E7}"},{code:"SO",dialCode:"+252",name:"Somalia",flag:"\u{1F1F8}\u{1F1F4}"},{code:"ZA",dialCode:"+27",name:"South Africa",flag:"\u{1F1FF}\u{1F1E6}"},{code:"KR",dialCode:"+82",name:"South Korea",flag:"\u{1F1F0}\u{1F1F7}"},{code:"SS",dialCode:"+211",name:"South Sudan",flag:"\u{1F1F8}\u{1F1F8}"},{code:"ES",dialCode:"+34",name:"Spain",flag:"\u{1F1EA}\u{1F1F8}"},{code:"LK",dialCode:"+94",name:"Sri Lanka",flag:"\u{1F1F1}\u{1F1F0}"},{code:"SD",dialCode:"+249",name:"Sudan",flag:"\u{1F1F8}\u{1F1E9}"},{code:"SR",dialCode:"+597",name:"Suriname",flag:"\u{1F1F8}\u{1F1F7}"},{code:"SE",dialCode:"+46",name:"Sweden",flag:"\u{1F1F8}\u{1F1EA}"},{code:"CH",dialCode:"+41",name:"Switzerland",flag:"\u{1F1E8}\u{1F1ED}"},{code:"SY",dialCode:"+963",name:"Syria",flag:"\u{1F1F8}\u{1F1FE}"},{code:"TW",dialCode:"+886",name:"Taiwan",flag:"\u{1F1F9}\u{1F1FC}"},{code:"TJ",dialCode:"+992",name:"Tajikistan",flag:"\u{1F1F9}\u{1F1EF}"},{code:"TZ",dialCode:"+255",name:"Tanzania",flag:"\u{1F1F9}\u{1F1FF}"},{code:"TH",dialCode:"+66",name:"Thailand",flag:"\u{1F1F9}\u{1F1ED}"},{code:"TL",dialCode:"+670",name:"Timor-Leste",flag:"\u{1F1F9}\u{1F1F1}"},{code:"TG",dialCode:"+228",name:"Togo",flag:"\u{1F1F9}\u{1F1EC}"},{code:"TO",dialCode:"+676",name:"Tonga",flag:"\u{1F1F9}\u{1F1F4}"},{code:"TT",dialCode:"+1",name:"Trinidad and Tobago",flag:"\u{1F1F9}\u{1F1F9}"},{code:"TN",dialCode:"+216",name:"Tunisia",flag:"\u{1F1F9}\u{1F1F3}"},{code:"TR",dialCode:"+90",name:"Turkey",flag:"\u{1F1F9}\u{1F1F7}"},{code:"TM",dialCode:"+993",name:"Turkmenistan",flag:"\u{1F1F9}\u{1F1F2}"},{code:"TV",dialCode:"+688",name:"Tuvalu",flag:"\u{1F1F9}\u{1F1FB}"},{code:"UG",dialCode:"+256",name:"Uganda",flag:"\u{1F1FA}\u{1F1EC}"},{code:"UA",dialCode:"+380",name:"Ukraine",flag:"\u{1F1FA}\u{1F1E6}"},{code:"AE",dialCode:"+971",name:"United Arab Emirates",flag:"\u{1F1E6}\u{1F1EA}"},{code:"GB",dialCode:"+44",name:"United Kingdom",flag:"\u{1F1EC}\u{1F1E7}"},{code:"US",dialCode:"+1",name:"United States",flag:"\u{1F1FA}\u{1F1F8}"},{code:"UY",dialCode:"+598",name:"Uruguay",flag:"\u{1F1FA}\u{1F1FE}"},{code:"UZ",dialCode:"+998",name:"Uzbekistan",flag:"\u{1F1FA}\u{1F1FF}"},{code:"VU",dialCode:"+678",name:"Vanuatu",flag:"\u{1F1FB}\u{1F1FA}"},{code:"VA",dialCode:"+39",name:"Vatican City",flag:"\u{1F1FB}\u{1F1E6}"},{code:"VE",dialCode:"+58",name:"Venezuela",flag:"\u{1F1FB}\u{1F1EA}"},{code:"VN",dialCode:"+84",name:"Vietnam",flag:"\u{1F1FB}\u{1F1F3}"},{code:"YE",dialCode:"+967",name:"Yemen",flag:"\u{1F1FE}\u{1F1EA}"},{code:"ZM",dialCode:"+260",name:"Zambia",flag:"\u{1F1FF}\u{1F1F2}"},{code:"ZW",dialCode:"+263",name:"Zimbabwe",flag:"\u{1F1FF}\u{1F1FC}"}],$=new Map(P.map(e=>[e.code,e])),V={"America/New_York":"US","America/Chicago":"US","America/Denver":"US","America/Los_Angeles":"US","America/Anchorage":"US","Pacific/Honolulu":"US","America/Phoenix":"US","America/Indiana/Indianapolis":"US","America/Toronto":"CA","America/Vancouver":"CA","America/Edmonton":"CA","America/Winnipeg":"CA","America/Halifax":"CA","America/St_Johns":"CA","Europe/London":"GB","Europe/Dublin":"IE","Europe/Paris":"FR","Europe/Berlin":"DE","Europe/Madrid":"ES","Europe/Rome":"IT","Europe/Amsterdam":"NL","Europe/Brussels":"BE","Europe/Zurich":"CH","Europe/Vienna":"AT","Europe/Stockholm":"SE","Europe/Oslo":"NO","Europe/Copenhagen":"DK","Europe/Helsinki":"FI","Europe/Warsaw":"PL","Europe/Prague":"CZ","Europe/Bucharest":"RO","Europe/Budapest":"HU","Europe/Athens":"GR","Europe/Istanbul":"TR","Europe/Moscow":"RU","Europe/Kiev":"UA","Europe/Lisbon":"PT","Europe/Belgrade":"RS","Europe/Zagreb":"HR","Europe/Bratislava":"SK","Europe/Ljubljana":"SI","Europe/Tallinn":"EE","Europe/Riga":"LV","Europe/Vilnius":"LT","Asia/Tokyo":"JP","Asia/Seoul":"KR","Asia/Shanghai":"CN","Asia/Hong_Kong":"HK","Asia/Taipei":"TW","Asia/Singapore":"SG","Asia/Kolkata":"IN","Asia/Karachi":"PK","Asia/Dhaka":"BD","Asia/Bangkok":"TH","Asia/Jakarta":"ID","Asia/Manila":"PH","Asia/Kuala_Lumpur":"MY","Asia/Ho_Chi_Minh":"VN","Asia/Riyadh":"SA","Asia/Dubai":"AE","Asia/Tehran":"IR","Asia/Baghdad":"IQ","Asia/Beirut":"LB","Asia/Jerusalem":"IL","Asia/Amman":"JO","Asia/Almaty":"KZ","Asia/Tashkent":"UZ","Asia/Colombo":"LK","Asia/Kathmandu":"NP","Asia/Yangon":"MM","Australia/Sydney":"AU","Australia/Melbourne":"AU","Australia/Brisbane":"AU","Australia/Perth":"AU","Australia/Adelaide":"AU","Pacific/Auckland":"NZ","Pacific/Fiji":"FJ","Africa/Cairo":"EG","Africa/Lagos":"NG","Africa/Nairobi":"KE","Africa/Johannesburg":"ZA","Africa/Casablanca":"MA","Africa/Accra":"GH","Africa/Addis_Ababa":"ET","Africa/Dar_es_Salaam":"TZ","Africa/Kampala":"UG","America/Mexico_City":"MX","America/Bogota":"CO","America/Lima":"PE","America/Santiago":"CL","America/Buenos_Aires":"AR","America/Sao_Paulo":"BR","America/Caracas":"VE","America/Guatemala":"GT","America/Havana":"CU","America/Panama":"PA","America/Jamaica":"JM"};function B(){try{let e=Intl.DateTimeFormat().resolvedOptions().timeZone;return V[e]??"US"}catch{return"US"}}function R(e){if(!e.startsWith("+"))return e;let a=e.slice(1);if(a.startsWith("1")&&a.length===11){let o=a.slice(1,4),i=a.slice(4,7),t=a.slice(7);return`+1 (${o}) ${i}-${t}`}for(let o=4;o>=1;o--){let i="+"+a.slice(0,o),t=P.find(n=>n.dialCode===i);if(t){let n=a.slice(o);return`${t.dialCode} ${n}`}}return e}function I(e,a){let o=e.replace(/\D/g,""),i=$.get(a);if(!i)return"+"+o;let t=i.dialCode.slice(1);return o.startsWith(t)?"+"+o:i.dialCode+o}function _(e){return/^\+[1-9]\d{6,14}$/.test(e)}function L(e){return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e)}async function h(e,a,o){let t={Authorization:`Bearer ${c().token}`};o!==void 0&&(t["Content-Type"]="application/json");let n=await fetch(e,{method:a,headers:t,...o!==void 0&&{body:JSON.stringify(o)}});if(!n.ok){let r=`Auth request failed: ${n.status} ${n.statusText}`,l="auth_error";try{let s=await n.json();s.error&&(r=s.error),s.code&&(l=s.code)}catch{}throw new d(r,l,n.status)}return await n.json()}var v=new Set;function b(e){x({token:e.token,user:e.user,methods:e.methods}),v.forEach(a=>a(e.user))}function k(e){if(!e.user)throw new d("Verification succeeded but no user was returned","auth_error");return e.user}var J={get currentUser(){return c().user},getCurrentUser(){return c().user},isAuthenticated(){return c().user!==null},sendEmailCode(e){return h("/_/auth/email/send","POST",{email:e})},async verifyEmailCode(e,a){let o=await h("/_/auth/email/verify","POST",{verificationId:e,code:a});return b(o),k(o)},sendSmsCode(e){return h("/_/auth/sms/send","POST",{phone:e})},async verifySmsCode(e,a){let o=await h("/_/auth/sms/verify","POST",{verificationId:e,code:a});return b(o),k(o)},async requestEmailChange(e){await h("/_/auth/email/change","POST",{newEmail:e})},async confirmEmailChange(e,a){let o=await h("/_/auth/email/change/confirm","POST",{newEmail:e,code:a});return b(o),k(o)},async requestPhoneChange(e){await h("/_/auth/phone/change","POST",{newPhone:e})},async confirmPhoneChange(e,a){let o=await h("/_/auth/phone/change/confirm","POST",{newPhone:e,code:a});return b(o),k(o)},async logout(){let e=await h("/_/auth/logout","POST",{});b(e)},onAuthStateChanged(e){return v.add(e),e(c().user),()=>{v.delete(e)}},phone:{countries:P,detectCountry:B,format:R,toE164:I,isValid:_},email:{isValid:L}};export{d as MindStudioInterfaceError,J as auth,D as createAgentChatClient,G as createClient,H as platform};
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/config.ts","../src/client.ts","../src/agent-chat.ts","../src/platform.ts","../src/auth.ts"],"sourcesContent":["/**\n * Error class for the @mindstudio-ai/interface SDK.\n *\n * Thrown by `createClient()` method calls on HTTP errors, by `platform`\n * actions on failures, and by the config reader when bootstrap globals\n * are missing.\n */\nexport class MindStudioInterfaceError extends Error {\n /** Machine-readable error code (e.g. 'method_not_found', 'not_initialized'). */\n readonly code: string;\n\n /** HTTP status code, when the error originated from an API response. */\n readonly status?: number;\n\n constructor(message: string, code: string, status?: number) {\n super(message);\n this.name = 'MindStudioInterfaceError';\n this.code = code;\n this.status = status;\n }\n}\n","/**\n * Bootstrap configuration reader.\n *\n * Reads and validates `window.__MINDSTUDIO__` — the globals injected by\n * the platform before the app's JavaScript runs. The result is cached\n * after first read.\n *\n * All other modules in the SDK call `getConfig()` internally. App code\n * should never need to call this directly — use `createClient()`, `auth`,\n * and `platform` instead.\n */\n\nimport { MindStudioInterfaceError } from './errors.js';\nimport type { BootstrapConfig } from './types.js';\n\nlet _config: BootstrapConfig | undefined;\n\n/**\n * Read and validate the bootstrap configuration from `window.__MINDSTUDIO__`.\n *\n * Caches the result after first successful read. Throws if the globals\n * are missing or incomplete — this means the page isn't running inside\n * the MindStudio platform.\n *\n * @returns The validated bootstrap config\n * @throws {MindStudioInterfaceError} if `window.__MINDSTUDIO__` is missing or invalid\n */\nexport function getConfig(): BootstrapConfig {\n if (_config) {\n return _config;\n }\n\n const raw = (globalThis as Record<string, unknown>).__MINDSTUDIO__ as\n | Partial<BootstrapConfig>\n | undefined;\n\n if (!raw) {\n throw new MindStudioInterfaceError(\n '@mindstudio-ai/interface requires the MindStudio platform context. ' +\n 'This page must be loaded inside MindStudio (window.__MINDSTUDIO__ is missing).',\n 'not_initialized',\n );\n }\n\n // Validate required fields\n if (!raw.token || !raw.releaseId || !raw.user || !raw.methods) {\n const missing = ['token', 'releaseId', 'user', 'methods']\n .filter((k) => !raw[k as keyof BootstrapConfig])\n .join(', ');\n\n throw new MindStudioInterfaceError(\n `MindStudio platform context is incomplete. Missing: ${missing}`,\n 'invalid_config',\n );\n }\n\n _config = raw as BootstrapConfig;\n return _config;\n}\n","/**\n * Method RPC client — typed function calls to backend methods.\n *\n * `createClient()` returns a Proxy where every property access creates\n * an async function that invokes the corresponding backend method via HTTP.\n * The method name matches the method's `export` field in `app.json`.\n *\n * ## How it works\n *\n * ```\n * api.submitVendorRequest({ name: 'Acme' })\n * │\n * ├─ Look up method ID: config.methods[\"submitVendorRequest\"]\n * │ → \"submit-vendor-request\"\n * │\n * ├─ POST /_/methods/submit-vendor-request/invoke\n * │ Authorization: Bearer {session token}\n * │ Body: { input: { name: \"Acme\" } }\n * │\n * └─ Returns response.output (or throws on error)\n * ```\n *\n * The method registry (export name → method ID mapping) is embedded in\n * `window.__MINDSTUDIO__.methods` at page load time — no extra HTTP call\n * needed to discover methods.\n *\n * ## Streaming\n *\n * Methods can optionally stream LLM token output via SSE. Pass\n * `{ stream: true }` as the second argument along with an `onToken`\n * callback to receive incremental updates:\n *\n * ```ts\n * const result = await api.submitVendorRequest({ name: 'Acme' }, {\n * stream: true,\n * onToken: (text) => setResponseText(text),\n * });\n * ```\n *\n * **Important:** The `text` value passed to `onToken` is the accumulated\n * response so far (not a delta). Replace your display content each time —\n * do not append. See {@link InvokeOptions.onToken} for details.\n *\n * ## Type safety\n *\n * Since the frontend and backend live in the same project, you can import\n * types directly:\n *\n * ```ts\n * import type { SubmitVendorInput, SubmitVendorOutput }\n * from '../../backend/src/submitVendorRequest';\n *\n * interface AppMethods {\n * submitVendorRequest(input: SubmitVendorInput): Promise<SubmitVendorOutput>;\n * getDashboard(): Promise<GetDashboardOutput>;\n * }\n *\n * const api = createClient<AppMethods>();\n * const result = await api.submitVendorRequest({ name: 'Acme' }); // fully typed\n * ```\n *\n * If no type parameter is provided, methods accept `any` input and return `any`.\n */\n\nimport { getConfig } from './config.js';\nimport { MindStudioInterfaceError } from './errors.js';\n\n/**\n * Options for method invocation. Pass as the optional second argument to\n * any method on the client returned by {@link createClient}.\n *\n * When `stream` is `true`, the SDK sends `{ stream: true }` in the request\n * body and parses the response as an SSE (Server-Sent Events) stream.\n * Three event types are handled:\n *\n * | SSE `type` | Payload field | SDK action |\n * |------------|---------------|------------|\n * | `token` | `text` | Calls {@link onToken} with accumulated text |\n * | `error` | `error` | Calls {@link onStreamError} with the message |\n * | `done` | `output` | Captures as the method's return value |\n *\n * The method's `Promise` resolves with the final `output` from the `done`\n * event — the same shape you'd get without streaming.\n */\nexport interface InvokeOptions {\n /**\n * Enable SSE streaming for LLM token output.\n *\n * When `true`, the request body includes `{ stream: true }` and the\n * response is parsed as an SSE stream instead of a single JSON payload.\n * The method still returns a `Promise` that resolves with the final\n * output once the stream completes.\n *\n * @default false\n */\n stream?: boolean;\n\n /**\n * Called with the accumulated response text each time a new token arrives.\n *\n * **Important:** `text` is the full response so far, not a delta/chunk.\n * Your frontend should **replace** (not append to) its display content\n * with this value each time the callback fires.\n *\n * This design avoids edge cases where the platform sanitizes or\n * reformats JSON mid-stream, causing earlier portions of the text to\n * change between tokens.\n *\n * @example\n * ```ts\n * const result = await api.generate({ prompt: 'Hello' }, {\n * stream: true,\n * onToken: (text) => {\n * // Replace — do NOT append\n * responseElement.textContent = text;\n * },\n * });\n * ```\n */\n onToken?: (text: string) => void;\n\n /**\n * Called if a stream-level error occurs.\n *\n * Stream errors are non-fatal at the protocol level — the method may\n * still complete and resolve with a final output. Use this callback for\n * logging or showing a transient warning, not for control flow.\n *\n * If the stream ends without a `done` event, the method rejects with a\n * `MindStudioInterfaceError` (code `stream_incomplete`) regardless of\n * whether `onStreamError` was provided.\n */\n onStreamError?: (error: string) => void;\n}\n\n/**\n * Default client type when no generic parameter is provided.\n * Every property returns an async function accepting optional input\n * and optional {@link InvokeOptions} for streaming.\n */\ntype DefaultMethodClient = Record<\n string,\n (input?: Record<string, unknown>, options?: InvokeOptions) => Promise<unknown>\n>;\n\n/**\n * Create a typed RPC client for calling backend methods.\n *\n * @typeParam T - Optional interface mapping method names to their signatures.\n * If omitted, all methods accept `any` and return `Promise<any>`.\n *\n * @returns A Proxy object where each property is an async function that\n * invokes the corresponding backend method.\n *\n * @example\n * ```ts\n * import { createClient } from '@mindstudio-ai/interface';\n *\n * const api = createClient();\n *\n * // Each method maps to a backend method export\n * const result = await api.submitVendorRequest({ name: 'Acme' });\n * const dashboard = await api.getDashboard();\n *\n * // Stream LLM output — onToken receives accumulated text (replace, don't append)\n * const streamed = await api.submitVendorRequest({ name: 'Acme' }, {\n * stream: true,\n * onToken: (text) => setResponseText(text),\n * });\n * // `streamed` is the same final output you'd get without streaming\n * ```\n */\nexport function createClient<T = DefaultMethodClient>(): T {\n return new Proxy({} as Record<string, unknown> as T & object, {\n get(_, methodName: string) {\n return async (input?: unknown, options?: InvokeOptions) => {\n const config = getConfig();\n\n const methodId = config.methods[methodName];\n if (!methodId) {\n throw new MindStudioInterfaceError(\n `Method \"${methodName}\" not found. Available methods: ${Object.keys(config.methods).join(', ') || '(none)'}`,\n 'method_not_found',\n );\n }\n\n const url = `/_/methods/${methodId}/invoke`;\n const wantsStream = options?.stream === true;\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.token}`,\n },\n body: JSON.stringify({\n input: input ?? {},\n ...(wantsStream && { stream: true }),\n }),\n });\n\n if (!res.ok) {\n let errorMessage = `Method \"${methodName}\" failed: ${res.status} ${res.statusText}`;\n let errorCode = 'method_error';\n try {\n const body = (await res.json()) as {\n error?: string;\n code?: string;\n };\n if (body.error) {\n errorMessage = body.error;\n }\n if (body.code) {\n errorCode = body.code;\n }\n } catch {\n // Response wasn't JSON — use the default message\n }\n throw new MindStudioInterfaceError(\n errorMessage,\n errorCode,\n res.status,\n );\n }\n\n // Non-streaming: unchanged behavior\n if (!wantsStream) {\n const body = (await res.json()) as { output?: unknown };\n return body.output;\n }\n\n // Streaming: parse SSE events from response body\n const reader = res.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let finalOutput: unknown;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) {\n continue;\n }\n const json = line.slice(6);\n try {\n const event = JSON.parse(json) as {\n type: string;\n text?: string;\n error?: string;\n output?: unknown;\n };\n\n if (event.type === 'token' && options?.onToken && event.text) {\n options.onToken(event.text);\n } else if (event.type === 'error' && event.error) {\n if (options?.onStreamError) {\n options.onStreamError(event.error);\n }\n } else if (event.type === 'done') {\n finalOutput = event.output;\n }\n } catch {\n // Skip malformed SSE lines\n }\n }\n }\n\n if (finalOutput === undefined) {\n throw new MindStudioInterfaceError(\n 'Stream ended without a done event',\n 'stream_incomplete',\n );\n }\n\n return finalOutput;\n };\n },\n });\n}\n","/**\n * Agent chat client — thread-based conversations with AI agents.\n *\n * `createAgentChatClient()` returns an object with methods for thread\n * lifecycle (create, list, get, rename, delete) and message streaming.\n * The agent runs server-side with access to the app's methods as tools.\n *\n * ## How it works\n *\n * ```\n * chat.sendMessage(threadId, 'Help me prep for my review', {\n * onText: (delta) => setAssistantMessage((prev) => prev + delta),\n * })\n * │\n * ├─ POST /_/agent/threads/{threadId}/messages\n * │ Authorization: Bearer {session token}\n * │ Body: { content: \"Help me prep for my review\" }\n * │\n * ├─ Response: SSE stream\n * │ data: {\"type\":\"text\",\"text\":\"I'll create...\",\"ts\":1711...}\n * │ data: {\"type\":\"tool_call_start\",\"id\":\"call_abc\",\"name\":\"create-todo\",\"ts\":1711...}\n * │ data: {\"type\":\"tool_call_result\",\"id\":\"call_abc\",\"output\":{...},\"ts\":1711...}\n * │ data: {\"type\":\"text\",\"text\":\"Done — added it.\",\"ts\":1711...}\n * │ data: {\"type\":\"done\",\"stopReason\":\"end_turn\",\"usage\":{...},\"ts\":1711...}\n * │\n * └─ Promise resolves with { stopReason, usage }\n * ```\n *\n * ## Stateless design\n *\n * This client is a thin wrapper over HTTP endpoints. It does not manage\n * local state — your React/framework layer owns the thread list, message\n * array, and UI state. Each method is an independent request.\n *\n * ## Streaming callbacks\n *\n * `sendMessage` accepts named callbacks for common events (onText,\n * onThinking, onToolCallStart, etc.) plus a catch-all `onEvent` that\n * receives every SSE event as a discriminated union. Both fire for the\n * same event — use named callbacks for convenience, `onEvent` for\n * logging or handling low-level events like `tool_use` and\n * `tool_input_delta`.\n *\n * @example\n * ```ts\n * import { createAgentChatClient } from '@mindstudio-ai/interface';\n *\n * const chat = createAgentChatClient();\n *\n * // Thread lifecycle\n * const thread = await chat.createThread();\n * const { threads } = await chat.listThreads();\n * const full = await chat.getThread(thread.id);\n *\n * // Send a message with streaming\n * const response = chat.sendMessage(thread.id, 'Hello!', {\n * onText: (delta) => setMessage((prev) => prev + delta),\n * onToolCallStart: (id, name) => showToolSpinner(name),\n * onToolCallResult: (id, output) => showToolResult(output),\n * });\n *\n * const { stopReason, usage } = await response;\n *\n * // Abort mid-stream\n * response.abort();\n * ```\n */\n\nimport { getConfig } from './config.js';\nimport { MindStudioInterfaceError } from './errors.js';\n\n// ---------------------------------------------------------------------------\n// Thread types\n// ---------------------------------------------------------------------------\n\n/** Thread summary returned from create and list endpoints. */\nexport interface ThreadSummary {\n /** Thread ID (UUID). */\n id: string;\n /** User-editable title, auto-generated on first message. */\n title: string | null;\n /** ISO 8601 creation timestamp. */\n dateCreated: string;\n /** ISO 8601 last-updated timestamp. */\n dateUpdated: string;\n}\n\n/** Full thread with message history, returned from get endpoint. */\nexport interface Thread {\n /** Thread ID (UUID). */\n id: string;\n /** User-editable title. */\n title: string | null;\n /** Full conversation history. */\n messages: Message[];\n /** ISO 8601 creation timestamp. */\n dateCreated: string;\n /** ISO 8601 last-updated timestamp. */\n dateUpdated: string;\n}\n\n/** A tool call requested by the assistant. */\nexport interface ToolCall {\n /** Tool call ID, used to match results. */\n id: string;\n /** Method name. */\n name: string;\n /** Input arguments. */\n input: Record<string, unknown>;\n}\n\n/** A single message in a thread's conversation history. */\nexport interface Message {\n /** `\"user\"` for human messages, `\"assistant\"` for agent responses. */\n role: 'user' | 'assistant';\n /** Message text content. For tool results, this is JSON-stringified output. */\n content: string;\n /**\n * MindStudio CDN URLs attached to this message (user messages only).\n *\n * Images (`i.mscdn.ai`) are sent to the model as vision input.\n * Documents (`f.mscdn.ai`, `files.mindstudio-cdn.com`) have their text\n * extracted server-side and included in the model context. The original\n * URLs are preserved in thread history for the frontend to render.\n */\n attachments?: string[];\n /** Tool calls the assistant requested (assistant messages only). */\n toolCalls?: ToolCall[];\n /** ID of the tool call this message is a result for (tool result messages only). */\n toolCallId?: string;\n /** Whether this tool result represents an error. */\n isToolError?: boolean;\n}\n\n/** Paginated thread list response. */\nexport interface ThreadListPage {\n /** Threads for this page. */\n threads: ThreadSummary[];\n /** Cursor for the next page, or `null` if this is the last page. */\n nextCursor: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// SSE event types\n// ---------------------------------------------------------------------------\n\n/** Assistant text delta — append to your display string, do not replace. */\nexport interface AgentTextEvent {\n type: 'text';\n text: string;\n ts: number;\n}\n\n/** Model thinking text (extended thinking). */\nexport interface AgentThinkingEvent {\n type: 'thinking';\n text: string;\n ts: number;\n}\n\n/** Thinking complete with full text and signature. */\nexport interface AgentThinkingCompleteEvent {\n type: 'thinking_complete';\n thinking: string;\n signature: string;\n ts: number;\n}\n\n/** Model requested a tool call (includes parsed input). */\nexport interface AgentToolUseEvent {\n type: 'tool_use';\n id: string;\n name: string;\n input: Record<string, unknown>;\n ts: number;\n}\n\n/** Streaming delta for tool call input JSON. */\nexport interface AgentToolInputDeltaEvent {\n type: 'tool_input_delta';\n id: string;\n name: string;\n delta: string;\n ts: number;\n}\n\n/** Agent began executing a tool call. */\nexport interface AgentToolCallStartEvent {\n type: 'tool_call_start';\n id: string;\n name: string;\n ts: number;\n}\n\n/** Tool call returned a result. */\nexport interface AgentToolCallResultEvent {\n type: 'tool_call_result';\n id: string;\n output: unknown;\n ts: number;\n}\n\n/** Stream complete. */\nexport interface AgentDoneEvent {\n type: 'done';\n stopReason: string;\n usage: { inputTokens: number; outputTokens: number };\n ts: number;\n}\n\n/** Stream-level error. */\nexport interface AgentErrorEvent {\n type: 'error';\n error: string;\n ts: number;\n}\n\n/** Discriminated union of all SSE events from the agent chat stream. */\nexport type AgentChatEvent =\n | AgentTextEvent\n | AgentThinkingEvent\n | AgentThinkingCompleteEvent\n | AgentToolUseEvent\n | AgentToolInputDeltaEvent\n | AgentToolCallStartEvent\n | AgentToolCallResultEvent\n | AgentDoneEvent\n | AgentErrorEvent;\n\n// ---------------------------------------------------------------------------\n// Callbacks and result types\n// ---------------------------------------------------------------------------\n\n/**\n * Callbacks for `sendMessage` streaming.\n *\n * Named callbacks fire for common events. The catch-all `onEvent` fires\n * for every event including those with named callbacks — use it for\n * logging or handling low-level events like `tool_use` and\n * `tool_input_delta`.\n */\nexport interface SendMessageCallbacks {\n /**\n * Called with each text delta as the assistant responds.\n *\n * **Important:** `text` is a delta (new chunk), not the accumulated\n * response. Append it to your display string — do not replace.\n *\n * @example\n * ```ts\n * onText: (delta) => setMessage((prev) => prev + delta)\n * ```\n */\n onText?: (text: string) => void;\n\n /** Called with model thinking text (extended thinking). */\n onThinking?: (text: string) => void;\n\n /** Called when thinking is complete. */\n onThinkingComplete?: (thinking: string, signature: string) => void;\n\n /** Called when a tool call begins executing. */\n onToolCallStart?: (id: string, name: string) => void;\n\n /** Called when a tool call produces a result. */\n onToolCallResult?: (id: string, output: unknown) => void;\n\n /** Called on a stream-level error event. */\n onError?: (error: string) => void;\n\n /** Called for every SSE event (including ones with named callbacks). */\n onEvent?: (event: AgentChatEvent) => void;\n\n /** AbortSignal to cancel the stream. */\n signal?: AbortSignal;\n}\n\n/** Options for `sendMessage`. */\nexport interface SendMessageOptions {\n /**\n * MindStudio CDN URLs to attach to the message.\n *\n * Images (`i.mscdn.ai`) are sent to the model as vision input (one per message).\n * Documents (`f.mscdn.ai`, `files.mindstudio-cdn.com`) have their text\n * extracted server-side and included in context.\n *\n * Upload files first via `platform.uploadFile()`.\n *\n * @example\n * ```ts\n * const url = await platform.uploadFile(file);\n * chat.sendMessage(threadId, 'What is this?', callbacks, {\n * attachments: [url],\n * });\n * ```\n */\n attachments?: string[];\n}\n\n/** Resolved value of `sendMessage` — extracted from the `done` event. */\nexport interface SendMessageResult {\n /** Why the agent stopped (`\"end_turn\"`, `\"max_tokens\"`, etc.). */\n stopReason: string;\n /** Token usage for the full agent loop. */\n usage: { inputTokens: number; outputTokens: number };\n}\n\n/** A Promise with an `abort()` method for cancelling the stream. */\nexport type AbortablePromise<T> = Promise<T> & {\n /** Cancel the in-flight stream. The promise rejects with `AbortError`. */\n abort: () => void;\n};\n\n// ---------------------------------------------------------------------------\n// Client interface\n// ---------------------------------------------------------------------------\n\n/** Agent chat client returned by {@link createAgentChatClient}. */\nexport interface AgentChatClient {\n /** Create a new conversation thread. */\n createThread(): Promise<ThreadSummary>;\n\n /** List threads with cursor-based pagination (50 per page). */\n listThreads(cursor?: string): Promise<ThreadListPage>;\n\n /** Get a thread with its full message history. */\n getThread(threadId: string): Promise<Thread>;\n\n /** Update a thread's title. */\n updateThread(threadId: string, title: string): Promise<void>;\n\n /** Delete a thread (soft delete). */\n deleteThread(threadId: string): Promise<void>;\n\n /**\n * Send a message and stream the agent's response.\n *\n * Returns an {@link AbortablePromise} that resolves with\n * `{ stopReason, usage }` when the stream completes. Call `.abort()`\n * on the returned promise to cancel mid-stream.\n *\n * @param options.attachments - MindStudio CDN URLs to attach.\n * Images (`i.mscdn.ai`) are sent as vision input.\n * Documents (`f.mscdn.ai`) have text extracted into context.\n * Upload files first via `platform.uploadFile()`.\n */\n sendMessage(\n threadId: string,\n content: string,\n callbacks?: SendMessageCallbacks,\n options?: SendMessageOptions,\n ): AbortablePromise<SendMessageResult>;\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers\n// ---------------------------------------------------------------------------\n\nconst AGENT_BASE = '/_/agent';\n\nasync function request<T>(\n path: string,\n method: string,\n body?: unknown,\n): Promise<T> {\n const config = getConfig();\n const headers: Record<string, string> = {\n Authorization: `Bearer ${config.token}`,\n };\n\n if (body !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n\n const res = await fetch(`${AGENT_BASE}${path}`, {\n method,\n headers,\n ...(body !== undefined && { body: JSON.stringify(body) }),\n });\n\n if (!res.ok) {\n let errorMessage = `Agent chat request failed: ${res.status} ${res.statusText}`;\n let errorCode = 'agent_chat_error';\n try {\n const err = (await res.json()) as { error?: string; code?: string };\n if (err.error) {\n errorMessage = err.error;\n }\n if (err.code) {\n errorCode = err.code;\n }\n } catch {\n // Response wasn't JSON — use the default message\n }\n throw new MindStudioInterfaceError(errorMessage, errorCode, res.status);\n }\n\n return (await res.json()) as T;\n}\n\nfunction dispatchEvent(\n event: AgentChatEvent,\n callbacks: SendMessageCallbacks,\n): void {\n // Catch-all fires for every event\n callbacks.onEvent?.(event);\n\n // Named callbacks\n switch (event.type) {\n case 'text':\n callbacks.onText?.(event.text);\n break;\n case 'thinking':\n callbacks.onThinking?.(event.text);\n break;\n case 'thinking_complete':\n callbacks.onThinkingComplete?.(event.thinking, event.signature);\n break;\n case 'tool_call_start':\n callbacks.onToolCallStart?.(event.id, event.name);\n break;\n case 'tool_call_result':\n callbacks.onToolCallResult?.(event.id, event.output);\n break;\n case 'error':\n callbacks.onError?.(event.error);\n break;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create an agent chat client for thread-based conversations.\n *\n * The client is stateless — each method is an independent HTTP request.\n * Your application layer owns thread state, message arrays, and UI updates.\n *\n * @returns An {@link AgentChatClient} with methods for thread CRUD and\n * message streaming.\n *\n * @example\n * ```ts\n * import { createAgentChatClient } from '@mindstudio-ai/interface';\n *\n * const chat = createAgentChatClient();\n *\n * const thread = await chat.createThread();\n *\n * const response = chat.sendMessage(thread.id, 'Hello!', {\n * onText: (delta) => setAssistantMessage((prev) => prev + delta),\n * onToolCallStart: (id, name) => showToolSpinner(name),\n * onToolCallResult: (id, output) => showToolResult(output),\n * });\n *\n * const { stopReason, usage } = await response;\n * ```\n */\nexport function createAgentChatClient(): AgentChatClient {\n return {\n createThread() {\n return request<ThreadSummary>('/threads', 'POST');\n },\n\n listThreads(cursor?: string) {\n const query = cursor ? `?cursor=${encodeURIComponent(cursor)}` : '';\n return request<ThreadListPage>(`/threads${query}`, 'GET');\n },\n\n getThread(threadId: string) {\n return request<Thread>(`/threads/${threadId}`, 'GET');\n },\n\n async updateThread(threadId: string, title: string) {\n await request(`/threads/${threadId}`, 'PATCH', { title });\n },\n\n async deleteThread(threadId: string) {\n await request(`/threads/${threadId}`, 'DELETE');\n },\n\n sendMessage(\n threadId: string,\n content: string,\n callbacks?: SendMessageCallbacks,\n options?: SendMessageOptions,\n ): AbortablePromise<SendMessageResult> {\n const controller = new AbortController();\n const cb = callbacks ?? {};\n\n // Wire caller's signal to our internal controller\n if (cb.signal) {\n if (cb.signal.aborted) {\n controller.abort();\n } else {\n cb.signal.addEventListener('abort', () => controller.abort(), {\n once: true,\n });\n }\n }\n\n const promise = (async (): Promise<SendMessageResult> => {\n const config = getConfig();\n const url = `${AGENT_BASE}/threads/${threadId}/messages`;\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.token}`,\n },\n body: JSON.stringify({\n content,\n ...(options?.attachments?.length && {\n attachments: options.attachments,\n }),\n }),\n signal: controller.signal,\n });\n\n if (!res.ok) {\n let errorMessage = `Send message failed: ${res.status} ${res.statusText}`;\n let errorCode = 'agent_chat_error';\n try {\n const err = (await res.json()) as {\n error?: string;\n code?: string;\n };\n if (err.error) {\n errorMessage = err.error;\n }\n if (err.code) {\n errorCode = err.code;\n }\n } catch {\n // Response wasn't JSON — use the default message\n }\n throw new MindStudioInterfaceError(\n errorMessage,\n errorCode,\n res.status,\n );\n }\n\n const reader = res.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let result: SendMessageResult | undefined;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) {\n continue;\n }\n const json = line.slice(6);\n try {\n const event = JSON.parse(json) as AgentChatEvent;\n\n if (event.type === 'done') {\n result = {\n stopReason: event.stopReason,\n usage: event.usage,\n };\n }\n\n dispatchEvent(event, cb);\n } catch {\n // Skip malformed SSE lines\n }\n }\n }\n\n if (!result) {\n throw new MindStudioInterfaceError(\n 'Stream ended without a done event',\n 'stream_incomplete',\n );\n }\n\n return result;\n })();\n\n const abortable = promise as AbortablePromise<SendMessageResult>;\n abortable.abort = () => controller.abort();\n return abortable;\n },\n };\n}\n","/**\n * Platform actions — file uploads via presigned S3 POST.\n *\n * Uses the session token from `window.__MINDSTUDIO__` to request a\n * presigned upload URL from the platform, then uploads directly to S3.\n * Returns the public CDN URL.\n *\n * @example\n * ```ts\n * import { platform } from '@mindstudio-ai/interface';\n *\n * const file = document.querySelector('input[type=file]').files[0];\n * const url = await platform.uploadFile(file);\n *\n * // With progress and abort\n * const controller = new AbortController();\n * const url = await platform.uploadFile(file, {\n * onProgress: (fraction) => console.log(`${Math.round(fraction * 100)}%`),\n * signal: controller.signal,\n * });\n * ```\n */\n\nimport { getConfig } from './config.js';\nimport { MindStudioInterfaceError } from './errors.js';\n\n/**\n * Options for `platform.uploadFile()`.\n */\nexport interface UploadFileOptions {\n /** Called with upload progress as a fraction from 0 to 1. */\n onProgress?: (fraction: number) => void;\n\n /** AbortSignal to cancel the upload. */\n signal?: AbortSignal;\n}\n\n/**\n * The platform namespace — file upload actions.\n */\nexport const platform = {\n /**\n * Upload a file to the MindStudio CDN.\n *\n * Requests a presigned upload URL from the platform, then uploads\n * the file directly to S3. Returns the public CDN URL.\n *\n * @param file - The File to upload\n * @param options - Optional progress callback and abort signal\n * @returns CDN URL of the uploaded file\n *\n * @example\n * ```ts\n * const url = await platform.uploadFile(file);\n *\n * // With progress\n * const url = await platform.uploadFile(file, {\n * onProgress: (f) => setProgress(f),\n * });\n *\n * // With abort\n * const controller = new AbortController();\n * const url = await platform.uploadFile(file, {\n * signal: controller.signal,\n * });\n * // controller.abort() to cancel\n * ```\n */\n async uploadFile(file: File, options?: UploadFileOptions): Promise<string> {\n const config = getConfig();\n const { onProgress, signal } = options ?? {};\n\n // Check if already aborted\n signal?.throwIfAborted();\n\n // Step 1: Get presigned upload URL\n const presignUrl = '/_/generate-upload-request';\n const presignRes = await fetch(presignUrl, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${config.token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n filename: file.name,\n contentType: file.type || 'application/octet-stream',\n }),\n signal,\n });\n\n if (!presignRes.ok) {\n throw new MindStudioInterfaceError(\n `Upload presign failed: ${presignRes.status} ${presignRes.statusText}`,\n 'upload_error',\n presignRes.status,\n );\n }\n\n const { uploadUrl, uploadFields, publicUrl } =\n (await presignRes.json()) as {\n uploadUrl: string;\n uploadFields: Record<string, string>;\n publicUrl: string;\n };\n\n // Step 2: Upload directly to S3\n const formData = new FormData();\n for (const [key, value] of Object.entries(uploadFields)) {\n formData.append(key, value);\n }\n formData.append('file', file); // must be last\n\n // Use XHR for upload progress (fetch doesn't support it)\n if (onProgress) {\n await xhrUpload(uploadUrl, formData, onProgress, signal);\n return publicUrl;\n }\n\n const uploadRes = await fetch(uploadUrl, {\n method: 'POST',\n body: formData,\n signal,\n });\n\n if (!uploadRes.ok) {\n throw new MindStudioInterfaceError(\n `File upload failed: ${uploadRes.status} ${uploadRes.statusText}`,\n 'upload_error',\n uploadRes.status,\n );\n }\n\n return publicUrl;\n },\n};\n\n/**\n * Upload via XMLHttpRequest to get progress events.\n * Falls back from fetch because fetch upload streams don't support progress.\n */\nfunction xhrUpload(\n url: string,\n formData: FormData,\n onProgress: (fraction: number) => void,\n signal?: AbortSignal,\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.addEventListener('progress', (e) => {\n if (e.lengthComputable) {\n onProgress(e.loaded / e.total);\n }\n });\n\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n onProgress(1);\n resolve();\n } else {\n reject(\n new MindStudioInterfaceError(\n `File upload failed: ${xhr.status} ${xhr.statusText}`,\n 'upload_error',\n xhr.status,\n ),\n );\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(\n new MindStudioInterfaceError(\n 'File upload failed: network error',\n 'upload_error',\n ),\n );\n });\n\n xhr.addEventListener('abort', () => {\n reject(new DOMException('Upload aborted', 'AbortError'));\n });\n\n // Wire up abort signal\n if (signal) {\n if (signal.aborted) {\n reject(new DOMException('Upload aborted', 'AbortError'));\n return;\n }\n signal.addEventListener('abort', () => xhr.abort(), { once: true });\n }\n\n xhr.open('POST', url);\n xhr.send(formData);\n });\n}\n","/**\n * Auth context — current user's identity for display purposes.\n *\n * Provides synchronous access to the current user's name, email, and\n * profile picture. Hydrated from the bootstrap globals injected by the\n * platform — no HTTP call needed.\n *\n * This is for **display only**. Role checking, permission enforcement,\n * and user lookups for other users are backend concerns — the frontend\n * calls backend methods that handle those via `@mindstudio-ai/agent`'s\n * `auth` namespace.\n *\n * @example\n * ```ts\n * import { auth } from '@mindstudio-ai/interface';\n *\n * return (\n * <div>\n * <p>Welcome, {auth.name}</p>\n * <img src={auth.profilePictureUrl} />\n * </div>\n * );\n * ```\n */\n\nimport { getConfig } from './config.js';\n\n/**\n * User identity context. All properties are read-only and synchronous.\n */\nexport interface AuthContext {\n /** Current user's ID (UUID). */\n readonly userId: string;\n\n /** Current user's display name. */\n readonly name: string;\n\n /** Current user's email address. */\n readonly email: string;\n\n /** Current user's profile picture URL, or null if not set. */\n readonly profilePictureUrl: string | null;\n}\n\n/**\n * Lazy auth proxy — reads from bootstrap globals on first property access.\n *\n * Using a Proxy lets us export `auth` as a module-level constant while\n * deferring the `getConfig()` call until the first property is accessed.\n * This avoids throwing during import if the page hasn't finished loading\n * the bootstrap script yet.\n */\nexport const auth: AuthContext = new Proxy({} as AuthContext, {\n get(_, prop: string) {\n const { user } = getConfig();\n\n switch (prop) {\n case 'userId':\n return user.id;\n case 'name':\n return user.name;\n case 'email':\n return user.email;\n case 'profilePictureUrl':\n return user.profilePictureUrl ?? null;\n default:\n return undefined;\n }\n },\n});\n"],"mappings":"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","names":["MindStudioInterfaceError","message","code","status","_config","getConfig","raw","MindStudioInterfaceError","missing","k","createClient","_","methodName","input","options","config","getConfig","methodId","MindStudioInterfaceError","url","wantsStream","res","errorMessage","errorCode","body","reader","decoder","buffer","finalOutput","done","value","lines","line","json","event","AGENT_BASE","request","path","method","body","headers","getConfig","res","errorMessage","errorCode","err","MindStudioInterfaceError","dispatchEvent","event","callbacks","createAgentChatClient","cursor","query","threadId","title","content","options","controller","cb","abortable","config","url","reader","decoder","buffer","result","done","value","lines","line","json","platform","file","options","config","getConfig","onProgress","signal","presignRes","MindStudioInterfaceError","uploadUrl","uploadFields","publicUrl","formData","key","value","xhrUpload","uploadRes","url","resolve","reject","xhr","e","auth","_","prop","user","getConfig"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/config.ts","../src/client.ts","../src/agent-chat.ts","../src/platform.ts","../src/auth-phone.ts","../src/auth-email.ts","../src/auth.ts"],"sourcesContent":["/**\n * Error class for the @mindstudio-ai/interface SDK.\n *\n * Thrown by `createClient()` method calls on HTTP errors, by `platform`\n * actions on failures, and by the config reader when bootstrap globals\n * are missing.\n */\nexport class MindStudioInterfaceError extends Error {\n /** Machine-readable error code (e.g. 'method_not_found', 'not_initialized'). */\n readonly code: string;\n\n /** HTTP status code, when the error originated from an API response. */\n readonly status?: number;\n\n constructor(message: string, code: string, status?: number) {\n super(message);\n this.name = 'MindStudioInterfaceError';\n this.code = code;\n this.status = status;\n }\n}\n","/**\n * Bootstrap configuration reader.\n *\n * Reads and validates `window.__MINDSTUDIO__` — the globals injected by\n * the platform before the app's JavaScript runs. The result is cached\n * after first read.\n *\n * All other modules in the SDK call `getConfig()` internally. App code\n * should never need to call this directly — use `createClient()`, `auth`,\n * and `platform` instead.\n */\n\nimport { MindStudioInterfaceError } from './errors.js';\nimport type { AppUser, BootstrapConfig } from './types.js';\n\nlet _config: BootstrapConfig | undefined;\n\n/**\n * Read and validate the bootstrap configuration from `window.__MINDSTUDIO__`.\n *\n * Caches the result after first successful read. Throws if the globals\n * are missing or incomplete — this means the page isn't running inside\n * the MindStudio platform.\n *\n * @returns The validated bootstrap config\n * @throws {MindStudioInterfaceError} if `window.__MINDSTUDIO__` is missing or invalid\n */\nexport function getConfig(): BootstrapConfig {\n if (_config) {\n return _config;\n }\n\n const raw = (globalThis as Record<string, unknown>).__MINDSTUDIO__ as\n | Partial<BootstrapConfig>\n | undefined;\n\n if (!raw) {\n throw new MindStudioInterfaceError(\n '@mindstudio-ai/interface requires the MindStudio platform context. ' +\n 'This page must be loaded inside MindStudio (window.__MINDSTUDIO__ is missing).',\n 'not_initialized',\n );\n }\n\n // Validate required fields (user can be null for unauthenticated sessions)\n if (!raw.token || !raw.releaseId || !raw.methods) {\n const missing = ['token', 'releaseId', 'methods']\n .filter((k) => !raw[k as keyof BootstrapConfig])\n .join(', ');\n\n throw new MindStudioInterfaceError(\n `MindStudio platform context is incomplete. Missing: ${missing}`,\n 'invalid_config',\n );\n }\n\n _config = raw as BootstrapConfig;\n return _config;\n}\n\n/**\n * Update the cached config in-place after an auth state transition.\n *\n * The cached config is a mutable reference — all SDK modules read it\n * on-demand via `getConfig()`, so mutations propagate immediately to\n * method invocation, agent chat, uploads, etc.\n *\n * @internal Not exported from the package — used by the auth module only.\n */\nexport function updateConfig(updates: {\n token?: string;\n user?: AppUser | null;\n methods?: Record<string, string>;\n}): void {\n const config = getConfig();\n if (updates.token !== undefined) {\n config.token = updates.token;\n }\n if (updates.user !== undefined) {\n config.user = updates.user;\n }\n if (updates.methods !== undefined) {\n config.methods = updates.methods;\n }\n}\n","/**\n * Method RPC client — typed function calls to backend methods.\n *\n * `createClient()` returns a Proxy where every property access creates\n * an async function that invokes the corresponding backend method via HTTP.\n * The method name matches the method's `export` field in `app.json`.\n *\n * ## How it works\n *\n * ```\n * api.submitVendorRequest({ name: 'Acme' })\n * │\n * ├─ Look up method ID: config.methods[\"submitVendorRequest\"]\n * │ → \"submit-vendor-request\"\n * │\n * ├─ POST /_/methods/submit-vendor-request/invoke\n * │ Authorization: Bearer {session token}\n * │ Body: { input: { name: \"Acme\" } }\n * │\n * └─ Returns response.output (or throws on error)\n * ```\n *\n * The method registry (export name → method ID mapping) is embedded in\n * `window.__MINDSTUDIO__.methods` at page load time — no extra HTTP call\n * needed to discover methods.\n *\n * ## Streaming\n *\n * Methods can optionally stream LLM token output via SSE. Pass\n * `{ stream: true }` as the second argument along with an `onToken`\n * callback to receive incremental updates:\n *\n * ```ts\n * const result = await api.submitVendorRequest({ name: 'Acme' }, {\n * stream: true,\n * onToken: (text) => setResponseText(text),\n * });\n * ```\n *\n * **Important:** The `text` value passed to `onToken` is the accumulated\n * response so far (not a delta). Replace your display content each time —\n * do not append. See {@link InvokeOptions.onToken} for details.\n *\n * ## Type safety\n *\n * Since the frontend and backend live in the same project, you can import\n * types directly:\n *\n * ```ts\n * import type { SubmitVendorInput, SubmitVendorOutput }\n * from '../../backend/src/submitVendorRequest';\n *\n * interface AppMethods {\n * submitVendorRequest(input: SubmitVendorInput): Promise<SubmitVendorOutput>;\n * getDashboard(): Promise<GetDashboardOutput>;\n * }\n *\n * const api = createClient<AppMethods>();\n * const result = await api.submitVendorRequest({ name: 'Acme' }); // fully typed\n * ```\n *\n * If no type parameter is provided, methods accept `any` input and return `any`.\n */\n\nimport { getConfig } from './config.js';\nimport { MindStudioInterfaceError } from './errors.js';\n\n/**\n * Options for method invocation. Pass as the optional second argument to\n * any method on the client returned by {@link createClient}.\n *\n * When `stream` is `true`, the SDK sends `{ stream: true }` in the request\n * body and parses the response as an SSE (Server-Sent Events) stream.\n * Three event types are handled:\n *\n * | SSE `type` | Payload field | SDK action |\n * |------------|---------------|------------|\n * | `token` | `text` | Calls {@link onToken} with accumulated text |\n * | `error` | `error` | Calls {@link onStreamError} with the message |\n * | `done` | `output` | Captures as the method's return value |\n *\n * The method's `Promise` resolves with the final `output` from the `done`\n * event — the same shape you'd get without streaming.\n */\nexport interface InvokeOptions {\n /**\n * Enable SSE streaming for LLM token output.\n *\n * When `true`, the request body includes `{ stream: true }` and the\n * response is parsed as an SSE stream instead of a single JSON payload.\n * The method still returns a `Promise` that resolves with the final\n * output once the stream completes.\n *\n * @default false\n */\n stream?: boolean;\n\n /**\n * Called with the accumulated response text each time a new token arrives.\n *\n * **Important:** `text` is the full response so far, not a delta/chunk.\n * Your frontend should **replace** (not append to) its display content\n * with this value each time the callback fires.\n *\n * This design avoids edge cases where the platform sanitizes or\n * reformats JSON mid-stream, causing earlier portions of the text to\n * change between tokens.\n *\n * @example\n * ```ts\n * const result = await api.generate({ prompt: 'Hello' }, {\n * stream: true,\n * onToken: (text) => {\n * // Replace — do NOT append\n * responseElement.textContent = text;\n * },\n * });\n * ```\n */\n onToken?: (text: string) => void;\n\n /**\n * Called if a stream-level error occurs.\n *\n * Stream errors are non-fatal at the protocol level — the method may\n * still complete and resolve with a final output. Use this callback for\n * logging or showing a transient warning, not for control flow.\n *\n * If the stream ends without a `done` event, the method rejects with a\n * `MindStudioInterfaceError` (code `stream_incomplete`) regardless of\n * whether `onStreamError` was provided.\n */\n onStreamError?: (error: string) => void;\n}\n\n/**\n * Default client type when no generic parameter is provided.\n * Every property returns an async function accepting optional input\n * and optional {@link InvokeOptions} for streaming.\n */\ntype DefaultMethodClient = Record<\n string,\n (input?: Record<string, unknown>, options?: InvokeOptions) => Promise<unknown>\n>;\n\n/**\n * Create a typed RPC client for calling backend methods.\n *\n * @typeParam T - Optional interface mapping method names to their signatures.\n * If omitted, all methods accept `any` and return `Promise<any>`.\n *\n * @returns A Proxy object where each property is an async function that\n * invokes the corresponding backend method.\n *\n * @example\n * ```ts\n * import { createClient } from '@mindstudio-ai/interface';\n *\n * const api = createClient();\n *\n * // Each method maps to a backend method export\n * const result = await api.submitVendorRequest({ name: 'Acme' });\n * const dashboard = await api.getDashboard();\n *\n * // Stream LLM output — onToken receives accumulated text (replace, don't append)\n * const streamed = await api.submitVendorRequest({ name: 'Acme' }, {\n * stream: true,\n * onToken: (text) => setResponseText(text),\n * });\n * // `streamed` is the same final output you'd get without streaming\n * ```\n */\nexport function createClient<T = DefaultMethodClient>(): T {\n return new Proxy({} as Record<string, unknown> as T & object, {\n get(_, methodName: string) {\n return async (input?: unknown, options?: InvokeOptions) => {\n const config = getConfig();\n\n const methodId = config.methods[methodName];\n if (!methodId) {\n throw new MindStudioInterfaceError(\n `Method \"${methodName}\" not found. Available methods: ${Object.keys(config.methods).join(', ') || '(none)'}`,\n 'method_not_found',\n );\n }\n\n const url = `/_/methods/${methodId}/invoke`;\n const wantsStream = options?.stream === true;\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.token}`,\n },\n body: JSON.stringify({\n input: input ?? {},\n ...(wantsStream && { stream: true }),\n }),\n });\n\n if (!res.ok) {\n let errorMessage = `Method \"${methodName}\" failed: ${res.status} ${res.statusText}`;\n let errorCode = 'method_error';\n try {\n const body = (await res.json()) as {\n error?: string;\n code?: string;\n };\n if (body.error) {\n errorMessage = body.error;\n }\n if (body.code) {\n errorCode = body.code;\n }\n } catch {\n // Response wasn't JSON — use the default message\n }\n throw new MindStudioInterfaceError(\n errorMessage,\n errorCode,\n res.status,\n );\n }\n\n // Non-streaming: unchanged behavior\n if (!wantsStream) {\n const body = (await res.json()) as { output?: unknown };\n return body.output;\n }\n\n // Streaming: parse SSE events from response body\n const reader = res.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let finalOutput: unknown;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) {\n continue;\n }\n const json = line.slice(6);\n try {\n const event = JSON.parse(json) as {\n type: string;\n text?: string;\n error?: string;\n output?: unknown;\n };\n\n if (event.type === 'token' && options?.onToken && event.text) {\n options.onToken(event.text);\n } else if (event.type === 'error' && event.error) {\n if (options?.onStreamError) {\n options.onStreamError(event.error);\n }\n } else if (event.type === 'done') {\n finalOutput = event.output;\n }\n } catch {\n // Skip malformed SSE lines\n }\n }\n }\n\n if (finalOutput === undefined) {\n throw new MindStudioInterfaceError(\n 'Stream ended without a done event',\n 'stream_incomplete',\n );\n }\n\n return finalOutput;\n };\n },\n });\n}\n","/**\n * Agent chat client — thread-based conversations with AI agents.\n *\n * `createAgentChatClient()` returns an object with methods for thread\n * lifecycle (create, list, get, rename, delete) and message streaming.\n * The agent runs server-side with access to the app's methods as tools.\n *\n * ## How it works\n *\n * ```\n * chat.sendMessage(threadId, 'Help me prep for my review', {\n * onText: (delta) => setAssistantMessage((prev) => prev + delta),\n * })\n * │\n * ├─ POST /_/agent/threads/{threadId}/messages\n * │ Authorization: Bearer {session token}\n * │ Body: { content: \"Help me prep for my review\" }\n * │\n * ├─ Response: SSE stream\n * │ data: {\"type\":\"text\",\"text\":\"I'll create...\",\"ts\":1711...}\n * │ data: {\"type\":\"tool_call_start\",\"id\":\"call_abc\",\"name\":\"create-todo\",\"ts\":1711...}\n * │ data: {\"type\":\"tool_call_result\",\"id\":\"call_abc\",\"output\":{...},\"ts\":1711...}\n * │ data: {\"type\":\"text\",\"text\":\"Done — added it.\",\"ts\":1711...}\n * │ data: {\"type\":\"done\",\"stopReason\":\"end_turn\",\"usage\":{...},\"ts\":1711...}\n * │\n * └─ Promise resolves with { stopReason, usage }\n * ```\n *\n * ## Stateless design\n *\n * This client is a thin wrapper over HTTP endpoints. It does not manage\n * local state — your React/framework layer owns the thread list, message\n * array, and UI state. Each method is an independent request.\n *\n * ## Streaming callbacks\n *\n * `sendMessage` accepts named callbacks for common events (onText,\n * onThinking, onToolCallStart, etc.) plus a catch-all `onEvent` that\n * receives every SSE event as a discriminated union. Both fire for the\n * same event — use named callbacks for convenience, `onEvent` for\n * logging or handling low-level events like `tool_use` and\n * `tool_input_delta`.\n *\n * @example\n * ```ts\n * import { createAgentChatClient } from '@mindstudio-ai/interface';\n *\n * const chat = createAgentChatClient();\n *\n * // Thread lifecycle\n * const thread = await chat.createThread();\n * const { threads } = await chat.listThreads();\n * const full = await chat.getThread(thread.id);\n *\n * // Send a message with streaming\n * const response = chat.sendMessage(thread.id, 'Hello!', {\n * onText: (delta) => setMessage((prev) => prev + delta),\n * onToolCallStart: (id, name) => showToolSpinner(name),\n * onToolCallResult: (id, output) => showToolResult(output),\n * });\n *\n * const { stopReason, usage } = await response;\n *\n * // Abort mid-stream\n * response.abort();\n * ```\n */\n\nimport { getConfig } from './config.js';\nimport { MindStudioInterfaceError } from './errors.js';\n\n// ---------------------------------------------------------------------------\n// Thread types\n// ---------------------------------------------------------------------------\n\n/** Thread summary returned from create and list endpoints. */\nexport interface ThreadSummary {\n /** Thread ID (UUID). */\n id: string;\n /** User-editable title, auto-generated on first message. */\n title: string | null;\n /** ISO 8601 creation timestamp. */\n dateCreated: string;\n /** ISO 8601 last-updated timestamp. */\n dateUpdated: string;\n}\n\n/** Full thread with message history, returned from get endpoint. */\nexport interface Thread {\n /** Thread ID (UUID). */\n id: string;\n /** User-editable title. */\n title: string | null;\n /** Full conversation history. */\n messages: Message[];\n /** ISO 8601 creation timestamp. */\n dateCreated: string;\n /** ISO 8601 last-updated timestamp. */\n dateUpdated: string;\n}\n\n/** A tool call requested by the assistant. */\nexport interface ToolCall {\n /** Tool call ID, used to match results. */\n id: string;\n /** Method name. */\n name: string;\n /** Input arguments. */\n input: Record<string, unknown>;\n}\n\n/** A single message in a thread's conversation history. */\nexport interface Message {\n /** `\"user\"` for human messages, `\"assistant\"` for agent responses. */\n role: 'user' | 'assistant';\n /** Message text content. For tool results, this is JSON-stringified output. */\n content: string;\n /**\n * MindStudio CDN URLs attached to this message (user messages only).\n *\n * Images (`i.mscdn.ai`) are sent to the model as vision input.\n * Documents (`f.mscdn.ai`, `files.mindstudio-cdn.com`) have their text\n * extracted server-side and included in the model context. The original\n * URLs are preserved in thread history for the frontend to render.\n */\n attachments?: string[];\n /** Tool calls the assistant requested (assistant messages only). */\n toolCalls?: ToolCall[];\n /** ID of the tool call this message is a result for (tool result messages only). */\n toolCallId?: string;\n /** Whether this tool result represents an error. */\n isToolError?: boolean;\n}\n\n/** Paginated thread list response. */\nexport interface ThreadListPage {\n /** Threads for this page. */\n threads: ThreadSummary[];\n /** Cursor for the next page, or `null` if this is the last page. */\n nextCursor: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// SSE event types\n// ---------------------------------------------------------------------------\n\n/** Assistant text delta — append to your display string, do not replace. */\nexport interface AgentTextEvent {\n type: 'text';\n text: string;\n ts: number;\n}\n\n/** Model thinking text (extended thinking). */\nexport interface AgentThinkingEvent {\n type: 'thinking';\n text: string;\n ts: number;\n}\n\n/** Thinking complete with full text and signature. */\nexport interface AgentThinkingCompleteEvent {\n type: 'thinking_complete';\n thinking: string;\n signature: string;\n ts: number;\n}\n\n/** Model requested a tool call (includes parsed input). */\nexport interface AgentToolUseEvent {\n type: 'tool_use';\n id: string;\n name: string;\n input: Record<string, unknown>;\n ts: number;\n}\n\n/** Streaming delta for tool call input JSON. */\nexport interface AgentToolInputDeltaEvent {\n type: 'tool_input_delta';\n id: string;\n name: string;\n delta: string;\n ts: number;\n}\n\n/** Agent began executing a tool call. */\nexport interface AgentToolCallStartEvent {\n type: 'tool_call_start';\n id: string;\n name: string;\n ts: number;\n}\n\n/** Tool call returned a result. */\nexport interface AgentToolCallResultEvent {\n type: 'tool_call_result';\n id: string;\n output: unknown;\n ts: number;\n}\n\n/** Stream complete. */\nexport interface AgentDoneEvent {\n type: 'done';\n stopReason: string;\n usage: { inputTokens: number; outputTokens: number };\n ts: number;\n}\n\n/** Stream-level error. */\nexport interface AgentErrorEvent {\n type: 'error';\n error: string;\n ts: number;\n}\n\n/** Discriminated union of all SSE events from the agent chat stream. */\nexport type AgentChatEvent =\n | AgentTextEvent\n | AgentThinkingEvent\n | AgentThinkingCompleteEvent\n | AgentToolUseEvent\n | AgentToolInputDeltaEvent\n | AgentToolCallStartEvent\n | AgentToolCallResultEvent\n | AgentDoneEvent\n | AgentErrorEvent;\n\n// ---------------------------------------------------------------------------\n// Callbacks and result types\n// ---------------------------------------------------------------------------\n\n/**\n * Callbacks for `sendMessage` streaming.\n *\n * Named callbacks fire for common events. The catch-all `onEvent` fires\n * for every event including those with named callbacks — use it for\n * logging or handling low-level events like `tool_use` and\n * `tool_input_delta`.\n */\nexport interface SendMessageCallbacks {\n /**\n * Called with each text delta as the assistant responds.\n *\n * **Important:** `text` is a delta (new chunk), not the accumulated\n * response. Append it to your display string — do not replace.\n *\n * @example\n * ```ts\n * onText: (delta) => setMessage((prev) => prev + delta)\n * ```\n */\n onText?: (text: string) => void;\n\n /** Called with model thinking text (extended thinking). */\n onThinking?: (text: string) => void;\n\n /** Called when thinking is complete. */\n onThinkingComplete?: (thinking: string, signature: string) => void;\n\n /** Called when a tool call begins executing. */\n onToolCallStart?: (id: string, name: string) => void;\n\n /** Called when a tool call produces a result. */\n onToolCallResult?: (id: string, output: unknown) => void;\n\n /** Called on a stream-level error event. */\n onError?: (error: string) => void;\n\n /** Called for every SSE event (including ones with named callbacks). */\n onEvent?: (event: AgentChatEvent) => void;\n\n /** AbortSignal to cancel the stream. */\n signal?: AbortSignal;\n}\n\n/** Options for `sendMessage`. */\nexport interface SendMessageOptions {\n /**\n * MindStudio CDN URLs to attach to the message.\n *\n * Images (`i.mscdn.ai`) are sent to the model as vision input (one per message).\n * Documents (`f.mscdn.ai`, `files.mindstudio-cdn.com`) have their text\n * extracted server-side and included in context.\n *\n * Upload files first via `platform.uploadFile()`.\n *\n * @example\n * ```ts\n * const url = await platform.uploadFile(file);\n * chat.sendMessage(threadId, 'What is this?', callbacks, {\n * attachments: [url],\n * });\n * ```\n */\n attachments?: string[];\n}\n\n/** Resolved value of `sendMessage` — extracted from the `done` event. */\nexport interface SendMessageResult {\n /** Why the agent stopped (`\"end_turn\"`, `\"max_tokens\"`, etc.). */\n stopReason: string;\n /** Token usage for the full agent loop. */\n usage: { inputTokens: number; outputTokens: number };\n}\n\n/** A Promise with an `abort()` method for cancelling the stream. */\nexport type AbortablePromise<T> = Promise<T> & {\n /** Cancel the in-flight stream. The promise rejects with `AbortError`. */\n abort: () => void;\n};\n\n// ---------------------------------------------------------------------------\n// Client interface\n// ---------------------------------------------------------------------------\n\n/** Agent chat client returned by {@link createAgentChatClient}. */\nexport interface AgentChatClient {\n /** Create a new conversation thread. */\n createThread(): Promise<ThreadSummary>;\n\n /** List threads with cursor-based pagination (50 per page). */\n listThreads(cursor?: string): Promise<ThreadListPage>;\n\n /** Get a thread with its full message history. */\n getThread(threadId: string): Promise<Thread>;\n\n /** Update a thread's title. */\n updateThread(threadId: string, title: string): Promise<void>;\n\n /** Delete a thread (soft delete). */\n deleteThread(threadId: string): Promise<void>;\n\n /**\n * Send a message and stream the agent's response.\n *\n * Returns an {@link AbortablePromise} that resolves with\n * `{ stopReason, usage }` when the stream completes. Call `.abort()`\n * on the returned promise to cancel mid-stream.\n *\n * @param options.attachments - MindStudio CDN URLs to attach.\n * Images (`i.mscdn.ai`) are sent as vision input.\n * Documents (`f.mscdn.ai`) have text extracted into context.\n * Upload files first via `platform.uploadFile()`.\n */\n sendMessage(\n threadId: string,\n content: string,\n callbacks?: SendMessageCallbacks,\n options?: SendMessageOptions,\n ): AbortablePromise<SendMessageResult>;\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers\n// ---------------------------------------------------------------------------\n\nconst AGENT_BASE = '/_/agent';\n\nasync function request<T>(\n path: string,\n method: string,\n body?: unknown,\n): Promise<T> {\n const config = getConfig();\n const headers: Record<string, string> = {\n Authorization: `Bearer ${config.token}`,\n };\n\n if (body !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n\n const res = await fetch(`${AGENT_BASE}${path}`, {\n method,\n headers,\n ...(body !== undefined && { body: JSON.stringify(body) }),\n });\n\n if (!res.ok) {\n let errorMessage = `Agent chat request failed: ${res.status} ${res.statusText}`;\n let errorCode = 'agent_chat_error';\n try {\n const err = (await res.json()) as { error?: string; code?: string };\n if (err.error) {\n errorMessage = err.error;\n }\n if (err.code) {\n errorCode = err.code;\n }\n } catch {\n // Response wasn't JSON — use the default message\n }\n throw new MindStudioInterfaceError(errorMessage, errorCode, res.status);\n }\n\n return (await res.json()) as T;\n}\n\nfunction dispatchEvent(\n event: AgentChatEvent,\n callbacks: SendMessageCallbacks,\n): void {\n // Catch-all fires for every event\n callbacks.onEvent?.(event);\n\n // Named callbacks\n switch (event.type) {\n case 'text':\n callbacks.onText?.(event.text);\n break;\n case 'thinking':\n callbacks.onThinking?.(event.text);\n break;\n case 'thinking_complete':\n callbacks.onThinkingComplete?.(event.thinking, event.signature);\n break;\n case 'tool_call_start':\n callbacks.onToolCallStart?.(event.id, event.name);\n break;\n case 'tool_call_result':\n callbacks.onToolCallResult?.(event.id, event.output);\n break;\n case 'error':\n callbacks.onError?.(event.error);\n break;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create an agent chat client for thread-based conversations.\n *\n * The client is stateless — each method is an independent HTTP request.\n * Your application layer owns thread state, message arrays, and UI updates.\n *\n * @returns An {@link AgentChatClient} with methods for thread CRUD and\n * message streaming.\n *\n * @example\n * ```ts\n * import { createAgentChatClient } from '@mindstudio-ai/interface';\n *\n * const chat = createAgentChatClient();\n *\n * const thread = await chat.createThread();\n *\n * const response = chat.sendMessage(thread.id, 'Hello!', {\n * onText: (delta) => setAssistantMessage((prev) => prev + delta),\n * onToolCallStart: (id, name) => showToolSpinner(name),\n * onToolCallResult: (id, output) => showToolResult(output),\n * });\n *\n * const { stopReason, usage } = await response;\n * ```\n */\nexport function createAgentChatClient(): AgentChatClient {\n return {\n createThread() {\n return request<ThreadSummary>('/threads', 'POST');\n },\n\n listThreads(cursor?: string) {\n const query = cursor ? `?cursor=${encodeURIComponent(cursor)}` : '';\n return request<ThreadListPage>(`/threads${query}`, 'GET');\n },\n\n getThread(threadId: string) {\n return request<Thread>(`/threads/${threadId}`, 'GET');\n },\n\n async updateThread(threadId: string, title: string) {\n await request(`/threads/${threadId}`, 'PATCH', { title });\n },\n\n async deleteThread(threadId: string) {\n await request(`/threads/${threadId}`, 'DELETE');\n },\n\n sendMessage(\n threadId: string,\n content: string,\n callbacks?: SendMessageCallbacks,\n options?: SendMessageOptions,\n ): AbortablePromise<SendMessageResult> {\n const controller = new AbortController();\n const cb = callbacks ?? {};\n\n // Wire caller's signal to our internal controller\n if (cb.signal) {\n if (cb.signal.aborted) {\n controller.abort();\n } else {\n cb.signal.addEventListener('abort', () => controller.abort(), {\n once: true,\n });\n }\n }\n\n const promise = (async (): Promise<SendMessageResult> => {\n const config = getConfig();\n const url = `${AGENT_BASE}/threads/${threadId}/messages`;\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.token}`,\n },\n body: JSON.stringify({\n content,\n ...(options?.attachments?.length && {\n attachments: options.attachments,\n }),\n }),\n signal: controller.signal,\n });\n\n if (!res.ok) {\n let errorMessage = `Send message failed: ${res.status} ${res.statusText}`;\n let errorCode = 'agent_chat_error';\n try {\n const err = (await res.json()) as {\n error?: string;\n code?: string;\n };\n if (err.error) {\n errorMessage = err.error;\n }\n if (err.code) {\n errorCode = err.code;\n }\n } catch {\n // Response wasn't JSON — use the default message\n }\n throw new MindStudioInterfaceError(\n errorMessage,\n errorCode,\n res.status,\n );\n }\n\n const reader = res.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let result: SendMessageResult | undefined;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) {\n continue;\n }\n const json = line.slice(6);\n try {\n const event = JSON.parse(json) as AgentChatEvent;\n\n if (event.type === 'done') {\n result = {\n stopReason: event.stopReason,\n usage: event.usage,\n };\n }\n\n dispatchEvent(event, cb);\n } catch {\n // Skip malformed SSE lines\n }\n }\n }\n\n if (!result) {\n throw new MindStudioInterfaceError(\n 'Stream ended without a done event',\n 'stream_incomplete',\n );\n }\n\n return result;\n })();\n\n const abortable = promise as AbortablePromise<SendMessageResult>;\n abortable.abort = () => controller.abort();\n return abortable;\n },\n };\n}\n","/**\n * Platform actions — file uploads via presigned S3 POST.\n *\n * Uses the session token from `window.__MINDSTUDIO__` to request a\n * presigned upload URL from the platform, then uploads directly to S3.\n * Returns the public CDN URL.\n *\n * @example\n * ```ts\n * import { platform } from '@mindstudio-ai/interface';\n *\n * const file = document.querySelector('input[type=file]').files[0];\n * const url = await platform.uploadFile(file);\n *\n * // With progress and abort\n * const controller = new AbortController();\n * const url = await platform.uploadFile(file, {\n * onProgress: (fraction) => console.log(`${Math.round(fraction * 100)}%`),\n * signal: controller.signal,\n * });\n * ```\n */\n\nimport { getConfig } from './config.js';\nimport { MindStudioInterfaceError } from './errors.js';\n\n/**\n * Options for `platform.uploadFile()`.\n */\nexport interface UploadFileOptions {\n /** Called with upload progress as a fraction from 0 to 1. */\n onProgress?: (fraction: number) => void;\n\n /** AbortSignal to cancel the upload. */\n signal?: AbortSignal;\n}\n\n/**\n * The platform namespace — file upload actions.\n */\nexport const platform = {\n /**\n * Upload a file to the MindStudio CDN.\n *\n * Requests a presigned upload URL from the platform, then uploads\n * the file directly to S3. Returns the public CDN URL.\n *\n * @param file - The File to upload\n * @param options - Optional progress callback and abort signal\n * @returns CDN URL of the uploaded file\n *\n * @example\n * ```ts\n * const url = await platform.uploadFile(file);\n *\n * // With progress\n * const url = await platform.uploadFile(file, {\n * onProgress: (f) => setProgress(f),\n * });\n *\n * // With abort\n * const controller = new AbortController();\n * const url = await platform.uploadFile(file, {\n * signal: controller.signal,\n * });\n * // controller.abort() to cancel\n * ```\n */\n async uploadFile(file: File, options?: UploadFileOptions): Promise<string> {\n const config = getConfig();\n const { onProgress, signal } = options ?? {};\n\n // Check if already aborted\n signal?.throwIfAborted();\n\n // Step 1: Get presigned upload URL\n const presignUrl = '/_/generate-upload-request';\n const presignRes = await fetch(presignUrl, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${config.token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n filename: file.name,\n contentType: file.type || 'application/octet-stream',\n }),\n signal,\n });\n\n if (!presignRes.ok) {\n throw new MindStudioInterfaceError(\n `Upload presign failed: ${presignRes.status} ${presignRes.statusText}`,\n 'upload_error',\n presignRes.status,\n );\n }\n\n const { uploadUrl, uploadFields, publicUrl } =\n (await presignRes.json()) as {\n uploadUrl: string;\n uploadFields: Record<string, string>;\n publicUrl: string;\n };\n\n // Step 2: Upload directly to S3\n const formData = new FormData();\n for (const [key, value] of Object.entries(uploadFields)) {\n formData.append(key, value);\n }\n formData.append('file', file); // must be last\n\n // Use XHR for upload progress (fetch doesn't support it)\n if (onProgress) {\n await xhrUpload(uploadUrl, formData, onProgress, signal);\n return publicUrl;\n }\n\n const uploadRes = await fetch(uploadUrl, {\n method: 'POST',\n body: formData,\n signal,\n });\n\n if (!uploadRes.ok) {\n throw new MindStudioInterfaceError(\n `File upload failed: ${uploadRes.status} ${uploadRes.statusText}`,\n 'upload_error',\n uploadRes.status,\n );\n }\n\n return publicUrl;\n },\n};\n\n/**\n * Upload via XMLHttpRequest to get progress events.\n * Falls back from fetch because fetch upload streams don't support progress.\n */\nfunction xhrUpload(\n url: string,\n formData: FormData,\n onProgress: (fraction: number) => void,\n signal?: AbortSignal,\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.addEventListener('progress', (e) => {\n if (e.lengthComputable) {\n onProgress(e.loaded / e.total);\n }\n });\n\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n onProgress(1);\n resolve();\n } else {\n reject(\n new MindStudioInterfaceError(\n `File upload failed: ${xhr.status} ${xhr.statusText}`,\n 'upload_error',\n xhr.status,\n ),\n );\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(\n new MindStudioInterfaceError(\n 'File upload failed: network error',\n 'upload_error',\n ),\n );\n });\n\n xhr.addEventListener('abort', () => {\n reject(new DOMException('Upload aborted', 'AbortError'));\n });\n\n // Wire up abort signal\n if (signal) {\n if (signal.aborted) {\n reject(new DOMException('Upload aborted', 'AbortError'));\n return;\n }\n signal.addEventListener('abort', () => xhr.abort(), { once: true });\n }\n\n xhr.open('POST', url);\n xhr.send(formData);\n });\n}\n","/**\n * Phone number utilities for auth flows.\n *\n * Provides country data, E.164 conversion, formatting, and validation.\n * No external dependencies — designed for login/signup UIs that need\n * a country code picker and phone number formatting.\n */\n\n// ---------------------------------------------------------------------------\n// Country data\n// ---------------------------------------------------------------------------\n\n/** A country with its dial code and flag emoji. */\nexport interface Country {\n /** ISO 3166-1 alpha-2 code (e.g. \"US\"). */\n code: string;\n /** International dial code with + prefix (e.g. \"+1\"). */\n dialCode: string;\n /** Country name in English. */\n name: string;\n /** Flag emoji. */\n flag: string;\n}\n\n/** All countries with dial codes, sorted alphabetically by name. */\nexport const countries: readonly Country[] = [\n { code: 'AF', dialCode: '+93', name: 'Afghanistan', flag: '🇦🇫' },\n { code: 'AL', dialCode: '+355', name: 'Albania', flag: '🇦🇱' },\n { code: 'DZ', dialCode: '+213', name: 'Algeria', flag: '🇩🇿' },\n { code: 'AD', dialCode: '+376', name: 'Andorra', flag: '🇦🇩' },\n { code: 'AO', dialCode: '+244', name: 'Angola', flag: '🇦🇴' },\n { code: 'AG', dialCode: '+1', name: 'Antigua and Barbuda', flag: '🇦🇬' },\n { code: 'AR', dialCode: '+54', name: 'Argentina', flag: '🇦🇷' },\n { code: 'AM', dialCode: '+374', name: 'Armenia', flag: '🇦🇲' },\n { code: 'AU', dialCode: '+61', name: 'Australia', flag: '🇦🇺' },\n { code: 'AT', dialCode: '+43', name: 'Austria', flag: '🇦🇹' },\n { code: 'AZ', dialCode: '+994', name: 'Azerbaijan', flag: '🇦🇿' },\n { code: 'BS', dialCode: '+1', name: 'Bahamas', flag: '🇧🇸' },\n { code: 'BH', dialCode: '+973', name: 'Bahrain', flag: '🇧🇭' },\n { code: 'BD', dialCode: '+880', name: 'Bangladesh', flag: '🇧🇩' },\n { code: 'BB', dialCode: '+1', name: 'Barbados', flag: '🇧🇧' },\n { code: 'BY', dialCode: '+375', name: 'Belarus', flag: '🇧🇾' },\n { code: 'BE', dialCode: '+32', name: 'Belgium', flag: '🇧🇪' },\n { code: 'BZ', dialCode: '+501', name: 'Belize', flag: '🇧🇿' },\n { code: 'BJ', dialCode: '+229', name: 'Benin', flag: '🇧🇯' },\n { code: 'BT', dialCode: '+975', name: 'Bhutan', flag: '🇧🇹' },\n { code: 'BO', dialCode: '+591', name: 'Bolivia', flag: '🇧🇴' },\n { code: 'BA', dialCode: '+387', name: 'Bosnia and Herzegovina', flag: '🇧🇦' },\n { code: 'BW', dialCode: '+267', name: 'Botswana', flag: '🇧🇼' },\n { code: 'BR', dialCode: '+55', name: 'Brazil', flag: '🇧🇷' },\n { code: 'BN', dialCode: '+673', name: 'Brunei', flag: '🇧🇳' },\n { code: 'BG', dialCode: '+359', name: 'Bulgaria', flag: '🇧🇬' },\n { code: 'BF', dialCode: '+226', name: 'Burkina Faso', flag: '🇧🇫' },\n { code: 'BI', dialCode: '+257', name: 'Burundi', flag: '🇧🇮' },\n { code: 'CV', dialCode: '+238', name: 'Cabo Verde', flag: '🇨🇻' },\n { code: 'KH', dialCode: '+855', name: 'Cambodia', flag: '🇰🇭' },\n { code: 'CM', dialCode: '+237', name: 'Cameroon', flag: '🇨🇲' },\n { code: 'CA', dialCode: '+1', name: 'Canada', flag: '🇨🇦' },\n {\n code: 'CF',\n dialCode: '+236',\n name: 'Central African Republic',\n flag: '🇨🇫',\n },\n { code: 'TD', dialCode: '+235', name: 'Chad', flag: '🇹🇩' },\n { code: 'CL', dialCode: '+56', name: 'Chile', flag: '🇨🇱' },\n { code: 'CN', dialCode: '+86', name: 'China', flag: '🇨🇳' },\n { code: 'CO', dialCode: '+57', name: 'Colombia', flag: '🇨🇴' },\n { code: 'KM', dialCode: '+269', name: 'Comoros', flag: '🇰🇲' },\n { code: 'CG', dialCode: '+242', name: 'Congo', flag: '🇨🇬' },\n { code: 'CD', dialCode: '+243', name: 'Congo (DRC)', flag: '🇨🇩' },\n { code: 'CR', dialCode: '+506', name: 'Costa Rica', flag: '🇨🇷' },\n { code: 'HR', dialCode: '+385', name: 'Croatia', flag: '🇭🇷' },\n { code: 'CU', dialCode: '+53', name: 'Cuba', flag: '🇨🇺' },\n { code: 'CY', dialCode: '+357', name: 'Cyprus', flag: '🇨🇾' },\n { code: 'CZ', dialCode: '+420', name: 'Czechia', flag: '🇨🇿' },\n { code: 'CI', dialCode: '+225', name: \"Cote d'Ivoire\", flag: '🇨🇮' },\n { code: 'DK', dialCode: '+45', name: 'Denmark', flag: '🇩🇰' },\n { code: 'DJ', dialCode: '+253', name: 'Djibouti', flag: '🇩🇯' },\n { code: 'DM', dialCode: '+1', name: 'Dominica', flag: '🇩🇲' },\n { code: 'DO', dialCode: '+1', name: 'Dominican Republic', flag: '🇩🇴' },\n { code: 'EC', dialCode: '+593', name: 'Ecuador', flag: '🇪🇨' },\n { code: 'EG', dialCode: '+20', name: 'Egypt', flag: '🇪🇬' },\n { code: 'SV', dialCode: '+503', name: 'El Salvador', flag: '🇸🇻' },\n { code: 'GQ', dialCode: '+240', name: 'Equatorial Guinea', flag: '🇬🇶' },\n { code: 'ER', dialCode: '+291', name: 'Eritrea', flag: '🇪🇷' },\n { code: 'EE', dialCode: '+372', name: 'Estonia', flag: '🇪🇪' },\n { code: 'SZ', dialCode: '+268', name: 'Eswatini', flag: '🇸🇿' },\n { code: 'ET', dialCode: '+251', name: 'Ethiopia', flag: '🇪🇹' },\n { code: 'FJ', dialCode: '+679', name: 'Fiji', flag: '🇫🇯' },\n { code: 'FI', dialCode: '+358', name: 'Finland', flag: '🇫🇮' },\n { code: 'FR', dialCode: '+33', name: 'France', flag: '🇫🇷' },\n { code: 'GA', dialCode: '+241', name: 'Gabon', flag: '🇬🇦' },\n { code: 'GM', dialCode: '+220', name: 'Gambia', flag: '🇬🇲' },\n { code: 'GE', dialCode: '+995', name: 'Georgia', flag: '🇬🇪' },\n { code: 'DE', dialCode: '+49', name: 'Germany', flag: '🇩🇪' },\n { code: 'GH', dialCode: '+233', name: 'Ghana', flag: '🇬🇭' },\n { code: 'GR', dialCode: '+30', name: 'Greece', flag: '🇬🇷' },\n { code: 'GD', dialCode: '+1', name: 'Grenada', flag: '🇬🇩' },\n { code: 'GT', dialCode: '+502', name: 'Guatemala', flag: '🇬🇹' },\n { code: 'GN', dialCode: '+224', name: 'Guinea', flag: '🇬🇳' },\n { code: 'GW', dialCode: '+245', name: 'Guinea-Bissau', flag: '🇬🇼' },\n { code: 'GY', dialCode: '+592', name: 'Guyana', flag: '🇬🇾' },\n { code: 'HT', dialCode: '+509', name: 'Haiti', flag: '🇭🇹' },\n { code: 'HN', dialCode: '+504', name: 'Honduras', flag: '🇭🇳' },\n { code: 'HK', dialCode: '+852', name: 'Hong Kong', flag: '🇭🇰' },\n { code: 'HU', dialCode: '+36', name: 'Hungary', flag: '🇭🇺' },\n { code: 'IS', dialCode: '+354', name: 'Iceland', flag: '🇮🇸' },\n { code: 'IN', dialCode: '+91', name: 'India', flag: '🇮🇳' },\n { code: 'ID', dialCode: '+62', name: 'Indonesia', flag: '🇮🇩' },\n { code: 'IR', dialCode: '+98', name: 'Iran', flag: '🇮🇷' },\n { code: 'IQ', dialCode: '+964', name: 'Iraq', flag: '🇮🇶' },\n { code: 'IE', dialCode: '+353', name: 'Ireland', flag: '🇮🇪' },\n { code: 'IL', dialCode: '+972', name: 'Israel', flag: '🇮🇱' },\n { code: 'IT', dialCode: '+39', name: 'Italy', flag: '🇮🇹' },\n { code: 'JM', dialCode: '+1', name: 'Jamaica', flag: '🇯🇲' },\n { code: 'JP', dialCode: '+81', name: 'Japan', flag: '🇯🇵' },\n { code: 'JO', dialCode: '+962', name: 'Jordan', flag: '🇯🇴' },\n { code: 'KZ', dialCode: '+7', name: 'Kazakhstan', flag: '🇰🇿' },\n { code: 'KE', dialCode: '+254', name: 'Kenya', flag: '🇰🇪' },\n { code: 'KI', dialCode: '+686', name: 'Kiribati', flag: '🇰🇮' },\n { code: 'KW', dialCode: '+965', name: 'Kuwait', flag: '🇰🇼' },\n { code: 'KG', dialCode: '+996', name: 'Kyrgyzstan', flag: '🇰🇬' },\n { code: 'LA', dialCode: '+856', name: 'Laos', flag: '🇱🇦' },\n { code: 'LV', dialCode: '+371', name: 'Latvia', flag: '🇱🇻' },\n { code: 'LB', dialCode: '+961', name: 'Lebanon', flag: '🇱🇧' },\n { code: 'LS', dialCode: '+266', name: 'Lesotho', flag: '🇱🇸' },\n { code: 'LR', dialCode: '+231', name: 'Liberia', flag: '🇱🇷' },\n { code: 'LY', dialCode: '+218', name: 'Libya', flag: '🇱🇾' },\n { code: 'LI', dialCode: '+423', name: 'Liechtenstein', flag: '🇱🇮' },\n { code: 'LT', dialCode: '+370', name: 'Lithuania', flag: '🇱🇹' },\n { code: 'LU', dialCode: '+352', name: 'Luxembourg', flag: '🇱🇺' },\n { code: 'MO', dialCode: '+853', name: 'Macao', flag: '🇲🇴' },\n { code: 'MG', dialCode: '+261', name: 'Madagascar', flag: '🇲🇬' },\n { code: 'MW', dialCode: '+265', name: 'Malawi', flag: '🇲🇼' },\n { code: 'MY', dialCode: '+60', name: 'Malaysia', flag: '🇲🇾' },\n { code: 'MV', dialCode: '+960', name: 'Maldives', flag: '🇲🇻' },\n { code: 'ML', dialCode: '+223', name: 'Mali', flag: '🇲🇱' },\n { code: 'MT', dialCode: '+356', name: 'Malta', flag: '🇲🇹' },\n { code: 'MH', dialCode: '+692', name: 'Marshall Islands', flag: '🇲🇭' },\n { code: 'MR', dialCode: '+222', name: 'Mauritania', flag: '🇲🇷' },\n { code: 'MU', dialCode: '+230', name: 'Mauritius', flag: '🇲🇺' },\n { code: 'MX', dialCode: '+52', name: 'Mexico', flag: '🇲🇽' },\n { code: 'FM', dialCode: '+691', name: 'Micronesia', flag: '🇫🇲' },\n { code: 'MD', dialCode: '+373', name: 'Moldova', flag: '🇲🇩' },\n { code: 'MC', dialCode: '+377', name: 'Monaco', flag: '🇲🇨' },\n { code: 'MN', dialCode: '+976', name: 'Mongolia', flag: '🇲🇳' },\n { code: 'ME', dialCode: '+382', name: 'Montenegro', flag: '🇲🇪' },\n { code: 'MA', dialCode: '+212', name: 'Morocco', flag: '🇲🇦' },\n { code: 'MZ', dialCode: '+258', name: 'Mozambique', flag: '🇲🇿' },\n { code: 'MM', dialCode: '+95', name: 'Myanmar', flag: '🇲🇲' },\n { code: 'NA', dialCode: '+264', name: 'Namibia', flag: '🇳🇦' },\n { code: 'NR', dialCode: '+674', name: 'Nauru', flag: '🇳🇷' },\n { code: 'NP', dialCode: '+977', name: 'Nepal', flag: '🇳🇵' },\n { code: 'NL', dialCode: '+31', name: 'Netherlands', flag: '🇳🇱' },\n { code: 'NZ', dialCode: '+64', name: 'New Zealand', flag: '🇳🇿' },\n { code: 'NI', dialCode: '+505', name: 'Nicaragua', flag: '🇳🇮' },\n { code: 'NE', dialCode: '+227', name: 'Niger', flag: '🇳🇪' },\n { code: 'NG', dialCode: '+234', name: 'Nigeria', flag: '🇳🇬' },\n { code: 'KP', dialCode: '+850', name: 'North Korea', flag: '🇰🇵' },\n { code: 'MK', dialCode: '+389', name: 'North Macedonia', flag: '🇲🇰' },\n { code: 'NO', dialCode: '+47', name: 'Norway', flag: '🇳🇴' },\n { code: 'OM', dialCode: '+968', name: 'Oman', flag: '🇴🇲' },\n { code: 'PK', dialCode: '+92', name: 'Pakistan', flag: '🇵🇰' },\n { code: 'PW', dialCode: '+680', name: 'Palau', flag: '🇵🇼' },\n { code: 'PS', dialCode: '+970', name: 'Palestine', flag: '🇵🇸' },\n { code: 'PA', dialCode: '+507', name: 'Panama', flag: '🇵🇦' },\n { code: 'PG', dialCode: '+675', name: 'Papua New Guinea', flag: '🇵🇬' },\n { code: 'PY', dialCode: '+595', name: 'Paraguay', flag: '🇵🇾' },\n { code: 'PE', dialCode: '+51', name: 'Peru', flag: '🇵🇪' },\n { code: 'PH', dialCode: '+63', name: 'Philippines', flag: '🇵🇭' },\n { code: 'PL', dialCode: '+48', name: 'Poland', flag: '🇵🇱' },\n { code: 'PT', dialCode: '+351', name: 'Portugal', flag: '🇵🇹' },\n { code: 'PR', dialCode: '+1', name: 'Puerto Rico', flag: '🇵🇷' },\n { code: 'QA', dialCode: '+974', name: 'Qatar', flag: '🇶🇦' },\n { code: 'RO', dialCode: '+40', name: 'Romania', flag: '🇷🇴' },\n { code: 'RU', dialCode: '+7', name: 'Russia', flag: '🇷🇺' },\n { code: 'RW', dialCode: '+250', name: 'Rwanda', flag: '🇷🇼' },\n { code: 'KN', dialCode: '+1', name: 'Saint Kitts and Nevis', flag: '🇰🇳' },\n { code: 'LC', dialCode: '+1', name: 'Saint Lucia', flag: '🇱🇨' },\n {\n code: 'VC',\n dialCode: '+1',\n name: 'Saint Vincent and the Grenadines',\n flag: '🇻🇨',\n },\n { code: 'WS', dialCode: '+685', name: 'Samoa', flag: '🇼🇸' },\n { code: 'SM', dialCode: '+378', name: 'San Marino', flag: '🇸🇲' },\n { code: 'ST', dialCode: '+239', name: 'Sao Tome and Principe', flag: '🇸🇹' },\n { code: 'SA', dialCode: '+966', name: 'Saudi Arabia', flag: '🇸🇦' },\n { code: 'SN', dialCode: '+221', name: 'Senegal', flag: '🇸🇳' },\n { code: 'RS', dialCode: '+381', name: 'Serbia', flag: '🇷🇸' },\n { code: 'SC', dialCode: '+248', name: 'Seychelles', flag: '🇸🇨' },\n { code: 'SL', dialCode: '+232', name: 'Sierra Leone', flag: '🇸🇱' },\n { code: 'SG', dialCode: '+65', name: 'Singapore', flag: '🇸🇬' },\n { code: 'SK', dialCode: '+421', name: 'Slovakia', flag: '🇸🇰' },\n { code: 'SI', dialCode: '+386', name: 'Slovenia', flag: '🇸🇮' },\n { code: 'SB', dialCode: '+677', name: 'Solomon Islands', flag: '🇸🇧' },\n { code: 'SO', dialCode: '+252', name: 'Somalia', flag: '🇸🇴' },\n { code: 'ZA', dialCode: '+27', name: 'South Africa', flag: '🇿🇦' },\n { code: 'KR', dialCode: '+82', name: 'South Korea', flag: '🇰🇷' },\n { code: 'SS', dialCode: '+211', name: 'South Sudan', flag: '🇸🇸' },\n { code: 'ES', dialCode: '+34', name: 'Spain', flag: '🇪🇸' },\n { code: 'LK', dialCode: '+94', name: 'Sri Lanka', flag: '🇱🇰' },\n { code: 'SD', dialCode: '+249', name: 'Sudan', flag: '🇸🇩' },\n { code: 'SR', dialCode: '+597', name: 'Suriname', flag: '🇸🇷' },\n { code: 'SE', dialCode: '+46', name: 'Sweden', flag: '🇸🇪' },\n { code: 'CH', dialCode: '+41', name: 'Switzerland', flag: '🇨🇭' },\n { code: 'SY', dialCode: '+963', name: 'Syria', flag: '🇸🇾' },\n { code: 'TW', dialCode: '+886', name: 'Taiwan', flag: '🇹🇼' },\n { code: 'TJ', dialCode: '+992', name: 'Tajikistan', flag: '🇹🇯' },\n { code: 'TZ', dialCode: '+255', name: 'Tanzania', flag: '🇹🇿' },\n { code: 'TH', dialCode: '+66', name: 'Thailand', flag: '🇹🇭' },\n { code: 'TL', dialCode: '+670', name: 'Timor-Leste', flag: '🇹🇱' },\n { code: 'TG', dialCode: '+228', name: 'Togo', flag: '🇹🇬' },\n { code: 'TO', dialCode: '+676', name: 'Tonga', flag: '🇹🇴' },\n { code: 'TT', dialCode: '+1', name: 'Trinidad and Tobago', flag: '🇹🇹' },\n { code: 'TN', dialCode: '+216', name: 'Tunisia', flag: '🇹🇳' },\n { code: 'TR', dialCode: '+90', name: 'Turkey', flag: '🇹🇷' },\n { code: 'TM', dialCode: '+993', name: 'Turkmenistan', flag: '🇹🇲' },\n { code: 'TV', dialCode: '+688', name: 'Tuvalu', flag: '🇹🇻' },\n { code: 'UG', dialCode: '+256', name: 'Uganda', flag: '🇺🇬' },\n { code: 'UA', dialCode: '+380', name: 'Ukraine', flag: '🇺🇦' },\n { code: 'AE', dialCode: '+971', name: 'United Arab Emirates', flag: '🇦🇪' },\n { code: 'GB', dialCode: '+44', name: 'United Kingdom', flag: '🇬🇧' },\n { code: 'US', dialCode: '+1', name: 'United States', flag: '🇺🇸' },\n { code: 'UY', dialCode: '+598', name: 'Uruguay', flag: '🇺🇾' },\n { code: 'UZ', dialCode: '+998', name: 'Uzbekistan', flag: '🇺🇿' },\n { code: 'VU', dialCode: '+678', name: 'Vanuatu', flag: '🇻🇺' },\n { code: 'VA', dialCode: '+39', name: 'Vatican City', flag: '🇻🇦' },\n { code: 'VE', dialCode: '+58', name: 'Venezuela', flag: '🇻🇪' },\n { code: 'VN', dialCode: '+84', name: 'Vietnam', flag: '🇻🇳' },\n { code: 'YE', dialCode: '+967', name: 'Yemen', flag: '🇾🇪' },\n { code: 'ZM', dialCode: '+260', name: 'Zambia', flag: '🇿🇲' },\n { code: 'ZW', dialCode: '+263', name: 'Zimbabwe', flag: '🇿🇼' },\n];\n\n// ---------------------------------------------------------------------------\n// Country lookup helpers\n// ---------------------------------------------------------------------------\n\nconst byCode = new Map(countries.map((c) => [c.code, c]));\n\n/** Common IANA timezone → country code mapping. */\nconst timezoneToCountry: Record<string, string> = {\n 'America/New_York': 'US',\n 'America/Chicago': 'US',\n 'America/Denver': 'US',\n 'America/Los_Angeles': 'US',\n 'America/Anchorage': 'US',\n 'Pacific/Honolulu': 'US',\n 'America/Phoenix': 'US',\n 'America/Indiana/Indianapolis': 'US',\n 'America/Toronto': 'CA',\n 'America/Vancouver': 'CA',\n 'America/Edmonton': 'CA',\n 'America/Winnipeg': 'CA',\n 'America/Halifax': 'CA',\n 'America/St_Johns': 'CA',\n 'Europe/London': 'GB',\n 'Europe/Dublin': 'IE',\n 'Europe/Paris': 'FR',\n 'Europe/Berlin': 'DE',\n 'Europe/Madrid': 'ES',\n 'Europe/Rome': 'IT',\n 'Europe/Amsterdam': 'NL',\n 'Europe/Brussels': 'BE',\n 'Europe/Zurich': 'CH',\n 'Europe/Vienna': 'AT',\n 'Europe/Stockholm': 'SE',\n 'Europe/Oslo': 'NO',\n 'Europe/Copenhagen': 'DK',\n 'Europe/Helsinki': 'FI',\n 'Europe/Warsaw': 'PL',\n 'Europe/Prague': 'CZ',\n 'Europe/Bucharest': 'RO',\n 'Europe/Budapest': 'HU',\n 'Europe/Athens': 'GR',\n 'Europe/Istanbul': 'TR',\n 'Europe/Moscow': 'RU',\n 'Europe/Kiev': 'UA',\n 'Europe/Lisbon': 'PT',\n 'Europe/Belgrade': 'RS',\n 'Europe/Zagreb': 'HR',\n 'Europe/Bratislava': 'SK',\n 'Europe/Ljubljana': 'SI',\n 'Europe/Tallinn': 'EE',\n 'Europe/Riga': 'LV',\n 'Europe/Vilnius': 'LT',\n 'Asia/Tokyo': 'JP',\n 'Asia/Seoul': 'KR',\n 'Asia/Shanghai': 'CN',\n 'Asia/Hong_Kong': 'HK',\n 'Asia/Taipei': 'TW',\n 'Asia/Singapore': 'SG',\n 'Asia/Kolkata': 'IN',\n 'Asia/Karachi': 'PK',\n 'Asia/Dhaka': 'BD',\n 'Asia/Bangkok': 'TH',\n 'Asia/Jakarta': 'ID',\n 'Asia/Manila': 'PH',\n 'Asia/Kuala_Lumpur': 'MY',\n 'Asia/Ho_Chi_Minh': 'VN',\n 'Asia/Riyadh': 'SA',\n 'Asia/Dubai': 'AE',\n 'Asia/Tehran': 'IR',\n 'Asia/Baghdad': 'IQ',\n 'Asia/Beirut': 'LB',\n 'Asia/Jerusalem': 'IL',\n 'Asia/Amman': 'JO',\n 'Asia/Almaty': 'KZ',\n 'Asia/Tashkent': 'UZ',\n 'Asia/Colombo': 'LK',\n 'Asia/Kathmandu': 'NP',\n 'Asia/Yangon': 'MM',\n 'Australia/Sydney': 'AU',\n 'Australia/Melbourne': 'AU',\n 'Australia/Brisbane': 'AU',\n 'Australia/Perth': 'AU',\n 'Australia/Adelaide': 'AU',\n 'Pacific/Auckland': 'NZ',\n 'Pacific/Fiji': 'FJ',\n 'Africa/Cairo': 'EG',\n 'Africa/Lagos': 'NG',\n 'Africa/Nairobi': 'KE',\n 'Africa/Johannesburg': 'ZA',\n 'Africa/Casablanca': 'MA',\n 'Africa/Accra': 'GH',\n 'Africa/Addis_Ababa': 'ET',\n 'Africa/Dar_es_Salaam': 'TZ',\n 'Africa/Kampala': 'UG',\n 'America/Mexico_City': 'MX',\n 'America/Bogota': 'CO',\n 'America/Lima': 'PE',\n 'America/Santiago': 'CL',\n 'America/Buenos_Aires': 'AR',\n 'America/Sao_Paulo': 'BR',\n 'America/Caracas': 'VE',\n 'America/Guatemala': 'GT',\n 'America/Havana': 'CU',\n 'America/Panama': 'PA',\n 'America/Jamaica': 'JM',\n};\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Detect the user's country from their timezone.\n * Falls back to `'US'` if the timezone can't be mapped.\n */\nexport function detectCountry(): string {\n try {\n const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;\n return timezoneToCountry[tz] ?? 'US';\n } catch {\n return 'US';\n }\n}\n\n/**\n * Format an E.164 phone number for display.\n *\n * US/CA numbers: `+1 (555) 123-4567`\n * Others: `+{dialCode} {rest}`\n */\nexport function format(e164: string): string {\n if (!e164.startsWith('+')) {\n return e164;\n }\n\n const digits = e164.slice(1);\n\n // US/CA: +1 followed by 10 digits\n if (digits.startsWith('1') && digits.length === 11) {\n const area = digits.slice(1, 4);\n const prefix = digits.slice(4, 7);\n const line = digits.slice(7);\n return `+1 (${area}) ${prefix}-${line}`;\n }\n\n // Find matching dial code (try longest match first)\n for (let len = 4; len >= 1; len--) {\n const candidate = '+' + digits.slice(0, len);\n const match = countries.find((c) => c.dialCode === candidate);\n if (match) {\n const rest = digits.slice(len);\n return `${match.dialCode} ${rest}`;\n }\n }\n\n return e164;\n}\n\n/**\n * Convert a national phone number to E.164 format.\n *\n * @param national - Phone number with or without formatting (e.g. \"(555) 123-4567\")\n * @param countryCode - ISO 3166-1 alpha-2 code (e.g. \"US\")\n * @returns E.164 formatted number (e.g. \"+15551234567\")\n */\nexport function toE164(national: string, countryCode: string): string {\n const digits = national.replace(/\\D/g, '');\n const country = byCode.get(countryCode);\n if (!country) {\n return '+' + digits;\n }\n const dialDigits = country.dialCode.slice(1); // remove +\n // If the number already starts with the dial code, don't double-add\n if (digits.startsWith(dialDigits)) {\n return '+' + digits;\n }\n return country.dialCode + digits;\n}\n\n/**\n * Check if a string is a valid E.164 phone number.\n * Must start with + followed by 7-15 digits.\n */\nexport function isValid(phone: string): boolean {\n return /^\\+[1-9]\\d{6,14}$/.test(phone);\n}\n","/**\n * Email validation helper for auth flows.\n */\n\n/** Basic email format check. Not RFC 5322 exhaustive. */\nexport function isValid(email: string): boolean {\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email);\n}\n","/**\n * Auth — app-managed authentication for MindStudio v2 apps.\n *\n * Provides verification code flows (email + SMS), session state\n * management, and phone/email validation helpers. The platform\n * handles code delivery, cookie management, and user storage —\n * developers build their own login UI using these methods.\n *\n * ## How it works\n *\n * ```\n * // 1. Send a verification code\n * const { verificationId } = await auth.sendEmailCode('user@example.com');\n *\n * // 2. User enters the code in your UI\n * const user = await auth.verifyEmailCode(verificationId, '123456');\n *\n * // 3. Session is now active — all SDK calls use the authenticated token\n * const result = await api.getDashboard(); // uses authenticated session\n * auth.getCurrentUser(); // { id, email, phone, roles, createdAt }\n * ```\n *\n * Verify, confirm, and logout methods update `window.__MINDSTUDIO__`\n * in-place so all downstream calls (method invocation, agent chat,\n * uploads) immediately use the new session. No page refresh needed.\n *\n * @example\n * ```tsx\n * import { auth } from '@mindstudio-ai/interface';\n *\n * function LoginPage() {\n * const [email, setEmail] = useState('');\n * const [verificationId, setVerificationId] = useState('');\n * const [code, setCode] = useState('');\n *\n * const handleSend = async () => {\n * const { verificationId } = await auth.sendEmailCode(email);\n * setVerificationId(verificationId);\n * };\n *\n * const handleVerify = async () => {\n * await auth.verifyEmailCode(verificationId, code);\n * // Session updated in-place — navigate to your app\n * window.location.href = '/dashboard';\n * };\n * }\n * ```\n */\n\nimport { getConfig, updateConfig } from './config.js';\nimport { MindStudioInterfaceError } from './errors.js';\nimport type { AppUser, AuthSessionBundle } from './types.js';\nimport * as phoneHelpers from './auth-phone.js';\nimport * as emailHelpers from './auth-email.js';\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nasync function authFetch<T>(\n path: string,\n method: 'GET' | 'POST',\n body?: Record<string, unknown>,\n): Promise<T> {\n const config = getConfig();\n const headers: Record<string, string> = {\n Authorization: `Bearer ${config.token}`,\n };\n\n if (body !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n\n const res = await fetch(path, {\n method,\n headers,\n ...(body !== undefined && { body: JSON.stringify(body) }),\n });\n\n if (!res.ok) {\n let message = `Auth request failed: ${res.status} ${res.statusText}`;\n let code = 'auth_error';\n try {\n const err = (await res.json()) as { error?: string; code?: string };\n if (err.error) {\n message = err.error;\n }\n if (err.code) {\n code = err.code;\n }\n } catch {\n // Response wasn't JSON\n }\n throw new MindStudioInterfaceError(message, code, res.status);\n }\n\n return (await res.json()) as T;\n}\n\nconst authListeners: Set<(user: AppUser | null) => void> = new Set();\n\nfunction applySession(bundle: AuthSessionBundle): void {\n updateConfig({\n token: bundle.token,\n user: bundle.user,\n methods: bundle.methods,\n });\n authListeners.forEach((cb) => cb(bundle.user));\n}\n\nfunction requireUser(bundle: AuthSessionBundle): AppUser {\n if (!bundle.user) {\n throw new MindStudioInterfaceError(\n 'Verification succeeded but no user was returned',\n 'auth_error',\n );\n }\n return bundle.user;\n}\n\n// ---------------------------------------------------------------------------\n// Auth interface\n// ---------------------------------------------------------------------------\n\n/**\n * The auth namespace — authentication flows, state, and helpers.\n *\n * Auth methods throw {@link MindStudioInterfaceError} on failure.\n * Common error codes:\n *\n * | Code | Status | Meaning |\n * |------|--------|---------|\n * | `rate_limited` | 429 | Too many code requests (max 5 per 15 min per identifier) |\n * | `invalid_code` | 400 | Wrong verification code |\n * | `verification_expired` | 400 | Code expired (10 min TTL) — request a new one |\n * | `max_attempts_exceeded` | 400 | Too many incorrect attempts (max 3) — request a new code |\n * | `not_authenticated` | 401 | Auth cookie missing (change/logout endpoints) |\n * | `invalid_session` | 401 | Auth cookie expired or invalid |\n */\nexport interface Auth {\n // -- State --\n\n /** The current authenticated user, or `null` if not authenticated. */\n readonly currentUser: AppUser | null;\n\n /** Get the current authenticated user, or `null` if not authenticated. */\n getCurrentUser(): AppUser | null;\n\n /** Whether the current session is authenticated. */\n isAuthenticated(): boolean;\n\n // -- Email code flow --\n\n /** Send a 6-digit verification code to an email address. */\n sendEmailCode(email: string): Promise<{ verificationId: string }>;\n\n /**\n * Verify an email code. On success, updates the session in-place\n * and returns the authenticated user.\n */\n verifyEmailCode(verificationId: string, code: string): Promise<AppUser>;\n\n // -- SMS code flow --\n\n /** Send a 6-digit verification code via SMS. Phone must be E.164. */\n sendSmsCode(phone: string): Promise<{ verificationId: string }>;\n\n /**\n * Verify an SMS code. On success, updates the session in-place\n * and returns the authenticated user.\n */\n verifySmsCode(verificationId: string, code: string): Promise<AppUser>;\n\n // -- Email/phone change (requires authentication) --\n\n /** Request an email change. Sends a code to the new email. */\n requestEmailChange(newEmail: string): Promise<void>;\n\n /** Confirm an email change with the verification code. */\n confirmEmailChange(newEmail: string, code: string): Promise<AppUser>;\n\n /** Request a phone change. Sends a code to the new phone (E.164). */\n requestPhoneChange(newPhone: string): Promise<void>;\n\n /** Confirm a phone change with the verification code. */\n confirmPhoneChange(newPhone: string, code: string): Promise<AppUser>;\n\n // -- Session --\n\n /** Log out. Clears the cookie and updates the session to unauthenticated. */\n logout(): Promise<void>;\n\n /**\n * Subscribe to auth state changes. Fires immediately with the\n * current state, then again whenever verify, confirm, or logout\n * updates the session.\n *\n * @returns An unsubscribe function.\n *\n * @example\n * ```ts\n * // React hook\n * function useAuth() {\n * const [user, setUser] = useState<AppUser | null>(null);\n * useEffect(() => auth.onAuthStateChanged(setUser), []);\n * return user;\n * }\n * ```\n */\n onAuthStateChanged(callback: (user: AppUser | null) => void): () => void;\n\n // -- Helpers --\n\n /** Phone number utilities — countries, formatting, validation. */\n phone: {\n /** All countries with dial codes, sorted alphabetically. */\n countries: readonly phoneHelpers.Country[];\n /** Detect the user's country from their timezone. Falls back to `'US'`. */\n detectCountry(): string;\n /** Format an E.164 number for display (e.g. `+1 (555) 123-4567`). */\n format(e164: string): string;\n /** Convert a national number to E.164 (e.g. `('5551234567', 'US') → '+15551234567'`). */\n toE164(national: string, countryCode: string): string;\n /** Check if a string is a valid E.164 phone number. */\n isValid(phone: string): boolean;\n };\n\n /** Email validation. */\n email: {\n /** Basic email format check. */\n isValid(email: string): boolean;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\nexport const auth: Auth = {\n // -- State --\n\n get currentUser() {\n return getConfig().user;\n },\n\n getCurrentUser() {\n return getConfig().user;\n },\n\n isAuthenticated() {\n return getConfig().user !== null;\n },\n\n // -- Email code flow --\n\n sendEmailCode(email: string) {\n return authFetch<{ verificationId: string }>('/_/auth/email/send', 'POST', {\n email,\n });\n },\n\n async verifyEmailCode(verificationId: string, code: string) {\n const bundle = await authFetch<AuthSessionBundle>(\n '/_/auth/email/verify',\n 'POST',\n { verificationId, code },\n );\n applySession(bundle);\n return requireUser(bundle);\n },\n\n // -- SMS code flow --\n\n sendSmsCode(phone: string) {\n return authFetch<{ verificationId: string }>('/_/auth/sms/send', 'POST', {\n phone,\n });\n },\n\n async verifySmsCode(verificationId: string, code: string) {\n const bundle = await authFetch<AuthSessionBundle>(\n '/_/auth/sms/verify',\n 'POST',\n { verificationId, code },\n );\n applySession(bundle);\n return requireUser(bundle);\n },\n\n // -- Email/phone change --\n\n async requestEmailChange(newEmail: string) {\n await authFetch('/_/auth/email/change', 'POST', { newEmail });\n },\n\n async confirmEmailChange(newEmail: string, code: string) {\n const bundle = await authFetch<AuthSessionBundle>(\n '/_/auth/email/change/confirm',\n 'POST',\n { newEmail, code },\n );\n applySession(bundle);\n return requireUser(bundle);\n },\n\n async requestPhoneChange(newPhone: string) {\n await authFetch('/_/auth/phone/change', 'POST', { newPhone });\n },\n\n async confirmPhoneChange(newPhone: string, code: string) {\n const bundle = await authFetch<AuthSessionBundle>(\n '/_/auth/phone/change/confirm',\n 'POST',\n { newPhone, code },\n );\n applySession(bundle);\n return requireUser(bundle);\n },\n\n // -- Session --\n\n async logout() {\n const bundle = await authFetch<AuthSessionBundle>(\n '/_/auth/logout',\n 'POST',\n {},\n );\n applySession(bundle);\n },\n\n onAuthStateChanged(callback: (user: AppUser | null) => void) {\n authListeners.add(callback);\n callback(getConfig().user);\n return () => {\n authListeners.delete(callback);\n };\n },\n\n // -- Helpers --\n\n phone: {\n countries: phoneHelpers.countries,\n detectCountry: phoneHelpers.detectCountry,\n format: phoneHelpers.format,\n toE164: phoneHelpers.toE164,\n isValid: phoneHelpers.isValid,\n },\n\n email: {\n isValid: emailHelpers.isValid,\n },\n};\n"],"mappings":"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","names":["MindStudioInterfaceError","message","code","status","_config","getConfig","raw","MindStudioInterfaceError","missing","k","updateConfig","updates","config","createClient","_","methodName","input","options","config","getConfig","methodId","MindStudioInterfaceError","url","wantsStream","res","errorMessage","errorCode","body","reader","decoder","buffer","finalOutput","done","value","lines","line","json","event","AGENT_BASE","request","path","method","body","headers","getConfig","res","errorMessage","errorCode","err","MindStudioInterfaceError","dispatchEvent","event","callbacks","createAgentChatClient","cursor","query","threadId","title","content","options","controller","cb","abortable","config","url","reader","decoder","buffer","result","done","value","lines","line","json","platform","file","options","config","getConfig","onProgress","signal","presignRes","MindStudioInterfaceError","uploadUrl","uploadFields","publicUrl","formData","key","value","xhrUpload","uploadRes","url","resolve","reject","xhr","e","countries","byCode","c","timezoneToCountry","detectCountry","tz","format","e164","digits","area","prefix","line","len","candidate","match","rest","toE164","national","countryCode","country","dialDigits","isValid","phone","isValid","email","authFetch","path","method","body","headers","getConfig","res","message","code","err","MindStudioInterfaceError","authListeners","applySession","bundle","updateConfig","cb","requireUser","auth","email","verificationId","phone","newEmail","newPhone","callback","countries","detectCountry","format","toE164","isValid"]}
|