@agent-os-sdk/client 0.1.1 → 0.2.1

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.
Files changed (69) hide show
  1. package/dist/client/AgentOsClient.d.ts +57 -38
  2. package/dist/client/AgentOsClient.d.ts.map +1 -1
  3. package/dist/client/AgentOsClient.js +192 -37
  4. package/dist/client/auth.d.ts +102 -0
  5. package/dist/client/auth.d.ts.map +1 -0
  6. package/dist/client/auth.js +44 -0
  7. package/dist/generated/openapi.d.ts +1009 -204
  8. package/dist/generated/openapi.d.ts.map +1 -1
  9. package/dist/index.d.ts +12 -2
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +12 -0
  12. package/dist/modules/approvals.d.ts +64 -0
  13. package/dist/modules/approvals.d.ts.map +1 -0
  14. package/dist/modules/approvals.js +54 -0
  15. package/dist/modules/artifacts.d.ts +49 -0
  16. package/dist/modules/artifacts.d.ts.map +1 -0
  17. package/dist/modules/artifacts.js +50 -0
  18. package/dist/modules/budgets.d.ts +81 -0
  19. package/dist/modules/budgets.d.ts.map +1 -0
  20. package/dist/modules/budgets.js +50 -0
  21. package/dist/modules/builder.d.ts +23 -3
  22. package/dist/modules/builder.d.ts.map +1 -1
  23. package/dist/modules/builder.js +28 -6
  24. package/dist/modules/capabilities.d.ts +57 -0
  25. package/dist/modules/capabilities.d.ts.map +1 -0
  26. package/dist/modules/capabilities.js +50 -0
  27. package/dist/modules/deployments.d.ts +67 -0
  28. package/dist/modules/deployments.d.ts.map +1 -0
  29. package/dist/modules/deployments.js +58 -0
  30. package/dist/modules/flows.d.ts +84 -0
  31. package/dist/modules/flows.d.ts.map +1 -0
  32. package/dist/modules/flows.js +66 -0
  33. package/dist/modules/handoff.d.ts +91 -0
  34. package/dist/modules/handoff.d.ts.map +1 -0
  35. package/dist/modules/handoff.js +65 -0
  36. package/dist/modules/incidents.d.ts +72 -0
  37. package/dist/modules/incidents.d.ts.map +1 -0
  38. package/dist/modules/incidents.js +54 -0
  39. package/dist/modules/members.d.ts +5 -0
  40. package/dist/modules/members.d.ts.map +1 -1
  41. package/dist/modules/members.js +10 -0
  42. package/dist/modules/policies.d.ts +76 -0
  43. package/dist/modules/policies.d.ts.map +1 -0
  44. package/dist/modules/policies.js +46 -0
  45. package/dist/modules/runs.d.ts +89 -3
  46. package/dist/modules/runs.d.ts.map +1 -1
  47. package/dist/modules/runs.js +75 -4
  48. package/dist/modules/tenants.d.ts +4 -0
  49. package/dist/modules/tenants.d.ts.map +1 -1
  50. package/dist/modules/tenants.js +8 -0
  51. package/package.json +49 -48
  52. package/src/client/AgentOsClient.ts +217 -61
  53. package/src/client/auth.ts +148 -0
  54. package/src/generated/openapi.ts +1009 -204
  55. package/src/generated/swagger.json +947 -700
  56. package/src/index.ts +25 -2
  57. package/src/modules/approvals.ts +109 -0
  58. package/src/modules/artifacts.ts +83 -0
  59. package/src/modules/budgets.ts +120 -0
  60. package/src/modules/builder.ts +26 -4
  61. package/src/modules/capabilities.ts +95 -0
  62. package/src/modules/deployments.ts +111 -0
  63. package/src/modules/flows.ts +133 -0
  64. package/src/modules/handoff.ts +140 -0
  65. package/src/modules/incidents.ts +113 -0
  66. package/src/modules/members.ts +11 -0
  67. package/src/modules/policies.ts +112 -0
  68. package/src/modules/runs.ts +123 -5
  69. package/src/modules/tenants.ts +9 -0
@@ -2,25 +2,40 @@
2
2
  * Agent OS SDK - Main Client
3
3
  *
4
4
  * Fully typed API client for Agent OS platform.
