@introspection-sdk/introspection-node 0.23.0 → 0.31.0

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 (72) hide show
  1. package/README.md +384 -8
  2. package/dist/auth-client.d.ts +182 -0
  3. package/dist/auth-client.d.ts.map +1 -0
  4. package/dist/auth-client.js +402 -0
  5. package/dist/auth-client.js.map +1 -0
  6. package/dist/auth.d.ts +12 -2
  7. package/dist/auth.d.ts.map +1 -1
  8. package/dist/auth.js.map +1 -1
  9. package/dist/client.d.ts +41 -3
  10. package/dist/client.d.ts.map +1 -1
  11. package/dist/client.js +50 -1
  12. package/dist/client.js.map +1 -1
  13. package/dist/data-plane.d.ts +69 -0
  14. package/dist/data-plane.d.ts.map +1 -0
  15. package/dist/data-plane.js +61 -0
  16. package/dist/data-plane.js.map +1 -0
  17. package/dist/http.d.ts +18 -0
  18. package/dist/http.d.ts.map +1 -1
  19. package/dist/http.js +22 -7
  20. package/dist/http.js.map +1 -1
  21. package/dist/index.d.ts +13 -3
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +8 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/otel/index.d.ts +3 -1
  26. package/dist/otel/index.d.ts.map +1 -1
  27. package/dist/otel/index.js +2 -1
  28. package/dist/otel/index.js.map +1 -1
  29. package/dist/otel/init.d.ts +7 -2
  30. package/dist/otel/init.d.ts.map +1 -1
  31. package/dist/otel/init.js +9 -2
  32. package/dist/otel/init.js.map +1 -1
  33. package/dist/otel/logs.d.ts +23 -6
  34. package/dist/otel/logs.d.ts.map +1 -1
  35. package/dist/otel/logs.js +56 -13
  36. package/dist/otel/logs.js.map +1 -1
  37. package/dist/resources/app-connections.d.ts +35 -0
  38. package/dist/resources/app-connections.d.ts.map +1 -0
  39. package/dist/resources/app-connections.js +66 -0
  40. package/dist/resources/app-connections.js.map +1 -0
  41. package/dist/resources/automations.d.ts +5 -0
  42. package/dist/resources/automations.d.ts.map +1 -0
  43. package/dist/resources/automations.js +6 -0
  44. package/dist/resources/automations.js.map +1 -0
  45. package/dist/resources/connectors.d.ts +25 -2
  46. package/dist/resources/connectors.d.ts.map +1 -1
  47. package/dist/resources/connectors.js +37 -1
  48. package/dist/resources/connectors.js.map +1 -1
  49. package/dist/resources/members.d.ts +33 -0
  50. package/dist/resources/members.d.ts.map +1 -0
  51. package/dist/resources/members.js +59 -0
  52. package/dist/resources/members.js.map +1 -0
  53. package/dist/resources/repositories.d.ts +52 -0
  54. package/dist/resources/repositories.d.ts.map +1 -0
  55. package/dist/resources/repositories.js +112 -0
  56. package/dist/resources/repositories.js.map +1 -0
  57. package/dist/runner.d.ts +19 -5
  58. package/dist/runner.d.ts.map +1 -1
  59. package/dist/runner.js +18 -3
  60. package/dist/runner.js.map +1 -1
  61. package/dist/types.d.ts +2 -2
  62. package/dist/types.d.ts.map +1 -1
  63. package/dist/types.js +1 -1
  64. package/dist/types.js.map +1 -1
  65. package/dist/utils.d.ts +2 -2
  66. package/dist/utils.d.ts.map +1 -1
  67. package/dist/utils.js +1 -1
  68. package/dist/utils.js.map +1 -1
  69. package/dist/version.d.ts +1 -1
  70. package/dist/version.js +1 -1
  71. package/dist/version.js.map +1 -1
  72. package/package.json +21 -16
package/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # @introspection-sdk/introspection-node
2
2
 
