@alter-ai/alter-sdk 0.2.1 → 0.2.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 CHANGED
@@ -8,7 +8,7 @@ Official TypeScript SDK for [Alter Vault](https://alterai.dev) — OAuth token m
8
8
  - **Single Entry Point**: One method (`vault.request()`) for all provider APIs
9
9
  - **Type-Safe Enums**: `Provider` and `HttpMethod` enums with autocomplete
10
10
  - **URL Templating**: Path parameter substitution with automatic URL encoding
11
- - **Automatic Audit Logging**: All API calls logged as fire-and-forget background tasks
11
+ - **Automatic Audit Logging**: All API calls logged with request metadata (HTTP method and URL) for full audit trail
12
12
  - **Real-time Policy Enforcement**: Every token request checked against current policies
13
13
  - **Automatic Token Refresh**: Tokens refreshed transparently by the backend
14
14
  - **Actor Tracking**: First-class support for AI agent and MCP server observability
@@ -103,6 +103,58 @@ const response = await vault.request(
103
103
  );
104
104
  ```
105
105
 
106
+ ### Connection Management
107
+
108
+ #### List Connections
109
+
110
+ Retrieve OAuth connections for your app, optionally filtered by provider:
111
+
112
+ ```typescript
113
+ const result = await vault.listConnections();
114
+ for (const conn of result.connections) {
115
+ console.log(`${conn.providerId}: ${conn.accountDisplayName} (${conn.status})`);
116
+ }
117
+
118
+ // Filter by provider with pagination
119
+ const googleConns = await vault.listConnections({
120
+ providerId: "google",
121
+ limit: 10,
122
+ offset: 0,
123
+ });
124
+ console.log(`Total: ${googleConns.total}, Has more: ${googleConns.hasMore}`);
125
+ ```
126
+
127
+ | Parameter | Type | Default | Description |
128
+ |-----------|------|---------|-------------|
129
+ | `providerId` | `string` | - | Filter by provider (e.g., `"google"`) |
130
+ | `limit` | `number` | `100` | Max connections to return |
131
+ | `offset` | `number` | `0` | Pagination offset |
132
+
133
+ Returns `ConnectionListResult` with: `connections` (`ConnectionInfo[]`), `total`, `limit`, `offset`, `hasMore`.
134
+
135
+ #### Create Connect Session
136
+
137
+ Generate a session URL for end-users to authenticate with OAuth providers:
138
+
139
+ ```typescript
140
+ const session = await vault.createConnectSession({
141
+ endUser: { id: "alice" },
142
+ allowedProviders: ["google", "github"],
143
+ returnUrl: "https://myapp.com/callback",
144
+ });
145
+ console.log(`Connect URL: ${session.connectUrl}`);
146
+ console.log(`Expires in: ${session.expiresIn}s`);
147
+ ```
148
+
149
+ | Parameter | Type | Default | Description |
150
+ |-----------|------|---------|-------------|
151
+ | `endUser` | `{ id: string }` | *required* | End user identity |
152
+ | `attributes` | `Record<string, unknown>` | - | Connection attributes |
153
+ | `allowedProviders` | `string[]` | - | Restrict to specific providers |
154
+ | `returnUrl` | `string` | - | Redirect URL after OAuth flow |
155
+
156
+ Returns `ConnectSession` with: `sessionToken`, `connectUrl`, `expiresIn`, `expiresAt`.
157
+
106
158
  ### AI Agent Actor Tracking
107
159
 
108
160
  ```typescript
package/dist/index.cjs CHANGED
@@ -23,6 +23,9 @@ __export(index_exports, {
23
23
  APICallAuditLog: () => APICallAuditLog,
24
24
  AlterSDKError: () => AlterSDKError,
25
25
  AlterVault: () => AlterVault,
26
+ ConnectSession: () => ConnectSession,
27
+ ConnectionInfo: () => ConnectionInfo,
28
+ ConnectionListResult: () => ConnectionListResult,
26
29
  ConnectionNotFoundError: () => ConnectionNotFoundError,
27
30
  HttpMethod: () => HttpMethod,
28
31
  NetworkError: () => NetworkError,
@@ -182,6 +185,87 @@ var TokenResponse = class _TokenResponse {
182
185
  return this.toString();
183
186
  }
184
187
  };
188
+ var ConnectionInfo = class {
189
+ id;
190
+ providerId;
191
+ attributes;
192
+ scopes;
193
+ accountIdentifier;
194
+ accountDisplayName;
195
+ status;
196
+ expiresAt;
197
+ createdAt;
198
+ lastUsedAt;
199
+ constructor(data) {
200
+ this.id = data.id;
201
+ this.providerId = data.provider_id;
202
+ this.attributes = data.attributes ?? {};
203
+ this.scopes = data.scopes ?? [];
204
+ this.accountIdentifier = data.account_identifier ?? null;
205
+ this.accountDisplayName = data.account_display_name ?? null;
206
+ this.status = data.status;
207
+ this.expiresAt = data.expires_at ?? null;
208
+ this.createdAt = data.created_at;
209
+ this.lastUsedAt = data.last_used_at ?? null;
210
+ Object.freeze(this);
211
+ }
212
+ toJSON() {
213
+ return {
214
+ id: this.id,
215
+ provider_id: this.providerId,
216
+ attributes: this.attributes,
217
+ scopes: this.scopes,
218
+ account_identifier: this.accountIdentifier,
219
+ account_display_name: this.accountDisplayName,
220
+ status: this.status,
221
+ expires_at: this.expiresAt,
222
+ created_at: this.createdAt,
223
+ last_used_at: this.lastUsedAt
224
+ };
225
+ }
226
+ toString() {
227
+ return `ConnectionInfo(id=${this.id}, provider=${this.providerId}, status=${this.status})`;
228
+ }
229
+ };
230
+ var ConnectSession = class {
231
+ sessionToken;
232
+ connectUrl;
233
+ expiresIn;
234
+ expiresAt;
235
+ constructor(data) {
236
+ this.sessionToken = data.session_token;
237
+ this.connectUrl = data.connect_url;
238
+ this.expiresIn = data.expires_in;
239
+ this.expiresAt = data.expires_at;
240
+ Object.freeze(this);
241
+ }
242
+ toJSON() {
243
+ return {
244
+ session_token: this.sessionToken,
245
+ connect_url: this.connectUrl,
246
+ expires_in: this.expiresIn,
247
+ expires_at: this.expiresAt
248
+ };
249
+ }
250
+ toString() {
251
+ return `ConnectSession(url=${this.connectUrl}, expires_in=${this.expiresIn})`;
252
+ }
253
+ };
254
+ var ConnectionListResult = class {
255
+ connections;
256
+ total;
257
+ limit;
258
+ offset;
259
+ hasMore;
260
+ constructor(data) {
261
+ this.connections = data.connections;
262
+ this.total = data.total;
263
+ this.limit = data.limit;
264
+ this.offset = data.offset;
265
+ this.hasMore = data.has_more;
266
+ Object.freeze(this);
267
+ }
268
+ };
185
269
  var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
186
270
  "authorization",
187
271
  "cookie",
@@ -276,7 +360,7 @@ function _extractAccessToken(token) {
276
360
  return value;
277
361
  }
278
362
  var _fetch;
279
- var SDK_VERSION = "0.2.1";
363
+ var SDK_VERSION = "0.2.2";
280
364
  var SDK_USER_AGENT = `alter-sdk-node/${SDK_VERSION}`;
281
365
  var VALID_ACTOR_TYPES = ["ai_agent", "mcp_server"];
282
366
  var HTTP_FORBIDDEN = 403;
@@ -627,7 +711,7 @@ var AlterVault = class _AlterVault {
627
711
  * This is a private method. Tokens are NEVER exposed to developers.
628
712
  * Use request() instead, which handles tokens internally.
629
713
  */
630
- async #getToken(providerId, attributes, reason, runId, threadId, toolCallId) {
714
+ async #getToken(providerId, attributes, reason, requestMetadata, runId, threadId, toolCallId) {
631
715
  const actorHeaders = this.#getActorRequestHeaders(
632
716
  runId,
633
717
  threadId,
@@ -639,7 +723,8 @@ var AlterVault = class _AlterVault {
639
723
  json: {
640
724
  provider_id: providerId,
641
725
  attributes,
642
- reason: reason ?? null
726
+ reason: reason ?? null,
727
+ request: requestMetadata ?? null
643
728
  },
644
729
  headers: actorHeaders
645
730
  });
@@ -816,6 +901,7 @@ var AlterVault = class _AlterVault {
816
901
  providerStr,
817
902
  options.user,
818
903
  options.reason,
904
+ { method: methodStr, url },
819
905
  options.runId,
820
906
  options.threadId,
821
907
  options.toolCallId
@@ -896,6 +982,112 @@ var AlterVault = class _AlterVault {
896
982
  }
897
983
  return response;
898
984
  }
985
+ /**
986
+ * List OAuth connections for this app.
987
+ *
988
+ * Returns paginated connection metadata (no tokens).
989
+ * Useful for discovering which services a user has connected.
990
+ */
991
+ async listConnections(options) {
992
+ if (this.#closed) {
993
+ throw new AlterSDKError(
994
+ "SDK instance has been closed. Create a new AlterVault instance to make requests."
995
+ );
996
+ }
997
+ const actorHeaders = this.#getActorRequestHeaders();
998
+ let response;
999
+ try {
1000
+ response = await this.#alterClient.post("/oauth/connections/list", {
1001
+ json: {
1002
+ provider_id: options?.providerId ?? null,
1003
+ limit: options?.limit ?? 100,
1004
+ offset: options?.offset ?? 0
1005
+ },
1006
+ headers: actorHeaders
1007
+ });
1008
+ } catch (error) {
1009
+ if (_AlterVault.#isTimeoutOrAbortError(error)) {
1010
+ throw new TimeoutError(
1011
+ `Request to Alter Vault backend timed out: ${error instanceof Error ? error.message : String(error)}`,
1012
+ { base_url: this.baseUrl }
1013
+ );
1014
+ }
1015
+ if (error instanceof TypeError) {
1016
+ throw new NetworkError(
1017
+ `Failed to connect to Alter Vault backend: ${error.message}`,
1018
+ { base_url: this.baseUrl }
1019
+ );
1020
+ }
1021
+ throw new AlterSDKError(
1022
+ `Failed to list connections: ${error instanceof Error ? error.message : String(error)}`
1023
+ );
1024
+ }
1025
+ this.#cacheActorIdFromResponse(response);
1026
+ await this.#handleErrorResponse(response);
1027
+ const data = await response.json();
1028
+ const connections = data.connections.map(
1029
+ (c) => new ConnectionInfo(
1030
+ c
1031
+ )
1032
+ );
1033
+ return new ConnectionListResult({
1034
+ connections,
1035
+ total: data.total,
1036
+ limit: data.limit,
1037
+ offset: data.offset,
1038
+ has_more: data.has_more
1039
+ });
1040
+ }
1041
+ /**
1042
+ * Create a Connect session for initiating OAuth flows.
1043
+ *
1044
+ * Returns a URL the user can open in their browser to authorize access.
1045
+ */
1046
+ async createConnectSession(options) {
1047
+ if (this.#closed) {
1048
+ throw new AlterSDKError(
1049
+ "SDK instance has been closed. Create a new AlterVault instance to make requests."
1050
+ );
1051
+ }
1052
+ if (!options.endUser?.id) {
1053
+ throw new AlterSDKError("endUser.id is required");
1054
+ }
1055
+ const actorHeaders = this.#getActorRequestHeaders();
1056
+ let response;
1057
+ try {
1058
+ response = await this.#alterClient.post("/oauth/connect/session", {
1059
+ json: {
1060
+ end_user: options.endUser,
1061
+ attributes: options.attributes ?? null,
1062
+ allowed_providers: options.allowedProviders ?? null,
1063
+ return_url: options.returnUrl ?? null,
1064
+ allowed_origin: options.allowedOrigin ?? null,
1065
+ metadata: options.metadata ?? null
1066
+ },
1067
+ headers: actorHeaders
1068
+ });
1069
+ } catch (error) {
1070
+ if (_AlterVault.#isTimeoutOrAbortError(error)) {
1071
+ throw new TimeoutError(
1072
+ `Request to Alter Vault backend timed out: ${error instanceof Error ? error.message : String(error)}`,
1073
+ { base_url: this.baseUrl }
1074
+ );
1075
+ }
1076
+ if (error instanceof TypeError) {
1077
+ throw new NetworkError(
1078
+ `Failed to connect to Alter Vault backend: ${error.message}`,
1079
+ { base_url: this.baseUrl }
1080
+ );
1081
+ }
1082
+ throw new AlterSDKError(
1083
+ `Failed to create connect session: ${error instanceof Error ? error.message : String(error)}`
1084
+ );
1085
+ }
1086
+ this.#cacheActorIdFromResponse(response);
1087
+ await this.#handleErrorResponse(response);
1088
+ const data = await response.json();
1089
+ return new ConnectSession(data);
1090
+ }
899
1091
  /**
900
1092
  * Close HTTP clients and release resources.
901
1093
  * Waits for any pending audit tasks before closing.
@@ -944,6 +1136,9 @@ var HttpMethod = /* @__PURE__ */ ((HttpMethod2) => {
944
1136
  APICallAuditLog,
945
1137
  AlterSDKError,
946
1138
  AlterVault,
1139
+ ConnectSession,
1140
+ ConnectionInfo,
1141
+ ConnectionListResult,
947
1142
  ConnectionNotFoundError,
948
1143
  HttpMethod,
949
1144
  NetworkError,
package/dist/index.d.cts CHANGED
@@ -1,3 +1,197 @@
1
+ /**
2
+ * Models for Alter SDK.
3
+ *
4
+ * These models provide type-safe data structures for SDK operations.
5
+ *
6
+ * Security Hardening (v0.4.0):
7
+ * - TokenResponse: accessToken stored in module-private WeakMap in client.ts
8
+ * (not readable as property, not importable from this module)
9
+ * - TokenResponse: Object.freeze(this) prevents mutation after creation
10
+ * - toJSON() and toString() exclude access token from serialization
11
+ */
12
+ /**
13
+ * OAuth token response from Alter Vault.
14
+ *
15
+ * This represents an access token retrieved from the backend.
16
+ * The access_token is NOT stored as a readable property — it lives in a
17
+ * module-private WeakMap in client.ts and is only accessible internally.
18
+ *
19
+ * SECURITY:
20
+ * - No accessToken property (stored in WeakMap inside client.ts)
21
+ * - Instance is frozen after construction to prevent mutation
22
+ * - toJSON() and toString() exclude access token
23
+ * - _extractAccessToken() lives in client.ts, NOT in this file,
24
+ * so it cannot be imported by consumers even via deep imports
25
+ */
26
+ declare class TokenResponse {
27
+ /** Token type (usually "Bearer") */
28
+ readonly tokenType: string;
29
+ /** Seconds until token expires */
30
+ readonly expiresIn: number | null;
31
+ /** Absolute expiration time */
32
+ readonly expiresAt: Date | null;
33
+ /** OAuth scopes granted */
34
+ readonly scopes: string[];
35
+ /** Connection ID that provided this token */
36
+ readonly connectionId: string;
37
+ constructor(data: {
38
+ access_token: string;
39
+ token_type?: string;
40
+ expires_in?: number | null;
41
+ expires_at?: string | null;
42
+ scopes?: string[];
43
+ connection_id: string;
44
+ });
45
+ /**
46
+ * Parse expires_at from ISO string.
47
+ */
48
+ private static parseExpiresAt;
49
+ /**
50
+ * Check if token is expired.
51
+ *
52
+ * @param bufferSeconds - Consider token expired N seconds before actual expiry.
53
+ * Useful for preventing race conditions.
54
+ * @returns True if token is expired or will expire within bufferSeconds.
55
+ */
56
+ isExpired(bufferSeconds?: number): boolean;
57
+ /**
58
+ * Check if token should be refreshed soon.
59
+ *
60
+ * @param bufferSeconds - Consider token needing refresh N seconds before expiry.
61
+ * Default 5 minutes (300 seconds).
62
+ * @returns True if token will expire within bufferSeconds.
63
+ */
64
+ needsRefresh(bufferSeconds?: number): boolean;
65
+ /**
66
+ * Custom JSON serialization — EXCLUDES access_token for security.
67
+ */
68
+ toJSON(): Record<string, unknown>;
69
+ /**
70
+ * Custom string representation — EXCLUDES access_token for security.
71
+ */
72
+ toString(): string;
73
+ }
74
+ /**
75
+ * OAuth connection info from Alter Vault.
76
+ *
77
+ * Represents a connected OAuth service (e.g., Google, GitHub).
78
+ * Returned by listConnections(). Contains metadata only — no tokens.
79
+ */
80
+ declare class ConnectionInfo {
81
+ readonly id: string;
82
+ readonly providerId: string;
83
+ readonly attributes: Record<string, unknown>;
84
+ readonly scopes: string[];
85
+ readonly accountIdentifier: string | null;
86
+ readonly accountDisplayName: string | null;
87
+ readonly status: string;
88
+ readonly expiresAt: string | null;
89
+ readonly createdAt: string;
90
+ readonly lastUsedAt: string | null;
91
+ constructor(data: {
92
+ id: string;
93
+ provider_id: string;
94
+ attributes?: Record<string, unknown>;
95
+ scopes?: string[];
96
+ account_identifier?: string | null;
97
+ account_display_name?: string | null;
98
+ status: string;
99
+ expires_at?: string | null;
100
+ created_at: string;
101
+ last_used_at?: string | null;
102
+ });
103
+ toJSON(): Record<string, unknown>;
104
+ toString(): string;
105
+ }
106
+ /**
107
+ * Connect session for initiating OAuth flows.
108
+ *
109
+ * Returned by createConnectSession(). Contains a URL the user
110
+ * opens in their browser to authorize access.
111
+ */
112
+ declare class ConnectSession {
113
+ readonly sessionToken: string;
114
+ readonly connectUrl: string;
115
+ readonly expiresIn: number;
116
+ readonly expiresAt: string;
117
+ constructor(data: {
118
+ session_token: string;
119
+ connect_url: string;
120
+ expires_in: number;
121
+ expires_at: string;
122
+ });
123
+ toJSON(): Record<string, unknown>;
124
+ toString(): string;
125
+ }
126
+ /**
127
+ * Paginated list of connections.
128
+ *
129
+ * Returned by listConnections(). Contains connection array plus pagination metadata.
130
+ */
131
+ declare class ConnectionListResult {
132
+ readonly connections: ConnectionInfo[];
133
+ readonly total: number;
134
+ readonly limit: number;
135
+ readonly offset: number;
136
+ readonly hasMore: boolean;
137
+ constructor(data: {
138
+ connections: ConnectionInfo[];
139
+ total: number;
140
+ limit: number;
141
+ offset: number;
142
+ has_more: boolean;
143
+ });
144
+ }
145
+ /**
146
+ * Audit log entry for an API call to a provider.
147
+ *
148
+ * This is sent to the backend audit endpoint.
149
+ */
150
+ declare class APICallAuditLog {
151
+ readonly connectionId: string;
152
+ readonly providerId: string;
153
+ readonly method: string;
154
+ readonly url: string;
155
+ readonly requestHeaders: Record<string, string> | null;
156
+ readonly requestBody: unknown | null;
157
+ readonly responseStatus: number;
158
+ readonly responseHeaders: Record<string, string> | null;
159
+ readonly responseBody: unknown | null;
160
+ readonly latencyMs: number;
161
+ /** Client-side timestamp (excluded from sanitize() output, like Python SDK) */
162
+ readonly timestamp: Date;
163
+ readonly reason: string | null;
164
+ /** Execution run ID for actor tracking */
165
+ readonly runId: string | null;
166
+ /** Conversation thread ID for actor tracking */
167
+ readonly threadId: string | null;
168
+ /** Tool invocation ID for actor tracking */
169
+ readonly toolCallId: string | null;
170
+ constructor(data: {
171
+ connectionId: string;
172
+ providerId: string;
173
+ method: string;
174
+ url: string;
175
+ requestHeaders?: Record<string, string> | null;
176
+ requestBody?: unknown | null;
177
+ responseStatus: number;
178
+ responseHeaders?: Record<string, string> | null;
179
+ responseBody?: unknown | null;
180
+ latencyMs: number;
181
+ reason?: string | null;
182
+ runId?: string | null;
183
+ threadId?: string | null;
184
+ toolCallId?: string | null;
185
+ });
186
+ /**
187
+ * Sanitize sensitive data before sending.
188
+ *
189
+ * Removes Authorization headers, cookies, etc.
190
+ */
191
+ sanitize(): Record<string, unknown>;
192
+ private filterSensitiveHeaders;
193
+ }
194
+
1
195
  /**
2
196
  * Provider and HttpMethod enums for type-safe SDK usage.
3
197
  *
@@ -111,6 +305,41 @@ interface RequestOptions {
111
305
  /** Tool invocation ID */
112
306
  toolCallId?: string;
113
307
  }
308
+ /**
309
+ * Options for the listConnections() method.
310
+ */
311
+ interface ListConnectionsOptions {
312
+ /** Filter by provider ID (e.g., "google") */
313
+ providerId?: string;
314
+ /** Maximum number of connections to return (default 100, max 1000) */
315
+ limit?: number;
316
+ /** Offset for pagination (default 0) */
317
+ offset?: number;
318
+ }
319
+ /**
320
+ * Options for the createConnectSession() method.
321
+ */
322
+ interface CreateConnectSessionOptions {
323
+ /** End user to create session for (requires at least id) */
324
+ endUser: {
325
+ id: string;
326
+ email?: string;
327
+ name?: string;
328
+ };
329
+ /** User attributes for connection matching */
330
+ attributes?: Record<string, unknown>;
331
+ /** Restrict to specific providers (e.g., ["google", "github"]) */
332
+ allowedProviders?: string[];
333
+ /** URL to redirect after OAuth completion */
334
+ returnUrl?: string;
335
+ /** Allowed origin for postMessage communication */
336
+ allowedOrigin?: string;
337
+ /** Request metadata for audit */
338
+ metadata?: {
339
+ ipAddress?: string;
340
+ userAgent?: string;
341
+ };
342
+ }
114
343
  /**
115
344
  * Main SDK class for Alter Vault OAuth token management.
116
345
  *
@@ -155,138 +384,28 @@ declare class AlterVault {
155
384
  */
156
385
  request(provider: Provider | string, method: HttpMethod | string, url: string, options: RequestOptions): Promise<Response>;
157
386
  /**
158
- * Close HTTP clients and release resources.
159
- * Waits for any pending audit tasks before closing.
160
- */
161
- close(): Promise<void>;
162
- /**
163
- * Async dispose support for `await using vault = new AlterVault(...)`.
164
- * Requires TypeScript 5.2+ and Node.js 18+.
165
- */
166
- [Symbol.asyncDispose](): Promise<void>;
167
- }
168
-
169
- /**
170
- * Models for Alter SDK.
171
- *
172
- * These models provide type-safe data structures for SDK operations.
173
- *
174
- * Security Hardening (v0.4.0):
175
- * - TokenResponse: accessToken stored in module-private WeakMap in client.ts
176
- * (not readable as property, not importable from this module)
177
- * - TokenResponse: Object.freeze(this) prevents mutation after creation
178
- * - toJSON() and toString() exclude access token from serialization
179
- */
180
- /**
181
- * OAuth token response from Alter Vault.
182
- *
183
- * This represents an access token retrieved from the backend.
184
- * The access_token is NOT stored as a readable property — it lives in a
185
- * module-private WeakMap in client.ts and is only accessible internally.
186
- *
187
- * SECURITY:
188
- * - No accessToken property (stored in WeakMap inside client.ts)
189
- * - Instance is frozen after construction to prevent mutation
190
- * - toJSON() and toString() exclude access token
191
- * - _extractAccessToken() lives in client.ts, NOT in this file,
192
- * so it cannot be imported by consumers even via deep imports
193
- */
194
- declare class TokenResponse {
195
- /** Token type (usually "Bearer") */
196
- readonly tokenType: string;
197
- /** Seconds until token expires */
198
- readonly expiresIn: number | null;
199
- /** Absolute expiration time */
200
- readonly expiresAt: Date | null;
201
- /** OAuth scopes granted */
202
- readonly scopes: string[];
203
- /** Connection ID that provided this token */
204
- readonly connectionId: string;
205
- constructor(data: {
206
- access_token: string;
207
- token_type?: string;
208
- expires_in?: number | null;
209
- expires_at?: string | null;
210
- scopes?: string[];
211
- connection_id: string;
212
- });
213
- /**
214
- * Parse expires_at from ISO string.
215
- */
216
- private static parseExpiresAt;
217
- /**
218
- * Check if token is expired.
387
+ * List OAuth connections for this app.
219
388
  *
220
- * @param bufferSeconds - Consider token expired N seconds before actual expiry.
221
- * Useful for preventing race conditions.
222
- * @returns True if token is expired or will expire within bufferSeconds.
389
+ * Returns paginated connection metadata (no tokens).
390
+ * Useful for discovering which services a user has connected.
223
391
  */
224
- isExpired(bufferSeconds?: number): boolean;
392
+ listConnections(options?: ListConnectionsOptions): Promise<ConnectionListResult>;
225
393
  /**
226
- * Check if token should be refreshed soon.
394
+ * Create a Connect session for initiating OAuth flows.
227
395
  *
228
- * @param bufferSeconds - Consider token needing refresh N seconds before expiry.
229
- * Default 5 minutes (300 seconds).
230
- * @returns True if token will expire within bufferSeconds.
396
+ * Returns a URL the user can open in their browser to authorize access.
231
397
  */
232
- needsRefresh(bufferSeconds?: number): boolean;
398
+ createConnectSession(options: CreateConnectSessionOptions): Promise<ConnectSession>;
233
399
  /**
234
- * Custom JSON serialization EXCLUDES access_token for security.
235
- */
236
- toJSON(): Record<string, unknown>;
237
- /**
238
- * Custom string representation — EXCLUDES access_token for security.
400
+ * Close HTTP clients and release resources.
401
+ * Waits for any pending audit tasks before closing.
239
402
  */
240
- toString(): string;
241
- }
242
- /**
243
- * Audit log entry for an API call to a provider.
244
- *
245
- * This is sent to the backend audit endpoint.
246
- */
247
- declare class APICallAuditLog {
248
- readonly connectionId: string;
249
- readonly providerId: string;
250
- readonly method: string;
251
- readonly url: string;
252
- readonly requestHeaders: Record<string, string> | null;
253
- readonly requestBody: unknown | null;
254
- readonly responseStatus: number;
255
- readonly responseHeaders: Record<string, string> | null;
256
- readonly responseBody: unknown | null;
257
- readonly latencyMs: number;
258
- /** Client-side timestamp (excluded from sanitize() output, like Python SDK) */
259
- readonly timestamp: Date;
260
- readonly reason: string | null;
261
- /** Execution run ID for actor tracking */
262
- readonly runId: string | null;
263
- /** Conversation thread ID for actor tracking */
264
- readonly threadId: string | null;
265
- /** Tool invocation ID for actor tracking */
266
- readonly toolCallId: string | null;
267
- constructor(data: {
268
- connectionId: string;
269
- providerId: string;
270
- method: string;
271
- url: string;
272
- requestHeaders?: Record<string, string> | null;
273
- requestBody?: unknown | null;
274
- responseStatus: number;
275
- responseHeaders?: Record<string, string> | null;
276
- responseBody?: unknown | null;
277
- latencyMs: number;
278
- reason?: string | null;
279
- runId?: string | null;
280
- threadId?: string | null;
281
- toolCallId?: string | null;
282
- });
403
+ close(): Promise<void>;
283
404
  /**
284
- * Sanitize sensitive data before sending.
285
- *
286
- * Removes Authorization headers, cookies, etc.
405
+ * Async dispose support for `await using vault = new AlterVault(...)`.
406
+ * Requires TypeScript 5.2+ and Node.js 18+.
287
407
  */
288
- sanitize(): Record<string, unknown>;
289
- private filterSensitiveHeaders;
408
+ [Symbol.asyncDispose](): Promise<void>;
290
409
  }
