@ateam-ai-mobile/core 0.1.1 → 0.1.3
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/dist/index.d.mts +48 -7
- package/dist/index.d.ts +48 -7
- package/dist/index.js +46 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +44 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/api/tenants.ts +59 -6
- package/src/chat/store.ts +4 -0
- package/src/chat/types.ts +6 -0
- package/src/config.ts +22 -0
- package/src/index.ts +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ateam-ai-mobile/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Headless ADAS SDK core \u2014 auth bridge, API client, chat/job streaming, MCP calls, theme tokens. No UI, no Expo dependencies.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
package/src/api/tenants.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { apiFetch } from './client';
|
|
2
|
+
import { getConsumerAuthApiUrl, getToken, getConfig, type AdasSession } from '../config';
|
|
3
|
+
import { useChatStore, closeChatStream } from '../chat/store';
|
|
2
4
|
|
|
3
5
|
export interface TenantMembership {
|
|
4
6
|
id: string;
|
|
@@ -9,14 +11,65 @@ export interface TenantMembership {
|
|
|
9
11
|
/**
|
|
10
12
|
* List the tenants the CURRENT user belongs to (GET /api/tenants/list).
|
|
11
13
|
*
|
|
12
|
-
*
|
|
13
|
-
* (google/apple oauth or embed id)
|
|
14
|
-
* their user-doc memberships.
|
|
15
|
-
*
|
|
16
|
-
* Note: switching tenant still requires a NEW JWT for that tenant (the
|
|
17
|
-
* token is tenant-scoped) — the host owns that re-auth flow.
|
|
14
|
+
* The list is membership-scoped: for consumer JWTs the backend returns ONLY
|
|
15
|
+
* tenants where the user's login identity (google/apple oauth or embed id)
|
|
16
|
+
* has an active actor; builder JWTs get their user-doc memberships. It never
|
|
17
|
+
* leaks the full tenant list. Returns [] when signed out or unknown.
|
|
18
18
|
*/
|
|
19
19
|
export async function listMyTenants(): Promise<TenantMembership[]> {
|
|
20
20
|
const data = await apiFetch<{ ok: boolean; tenants?: TenantMembership[] }>('/api/tenants/list');
|
|
21
21
|
return data.ok && Array.isArray(data.tenants) ? data.tenants : [];
|
|
22
22
|
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Re-scope the whole session to another tenant the user already belongs to.
|
|
26
|
+
*
|
|
27
|
+
* The backend treats the JWT tenant claim as the sole source of truth, so
|
|
28
|
+
* switching = minting a fresh JWT for the target tenant's actor. The SDK
|
|
29
|
+
* owns the call (POST consumer-auth /platform/consumer/switch-tenant, authed
|
|
30
|
+
* by the current token); the HOST persists the new session via
|
|
31
|
+
* `config.onSession` (the SDK never stores credentials). The chat stream is
|
|
32
|
+
* then torn down and cleared so it reconnects against the new tenant.
|
|
33
|
+
*
|
|
34
|
+
* Requires `consumerAuthUrl` and `onSession` in the initAdas() config.
|
|
35
|
+
* Throws on any non-OK response (e.g. not a member of the target tenant).
|
|
36
|
+
*/
|
|
37
|
+
export async function switchTenant(targetTenantId: string): Promise<AdasSession> {
|
|
38
|
+
const token = getToken();
|
|
39
|
+
if (!token) throw new Error('[adas] switchTenant: not signed in');
|
|
40
|
+
|
|
41
|
+
const res = await fetch(`${getConsumerAuthApiUrl()}/platform/consumer/switch-tenant`, {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
headers: {
|
|
44
|
+
'Content-Type': 'application/json',
|
|
45
|
+
Authorization: `Bearer ${token}`,
|
|
46
|
+
},
|
|
47
|
+
body: JSON.stringify({ target_tenant: targetTenantId }),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const data = await res.json().catch(() => null);
|
|
51
|
+
if (!res.ok || !data?.ok || !data.jwt) {
|
|
52
|
+
throw new Error(data?.error || `switchTenant failed (${res.status})`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const session: AdasSession = {
|
|
56
|
+
jwt: data.jwt,
|
|
57
|
+
tenant: data.tenant,
|
|
58
|
+
actor: data.actor,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Host persists the new token + active tenant BEFORE we reconnect, so the
|
|
62
|
+
// fresh stream/requests pick up the target-tenant credentials.
|
|
63
|
+
const onSession = getConfig().onSession;
|
|
64
|
+
if (!onSession) {
|
|
65
|
+
throw new Error('[adas] switchTenant: config.onSession is required to persist the new session');
|
|
66
|
+
}
|
|
67
|
+
await onSession(session);
|
|
68
|
+
|
|
69
|
+
// Tear down the old-tenant stream and history; the chat screen reloads
|
|
70
|
+
// against the new tenant on its next mount / loadConversation().
|
|
71
|
+
closeChatStream();
|
|
72
|
+
useChatStore.getState().clearChat();
|
|
73
|
+
|
|
74
|
+
return session;
|
|
75
|
+
}
|
package/src/chat/store.ts
CHANGED
|
@@ -128,6 +128,10 @@ function snapshotToProgress(snap: any, rootJobId: string): Partial<JobProgress>
|
|
|
128
128
|
const liveProgress = extractLiveProgress(snap);
|
|
129
129
|
if (liveProgress) progress.liveProgress = liveProgress;
|
|
130
130
|
|
|
131
|
+
// Human-language status (additive; absent when disabled/uncached).
|
|
132
|
+
if (typeof snap?.humanStatus === 'string' && snap.humanStatus) progress.humanStatus = snap.humanStatus;
|
|
133
|
+
if (snap?.humanLabels && typeof snap.humanLabels === 'object') progress.humanLabels = snap.humanLabels;
|
|
134
|
+
|
|
131
135
|
if (pq) {
|
|
132
136
|
progress.pendingQuestion = {
|
|
133
137
|
inputId: pq.correlation_id || pq.correlationId || pq.inputId || '',
|
package/src/chat/types.ts
CHANGED
|
@@ -26,6 +26,12 @@ export interface JobProgress {
|
|
|
26
26
|
resultActions?: UIAction[];
|
|
27
27
|
/** Live progress from adas_emit_progress() — latest event from a running fat tool */
|
|
28
28
|
liveProgress?: { message: string; step?: number; total?: number };
|
|
29
|
+
/** One warm, present-tense, localized line describing the current action
|
|
30
|
+
* (backend statusI18n). Absent when disabled / not yet cached → fall back
|
|
31
|
+
* to raw tokens. */
|
|
32
|
+
humanStatus?: string;
|
|
33
|
+
/** Lookup { rawToken → friendly phrase } for tokens in this snapshot. */
|
|
34
|
+
humanLabels?: Record<string, string>;
|
|
29
35
|
pendingQuestion?: {
|
|
30
36
|
inputId: string;
|
|
31
37
|
question: string;
|
package/src/config.ts
CHANGED
|
@@ -57,6 +57,10 @@ export interface AdasConfig {
|
|
|
57
57
|
coreUrl: UrlSource;
|
|
58
58
|
/** Voice service base URL (optional — only voice features need it). */
|
|
59
59
|
voiceUrl?: UrlSource;
|
|
60
|
+
/** Consumer-auth base URL, e.g. https://consumer-auth.ateam-ai.com. Only
|
|
61
|
+
* needed for `switchTenant()` (re-scoping the session to another tenant
|
|
62
|
+
* the user belongs to). */
|
|
63
|
+
consumerAuthUrl?: UrlSource;
|
|
60
64
|
tokenProvider: TokenProvider;
|
|
61
65
|
/** Tenant slug — used for plugin URL resolution and voice, never sent as a
|
|
62
66
|
* header (the backend derives the tenant from the JWT). Pass a getter when
|
|
@@ -70,6 +74,18 @@ export interface AdasConfig {
|
|
|
70
74
|
* survive app restarts. No-op when omitted (previews live in memory for
|
|
71
75
|
* the session only). @ateam-ai-mobile/ui ships an Expo/RN implementation. */
|
|
72
76
|
attachmentCache?: AttachmentCache;
|
|
77
|
+
/** Called by `switchTenant()` with the freshly-minted session for the
|
|
78
|
+
* target tenant. The host MUST persist it here (store the new JWT so
|
|
79
|
+
* tokenProvider.getToken() returns it, and update the active tenant) —
|
|
80
|
+
* the SDK never stores credentials. Awaited before the chat reconnects. */
|
|
81
|
+
onSession?: (session: AdasSession) => void | Promise<void>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** A tenant-scoped session minted by the platform (e.g. via switchTenant). */
|
|
85
|
+
export interface AdasSession {
|
|
86
|
+
jwt: string;
|
|
87
|
+
tenant: { id: string; name: string };
|
|
88
|
+
actor: { id: string; email: string | null; roles: string[] };
|
|
73
89
|
}
|
|
74
90
|
|
|
75
91
|
let _config: AdasConfig | null = null;
|
|
@@ -110,6 +126,12 @@ export function getVoiceApiUrl(): string {
|
|
|
110
126
|
return resolveUrl(url);
|
|
111
127
|
}
|
|
112
128
|
|
|
129
|
+
export function getConsumerAuthApiUrl(): string {
|
|
130
|
+
const url = getConfig().consumerAuthUrl;
|
|
131
|
+
if (!url) throw new Error('[adas] consumerAuthUrl not configured — required for switchTenant()');
|
|
132
|
+
return resolveUrl(url);
|
|
133
|
+
}
|
|
134
|
+
|
|
113
135
|
export function getTenant(): string {
|
|
114
136
|
const t = getConfig().tenant;
|
|
115
137
|
return (typeof t === 'function' ? t() : t) || '';
|
package/src/index.ts
CHANGED
|
@@ -23,11 +23,12 @@ export {
|
|
|
23
23
|
getCoreApiUrl,
|
|
24
24
|
getCoreWsUrl,
|
|
25
25
|
getVoiceApiUrl,
|
|
26
|
+
getConsumerAuthApiUrl,
|
|
26
27
|
getTenant,
|
|
27
28
|
getUser,
|
|
28
29
|
getToken,
|
|
29
30
|
} from './config';
|
|
30
|
-
export type { AdasConfig, TokenProvider, RefreshOutcome, AdasUser } from './config';
|
|
31
|
+
export type { AdasConfig, TokenProvider, RefreshOutcome, AdasUser, AdasSession } from './config';
|
|
31
32
|
|
|
32
33
|
// API client
|
|
33
34
|
export { apiFetch, apiFetchRaw } from './api/client';
|
|
@@ -42,7 +43,7 @@ export {
|
|
|
42
43
|
} from './api/mcp';
|
|
43
44
|
|
|
44
45
|
// Tenant memberships
|
|
45
|
-
export { listMyTenants } from './api/tenants';
|
|
46
|
+
export { listMyTenants, switchTenant } from './api/tenants';
|
|
46
47
|
export type { TenantMembership } from './api/tenants';
|
|
47
48
|
|
|
48
49
|
// Chat
|