3
3
  Node.js platform SDK for [Introspection](https://introspection.dev) — open runtimes,
4
- drive tasks, and manage experiments, recipes, files, conversations, and shares.
4
+ drive tasks, and manage experiments, recipes, repositories, members,
5
+ automations, app connections, files, conversations, and shares.
5
6
 
6
7
  ## Install
7
8
 
@@ -54,6 +55,283 @@ teardown.
54
55
  Interrupted runs resume through
55
56
  `runner.tasks.runs.resume(taskId, { resume: entries })`.
56
57
 
58
+ `identity.metadata` sets `key: value` labels on the `customer` member the
59
+ identity names. It grants nothing, so unlike `identity.tags` (which seed a new
60
+ member only) it merges into an existing member too: asserted keys overwrite
61
+ keys of the same name and the rest are kept. Absent or `{}` changes nothing.
62
+
63
+ ```typescript
64
+ const runner = await client.runtimes("customer-agent").run({
65
+ identity: { user_id: "u_42", metadata: { plan: "enterprise" } },
66
+ });
67
+ ```
68
+
69
+ ### Data Plane resources: client and runner
70
+
71
+ `IntrospectionClient` and `Runner` expose the same Data Plane namespaces, and
72
+ both implement the exported `DataPlaneResources` interface: `tasks` (with
73
+ `tasks.runs`), `files`, `conversations`, `events`, `metrics`, `shares`,
74
+ `automations` and `connections`. So does the `DataPlaneClient` that
75
+ `AuthClient.dataPlane()` returns. Code written against `DataPlaneResources`
76
+ runs against any of them.
77
+
78
+ ```typescript
79
+ import type { DataPlaneResources } from "@introspection-sdk/introspection-node";
80
+
81
+ async function connectedApps(dp: DataPlaneResources) {
82
+ return (await dp.connections.list()).records.map((c) => c.app);
83
+ }
84
+
85
+ await connectedApps(client); // the client's own token, on its Data Plane
86
+ await connectedApps(runner); // the runner's token, on the runner's Data Plane
87
+ ```
88
+
89
+ The difference is the credential. A runner sends its session token, scoped to
90
+ the runtime and identity it was opened for; the client sends its own token,
91
+ so the server decides per call what that credential may do. A runner a member
92
+ opens for themself carries `automations:read` and `automations:write`, so
93
+ `runner.automations` manages that member's automations (a platform change in
94
+ progress; until it ships those routes refuse a runner with a 403).
95
+
96
+ ### Members
97
+
98
+ `client.members` lists, reads, creates and updates members on the Control
99
+ Plane's `/v1/members`; removing one is an org-admin action left to the CLI. A
100
+ member's `tags` are access-bearing (it receives every share whose `granted_tag`
101
+ it holds) and writable only with `members:manage`. A member can also still read
102
+ and write any file or task whose tags intersect its own, but that implicit tag
103
+ access is deprecated: share with a cohort through a tag share instead. Its `metadata` is a
104
+ map of string labels that grants nothing; keys are `[A-Za-z0-9_-]+`, values are
105
+ non-empty strings, at most 64 entries.
106
+
107
+ ```typescript
108
+ const ada = await client.members.create({
109
+ email: "ada@example.com",
110
+ name: "Ada Lovelace",
111
+ metadata: { plan: "enterprise" },
112
+ });
113
+
114
+ // `tag` and `metadata` narrow the list; metadata pairs are ANDed (at most 16).
115
+ for await (const member of client.members.list({
116
+ member_type: "customer",
117
+ metadata: { plan: "enterprise", region: "eu" },
118
+ })) {
119
+ console.log(member.id, member.metadata);
120
+ }
121
+
122
+ // PATCH replaces `tags` and `metadata` wholesale; `{}` clears metadata.
123
+ await client.members.update(ada.id, { metadata: { plan: "team" } });
124
+ ```
125
+
126
+ Seeding `tags` on create needs `members:manage` as well as `members:write`.
127
+
128
+ ### Automations
129
+
130
+ `client.automations` (and `dataPlaneClient.automations`) lists, reads,
131
+ creates, updates and deletes the project's automations on the Data Plane's
132
+ `/v1/automations`, and `trigger(id)` runs one now. Reading needs
133
+ `automations:read` and writing `automations:write`; each record's `can_manage`
134
+ says whether the caller may change it. An automation is a scheduled prompt (`kind: null`) or platform work
135
+ (`observation_synthesis`, `observation_clustering`, `project_check_in`). A
136
+ `cron` automation fires on `cron_schedule`, or on `metadata.cron_schedules` in
137
+ `metadata.timezone`; a one-off reminder is a `manual` automation with a future
138
+ `next_trigger_at`. Set `task_id` to post each firing into an existing task
139
+ instead of creating one.
140
+
141
+ The server serves these routes to administrators only today (a 403 otherwise).
142
+ introspection-cloud#3137, not yet released, opens them to members for their own
143
+ automations that post into one of their tasks; until it ships, the `task_id`
144
+ list filter is ignored by the server.
145
+
146
+ ```typescript
147
+ const reminder = await client.automations.create({
148
+ name: "Friday check-in",
149
+ trigger_type: "manual",
150
+ prompt: "How did the week go?",
151
+ runtime_group_id: runtimeGroupId,
152
+ task_id: taskId,
153
+ next_trigger_at: "2026-10-09T09:00:00Z",
154
+ });
155
+
156
+ // Recurring: one or more cron schedules in a timezone.
157
+ await client.automations.create({
158
+ name: "Weekday digest",
159
+ trigger_type: "cron",
160
+ cron_schedule: "0 9 * * 1-5",
161
+ metadata: {
162
+ cron_schedules: ["0 9 * * 1-5"],
163
+ timezone: "America/Los_Angeles",
164
+ },
165
+ prompt: "Summarize yesterday's conversations.",
166
+ runtime_group_id: runtimeGroupId,
167
+ });
168
+
169
+ // PATCH sends only the fields you set; `metadata` replaces wholesale.
170
+ await client.automations.update(reminder.id, { enabled: false });
171
+
172
+ for await (const automation of client.automations.list({ task_id: taskId })) {
173
+ console.log(automation.id, automation.next_trigger_at, automation.can_manage);
174
+ }
175
+
176
+ const { status, task_id } = await client.automations.trigger(reminder.id);
177
+ ```
178
+
179
+ A firing clears a manual automation's slot; re-arm it with
180
+ `update(id, { next_trigger_at })`. A `cron` automation derives its own slot and
181
+ rejects one set by the client.
182
+
183
+ Each task-run trigger is recorded as an `introspection.automation.triggered`
184
+ event, and a scheduled slot that ran nothing as
185
+ `introspection.automation.skipped` (with a `reason` such as
186
+ `conditions_not_met` or `target_busy`; readable only with project-wide
187
+ telemetry access). Read them with `client.events.list`, filtered by
188
+ `automation_id` or `task_id`:
189
+
190
+ ```typescript
191
+ const page = await client.events.list({
192
+ event_name: "introspection.automation.triggered",
193
+ automation_id: reminder.id,
194
+ });
195
+ for (const ev of page.records)
196
+ console.log(ev.payload.task_id, ev.payload.posted);
197
+ ```
198
+
199
+ ### Connections
200
+
201
+ `connections` lists, reads, creates and deletes the apps members connected for
202
+ themselves, on the Data Plane's `/v1/connections`. A caller who is not an
203
+ administrator always sees and manages only their own. Reads need
204
+ `connections:read`, `create` needs `connections:write` and `delete` needs
205
+ `connections:delete`. These are a different resource from the Control Plane's
206
+ `client.connectors.connections`, which are nested under a connector.
207
+
208
+ `create` returns a single-use connect page; send the member to its
209
+ `authorize_url`, and the connection exists once they finish there. On a runner,
210
+ `runtime` defaults to the runner's runtime group. On the client, pass it.
211
+
212
+ ```typescript
213
+ const { authorize_url } = await runner.connections.create({ app: "gmail" });
214
+
215
+ for await (const connection of runner.connections.list({ app: "gmail" })) {
216
+ console.log(connection.id, connection.account_name, connection.healthy);
217
+ }
218
+
219
+ // Administrators can list another member's connections.
220
+ await client.connections.list({ member_id: memberId });
221
+
222
+ await runner.connections.delete(connectionId);
223
+ ```
224
+
225
+ ### Native email-code sign-in
226
+
227
+ `AuthClient` signs in the end users of a `native` Application with emailed
228
+ one-time codes, shaped like the Supabase Auth client. The session belongs to a
229
+ `customer` member of the Application's organization, is scoped to one project,
230
+ and carries the Application's `allowed_scopes`.
231
+
232
+ ```typescript
233
+ import { AuthClient } from "@introspection-sdk/introspection-node";
234
+
235
+ const auth = new AuthClient({
236
+ clientId: "intro_app_…", // a `native` Application
237
+ project: "my-project",
238
+ storage: sessionStore, // getItem / setItem / removeItem, sync or async
239
+ });
240
+
241
+ await auth.signInWithOtp({ email });
242
+ await auth.verifyOtp({ email, token: code });
243
+
244
+ const dp = await auth.dataPlane();
245
+ const run = await dp.tasks.start({ prompt: "Hello", runtime_id: runtimeId });
246
+ console.log(await run.text());
247
+ ```
248
+
249
+ - **Codes are opaque.** A returning user's code is 6 digits; a new user's first
250
+ code is 6 characters of A–Z and 0–9. Do not restrict the input to digits.
251
+ A wrong, expired or reused code throws `ValidationError` with
252
+ `code === "invalid_grant"`; requesting codes too often throws
253
+ `RateLimitError` with `retryAfter`.
254
+ - **Refresh.** `getSession()` renews the session within `leewaySeconds`
255
+ (default 60) of expiry, and every request made through `auth.dataPlane()`
256
+ renews it once after a `401`; concurrent renewals share one refresh. A
257
+ refresh the server rejects signs the user out and throws
258
+ `AuthenticationError`; a network failure keeps the session.
259
+ - **Superseded responses.** A sign-in that resolves after a sign-out or a newer
260
+ sign-in rejects with an `AbortError` instead of overwriting the newer state.
261
+ - **Events.** `onAuthStateChange((event, session) => …)` reports
262
+ `INITIAL_SESSION`, `SIGNED_IN`, `TOKEN_REFRESHED` and `SIGNED_OUT`.
263
+ - **Data Plane only.** The token is a Data Plane credential, so the Control
264
+ Plane routes on `IntrospectionClient` (runtimes, connectors, members) reject
265
+ it. Pass the runtime as `runtime_id` when creating a task. `auth.dataPlane()`
266
+ returns a `DataPlaneClient`, which implements `DataPlaneResources`: `tasks`,
267
+ `files`, `conversations`, `shares`, `events`, `metrics`, `automations` and
268
+ `connections` (where `create` takes the `runtime`).
269
+ - **Sign-out** clears the local session, then revokes it with
270
+ `POST /v1/oauth/revoke`; the local session is gone even if revocation fails.
271
+
272
+ See [`examples/api/native-email-code.ts`](../../examples/api/native-email-code.ts).
273
+ Federated sign-in through your own IdP (a `jwks` Application) keeps its session
274
+ in that IdP's SDK; exchange its token with `tokenExchange()` instead.
275
+
276
+ ### Repositories
277
+
278
+ `client.repositories` looks up a project's linked repositories on the Control
279
+ Plane and reads their contents through the Data Plane (`repositories:read`).
280
+
281
+ ```typescript
282
+ const [repo] = await client.repositories.list({
283
+ project: "demo",
284
+ slug: "acme/agent",
285
+ });
286
+
287
+ // Every entry of a directory; pages are followed automatically.
288
+ for await (const entry of client.repositories.contents(repo.id, {
289
+ path: "src",
290
+ ref: "main",
291
+ })) {
292
+ console.log(entry.type, entry.path);
293
+ }
294
+
295
+ // One path: a directory page or a file, discriminated on `type`.
296
+ const readme = await client.repositories.contents.get(repo.id, "README.md");
297
+ if (readme.type === "file") console.log(readme.encoding, readme.content);
298
+ ```
299
+
300
+ Enumerating a path that is a file throws a `ValidationError`; read it with
301
+ `contents.get()` instead.
302
+
303
+ History reads the same way: `commits()` pages back from a branch, tag or sha
304
+ (the default branch when omitted), and `commit()` returns one commit with its
305
+ changed files and unified diff.
306
+
307
+ ```typescript
308
+ for await (const commit of client.repositories.commits(repo.id, {
309
+ sha: "main",
310
+ path: "src",
311
+ })) {
312
+ console.log(commit.sha, commit.message);
313
+ }
314
+
315
+ const detail = await client.repositories.commit(repo.id, "abc123");
316
+ console.log(
317
+ detail.files.map((file) => file.filename),
318
+ detail.patch,
319
+ );
320
+ ```
321
+
322
+ `merge()` merges a branch or commit sha into a branch, like GitHub's merges API
323
+ (`repositories:write`). It resolves to the merge commit, or `null` when `base`
324
+ already contains `head`; a conflict throws a `ConflictError` and changes
325
+ nothing.
326
+
327
+ ```typescript
328
+ const merge = await client.repositories.merge(repo.id, {
329
+ base: "main",
330
+ head: "feature",
331
+ });
332
+ console.log(merge?.sha ?? "already up to date");
333
+ ```
334
+
57
335
  ### Annotations