5
- * All HTTP calls must go through this client.
5
+ * Supports both API Token (server) and JWT (browser) authentication.
6
6
  *
7
7
  * @example
8
8
  * ```ts
9
+ * // API Token (server-to-server)
9
10
  * const api = new AgentOsClient({
10
- * baseUrl: "http://localhost:5000",
11
- * tenantId: "...",
12
- * workspaceId: "...",
13
- * token: "...",
11
+ * baseUrl: "https://api.agentos.io",
12
+ * auth: { type: "api_token", apiKey: "aosk_live_xxx" }
14
13
  * });
15
14
  *
16
- * const run = await api.runs.create({
17
- * agent_id: "...",
18
- * input: { message: "Hello" },
15
+ * // JWT (browser)
16
+ * const api = new AgentOsClient({
17
+ * baseUrl: "https://api.agentos.io",
18
+ * auth: {
19
+ * type: "jwt",
20
+ * getToken: () => supabase.auth.getSession().then(s => s.data.session?.access_token),
21
+ * getWorkspaceId: () => localStorage.getItem("agentos.workspaceId")!
22
+ * }
19
23
  * });
20
24
  * ```
21
25
  */
22
26
 
23
27
  import { createRawClient, type RawClient } from "./raw.js";
28
+ import {
29
+ type AgentOsClientOptions,
30
+ type AgentOsClientOptionsLegacy,
31
+ type AuthProvider,
32
+ isNewAuthOptions,
33
+ isApiTokenAuth,
34
+ isJwtAuth,
35
+ isApiToken,
36
+ isBrowser,
37
+ } from "./auth.js";
38
+
24
39
  import { AgentsModule } from "../modules/agents.js";
25
40
  import { RunsModule } from "../modules/runs.js";
26
41
  import { ThreadsModule } from "../modules/threads.js";
@@ -53,38 +68,32 @@ import { InfoModule } from "../modules/info.js";
53
68
  import { MetricsModule } from "../modules/metrics.js";
54
69
  import { GraphsModule } from "../modules/graphs.js";
55
70
 
56
- export type AgentOsClientOptions = {
57
- /** Base URL of the Agent OS API */
58
- baseUrl: string;
59
-
60
- /** Tenant ID (multi-tenant) */
61
- tenantId: string;
62
-
63
- /** Workspace ID */
64
- workspaceId: string;
65
-
66
- /** Optional auth token */
67
- token?: string;
68
-
69
- /** Optional member ID (for identity header) */
70
- memberId?: string;
71
+ // MOCK - Future modules (marked for replacement when backend is ready)
72
+ import { HandoffModule } from "../modules/handoff.js";
73
+ import { FlowsModule } from "../modules/flows.js";
74
+ import { CapabilitiesModule } from "../modules/capabilities.js";
75
+ import { PoliciesModule } from "../modules/policies.js";
76
+ import { ApprovalsModule } from "../modules/approvals.js";
77
+ import { BudgetsModule } from "../modules/budgets.js";
78
+ import { DeploymentsModule } from "../modules/deployments.js";
79
+ import { IncidentsModule } from "../modules/incidents.js";
80
+ import { ArtifactsModule } from "../modules/artifacts.js";
71
81
 
72
- /** Custom headers */
73
- headers?: Record<string, string>;
74
-
75
- /** Kernel base URL (for meta-agent, different from control plane) */
76
- kernelBaseUrl?: string;
77
- };
82
+ // Re-export auth types
83
+ export type { AgentOsClientOptions, AgentOsClientOptionsLegacy, AuthProvider } from "./auth.js";
84
+ export { isApiTokenAuth, isJwtAuth, isNewAuthOptions } from "./auth.js";
78
85
 