291
410
 
292
411
  /**
@@ -368,4 +487,4 @@ declare class TimeoutError extends NetworkError {
368
487
  constructor(message: string, details?: Record<string, unknown>);
369
488
  }
370
489
 
371
- export { APICallAuditLog, AlterSDKError, AlterVault, type AlterVaultOptions, ConnectionNotFoundError, HttpMethod, NetworkError, PolicyViolationError, Provider, ProviderAPIError, type RequestOptions, TimeoutError, TokenExpiredError, TokenResponse, TokenRetrievalError };
490
+ export { APICallAuditLog, AlterSDKError, AlterVault, type AlterVaultOptions, ConnectSession, ConnectionInfo, ConnectionListResult, ConnectionNotFoundError, type CreateConnectSessionOptions, HttpMethod, type ListConnectionsOptions, NetworkError, PolicyViolationError, Provider, ProviderAPIError, type RequestOptions, TimeoutError, TokenExpiredError, TokenResponse, TokenRetrievalError };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,197 @@
1
+ /**
2
+ * Models for Alter SDK.
3
+ *
4
+ * These models provide type-safe data structures for SDK operations.
5
+ *
6
+ * Security Hardening (v0.4.0):
7
+ * - TokenResponse: accessToken stored in module-private WeakMap in client.ts
8
+ * (not readable as property, not importable from this module)
9
+ * - TokenResponse: Object.freeze(this) prevents mutation after creation
10
+ * - toJSON() and toString() exclude access token from serialization
11
+ */
12
+ /**
13
+ * OAuth token response from Alter Vault.
14
+ *
15
+ * This represents an access token retrieved from the backend.
16
+ * The access_token is NOT stored as a readable property — it lives in a
17
+ * module-private WeakMap in client.ts and is only accessible internally.
18
+ *
19
+ * SECURITY:
20
+ * - No accessToken property (stored in WeakMap inside client.ts)
21
+ * - Instance is frozen after construction to prevent mutation
22
+ * - toJSON() and toString() exclude access token
23
+ * - _extractAccessToken() lives in client.ts, NOT in this file,
24
+ * so it cannot be imported by consumers even via deep imports
25
+ */
26
+ declare class TokenResponse {
27
+ /** Token type (usually "Bearer") */
28
+ readonly tokenType: string;
29
+ /** Seconds until token expires */
30
+ readonly expiresIn: number | null;
31
+ /** Absolute expiration time */
32
+ readonly expiresAt: Date | null;
33
+ /** OAuth scopes granted */
34
+ readonly scopes: string[];
35
+ /** Connection ID that provided this token */
36
+ readonly connectionId: string;
37
+ constructor(data: {
38
+ access_token: string;
39
+ token_type?: string;
40
+ expires_in?: number | null;
41
+ expires_at?: string | null;
42
+ scopes?: string[];
43
+ connection_id: string;
44
+ });
45
+ /**
46
+ * Parse expires_at from ISO string.
47
+ */
48
+ private static parseExpiresAt;
49
+ /**
50
+ * Check if token is expired.
51
+ *
52
+ * @param bufferSeconds - Consider token expired N seconds before actual expiry.
53
+ * Useful for preventing race conditions.
54
+ * @returns True if token is expired or will expire within bufferSeconds.
55
+ */
56
+ isExpired(bufferSeconds?: number): boolean;
57
+ /**
58
+ * Check if token should be refreshed soon.
59
+ *
60
+ * @param bufferSeconds - Consider token needing refresh N seconds before expiry.
61
+ * Default 5 minutes (300 seconds).
62
+ * @returns True if token will expire within bufferSeconds.
63
+ */
64
+ needsRefresh(bufferSeconds?: number): boolean;
65
+ /**
66
+ * Custom JSON serialization — EXCLUDES access_token for security.
67
+ */
68
+ toJSON(): Record<string, unknown>;
69
+ /**
70
+ * Custom string representation — EXCLUDES access_token for security.
71
+ */
72
+ toString(): string;
73
+ }
74
+ /**
75
+ * OAuth connection info from Alter Vault.
76
+ *
77
+ * Represents a connected OAuth service (e.g., Google, GitHub).
78
+ * Returned by listConnections(). Contains metadata only — no tokens.
79
+ */
80
+ declare class ConnectionInfo {
81
+ readonly id: string;
82
+ readonly providerId: string;
83
+ readonly attributes: Record<string, unknown>;
84
+ readonly scopes: string[];
85
+ readonly accountIdentifier: string | null;
86
+ readonly accountDisplayName: string | null;
87
+ readonly status: string;
88
+ readonly expiresAt: string | null;
89
+ readonly createdAt: string;
90
+ readonly lastUsedAt: string | null;
91
+ constructor(data: {
92
+ id: string;
93
+ provider_id: string;
94
+ attributes?: Record<string, unknown>;
95
+ scopes?: string[];
96
+ account_identifier?: string | null;
97
+ account_display_name?: string | null;
98
+ status: string;
99
+ expires_at?: string | null;
100
+ created_at: string;
101
+ last_used_at?: string | null;
102
+ });
103
+ toJSON(): Record<string, unknown>;
104
+ toString(): string;
105
+ }
106
+ /**
107
+ * Connect session for initiating OAuth flows.
108
+ *
109
+ * Returned by createConnectSession(). Contains a URL the user
110
+ * opens in their browser to authorize access.
111
+ */
112
+ declare class ConnectSession {
113
+ readonly sessionToken: string;
114
+ readonly connectUrl: string;
115
+ readonly expiresIn: number;
116
+ readonly expiresAt: string;
117
+ constructor(data: {
118
+ session_token: string;
119
+ connect_url: string;
120
+ expires_in: number;
121
+ expires_at: string;
122
+ });
123
+ toJSON(): Record<string, unknown>;
124
+ toString(): string;
125
+ }
126
+ /**
127
+ * Paginated list of connections.
128
+ *
129
+ * Returned by listConnections(). Contains connection array plus pagination metadata.
130
+ */
131
+ declare class ConnectionListResult {
132
+ readonly connections: ConnectionInfo[];
133
+ readonly total: number;
134
+ readonly limit: number;
135
+ readonly offset: number;
136
+ readonly hasMore: boolean;
137
+ constructor(data: {
138
+ connections: ConnectionInfo[];
139
+ total: number;
140
+ limit: number;
141
+ offset: number;
142
+ has_more: boolean;
143
+ });
144
+ }
145
+ /**
146
+ * Audit log entry for an API call to a provider.
147
+ *
148
+ * This is sent to the backend audit endpoint.
149
+ */
150
+ declare class APICallAuditLog {
151
+ readonly connectionId: string;
152
+ readonly providerId: string;
153
+ readonly method: string;
154
+ readonly url: string;
155
+ readonly requestHeaders: Record<string, string> | null;
156
+ readonly requestBody: unknown | null;
157
+ readonly responseStatus: number;
158
+ readonly responseHeaders: Record<string, string> | null;
159
+ readonly responseBody: unknown | null;
160
+ readonly latencyMs: number;
161
+ /** Client-side timestamp (excluded from sanitize() output, like Python SDK) */
162
+ readonly timestamp: Date;
163
+ readonly reason: string | null;
164
+ /** Execution run ID for actor tracking */
165
+ readonly runId: string | null;
166
+ /** Conversation thread ID for actor tracking */
167
+ readonly threadId: string | null;
168
+ /** Tool invocation ID for actor tracking */
169
+ readonly toolCallId: string | null;
170
+ constructor(data: {
171
+ connectionId: string;
172
+ providerId: string;
173
+ method: string;
174
+ url: string;
175
+ requestHeaders?: Record<string, string> | null;
176
+ requestBody?: unknown | null;
177
+ responseStatus: number;
178
+ responseHeaders?: Record<string, string> | null;
179
+ responseBody?: unknown | null;
180
+ latencyMs: number;
181
+ reason?: string | null;
182
+ runId?: string | null;
183
+ threadId?: string | null;
184
+ toolCallId?: string | null;
185
+ });
186
+ /**
187
+ * Sanitize sensitive data before sending.
188
+ *
189
+ * Removes Authorization headers, cookies, etc.
190
+ */
191
+ sanitize(): Record<string, unknown>;
192
+ private filterSensitiveHeaders;
193
+ }
194
+
1
195
  /**
2
196
  * Provider and HttpMethod enums for type-safe SDK usage.
3
197
  *
@@ -111,6 +305,41 @@ interface RequestOptions {
111
305
  /** Tool invocation ID */