58
336
 
59
337
  Annotations capture what a domain expert found good or bad on an OTel span. They
@@ -194,13 +472,14 @@ await logs.shutdown();
194
472
 
195
473
  ### Methods
196
474
 
197
- | Method | Description |
198
- | --------------------------- | ------------------------------ |
199
- | `track(event, properties?)` | Track any user action |
200
- | `feedback(type, options?)` | Track feedback on AI responses |
201
- | `identify(userId, traits?)` | Associate a user with traits |
202
- | `flush()` | Flush pending events |
203
- | `shutdown()` | Shutdown and flush |
475
+ | Method | Description |
476
+ | --------------------------------------- | -------------------------------------------------- |
477
+ | `logEvent(name, attributes?, options?)` | Log an app event under any custom name (see below) |
478
+ | `track(event, properties?)` | Track any user action (an alias of `logEvent`) |
479
+ | `feedback(type, options?)` | Track feedback on AI responses |
480
+ | `identify(userId, traits?)` | Associate a user with traits |
481
+ | `flush()` | Flush pending events |
482
+ | `shutdown()` | Shutdown and flush |
204
483
 
205
484
  ### Context helpers (OTel baggage)
206
485
 
@@ -212,6 +491,103 @@ await logs.shutdown();
212
491
  | `withAnonymousId(id, callback)` | Set anonymous ID |
