@tenxyte/core 0.9.0 → 0.9.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/LICENSE +21 -0
- package/README.md +362 -102
- package/dist/index.cjs +966 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1073 -15
- package/dist/index.d.ts +1073 -15
- package/dist/index.js +946 -35
- package/dist/index.js.map +1 -1
- package/package.json +15 -3
- package/patched-schema.json +0 -11388
- package/src/client.ts +0 -50
- package/src/config.ts +0 -0
- package/src/http/client.ts +0 -162
- package/src/http/index.ts +0 -1
- package/src/http/interceptors.ts +0 -117
- package/src/index.ts +0 -7
- package/src/modules/ai.ts +0 -178
- package/src/modules/auth.ts +0 -116
- package/src/modules/b2b.ts +0 -177
- package/src/modules/rbac.ts +0 -207
- package/src/modules/security.ts +0 -313
- package/src/modules/user.ts +0 -95
- package/src/storage/cookie.ts +0 -39
- package/src/storage/index.ts +0 -29
- package/src/storage/localStorage.ts +0 -75
- package/src/storage/memory.ts +0 -30
- package/src/types/api-schema.d.ts +0 -6590
- package/src/types/index.ts +0 -152
- package/src/utils/base64url.ts +0 -25
- package/src/utils/device_info.ts +0 -94
- package/src/utils/events.ts +0 -71
- package/src/utils/jwt.ts +0 -51
- package/tests/http.test.ts +0 -144
- package/tests/modules/auth.test.ts +0 -93
- package/tests/modules/rbac.test.ts +0 -95
- package/tests/modules/security.test.ts +0 -85
- package/tests/modules/user.test.ts +0 -76
- package/tests/storage.test.ts +0 -96
- package/tests/utils.test.ts +0 -71
- package/tsconfig.json +0 -26
- package/tsup.config.ts +0 -10
- package/vitest.config.ts +0 -7
package/src/client.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { TenxyteHttpClient, HttpClientOptions } from './http/client';
|
|
2
|
-
import { AuthModule } from './modules/auth';
|
|
3
|
-
import { SecurityModule } from './modules/security';
|
|
4
|
-
import { RbacModule } from './modules/rbac';
|
|
5
|
-
import { UserModule } from './modules/user';
|
|
6
|
-
import { B2bModule } from './modules/b2b';
|
|
7
|
-
import { AiModule } from './modules/ai';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* The primary entry point for the Tenxyte SDK.
|
|
11
|
-
* Groups together logic for authentication, security, organization switching, and AI control.
|
|
12
|
-
*/
|
|
13
|
-
export class TenxyteClient {
|
|
14
|
-
/** The core HTTP wrapper handling network interception and parsing */
|
|
15
|
-
public http: TenxyteHttpClient;
|
|
16
|
-
/** Authentication module (Login, Signup, Magic link, session handling) */
|
|
17
|
-
public auth: AuthModule;
|
|
18
|
-
/** Security module (2FA, WebAuthn, Passwords, OTPs) */
|
|
19
|
-
public security: SecurityModule;
|
|
20
|
-
/** Role-Based Access Control and permission checking module */
|
|
21
|
-
public rbac: RbacModule;
|
|
22
|
-
/** Connected user's profile and management module */
|
|
23
|
-
public user: UserModule;
|
|
24
|
-
/** Business-to-Business organizations module (multi-tenant environments) */
|
|
25
|
-
public b2b: B2bModule;
|
|
26
|
-
/** AIRS - AI Responsibility & Security module (Agent tokens, Circuit breakers, HITL) */
|
|
27
|
-
public ai: AiModule;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Initializes the SDK with connection details for your Tenxyte-powered API.
|
|
31
|
-
* @param options Configuration options including `baseUrl` and custom headers like `X-Access-Key`
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```typescript
|
|
35
|
-
* const tx = new TenxyteClient({
|
|
36
|
-
* baseUrl: 'https://api.my-service.com',
|
|
37
|
-
* headers: { 'X-Access-Key': 'pkg_abc123' }
|
|
38
|
-
* });
|
|
39
|
-
* ```
|
|
40
|
-
*/
|
|
41
|
-
constructor(options: HttpClientOptions) {
|
|
42
|
-
this.http = new TenxyteHttpClient(options);
|
|
43
|
-
this.auth = new AuthModule(this.http);
|
|
44
|
-
this.security = new SecurityModule(this.http);
|
|
45
|
-
this.rbac = new RbacModule(this.http);
|
|
46
|
-
this.user = new UserModule(this.http);
|
|
47
|
-
this.b2b = new B2bModule(this.http);
|
|
48
|
-
this.ai = new AiModule(this.http);
|
|
49
|
-
}
|
|
50
|
-
}
|
package/src/config.ts
DELETED
|
File without changes
|
package/src/http/client.ts
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import type { TenxyteError } from '../types';
|
|
2
|
-
|
|
3
|
-
export interface HttpClientOptions {
|
|
4
|
-
baseUrl: string;
|
|
5
|
-
timeoutMs?: number;
|
|
6
|
-
headers?: Record<string, string>;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type RequestConfig = Omit<RequestInit, 'body' | 'headers'> & {
|
|
10
|
-
body?: unknown;
|
|
11
|
-
headers?: Record<string, string>;
|
|
12
|
-
params?: Record<string, string | number | boolean>;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Core HTTP Client underlying the SDK.
|
|
17
|
-
* Handles JSON parsing, standard headers, simple request processing,
|
|
18
|
-
* and normalizing errors into TenxyteError format.
|
|
19
|
-
*/
|
|
20
|
-
export class TenxyteHttpClient {
|
|
21
|
-
private baseUrl: string;
|
|
22
|
-
private defaultHeaders: Record<string, string>;
|
|
23
|
-
|
|
24
|
-
// Interceptors
|
|
25
|
-
private requestInterceptors: Array<(config: RequestConfig & { url: string }) => Promise<RequestConfig & { url: string }> | (RequestConfig & { url: string })> = [];
|
|
26
|
-
private responseInterceptors: Array<(response: Response, request: { url: string; config: RequestConfig }) => Promise<Response> | Response> = [];
|
|
27
|
-
|
|
28
|
-
constructor(options: HttpClientOptions) {
|
|
29
|
-
this.baseUrl = options.baseUrl.replace(/\/$/, ''); // Remove trailing slash
|
|
30
|
-
this.defaultHeaders = {
|
|
31
|
-
'Content-Type': 'application/json',
|
|
32
|
-
Accept: 'application/json',
|
|
33
|
-
...options.headers,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Interceptor Registration
|
|
38
|
-
addRequestInterceptor(interceptor: typeof this.requestInterceptors[0]) {
|
|
39
|
-
this.requestInterceptors.push(interceptor);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
addResponseInterceptor(interceptor: typeof this.responseInterceptors[0]) {
|
|
43
|
-
this.responseInterceptors.push(interceptor);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Main request method wrapping fetch
|
|
48
|
-
*/
|
|
49
|
-
async request<T>(endpoint: string, config: RequestConfig = {}): Promise<T> {
|
|
50
|
-
const urlStr = endpoint.startsWith('http')
|
|
51
|
-
? endpoint
|
|
52
|
-
: `${this.baseUrl}${endpoint.startsWith('/') ? '' : '/'}${endpoint}`;
|
|
53
|
-
|
|
54
|
-
let urlObj = new URL(urlStr);
|
|
55
|
-
|
|
56
|
-
if (config.params) {
|
|
57
|
-
Object.entries(config.params).forEach(([key, value]) => {
|
|
58
|
-
if (value !== undefined && value !== null) {
|
|
59
|
-
urlObj.searchParams.append(key, String(value));
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
let requestContext: any = {
|
|
65
|
-
url: urlObj.toString(),
|
|
66
|
-
...config,
|
|
67
|
-
headers: { ...this.defaultHeaders, ...(config.headers || {}) } as Record<string, string>,
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// Handle FormData implicitly for multipart requests
|
|
71
|
-
if (typeof FormData !== 'undefined' && requestContext.body instanceof FormData) {
|
|
72
|
-
const headers = requestContext.headers as Record<string, string>;
|
|
73
|
-
// Explicitly remove Content-Type so fetch can auto-assign the multipart boundary
|
|
74
|
-
delete headers['Content-Type'];
|
|
75
|
-
delete headers['content-type'];
|
|
76
|
-
} else if (requestContext.body && typeof requestContext.body === 'object') {
|
|
77
|
-
const contentType = (requestContext.headers as Record<string, string>)['Content-Type'] || '';
|
|
78
|
-
if (contentType.toLowerCase().includes('application/json')) {
|
|
79
|
-
requestContext.body = JSON.stringify(requestContext.body);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Run Request Interceptors
|
|
84
|
-
for (const interceptor of this.requestInterceptors) {
|
|
85
|
-
requestContext = await interceptor(requestContext);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const { url, ...fetchConfig } = requestContext as any;
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
let response = await fetch(url, fetchConfig as RequestInit);
|
|
92
|
-
|
|
93
|
-
// Run Response Interceptors (e.g., token refresh logic)
|
|
94
|
-
for (const interceptor of this.responseInterceptors) {
|
|
95
|
-
response = await interceptor(response, { url, config: fetchConfig as RequestConfig });
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (!response.ok) {
|
|
99
|
-
throw await this.normalizeError(response);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Handle NoContent
|
|
103
|
-
if (response.status === 204) {
|
|
104
|
-
return {} as T;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const contentType = response.headers.get('content-type');
|
|
108
|
-
if (contentType && contentType.includes('application/json')) {
|
|
109
|
-
return (await response.json()) as T;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return (await response.text()) as unknown as T;
|
|
113
|
-
} catch (error: any) {
|
|
114
|
-
if (error && error.code) {
|
|
115
|
-
throw error; // Already normalized
|
|
116
|
-
}
|
|
117
|
-
throw {
|
|
118
|
-
error: error.message || 'Network request failed',
|
|
119
|
-
code: 'NETWORK_ERROR' as unknown as import('../types').TenxyteErrorCode,
|
|
120
|
-
details: String(error)
|
|
121
|
-
} as TenxyteError;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
private async normalizeError(response: Response): Promise<TenxyteError> {
|
|
126
|
-
try {
|
|
127
|
-
const body = await response.json();
|
|
128
|
-
return {
|
|
129
|
-
error: body.error || body.detail || 'API request failed',
|
|
130
|
-
code: body.code || `HTTP_${response.status}`,
|
|
131
|
-
details: body.details || body,
|
|
132
|
-
retry_after: response.headers.has('Retry-After') ? parseInt(response.headers.get('Retry-After')!, 10) : undefined,
|
|
133
|
-
} as TenxyteError;
|
|
134
|
-
} catch (e) {
|
|
135
|
-
return {
|
|
136
|
-
error: `HTTP Error ${response.status}: ${response.statusText}`,
|
|
137
|
-
code: `HTTP_${response.status}` as unknown as import('../types').TenxyteErrorCode,
|
|
138
|
-
} as TenxyteError;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Convenience methods
|
|
143
|
-
get<T>(endpoint: string, config?: Omit<RequestConfig, 'method' | 'body'>) {
|
|
144
|
-
return this.request<T>(endpoint, { ...config, method: 'GET' });
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
post<T>(endpoint: string, data?: unknown, config?: Omit<RequestConfig, 'method' | 'body'>) {
|
|
148
|
-
return this.request<T>(endpoint, { ...config, method: 'POST', body: data });
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
put<T>(endpoint: string, data?: unknown, config?: Omit<RequestConfig, 'method' | 'body'>) {
|
|
152
|
-
return this.request<T>(endpoint, { ...config, method: 'PUT', body: data });
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
patch<T>(endpoint: string, data?: unknown, config?: Omit<RequestConfig, 'method' | 'body'>) {
|
|
156
|
-
return this.request<T>(endpoint, { ...config, method: 'PATCH', body: data });
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
delete<T>(endpoint: string, config?: Omit<RequestConfig, 'method' | 'body'>) {
|
|
160
|
-
return this.request<T>(endpoint, { ...config, method: 'DELETE' });
|
|
161
|
-
}
|
|
162
|
-
}
|
package/src/http/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './client';
|
package/src/http/interceptors.ts
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import type { TenxyteStorage } from '../storage';
|
|
2
|
-
import type { RequestConfig, TenxyteHttpClient } from './client';
|
|
3
|
-
|
|
4
|
-
export interface TenxyteContext {
|
|
5
|
-
activeOrgSlug: string | null;
|
|
6
|
-
agentTraceId: string | null;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function createAuthInterceptor(storage: TenxyteStorage, context: TenxyteContext) {
|
|
10
|
-
return async (request: RequestConfig & { url: string }) => {
|
|
11
|
-
// Inject Authorization if present
|
|
12
|
-
const token = await storage.getItem('tx_access');
|
|
13
|
-
const headers = { ...(request.headers as Record<string, string>) || {} };
|
|
14
|
-
|
|
15
|
-
if (token && !headers['Authorization']) {
|
|
16
|
-
headers['Authorization'] = `Bearer ${token}`;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Inject Contextual Headers based on SDK state
|
|
20
|
-
if (context.activeOrgSlug && !headers['X-Org-Slug']) {
|
|
21
|
-
headers['X-Org-Slug'] = context.activeOrgSlug;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (context.agentTraceId && !headers['X-Prompt-Trace-ID']) {
|
|
25
|
-
headers['X-Prompt-Trace-ID'] = context.agentTraceId;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return { ...request, headers };
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function createRefreshInterceptor(
|
|
33
|
-
client: TenxyteHttpClient,
|
|
34
|
-
storage: TenxyteStorage,
|
|
35
|
-
onSessionExpired: () => void
|
|
36
|
-
) {
|
|
37
|
-
let isRefreshing = false;
|
|
38
|
-
let refreshQueue: Array<(token: string | null) => void> = [];
|
|
39
|
-
|
|
40
|
-
const processQueue = (error: Error | null, token: string | null = null) => {
|
|
41
|
-
refreshQueue.forEach(prom => prom(token));
|
|
42
|
-
refreshQueue = [];
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
return async (response: Response, request: { url: string; config: RequestConfig }): Promise<Response> => {
|
|
46
|
-
// Only intercept 401s when not attempting to login/refresh itself
|
|
47
|
-
if (response.status === 401 && !request.url.includes('/auth/refresh') && !request.url.includes('/auth/login')) {
|
|
48
|
-
const refreshToken = await storage.getItem('tx_refresh');
|
|
49
|
-
|
|
50
|
-
if (!refreshToken) {
|
|
51
|
-
onSessionExpired();
|
|
52
|
-
return response; // Pass through 401 if we cannot refresh
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (isRefreshing) {
|
|
56
|
-
// Wait in queue for the refresh to complete
|
|
57
|
-
return new Promise<Response>((resolve) => {
|
|
58
|
-
refreshQueue.push((newToken: string | null) => {
|
|
59
|
-
if (newToken) {
|
|
60
|
-
const retryHeaders = { ...(request.config.headers as Record<string, string>), Authorization: `Bearer ${newToken}` };
|
|
61
|
-
resolve(fetch(request.url, { ...request.config, headers: retryHeaders } as RequestInit));
|
|
62
|
-
} else {
|
|
63
|
-
resolve(response);
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// We are the first one, initiate refresh
|
|
70
|
-
isRefreshing = true;
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
const refreshResponse = await fetch(`${client['baseUrl']}/auth/refresh/`, {
|
|
74
|
-
method: 'POST',
|
|
75
|
-
headers: { 'Content-Type': 'application/json' },
|
|
76
|
-
body: JSON.stringify({ refresh_token: refreshToken })
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
if (!refreshResponse.ok) {
|
|
80
|
-
throw new Error('Refresh failed');
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const data = await refreshResponse.json();
|
|
84
|
-
|
|
85
|
-
await storage.setItem('tx_access', data.access);
|
|
86
|
-
if (data.refresh) {
|
|
87
|
-
await storage.setItem('tx_refresh', data.refresh);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
isRefreshing = false;
|
|
91
|
-
processQueue(null, data.access);
|
|
92
|
-
|
|
93
|
-
// Retry original request seamlessly for the caller that initiated this
|
|
94
|
-
const retryHeaders = { ...(request.config.headers as Record<string, string>), Authorization: `Bearer ${data.access}` };
|
|
95
|
-
// We use fetch directly to return a true Response object back to the chain,
|
|
96
|
-
// rather than using client.request which resolves the JSON.
|
|
97
|
-
// Wait, the interceptor must return a Promise<Response>!
|
|
98
|
-
const r = await fetch(request.url, { ...request.config, headers: retryHeaders } as RequestInit);
|
|
99
|
-
return r;
|
|
100
|
-
|
|
101
|
-
} catch (err) {
|
|
102
|
-
// Refresh failed (invalid token, expired, network error)
|
|
103
|
-
isRefreshing = false;
|
|
104
|
-
await storage.removeItem('tx_access');
|
|
105
|
-
await storage.removeItem('tx_refresh');
|
|
106
|
-
|
|
107
|
-
processQueue(err as Error, null);
|
|
108
|
-
onSessionExpired();
|
|
109
|
-
|
|
110
|
-
// Pass original 401 back
|
|
111
|
-
return response;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return response;
|
|
116
|
-
};
|
|
117
|
-
}
|
package/src/index.ts
DELETED
package/src/modules/ai.ts
DELETED
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import { TenxyteHttpClient } from '../http/client';
|
|
2
|
-
import { AgentTokenSummary, AgentPendingAction } from '../types';
|
|
3
|
-
|
|
4
|
-
export class AiModule {
|
|
5
|
-
private agentToken: string | null = null;
|
|
6
|
-
private traceId: string | null = null;
|
|
7
|
-
|
|
8
|
-
constructor(private client: TenxyteHttpClient) {
|
|
9
|
-
// Register an interceptor to auto-inject AgentBearer and Trace ID
|
|
10
|
-
this.client.addRequestInterceptor((config) => {
|
|
11
|
-
const headers: Record<string, string> = { ...config.headers };
|
|
12
|
-
|
|
13
|
-
if (this.agentToken) {
|
|
14
|
-
// Determine if we should replace the standard Authorization
|
|
15
|
-
// By Tenxyte specification, AgentToken uses "AgentBearer"
|
|
16
|
-
headers['Authorization'] = `AgentBearer ${this.agentToken}`;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (this.traceId) {
|
|
20
|
-
headers['X-Prompt-Trace-ID'] = this.traceId;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return { ...config, headers };
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
// Intercept 202 Accepted and specific 403 errors (Circuit Breaker)
|
|
27
|
-
// Usually, these should be emitted via the main TenxyteClient EventEmitter.
|
|
28
|
-
// For now, we add a response interceptor to handle the HTTP side.
|
|
29
|
-
this.client.addResponseInterceptor(async (response, request) => {
|
|
30
|
-
// Note: Since response streams can only be read once, full integration
|
|
31
|
-
// with EventEmitter for deep inspection (like 202s body) requires cloning.
|
|
32
|
-
if (response.status === 202) {
|
|
33
|
-
// HTTP 202 Accepted indicates HITL awaiting confirmation
|
|
34
|
-
const cloned = response.clone();
|
|
35
|
-
try {
|
|
36
|
-
const data = await cloned.json();
|
|
37
|
-
// Assuming TenxyteClient will fire 'agent:awaiting_approval' based on this later
|
|
38
|
-
console.debug('[Tenxyte AI] Received 202 Awaiting Approval:', data);
|
|
39
|
-
} catch {
|
|
40
|
-
// Ignore parsing errors
|
|
41
|
-
}
|
|
42
|
-
} else if (response.status === 403) {
|
|
43
|
-
const cloned = response.clone();
|
|
44
|
-
try {
|
|
45
|
-
const data = await cloned.json();
|
|
46
|
-
if (data.code === 'BUDGET_EXCEEDED') {
|
|
47
|
-
console.warn('[Tenxyte AI] Network responded with Budget Exceeded for Agent.');
|
|
48
|
-
} else if (data.status === 'suspended') {
|
|
49
|
-
console.warn('[Tenxyte AI] Circuit breaker open for Agent.');
|
|
50
|
-
}
|
|
51
|
-
} catch {
|
|
52
|
-
// Ignore parsing errors
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return response;
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// ─── AgentToken Lifecycle ───
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Create an AgentToken granting specific deterministic limits to an AI Agent.
|
|
63
|
-
*/
|
|
64
|
-
async createAgentToken(data: {
|
|
65
|
-
agent_id: string;
|
|
66
|
-
permissions?: string[];
|
|
67
|
-
expires_in?: number;
|
|
68
|
-
organization?: string;
|
|
69
|
-
budget_limit_usd?: number;
|
|
70
|
-
circuit_breaker?: {
|
|
71
|
-
max_requests?: number;
|
|
72
|
-
window_seconds?: number;
|
|
73
|
-
};
|
|
74
|
-
dead_mans_switch?: {
|
|
75
|
-
heartbeat_required_every?: number;
|
|
76
|
-
};
|
|
77
|
-
}): Promise<{
|
|
78
|
-
id: number;
|
|
79
|
-
token: string;
|
|
80
|
-
agent_id: string;
|
|
81
|
-
status: string;
|
|
82
|
-
expires_at: string;
|
|
83
|
-
}> {
|
|
84
|
-
return this.client.post('/api/v1/auth/ai/tokens/', data);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Set the SDK to operate on behalf of an Agent using the generated Agent Token payload.
|
|
89
|
-
* Overrides standard `Authorization` headers with `AgentBearer`.
|
|
90
|
-
*/
|
|
91
|
-
setAgentToken(token: string): void {
|
|
92
|
-
this.agentToken = token;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/** Disables the active Agent override and reverts to standard User session requests. */
|
|
96
|
-
clearAgentToken(): void {
|
|
97
|
-
this.agentToken = null;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** Check if the SDK is currently mocking requests as an AI Agent. */
|
|
101
|
-
isAgentMode(): boolean {
|
|
102
|
-
return this.agentToken !== null;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/** List previously provisioned active Agent tokens. */
|
|
106
|
-
async listAgentTokens(): Promise<AgentTokenSummary[]> {
|
|
107
|
-
return this.client.get('/api/v1/auth/ai/tokens/');
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/** Fetch the status and configuration of a specific AgentToken. */
|
|
111
|
-
async getAgentToken(tokenId: number): Promise<AgentTokenSummary> {
|
|
112
|
-
return this.client.get(`/api/v1/auth/ai/tokens/${tokenId}/`);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/** Irreversibly revoke a targeted AgentToken from acting upon the Tenant. */
|
|
116
|
-
async revokeAgentToken(tokenId: number): Promise<{ status: 'revoked' }> {
|
|
117
|
-
return this.client.post(`/api/v1/auth/ai/tokens/${tokenId}/revoke/`);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** Temporarily freeze an AgentToken by forcibly closing its Circuit Breaker. */
|
|
121
|
-
async suspendAgentToken(tokenId: number): Promise<{ status: 'suspended' }> {
|
|
122
|
-
return this.client.post(`/api/v1/auth/ai/tokens/${tokenId}/suspend/`);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/** Emergency kill-switch to wipe all operational Agent Tokens. */
|
|
126
|
-
async revokeAllAgentTokens(): Promise<{ status: 'revoked'; count: number }> {
|
|
127
|
-
return this.client.post('/api/v1/auth/ai/tokens/revoke-all/');
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// ─── Circuit Breaker ───
|
|
131
|
-
|
|
132
|
-
/** Satisfy an Agent's Dead-Man's switch heartbeat requirement to prevent suspension. */
|
|
133
|
-
async sendHeartbeat(tokenId: number): Promise<{ status: 'ok' }> {
|
|
134
|
-
return this.client.post(`/api/v1/auth/ai/tokens/${tokenId}/heartbeat/`);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// ─── Human in the Loop (HITL) ───
|
|
138
|
-
|
|
139
|
-
/** List intercepted HTTP 202 actions waiting for Human interaction / approval. */
|
|
140
|
-
async listPendingActions(): Promise<AgentPendingAction[]> {
|
|
141
|
-
return this.client.get('/api/v1/auth/ai/pending-actions/');
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/** Complete a pending HITL authorization to finally flush the Agent action to backend systems. */
|
|
145
|
-
async confirmPendingAction(confirmationToken: string): Promise<{ status: 'confirmed' }> {
|
|
146
|
-
return this.client.post('/api/v1/auth/ai/pending-actions/confirm/', { token: confirmationToken });
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/** Block an Agent action permanently. */
|
|
150
|
-
async denyPendingAction(confirmationToken: string): Promise<{ status: 'denied' }> {
|
|
151
|
-
return this.client.post('/api/v1/auth/ai/pending-actions/deny/', { token: confirmationToken });
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// ─── Traceability and Budget ───
|
|
155
|
-
|
|
156
|
-
/** Start piping the `X-Prompt-Trace-ID` custom header outwards for tracing logs against LLM inputs. */
|
|
157
|
-
setTraceId(traceId: string): void {
|
|
158
|
-
this.traceId = traceId;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/** Disable trace forwarding context. */
|
|
162
|
-
clearTraceId(): void {
|
|
163
|
-
this.traceId = null;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Report consumption costs associated with a backend invocation back to Tenxyte for strict circuit budgeting.
|
|
168
|
-
* @param tokenId - AgentToken evaluating ID.
|
|
169
|
-
* @param usage - Sunk token costs or explicit USD derivations.
|
|
170
|
-
*/
|
|
171
|
-
async reportUsage(tokenId: number, usage: {
|
|
172
|
-
cost_usd: number;
|
|
173
|
-
prompt_tokens: number;
|
|
174
|
-
completion_tokens: number;
|
|
175
|
-
}): Promise<{ status: 'ok' } | { error: 'Budget exceeded'; status: 'suspended' }> {
|
|
176
|
-
return this.client.post(`/api/v1/auth/ai/tokens/${tokenId}/report-usage/`, usage);
|
|
177
|
-
}
|
|
178
|
-
}
|
package/src/modules/auth.ts
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { TenxyteHttpClient } from '../http/client';
|
|
2
|
-
import { TokenPair, GeneratedSchema } from '../types';
|
|
3
|
-
|
|
4
|
-
export interface LoginEmailOptions {
|
|
5
|
-
totp_code?: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface LoginPhoneOptions {
|
|
9
|
-
totp_code?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export type RegisterRequest = any;
|
|
13
|
-
|
|
14
|
-
export interface MagicLinkRequest {
|
|
15
|
-
email: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface SocialLoginRequest {
|
|
19
|
-
access_token?: string;
|
|
20
|
-
authorization_code?: string;
|
|
21
|
-
id_token?: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export class AuthModule {
|
|
25
|
-
constructor(private client: TenxyteHttpClient) { }
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Authenticate a user with their email and password.
|
|
29
|
-
* @param data - The login credentials and optional TOTP code if 2FA is required.
|
|
30
|
-
* @returns A pair of Access and Refresh tokens upon successful authentication.
|
|
31
|
-
* @throws {TenxyteError} If credentials are invalid, or if `2FA_REQUIRED` without a valid `totp_code`.
|
|
32
|
-
*/
|
|
33
|
-
async loginWithEmail(
|
|
34
|
-
data: GeneratedSchema['LoginEmail'],
|
|
35
|
-
): Promise<TokenPair> {
|
|
36
|
-
return this.client.post<TokenPair>('/api/v1/auth/login/email/', data);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Authenticate a user with an international phone number and password.
|
|
41
|
-
* @param data - The login credentials and optional TOTP code if 2FA is required.
|
|
42
|
-
* @returns A pair of Access and Refresh tokens.
|
|
43
|
-
*/
|
|
44
|
-
async loginWithPhone(
|
|
45
|
-
data: GeneratedSchema['LoginPhone'],
|
|
46
|
-
): Promise<TokenPair> {
|
|
47
|
-
return this.client.post<TokenPair>('/api/v1/auth/login/phone/', data);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Registers a new user account.
|
|
52
|
-
* @param data - The registration details (email, password, etc.).
|
|
53
|
-
* @returns The registered user data or a confirmation message.
|
|
54
|
-
*/
|
|
55
|
-
async register(data: RegisterRequest): Promise<any> {
|
|
56
|
-
return this.client.post<any>('/api/v1/auth/register/', data);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Logout from the current session.
|
|
61
|
-
* Informs the backend to immediately revoke the specified refresh token.
|
|
62
|
-
* @param refreshToken - The refresh token to revoke.
|
|
63
|
-
*/
|
|
64
|
-
async logout(refreshToken: string): Promise<void> {
|
|
65
|
-
return this.client.post<void>('/api/v1/auth/logout/', { refresh_token: refreshToken });
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Logout from all sessions across all devices.
|
|
70
|
-
* Revokes all refresh tokens currently assigned to the user.
|
|
71
|
-
*/
|
|
72
|
-
async logoutAll(): Promise<void> {
|
|
73
|
-
return this.client.post<void>('/api/v1/auth/logout/all/');
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Request a Magic Link for passwordless sign-in.
|
|
78
|
-
* @param data - The email to send the logic link to.
|
|
79
|
-
*/
|
|
80
|
-
async requestMagicLink(data: MagicLinkRequest): Promise<void> {
|
|
81
|
-
return this.client.post<void>('/api/v1/auth/magic-link/request/', data);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Verifies a magic link token extracted from the URL.
|
|
86
|
-
* @param token - The cryptographic token received via email.
|
|
87
|
-
* @returns A session token pair if the token is valid and unexpired.
|
|
88
|
-
*/
|
|
89
|
-
async verifyMagicLink(token: string): Promise<TokenPair> {
|
|
90
|
-
return this.client.get<TokenPair>(`/api/v1/auth/magic-link/verify/`, { params: { token } });
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Submits OAuth2 Social Authentication payloads to the backend.
|
|
95
|
-
* Can be used with native mobile SDK tokens (like Apple Sign-In JWTs).
|
|
96
|
-
* @param provider - The OAuth provider ('google', 'github', etc.)
|
|
97
|
-
* @param data - The OAuth tokens (access_token, id_token, etc.)
|
|
98
|
-
* @returns An active session token pair.
|
|
99
|
-
*/
|
|
100
|
-
async loginWithSocial(provider: 'google' | 'github' | 'microsoft' | 'facebook', data: SocialLoginRequest): Promise<TokenPair> {
|
|
101
|
-
return this.client.post<TokenPair>(`/api/v1/auth/social/${provider}/`, data);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Handle Social Auth Callbacks (Authorization Code flow).
|
|
106
|
-
* @param provider - The OAuth provider ('google', 'github', etc.)
|
|
107
|
-
* @param code - The authorization code retrieved from the query string parameters.
|
|
108
|
-
* @param redirectUri - The original redirect URI that was requested.
|
|
109
|
-
* @returns An active session token pair after successful code exchange.
|
|
110
|
-
*/
|
|
111
|
-
async handleSocialCallback(provider: 'google' | 'github' | 'microsoft' | 'facebook', code: string, redirectUri: string): Promise<TokenPair> {
|
|
112
|
-
return this.client.get<TokenPair>(`/api/v1/auth/social/${provider}/callback/`, {
|
|
113
|
-
params: { code, redirect_uri: redirectUri },
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
}
|