112
306
  toolCallId?: string;
113
307
  }
308
+ /**
309
+ * Options for the listConnections() method.
310
+ */
311
+ interface ListConnectionsOptions {
312
+ /** Filter by provider ID (e.g., "google") */
313
+ providerId?: string;
314
+ /** Maximum number of connections to return (default 100, max 1000) */
315
+ limit?: number;
316
+ /** Offset for pagination (default 0) */
317
+ offset?: number;
318
+ }
319
+ /**
320
+ * Options for the createConnectSession() method.
321
+ */
322
+ interface CreateConnectSessionOptions {
323
+ /** End user to create session for (requires at least id) */
324
+ endUser: {
325
+ id: string;
326
+ email?: string;
327
+ name?: string;
328
+ };
329
+ /** User attributes for connection matching */
330
+ attributes?: Record<string, unknown>;
331
+ /** Restrict to specific providers (e.g., ["google", "github"]) */
332
+ allowedProviders?: string[];
333
+ /** URL to redirect after OAuth completion */
334
+ returnUrl?: string;
335
+ /** Allowed origin for postMessage communication */
336
+ allowedOrigin?: string;
337
+ /** Request metadata for audit */
338
+ metadata?: {
339
+ ipAddress?: string;
340
+ userAgent?: string;
341
+ };
342
+ }
114
343
  /**
115
344
  * Main SDK class for Alter Vault OAuth token management.
116
345
  *
@@ -155,138 +384,28 @@ declare class AlterVault {
155
384
  */