213
492
  | `withBaggage(values, callback)` | Set arbitrary baggage values |
214
493
 
494
+ ## Logging custom events
495
+
496
+ `logEvent(name, attributes?, options?)` records an app event under a name you
497
+ choose — `ark.feed.entry`, `checkout.completed` — so the platform stores it and
498
+ you can query it back by name. `track()` is a thin alias of it. Attributes are
499
+ stored under `properties.*`.
500
+
501
+ ```typescript
502
+ import { IntrospectionLogs } from "@introspection-sdk/introspection-node/otel";
503
+
504
+ const logs = new IntrospectionLogs({ serviceName: "ark" }); // INTROSPECTION_TOKEN
505
+
506
+ logs.logEvent(
507
+ "ark.feed.entry",
508
+ { entry_id: entry.id, source: "rss", score: 0.92 },
509
+ {
510
+ eventId: `feed-entry:${entry.id}`, // stable id: consumers dedupe on it
511
+ timestamp: entry.publishedAt, // Date or epoch ms; default now
512
+ identity: { userId: entry.ownerId }, // overrides the scoped identity
513
+ severity: "INFO", // DEBUG | INFO | WARN | ERROR
514
+ },
515
+ );
516
+
517
+ await logs.shutdown(); // flushes
518
+ ```
519
+
520
+ After `init()`, the same call is available as a module-level function:
521
+
522
+ ```typescript
523
+ import * as introspection from "@introspection-sdk/introspection-node/otel";
524
+
525
+ await introspection.init({ serviceName: "ark" });
526
+ introspection.logEvent("ark.feed.entry", { entry_id: "e_1" });
527
+ ```
528
+
529
+ - **Names.** Use your own namespace. Names starting with `introspection.`
530
+ (platform events) or `gen_ai.` (OpenTelemetry GenAI conventions) are
531
+ reserved and `logEvent` throws on them. `track()` delegates to `logEvent`,
532
+ so it rejects them too.
533
+ - **Idempotency.** Without `eventId` each call gets a fresh id. When delivery
534
+ can repeat (retries, replayed jobs, an agent re-running a step), pass an id
535
+ derived from the thing being recorded: re-sending the same `eventId` is how
536
+ consumers recognise and drop the duplicate.
537
+ - **Identity and context.** User / anonymous id and `gen_ai.conversation.id` /
538
+ agent come from the active baggage (`withUserId`, `withConversation`,
539
+ `withAgent`); `options.identity` overrides per field.
540
+
541
+ ### From a recipe or agent sandbox
542
+
543
+ A Runtime sandbox is started with `INTROSPECTION_TOKEN` and
544
+ `INTROSPECTION_BASE_OTEL_URL` in its environment, so a recipe tool or
545
+ agent step needs no configuration — construct the logger and flush before the
546
+ step returns:
547
+
548
+ ```typescript
549
+ import { IntrospectionLogs } from "@introspection-sdk/introspection-node/otel";
550
+
551
+ const events = new IntrospectionLogs({ serviceName: "ark-recipe" });
552
+
553
+ export async function recordFeedEntries(entries: FeedEntry[]) {
554
+ for (const entry of entries) {
555
+ events.logEvent(
556
+ "ark.feed.entry",
557
+ { entry_id: entry.id, title: entry.title, url: entry.url },
558
+ { eventId: `ark.feed.entry:${entry.id}` },
559
+ );
560
+ }
561
+ await events.flush(); // the sandbox may be torn down after the step
562
+ }
563
+ ```
564
+
565
+ ### Reading custom events back
566
+
567
+ Custom events are served by `/v1/events` under the `introspection.track`
568
+ family, whose `payload` carries the original `name` and the `properties`:
569
+
570
+ ```typescript
571
+ import { IntrospectionClient } from "@introspection-sdk/introspection-node";
572
+
573
+ const client = new IntrospectionClient();
574
+
575
+ type TrackPayload = { name: string; properties?: Record<string, unknown> };
576
+
577
+ for await (const ev of client.events.list({
578
+ event_name: "introspection.track",
579
+ lookback: "24h",
580
+ })) {
581
+ const payload = ev.payload as TrackPayload;
582
+ if (payload.name !== "ark.feed.entry") continue;
583
+ console.log(ev.timestamp, payload.properties?.entry_id);
584
+ }
585
+ ```
586
+
587
+ Filtering by `name` server-side is not available yet — it arrives with a
588
+ pending platform change. Until then, filter on `payload.name` client-side as
589
+ above.
590
+
215
591
  ## OpenTelemetry span processor