79
86
  export class AgentOsClient {
80
87
  private readonly _client: RawClient;
81
88
  private readonly _baseUrl: string;
82
- private readonly _tenantId: string;
83
- private readonly _workspaceId: string;
89
+ private readonly _auth: AuthProvider | null;
90
+ private readonly _customHeaders: Record<string, string>;
91
+
92
+ // Legacy fields (for backwards compat)
93
+ private readonly _tenantId?: string;
94
+ private readonly _workspaceId?: string;
84
95
  private readonly _token?: string;
85
96
  private readonly _memberId?: string;
86
- private readonly _customHeaders: Record<string, string>;
87
- private readonly _kernelBaseUrl: string;
88
97
 
89
98
  // Core modules
90
99
  /** Agents API: CRUD, versions, graph */
@@ -148,43 +157,70 @@ export class AgentOsClient {
148
157
  /** Graphs API: Validation and introspection */
149
158
  readonly graphs: GraphsModule;
150
159
 
151
- // ======================== Convenience Aliases ========================
152
- /**
153
- * Alias for evaluation.listExperiments() - provides first-level access to experiments
154
- * @example client.experiments.list() // same as client.evaluation.listExperiments()
155
- */
160
+ // MOCK - Future modules
161
+ readonly handoff: HandoffModule;
162
+ readonly flows: FlowsModule;
163
+ readonly capabilities: CapabilitiesModule;
164
+ readonly policies: PoliciesModule;
165
+ readonly approvals: ApprovalsModule;
166
+ readonly budgets: BudgetsModule;
167
+ readonly deployments: DeploymentsModule;
168
+ readonly incidents: IncidentsModule;
169
+ readonly artifacts: ArtifactsModule;
170
+
171
+ // Convenience Aliases
156
172
  readonly experiments: {
157
173
  list: EvaluationModule["listExperiments"];
158
174
  get: EvaluationModule["getExperiment"];
159
175
  create: EvaluationModule["createExperiment"];
160
176
  };
161
177
 
162
- /**
163
- * Alias for agents.listVersions() - provides first-level access to agent versions
164
- */
165
178
  readonly agentVersions: {
166
179
  list: (agentId: string) => ReturnType<AgentsModule["listVersions"]>;
167
180
  get: (agentId: string, versionId: string) => ReturnType<AgentsModule["getVersion"]>;
168
181
  create: (agentId: string, body: Parameters<AgentsModule["createVersion"]>[1]) => ReturnType<AgentsModule["createVersion"]>;
169
182
  };
170
183
 
171
- constructor(options: AgentOsClientOptions) {
184
+ constructor(options: AgentOsClientOptions | AgentOsClientOptionsLegacy) {
172
185
  this._baseUrl = options.baseUrl;
173
- this._kernelBaseUrl = options.kernelBaseUrl ?? options.baseUrl.replace(':5000', ':8001');
174
- this._tenantId = options.tenantId;
175
- this._workspaceId = options.workspaceId;
176
- this._token = options.token;
177
- this._memberId = options.memberId;
178
186
  this._customHeaders = options.headers ?? {};
179
187
 
188
+ // Detect auth mode
189
+ if (isNewAuthOptions(options)) {
190
+ // New auth provider mode
191
+ this._auth = options.auth;
192
+ this._validateAuth(options);
193
+ } else {
194
+ // Legacy mode - convert to new auth if possible
195
+ this._tenantId = options.tenantId;
196
+ this._workspaceId = options.workspaceId;
197
+ this._token = options.token;
198
+ this._memberId = options.memberId;
199
+
200
+ // Attempt to detect auth type from token
201
+ if (options.token && isApiToken(options.token)) {
202
+ // Token looks like API token - use as api_token
203
+ this._auth = { type: "api_token", apiKey: options.token };
204
+ console.warn(
205
+ "[AgentOS SDK] Using legacy options with API token. " +
206
+ "Consider migrating to: new AgentOsClient({ auth: { type: 'api_token', apiKey: '...' } })"
207
+ );
208
+ } else {
209
+ // Legacy JWT mode - keep using headers
210
+ this._auth = null;
211
+ }
212
+ }
213
+
214
+ // Create raw HTTP client
180
215
  this._client = createRawClient({
181
216
  baseUrl: options.baseUrl,
182
- headers: this._getHeaders(),
217
+ headers: {}, // Headers resolved dynamically
183
218
  });
184
219
 
220
+ // Bound header getter
185
221
  const getHeaders = () => this._getHeaders();
186
- const getWorkspaceId = () => this._workspaceId;
187
- const getTenantId = () => this._tenantId;
222
+ const getWorkspaceId = () => this._getWorkspaceIdSync();
223
+ const getTenantId = () => this._tenantId ?? "";
188
224
 
189
225
  // Initialize core modules
190
226
  this.agents = new AgentsModule(this._client, getHeaders);
@@ -194,7 +230,7 @@ export class AgentOsClient {
194
230
  this.knowledge = new KnowledgeModule(this._client, getWorkspaceId, getHeaders);
195
231
  this.triggers = new TriggersModule(this._client, getHeaders);
196
232
  this.credentials = new CredentialsModule(this._client, getWorkspaceId, getHeaders);
197
- this.builder = new BuilderModule(this._kernelBaseUrl, getHeaders);
233
+ this.builder = new BuilderModule(this._baseUrl, getHeaders);
198
234
  this.members = new MembersModule(this._client, getHeaders);
199
235
  this.tenants = new TenantsModule(this._client, getHeaders);
200
236
  this.workspaces = new WorkspacesModule(this._client, getTenantId, getHeaders);
@@ -219,6 +255,17 @@ export class AgentOsClient {
219
255
  this.metrics = new MetricsModule(this._baseUrl, getHeaders);
220
256
  this.graphs = new GraphsModule(this._client, getHeaders);
221
257
 
258
+ // MOCK - Initialize future modules
259
+ this.handoff = new HandoffModule(this._client, getHeaders);
260
+ this.flows = new FlowsModule(this._client, getHeaders);
261
+ this.capabilities = new CapabilitiesModule(this._client, getHeaders);
262
+ this.policies = new PoliciesModule(this._client, getHeaders);
263
+ this.approvals = new ApprovalsModule(this._client, getHeaders);
264
+ this.budgets = new BudgetsModule(this._client, getHeaders);
265
+ this.deployments = new DeploymentsModule(this._client, getHeaders);
266
+ this.incidents = new IncidentsModule(this._client, getHeaders);
267
+ this.artifacts = new ArtifactsModule(this._client, getHeaders);
268
+
222
269
  // Initialize convenience aliases
223
270
  this.experiments = {
224
271
  list: this.evaluation.listExperiments.bind(this.evaluation),
@@ -233,33 +280,142 @@ export class AgentOsClient {
233
280
  };
234
281
  }
235
282
 
283
+ /**
284
+ * Validate auth configuration
285
+ */
286
+ private _validateAuth(options: AgentOsClientOptions): void {
287
+ const { auth, allowApiTokenInBrowser } = options;
288
+
289
+ // Browser guard for API tokens
290
+ if (isApiTokenAuth(auth) && isBrowser() && !allowApiTokenInBrowser) {
291
+ throw new Error(
292
+ "[AgentOS SDK] API tokens should not be used in the browser. " +
293
+ "They may be exposed in network requests. " +
294
+ "Use JWT auth for browser clients, or set allowApiTokenInBrowser: true if you understand the risks."
295
+ );
296
+ }
297
+ }
298
+
299
+ /**
300
+ * Get headers for current request (sync version for modules)
301
+ */
236
302
  private _getHeaders(): Record<string, string> {
237
303
  const headers: Record<string, string> = {
238
304
  "Content-Type": "application/json",
239
- "X-Tenant-Id": this._tenantId,
240
- "X-Workspace-Id": this._workspaceId,
241
305
  ...this._customHeaders,
242
306
  };
243
307
 
244
- if (this._token) {
245
- headers["Authorization"] = `Bearer ${this._token}`;
308
+ if (this._auth) {
309
+ // New auth mode
310
+ if (isApiTokenAuth(this._auth)) {
311
+ // API Token: Only Authorization header
312
+ const apiKey = typeof this._auth.apiKey === "function"
313
+ ? "" // Will be resolved async
314
+ : this._auth.apiKey;
315
+ if (apiKey) {
316
+ headers["Authorization"] = `Bearer ${apiKey}`;
317
+ }
318
+ // NO X-Tenant-Id, NO X-Workspace-Id (backend resolves from claims)
319
+ } else if (isJwtAuth(this._auth)) {
320
+ // JWT: Authorization + X-Workspace-Id
321
+ // Note: For sync header getter, we use empty strings as placeholders
322
+ // The actual values are resolved in _getHeadersAsync
323
+ // This is a limitation of the current module architecture
324
+ }
325
+ } else {
326
+ // Legacy mode - use stored values
327
+ if (this._token) {
328
+ headers["Authorization"] = `Bearer ${this._token}`;
329
+ }
330
+ if (this._tenantId) {
331
+ headers["X-Tenant-Id"] = this._tenantId;
332
+ }
333
+ if (this._workspaceId) {
334
+ headers["X-Workspace-Id"] = this._workspaceId;
335
+ }
336
+ if (this._memberId) {
337
+ headers["X-Member-Id"] = this._memberId;
338
+ }
246
339
  }
247
340
 
248
- if (this._memberId) {
249
- headers["X-Member-Id"] = this._memberId;
341
+ return headers;
342
+ }
343
+
344
+ /**
345
+ * Get headers for current request (async version - resolves auth functions)
346
+ */
347
+ async getHeadersAsync(): Promise<Record<string, string>> {
348
+ const headers: Record<string, string> = {
349
+ "Content-Type": "application/json",
350
+ ...this._customHeaders,
351
+ };
352
+
353
+ if (this._auth) {
354
+ if (isApiTokenAuth(this._auth)) {
355
+ // API Token
356
+ const apiKey = typeof this._auth.apiKey === "function"
357
+ ? await this._auth.apiKey()
358
+ : this._auth.apiKey;
359
+ headers["Authorization"] = `Bearer ${apiKey}`;
360
+ // NO X-Tenant-Id, NO X-Workspace-Id
361
+ } else if (isJwtAuth(this._auth)) {
362
+ // JWT
363
+ const [token, workspaceId] = await Promise.all([
364
+ this._auth.getToken(),
365
+ this._auth.getWorkspaceId(),
366
+ ]);
367
+
368
+ if (!token) {
369
+ throw new Error("[AgentOS SDK] JWT token not available. User may not be authenticated.");
370
+ }
371
+ if (!workspaceId) {
372
+ throw new Error("[AgentOS SDK] Workspace ID not available. Please select a workspace first.");
373
+ }
374
+
375
+ headers["Authorization"] = `Bearer ${token}`;
376
+ headers["X-Workspace-Id"] = workspaceId;
377
+ // NO X-Tenant-Id (backend derives from workspace membership)
378
+ }
379
+ } else {
380
+ // Legacy mode
381
+ if (this._token) {
382
+ headers["Authorization"] = `Bearer ${this._token}`;
383
+ }
384
+ if (this._tenantId) {
385
+ headers["X-Tenant-Id"] = this._tenantId;
386
+ }
387
+ if (this._workspaceId) {
388
+ headers["X-Workspace-Id"] = this._workspaceId;
389
+ }
390
+ if (this._memberId) {
391
+ headers["X-Member-Id"] = this._memberId;
392
+ }
250
393
  }
251
394
 
252
395
  return headers;
253
396
  }
254
397
 
255
- /** Current tenant ID */
398
+ /**
399
+ * Get workspace ID synchronously (for modules that need it)
400
+ */
401
+ private _getWorkspaceIdSync(): string {
402
+ return this._workspaceId ?? "";
403
+ }
404
+
405
+ /** Current tenant ID (legacy) */
256
406
  get tenantId(): string {
257
- return this._tenantId;
407
+ return this._tenantId ?? "";
258
408
  }
259
409
 
260
- /** Current workspace ID */
410
+ /** Current workspace ID (legacy) */
261
411
  get workspaceId(): string {
262
- return this._workspaceId;
412
+ return this._workspaceId ?? "";
413
+ }
414
+
415
+ /** Auth provider type */
416
+ get authType(): "api_token" | "jwt" | "legacy" {
417
+ if (!this._auth) return "legacy";
418
+ return this._auth.type;
263
419
  }
264
420
 
265
421
  /** Raw HTTP client (use modules instead) */
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Auth Provider Types for Agent OS SDK
3
+ *
4
+ * Supports two modes:
5
+ * - JWT (browser): Uses Supabase JWT + workspace header
6
+ * - API Token (server): Uses aosk_* token with embedded claims
7
+ */
8
+
9
+ // ============================================================================
10
+ // Auth Provider Types
11
+ // ============================================================================
12
+
13
+ /**
14
+ * API Token authentication for server-to-server integrations.
15
+ * Token format: aosk_live_* or aosk_test_*
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const client = new AgentOsClient({
20
+ * baseUrl: "https://api.example.com",
21
+ * auth: { type: "api_token", apiKey: "aosk_live_xxx" }
22
+ * })
23
+ * ```
24
+ */
25
+ export type ApiTokenAuth = {
26
+ type: "api_token"
27
+ /** API key (aosk_*) or function that returns one */
28
+ apiKey: string | (() => string | Promise<string>)
29
+ }
30
+
31
+ /**
32
+ * JWT authentication for browser/frontend clients.
33
+ * Uses Supabase JWT with workspace header.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * const client = new AgentOsClient({
38
+ * baseUrl: "https://api.example.com",
39
+ * auth: {
40
+ * type: "jwt",
41
+ * getToken: async () => (await supabase.auth.getSession()).data.session?.access_token,
42
+ * getWorkspaceId: () => localStorage.getItem("agentos.workspaceId")!,
43
+ * }
44
+ * })
45
+ * ```
46
+ */
47
+ export type JwtAuth = {
48
+ type: "jwt"
49
+ /** Function to get the JWT access token */
50
+ getToken: () => string | Promise<string>
51
+ /** Function to get the current workspace ID */
52
+ getWorkspaceId: () => string | Promise<string>
53
+ }
54
+
55
+ /**
56
+ * Auth provider union type
57
+ */
58
+ export type AuthProvider = ApiTokenAuth | JwtAuth
59
+
60
+ // ============================================================================
61
+ // Client Options
62
+ // ============================================================================
63
+
64
+ /**
65
+ * New auth-aware options for AgentOsClient
66
+ */
67
+ export type AgentOsClientOptions = {
68
+ /** Base URL of the Agent OS Control Plane */
69
+ baseUrl: string
70
+ /** Authentication provider */
71
+ auth: AuthProvider
72
+ /**
73
+ * Allow API token in browser environment.
74
+ * Default: false (throws error to prevent accidental exposure)
75
+ */
76
+ allowApiTokenInBrowser?: boolean
77
+ /** Custom headers to add to all requests */
78
+ headers?: Record<string, string>
79
+ }
80
+
81
+ /**
82
+ * Legacy options (backwards compatibility)
83
+ * @deprecated Use AgentOsClientOptions with auth provider instead
84
+ */
85
+ export type AgentOsClientOptionsLegacy = {
86
+ /** Base URL of the Agent OS API */
87
+ baseUrl: string
88
+ /** Tenant ID @deprecated */
89
+ tenantId: string
90
+ /** Workspace ID @deprecated */
91
+ workspaceId: string
92
+ /** Auth token @deprecated */
93
+ token?: string
94
+ /** Member ID @deprecated */
95
+ memberId?: string
96
+ /** Custom headers */
97
+ headers?: Record<string, string>
98
+ }
99
+
100
+ // ============================================================================
101
+ // Type Guards
102
+ // ============================================================================
103
+
104
+ export function isNewAuthOptions(
105
+ opts: AgentOsClientOptions | AgentOsClientOptionsLegacy
106
+ ): opts is AgentOsClientOptions {
107
+ return "auth" in opts && opts.auth !== undefined
108
+ }
109
+
110
+ export function isLegacyOptions(
111
+ opts: AgentOsClientOptions | AgentOsClientOptionsLegacy
112
+ ): opts is AgentOsClientOptionsLegacy {
113
+ return "tenantId" in opts || "workspaceId" in opts
114
+ }
115
+
116
+ export function isApiTokenAuth(auth: AuthProvider): auth is ApiTokenAuth {
117
+ return auth.type === "api_token"
118
+ }
119
+
120
+ export function isJwtAuth(auth: AuthProvider): auth is JwtAuth {
121
+ return auth.type === "jwt"
122
+ }
123
+
124
+ // ============================================================================
125
+ // Helpers
126
+ // ============================================================================
127
+
128
+ /**
129
+ * Detect browser environment
130
+ */
131
+ export function isBrowser(): boolean {
132
+ return typeof window !== "undefined" && typeof document !== "undefined"
133
+ }
134
+
135
+ /**
136
+ * Check if token looks like an API token (aosk_*)
137
+ */
138
+ export function isApiToken(token: string): boolean {
139
+ return token.startsWith("aosk_")
140
+ }
141
+
142
+ /**
143
+ * Check if token looks like a JWT (three base64 segments)
144
+ */
145
+ export function isJwtToken(token: string): boolean {
146
+ const parts = token.split(".")
147
+ return parts.length === 3 && parts.every(p => p.length > 0)
148
+ }