156
385
  request(provider: Provider | string, method: HttpMethod | string, url: string, options: RequestOptions): Promise<Response>;
157
386
  /**
158
- * Close HTTP clients and release resources.
159
- * Waits for any pending audit tasks before closing.
160
- */
161
- close(): Promise<void>;
162
- /**
163
- * Async dispose support for `await using vault = new AlterVault(...)`.
164
- * Requires TypeScript 5.2+ and Node.js 18+.
165
- */
166
- [Symbol.asyncDispose](): Promise<void>;
167
- }
168
-
169
- /**
170
- * Models for Alter SDK.
171
- *
172
- * These models provide type-safe data structures for SDK operations.
173
- *
174
- * Security Hardening (v0.4.0):
175
- * - TokenResponse: accessToken stored in module-private WeakMap in client.ts
176
- * (not readable as property, not importable from this module)
177
- * - TokenResponse: Object.freeze(this) prevents mutation after creation
178
- * - toJSON() and toString() exclude access token from serialization
179
- */
180
- /**
181
- * OAuth token response from Alter Vault.
182
- *
183
- * This represents an access token retrieved from the backend.
184
- * The access_token is NOT stored as a readable property — it lives in a
185
- * module-private WeakMap in client.ts and is only accessible internally.
186
- *
187
- * SECURITY:
188
- * - No accessToken property (stored in WeakMap inside client.ts)
189
- * - Instance is frozen after construction to prevent mutation
190
- * - toJSON() and toString() exclude access token
191
- * - _extractAccessToken() lives in client.ts, NOT in this file,
192
- * so it cannot be imported by consumers even via deep imports
193
- */
194
- declare class TokenResponse {
195
- /** Token type (usually "Bearer") */
196
- readonly tokenType: string;
197
- /** Seconds until token expires */
198
- readonly expiresIn: number | null;
199
- /** Absolute expiration time */
200
- readonly expiresAt: Date | null;
201
- /** OAuth scopes granted */
202
- readonly scopes: string[];
203
- /** Connection ID that provided this token */
204
- readonly connectionId: string;
205
- constructor(data: {
206
- access_token: string;
207
- token_type?: string;
208
- expires_in?: number | null;
209
- expires_at?: string | null;
210
- scopes?: string[];
211
- connection_id: string;
212
- });
213
- /**
214
- * Parse expires_at from ISO string.
215
- */
216
- private static parseExpiresAt;
217
- /**
218
- * Check if token is expired.
387
+ * List OAuth connections for this app.
219
388
  *
220
- * @param bufferSeconds - Consider token expired N seconds before actual expiry.
221
- * Useful for preventing race conditions.
222
- * @returns True if token is expired or will expire within bufferSeconds.
389
+ * Returns paginated connection metadata (no tokens).
390
+ * Useful for discovering which services a user has connected.
223
391
  */
224
- isExpired(bufferSeconds?: number): boolean;
392
+ listConnections(options?: ListConnectionsOptions): Promise<ConnectionListResult>;
225
393
  /**
226
- * Check if token should be refreshed soon.
394
+ * Create a Connect session for initiating OAuth flows.
227
395
  *
228
- * @param bufferSeconds - Consider token needing refresh N seconds before expiry.
229
- * Default 5 minutes (300 seconds).
230
- * @returns True if token will expire within bufferSeconds.
396
+ * Returns a URL the user can open in their browser to authorize access.
231
397
  */
232
- needsRefresh(bufferSeconds?: number): boolean;
398
+ createConnectSession(options: CreateConnectSessionOptions): Promise<ConnectSession>;
233
399
  /**
234
- * Custom JSON serialization EXCLUDES access_token for security.
235
- */
236
- toJSON(): Record<string, unknown>;
237
- /**
238
- * Custom string representation — EXCLUDES access_token for security.
400
+ * Close HTTP clients and release resources.
401
+ * Waits for any pending audit tasks before closing.
239
402
  */
240
- toString(): string;
241
- }
242
- /**
243
- * Audit log entry for an API call to a provider.
244
- *
245
- * This is sent to the backend audit endpoint.
246
- */
247
- declare class APICallAuditLog {
248
- readonly connectionId: string;
249
- readonly providerId: string;
250
- readonly method: string;
251
- readonly url: string;
252
- readonly requestHeaders: Record<string, string> | null;
253
- readonly requestBody: unknown | null;
254
- readonly responseStatus: number;
255
- readonly responseHeaders: Record<string, string> | null;
256
- readonly responseBody: unknown | null;
257
- readonly latencyMs: number;
258
- /** Client-side timestamp (excluded from sanitize() output, like Python SDK) */
259
- readonly timestamp: Date;
260
- readonly reason: string | null;
261
- /** Execution run ID for actor tracking */
262
- readonly runId: string | null;
263
- /** Conversation thread ID for actor tracking */
264
- readonly threadId: string | null;
265
- /** Tool invocation ID for actor tracking */
266
- readonly toolCallId: string | null;
267
- constructor(data: {
268
- connectionId: string;
269
- providerId: string;
270
- method: string;
271
- url: string;
272
- requestHeaders?: Record<string, string> | null;
273
- requestBody?: unknown | null;
274
- responseStatus: number;
275
- responseHeaders?: Record<string, string> | null;
276
- responseBody?: unknown | null;
277
- latencyMs: number;
278
- reason?: string | null;
279
- runId?: string | null;
280
- threadId?: string | null;
281
- toolCallId?: string | null;
282
- });
403
+ close(): Promise<void>;
283
404
  /**
284
- * Sanitize sensitive data before sending.
285
- *
286
- * Removes Authorization headers, cookies, etc.
405
+ * Async dispose support for `await using vault = new AlterVault(...)`.
406
+ * Requires TypeScript 5.2+ and Node.js 18+.
287
407
  */
288
- sanitize(): Record<string, unknown>;
289
- private filterSensitiveHeaders;
408
+ [Symbol.asyncDispose](): Promise<void>;
290
409
  }