216
592
 
217
593
  Attach the processor to a provider you already own, or stand one up with
@@ -0,0 +1,182 @@
1
+ import type { OAuthToken } from "./auth.js";
2
+ import { DataPlaneClient } from "./data-plane.js";
3
+ import type { BearerCredentials } from "./http.js";
4
+ /** The `grant_type` of the native email-code grant. */
5
+ export declare const EMAIL_CODE_GRANT_TYPE = "urn:introspection:params:oauth:grant-type:email_code";
6
+ /**
7
+ * Where a session is kept between launches. The Web Storage shape (sync or
8
+ * async), so a file- or keychain-backed store or any `localStorage`-like
9
+ * object plugs in directly. Values are JSON strings.
10
+ */
11
+ export interface SessionStorage {
12
+ getItem(key: string): string | null | Promise<string | null>;
13
+ setItem(key: string, value: string): void | Promise<void>;
14
+ removeItem(key: string): void | Promise<void>;
15
+ }
16
+ /** Keeps the session in memory only; nothing survives a restart. */
17
+ export declare class InMemorySessionStorage implements SessionStorage {
18
+ private readonly values;
19
+ getItem(key: string): string | null;
20
+ setItem(key: string, value: string): void;
21
+ removeItem(key: string): void;
22
+ }
23
+ /**
24
+ * A signed-in member's session: the token response plus when it expires.
25
+ * Field names stay on the wire (snake_case), as the token response sends them.
26
+ */
27
+ export interface AuthSession {
28
+ access_token: string;
29
+ token_type: string;
30
+ /** Rotated on every refresh. */
31
+ refresh_token: string | null;
32
+ /** Epoch milliseconds the access token expires at, when known. */
33
+ expires_at: number | null;
34
+ /** The granted scope: the Application's `allowed_scopes` ceiling. */
35
+ scope: string | null;
36
+ /** The platform session; refresh and sign-out are keyed on it. */
37
+ session_id: string | null;
38
+ org_id: string | null;
39
+ project_id: string | null;
40
+ /** The `customer` member the session belongs to. */
41
+ member_id: string | null;
42
+ /** The Data Plane URL for the session's project. */
43
+ dp_url: string | null;
44
+ }
45
+ /** What changed, mirroring Supabase's `AuthChangeEvent`. */
46
+ export type AuthChangeEvent = "INITIAL_SESSION" | "SIGNED_IN" | "SIGNED_OUT" | "TOKEN_REFRESHED";
47
+ export type AuthStateListener = (event: AuthChangeEvent, session: AuthSession | null) => void;
48
+ export interface AuthClientOptions {
49
+ /** The `native` Application's `client_id`. */
50
+ clientId: string;
51
+ /** The project the session is scoped to (slug or id). */
52
+ project: string;
53
+ /**
54
+ * CP API base URL. Defaults to `INTROSPECTION_BASE_API_URL` where a
55
+ * `process` global exists, then `https://api.introspection.dev`.
56
+ */
57
+ baseApiUrl?: string;
58
+ /** Session persistence. Defaults to {@link InMemorySessionStorage}. */
59
+ storage?: SessionStorage;
60
+ /** Storage key; change it to keep separate sessions per environment. */
61
+ storageKey?: string;
62
+ /** Refresh this many seconds before expiry. Default `60`. */
63
+ leewaySeconds?: number;
64
+ /** Custom `fetch` (for tests or non-standard runtimes). */
65
+ fetch?: typeof fetch;
66
+ /** Clock in epoch milliseconds (for tests). */
67
+ now?: () => number;
68
+ }
69
+ /**
70
+ * Email-code sign-in, session persistence and refresh for a `native`
71
+ * Application.
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * const auth = new AuthClient({
76
+ * clientId: "intro_app_…",
77
+ * project: "my-project",
78
+ * storage: sessionStore, // getItem / setItem / removeItem
79
+ * });
80
+ *
81
+ * await auth.signInWithOtp({ email });
82
+ * // A returning user's code is 6 digits; a new user's first code is 6
83
+ * // characters of A–Z and 0–9, so do not restrict the input to digits.
84
+ * await auth.verifyOtp({ email, token: code });
85
+ *
86
+ * const dp = await auth.dataPlane();
87
+ * const run = await dp.tasks.start({ prompt: "Hello", runtime_id });
88
+ * ```
89
+ */
90
+ export declare class AuthClient {
91
+ private readonly options;
92
+ private readonly baseApiUrl;
93
+ private readonly storage;
94
+ private readonly storageKey;
95
+ private readonly leewayMs;
96
+ private readonly fetchImpl;
97
+ private readonly now;
98
+ private current;
99
+ /** Bumped by every sign-in and sign-out; a response read under an older value is stale. */
100
+ private generation;
101
+ private restored;
102
+ private restoring?;
103
+ private refreshing?;
104
+ private storageWrite;
105
+ private readonly listeners;
106
+ constructor(options: AuthClientOptions);
107
+ /**
108
+ * Email a one-time code to `email`. The answer is the same whether or not
109
+ * the email has an account; a new address gets an account on verify.
110
+ * Throws `RateLimitError` (with `retryAfter`) when codes are requested too
111
+ * often.
112
+ */
113
+ signInWithOtp(params: {
114
+ email: string;
115
+ }): Promise<void>;
116
+ /**
117
+ * Verify the code sent to `email` and sign in. The code is opaque: 6 digits
118
+ * for a returning user, 6 characters of A–Z and 0–9 for a new user's first
119
+ * sign-in. A wrong, expired or reused code throws a `ValidationError` with
120
+ * `code === "invalid_grant"`.
121
+ *
122
+ * Rejects with an `AbortError` when a sign-out or another sign-in completes
123
+ * while the code is being verified, so a slow response never overwrites a
124
+ * newer session.
125
+ */
126
+ verifyOtp(params: {
127
+ email: string;
128
+ token: string;
129
+ }): Promise<AuthSession>;
130
+ /** Adopt a platform token obtained elsewhere, for example from your own backend. */
131
+ setSession(token: OAuthToken): Promise<AuthSession>;
132
+ /**
133
+ * The current session, restored from storage on first use and refreshed
134
+ * when it is within `leewaySeconds` of expiry. `null` when signed out.
135
+ */
136
+ getSession(): Promise<AuthSession | null>;
137
+ /**
138
+ * Renew the session now, or join the renewal already in flight. A refresh
139
+ * the server rejects signs the user out and throws `AuthenticationError`;
140
+ * a network failure keeps the session and rethrows.
141
+ */
142
+ refreshSession(): Promise<AuthSession>;
143
+ /**
144
+ * Sign out: forget the session locally, then revoke it on the platform.
145
+ * The local session is cleared even when revocation fails; the revocation
146
+ * error is still thrown.
147
+ */
148
+ signOut(): Promise<void>;
149
+ /**
150
+ * Listen for session changes. A new listener first receives
151
+ * `INITIAL_SESSION` with the session restored from storage (or `null`).
152
+ */
153
+ onAuthStateChange(listener: AuthStateListener): {
154
+ unsubscribe(): void;
155
+ };
156
+ /**
157
+ * A bearer credential backed by this client: refreshed before expiry, and
158
+ * once more after a `401` unless another request already renewed it.
159
+ */
160
+ get credentials(): BearerCredentials;
161
+ /**
162
+ * A Data Plane client authenticated as the signed-in member. The Data Plane
163
+ * URL defaults to the one returned with the session.
164
+ */
165
+ dataPlane(options?: {
166
+ dpUrl?: string;
167
+ additionalHeaders?: Record<string, string>;
168
+ }): Promise<DataPlaneClient>;
169
+ private postToken;
170
+ /** The refresh response omits `dp_url`, so it carries over from `previous`. */
171
+ private toSession;
172
+ private renew;
173
+ /** `expected` is the generation read before a sign-in request was sent. */
174
+ private signedIn;
175
+ /** Storage may be async, so writes are chained to land in transition order. */
176
+ private store;
177
+ private clear;
178
+ /** Concurrent first callers share one load, so none sees the session as absent while it is read. */
179
+ private restoreIfNeeded;
180
+ private emit;
181
+ }
182
+ //# sourceMappingURL=auth-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-client.d.ts","sourceRoot":"","sources":["../src/auth-client.ts"],"names":[],"mappings":"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"}