291
410
 
292
411
  /**
@@ -368,4 +487,4 @@ declare class TimeoutError extends NetworkError {
368
487
  constructor(message: string, details?: Record<string, unknown>);
369
488
  }
370
489
 
371
- export { APICallAuditLog, AlterSDKError, AlterVault, type AlterVaultOptions, ConnectionNotFoundError, HttpMethod, NetworkError, PolicyViolationError, Provider, ProviderAPIError, type RequestOptions, TimeoutError, TokenExpiredError, TokenResponse, TokenRetrievalError };
490
+ export { APICallAuditLog, AlterSDKError, AlterVault, type AlterVaultOptions, ConnectSession, ConnectionInfo, ConnectionListResult, ConnectionNotFoundError, type CreateConnectSessionOptions, HttpMethod, type ListConnectionsOptions, NetworkError, PolicyViolationError, Provider, ProviderAPIError, type RequestOptions, TimeoutError, TokenExpiredError, TokenResponse, TokenRetrievalError };
package/dist/index.js CHANGED
@@ -144,6 +144,87 @@ var TokenResponse = class _TokenResponse {
144
144
  return this.toString();
145
145
  }
146
146
  };
147
+ var ConnectionInfo = class {
148
+ id;
149
+ providerId;
150
+ attributes;
151
+ scopes;
152
+ accountIdentifier;
153
+ accountDisplayName;
154
+ status;
155
+ expiresAt;
156
+ createdAt;
157
+ lastUsedAt;
158
+ constructor(data) {
159
+ this.id = data.id;
160
+ this.providerId = data.provider_id;
161
+ this.attributes = data.attributes ?? {};
162
+ this.scopes = data.scopes ?? [];
163
+ this.accountIdentifier = data.account_identifier ?? null;
164
+ this.accountDisplayName = data.account_display_name ?? null;
165
+ this.status = data.status;
166
+ this.expiresAt = data.expires_at ?? null;
167
+ this.createdAt = data.created_at;
168
+ this.lastUsedAt = data.last_used_at ?? null;
169
+ Object.freeze(this);
170
+ }
171
+ toJSON() {
172
+ return {
173
+ id: this.id,
174
+ provider_id: this.providerId,
175
+ attributes: this.attributes,
176
+ scopes: this.scopes,
177
+ account_identifier: this.accountIdentifier,
178
+ account_display_name: this.accountDisplayName,
179
+ status: this.status,
180
+ expires_at: this.expiresAt,
181
+ created_at: this.createdAt,
182
+ last_used_at: this.lastUsedAt
183
+ };
184
+ }
185
+ toString() {
186
+ return `ConnectionInfo(id=${this.id}, provider=${this.providerId}, status=${this.status})`;
187
+ }
188
+ };
189
+ var ConnectSession = class {
190
+ sessionToken;
191
+ connectUrl;
192
+ expiresIn;
193
+ expiresAt;
194
+ constructor(data) {
195
+ this.sessionToken = data.session_token;
196
+ this.connectUrl = data.connect_url;
197
+ this.expiresIn = data.expires_in;
198
+ this.expiresAt = data.expires_at;
199
+ Object.freeze(this);
200
+ }
201
+ toJSON() {
202
+ return {
203
+ session_token: this.sessionToken,
204
+ connect_url: this.connectUrl,
205
+ expires_in: this.expiresIn,
206
+ expires_at: this.expiresAt
207
+ };
208
+ }
209
+ toString() {
210
+ return `ConnectSession(url=${this.connectUrl}, expires_in=${this.expiresIn})`;
211
+ }
212
+ };
213
+ var ConnectionListResult = class {
214
+ connections;
215
+ total;
216
+ limit;
217
+ offset;
218
+ hasMore;
219
+ constructor(data) {
220
+ this.connections = data.connections;
221
+ this.total = data.total;
222
+ this.limit = data.limit;
223
+ this.offset = data.offset;
224
+ this.hasMore = data.has_more;
225
+ Object.freeze(this);
226
+ }
227
+ };
147
228
  var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
148
229
  "authorization",
149
230
  "cookie",
@@ -238,7 +319,7 @@ function _extractAccessToken(token) {
238
319
  return value;
239
320
  }
240
321
  var _fetch;
241
- var SDK_VERSION = "0.2.1";
322
+ var SDK_VERSION = "0.2.2";
242
323
  var SDK_USER_AGENT = `alter-sdk-node/${SDK_VERSION}`;
243
324
  var VALID_ACTOR_TYPES = ["ai_agent", "mcp_server"];
244
325
  var HTTP_FORBIDDEN = 403;
@@ -589,7 +670,7 @@ var AlterVault = class _AlterVault {
589
670
  * This is a private method. Tokens are NEVER exposed to developers.
590
671
  * Use request() instead, which handles tokens internally.
591
672
  */
592
- async #getToken(providerId, attributes, reason, runId, threadId, toolCallId) {
673
+ async #getToken(providerId, attributes, reason, requestMetadata, runId, threadId, toolCallId) {
593
674
  const actorHeaders = this.#getActorRequestHeaders(
594
675
  runId,
595
676
  threadId,
@@ -601,7 +682,8 @@ var AlterVault = class _AlterVault {
601
682
  json: {
602
683
  provider_id: providerId,
603
684
  attributes,
604
- reason: reason ?? null
685
+ reason: reason ?? null,
686
+ request: requestMetadata ?? null
605
687
  },
606
688
  headers: actorHeaders
607
689
  });
@@ -778,6 +860,7 @@ var AlterVault = class _AlterVault {
778
860
  providerStr,
779
861
  options.user,
780
862
  options.reason,
863
+ { method: methodStr, url },
781
864
  options.runId,
782
865
  options.threadId,
783
866
  options.toolCallId
@@ -858,6 +941,112 @@ var AlterVault = class _AlterVault {
858
941
  }
859
942
  return response;
860
943
  }
944
+ /**
945
+ * List OAuth connections for this app.
946
+ *
947
+ * Returns paginated connection metadata (no tokens).
948
+ * Useful for discovering which services a user has connected.
949
+ */
950
+ async listConnections(options) {
951
+ if (this.#closed) {
952
+ throw new AlterSDKError(
953
+ "SDK instance has been closed. Create a new AlterVault instance to make requests."
954
+ );
955
+ }
956
+ const actorHeaders = this.#getActorRequestHeaders();
957
+ let response;
958
+ try {
959
+ response = await this.#alterClient.post("/oauth/connections/list", {
960
+ json: {
961
+ provider_id: options?.providerId ?? null,
962
+ limit: options?.limit ?? 100,
963
+ offset: options?.offset ?? 0
964
+ },
965
+ headers: actorHeaders
966
+ });
967
+ } catch (error) {
968
+ if (_AlterVault.#isTimeoutOrAbortError(error)) {
969
+ throw new TimeoutError(
970
+ `Request to Alter Vault backend timed out: ${error instanceof Error ? error.message : String(error)}`,
971
+ { base_url: this.baseUrl }
972
+ );
973
+ }
974
+ if (error instanceof TypeError) {
975
+ throw new NetworkError(
976
+ `Failed to connect to Alter Vault backend: ${error.message}`,
977
+ { base_url: this.baseUrl }
978
+ );
979
+ }
980
+ throw new AlterSDKError(
981
+ `Failed to list connections: ${error instanceof Error ? error.message : String(error)}`
982
+ );
983
+ }
984
+ this.#cacheActorIdFromResponse(response);
985
+ await this.#handleErrorResponse(response);
986
+ const data = await response.json();
987
+ const connections = data.connections.map(
988
+ (c) => new ConnectionInfo(
989
+ c
990
+ )
991
+ );
992
+ return new ConnectionListResult({
993
+ connections,
994
+ total: data.total,
995
+ limit: data.limit,
996
+ offset: data.offset,
997
+ has_more: data.has_more
998
+ });
999
+ }
1000
+ /**
1001
+ * Create a Connect session for initiating OAuth flows.
1002
+ *
1003
+ * Returns a URL the user can open in their browser to authorize access.
1004
+ */
1005
+ async createConnectSession(options) {
1006
+ if (this.#closed) {
1007
+ throw new AlterSDKError(
1008
+ "SDK instance has been closed. Create a new AlterVault instance to make requests."
1009
+ );
1010
+ }
1011
+ if (!options.endUser?.id) {
1012
+ throw new AlterSDKError("endUser.id is required");
1013
+ }
1014
+ const actorHeaders = this.#getActorRequestHeaders();
1015
+ let response;
1016
+ try {
1017
+ response = await this.#alterClient.post("/oauth/connect/session", {
1018
+ json: {
1019
+ end_user: options.endUser,
1020
+ attributes: options.attributes ?? null,
1021
+ allowed_providers: options.allowedProviders ?? null,
1022
+ return_url: options.returnUrl ?? null,
1023
+ allowed_origin: options.allowedOrigin ?? null,
1024
+ metadata: options.metadata ?? null
1025
+ },
1026
+ headers: actorHeaders
1027
+ });
1028
+ } catch (error) {
1029
+ if (_AlterVault.#isTimeoutOrAbortError(error)) {
1030
+ throw new TimeoutError(
1031
+ `Request to Alter Vault backend timed out: ${error instanceof Error ? error.message : String(error)}`,
1032
+ { base_url: this.baseUrl }
1033
+ );
1034
+ }
1035
+ if (error instanceof TypeError) {
1036
+ throw new NetworkError(
1037
+ `Failed to connect to Alter Vault backend: ${error.message}`,
1038
+ { base_url: this.baseUrl }
1039
+ );
1040
+ }
1041
+ throw new AlterSDKError(
1042
+ `Failed to create connect session: ${error instanceof Error ? error.message : String(error)}`
1043
+ );
1044
+ }
1045
+ this.#cacheActorIdFromResponse(response);
1046
+ await this.#handleErrorResponse(response);
1047
+ const data = await response.json();
1048
+ return new ConnectSession(data);
1049
+ }
861
1050
  /**
862
1051
  * Close HTTP clients and release resources.
863
1052
  * Waits for any pending audit tasks before closing.
@@ -905,6 +1094,9 @@ export {
905
1094
  APICallAuditLog,
906
1095
  AlterSDKError,
907
1096
  AlterVault,
1097
+ ConnectSession,
1098
+ ConnectionInfo,
1099
+ ConnectionListResult,
908
1100
  ConnectionNotFoundError,
909
1101
  HttpMethod,
910
1102
  NetworkError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alter-ai/alter-sdk",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Official TypeScript SDK for Alter Vault — OAuth token management with policy enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",