@possibl/rcrt-sdk 0.1.2 → 0.5.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 (108) hide show
  1. package/CHANGELOG.md +99 -62
  2. package/LICENSE +21 -0
  3. package/README.md +35 -21
  4. package/dist/auth.d.ts +45 -0
  5. package/dist/auth.d.ts.map +1 -0
  6. package/{src/auth.ts → dist/auth.js} +9 -24
  7. package/dist/auth.js.map +1 -0
  8. package/dist/authn.d.ts +114 -0
  9. package/dist/authn.d.ts.map +1 -0
  10. package/dist/authn.js +107 -0
  11. package/dist/authn.js.map +1 -0
  12. package/dist/breadcrumbs.d.ts +43 -0
  13. package/dist/breadcrumbs.d.ts.map +1 -0
  14. package/dist/breadcrumbs.js +122 -0
  15. package/dist/breadcrumbs.js.map +1 -0
  16. package/dist/cards.d.ts +28 -0
  17. package/dist/cards.d.ts.map +1 -0
  18. package/dist/cards.js +105 -0
  19. package/dist/cards.js.map +1 -0
  20. package/dist/chat.d.ts +103 -0
  21. package/dist/chat.d.ts.map +1 -0
  22. package/dist/chat.js +105 -0
  23. package/dist/chat.js.map +1 -0
  24. package/dist/client.d.ts +85 -0
  25. package/dist/client.d.ts.map +1 -0
  26. package/dist/client.js +133 -0
  27. package/dist/client.js.map +1 -0
  28. package/dist/errors.d.ts +32 -0
  29. package/dist/errors.d.ts.map +1 -0
  30. package/dist/errors.js +76 -0
  31. package/dist/errors.js.map +1 -0
  32. package/dist/files.d.ts +41 -0
  33. package/dist/files.d.ts.map +1 -0
  34. package/dist/files.js +64 -0
  35. package/dist/files.js.map +1 -0
  36. package/dist/generated/conformance.d.ts +48 -0
  37. package/dist/generated/conformance.d.ts.map +1 -0
  38. package/dist/generated/conformance.js +24 -0
  39. package/dist/generated/conformance.js.map +1 -0
  40. package/dist/generated/index.d.ts +34 -0
  41. package/dist/generated/index.d.ts.map +1 -0
  42. package/dist/generated/index.js +34 -0
  43. package/dist/generated/index.js.map +1 -0
  44. package/dist/generated/openapi.d.ts +3900 -0
  45. package/dist/generated/openapi.d.ts.map +1 -0
  46. package/dist/generated/openapi.js +6 -0
  47. package/dist/generated/openapi.js.map +1 -0
  48. package/dist/grants.d.ts +41 -0
  49. package/dist/grants.d.ts.map +1 -0
  50. package/dist/grants.js +50 -0
  51. package/dist/grants.js.map +1 -0
  52. package/dist/index.d.ts +34 -0
  53. package/dist/index.d.ts.map +1 -0
  54. package/dist/index.js +24 -0
  55. package/dist/index.js.map +1 -0
  56. package/dist/internal/fetch.d.ts +47 -0
  57. package/dist/internal/fetch.d.ts.map +1 -0
  58. package/dist/internal/fetch.js +108 -0
  59. package/dist/internal/fetch.js.map +1 -0
  60. package/dist/internal/sse.d.ts +82 -0
  61. package/dist/internal/sse.d.ts.map +1 -0
  62. package/dist/internal/sse.js +161 -0
  63. package/dist/internal/sse.js.map +1 -0
  64. package/dist/marketplace.d.ts +98 -0
  65. package/dist/marketplace.d.ts.map +1 -0
  66. package/dist/marketplace.js +74 -0
  67. package/dist/marketplace.js.map +1 -0
  68. package/dist/members.d.ts +60 -0
  69. package/dist/members.d.ts.map +1 -0
  70. package/dist/members.js +74 -0
  71. package/dist/members.js.map +1 -0
  72. package/dist/org.d.ts +85 -0
  73. package/dist/org.d.ts.map +1 -0
  74. package/dist/org.js +70 -0
  75. package/dist/org.js.map +1 -0
  76. package/dist/types/breadcrumb.d.ts +70 -0
  77. package/dist/types/breadcrumb.d.ts.map +1 -0
  78. package/dist/types/breadcrumb.js +8 -0
  79. package/dist/types/breadcrumb.js.map +1 -0
  80. package/dist/types/card.d.ts +251 -0
  81. package/dist/types/card.d.ts.map +1 -0
  82. package/dist/types/card.js +10 -0
  83. package/dist/types/card.js.map +1 -0
  84. package/dist/types/engine.d.ts +69 -0
  85. package/dist/types/engine.d.ts.map +1 -0
  86. package/dist/types/engine.js +53 -0
  87. package/dist/types/engine.js.map +1 -0
  88. package/dist/types/index.d.ts +4 -0
  89. package/dist/types/index.d.ts.map +1 -0
  90. package/dist/types/index.js +4 -0
  91. package/dist/types/index.js.map +1 -0
  92. package/package.json +35 -6
  93. package/src/authn.ts +0 -159
  94. package/src/breadcrumbs.ts +0 -111
  95. package/src/capabilities.ts +0 -93
  96. package/src/cards.ts +0 -109
  97. package/src/chat.ts +0 -83
  98. package/src/client.ts +0 -97
  99. package/src/errors.ts +0 -101
  100. package/src/files.ts +0 -135
  101. package/src/grants.ts +0 -99
  102. package/src/index.ts +0 -103
  103. package/src/internal/fetch.ts +0 -133
  104. package/src/internal/sse.ts +0 -236
  105. package/src/sessions.ts +0 -110
  106. package/src/types/breadcrumb.ts +0 -77
  107. package/src/types/card.ts +0 -298
  108. package/src/types/index.ts +0 -2
package/CHANGELOG.md CHANGED
@@ -1,82 +1,119 @@
1
- # Changelog
1
+ # @possibl/rcrt-sdk — Changelog
2
2
 
3
- All notable changes to `@possibl/rcrt-sdk` are documented here. The
4
- format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
5
- this package follows [SemVer](https://semver.org/spec/v2.0.0.html) once
6
- published.
3
+ All notable changes are documented here. Follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) + [semver](https://semver.org/).
7
4
 
8
- ## [0.1.2] — 2026-04-30
5
+ ## 0.5.0 — 2026-06-22
9
6
 
10
- Docs-only patch. Removes a customer reference from the README that
11
- shouldn't have been published — content of the package itself is
12
- unchanged from 0.1.1.
7
+ Workspace membership + invitation management, plus self-serve signup and open-enrollment join. Additive only no breaking changes.
13
8
 
14
- ## [0.1.1] — 2026-04-30
9
+ ### Added
15
10
 
16
- Patch release. The SDK ships TypeScript source directly (`main:
17
- ./src/index.ts`), which means consumer tsconfigs compile it. Stricter
18
- consumer settings (e.g. `noUnusedLocals: true` in
19
- `possibl-ai/ritual-app`) caught a stray unused import in `cards.ts`
20
- that the SDK's own tsconfig let pass.
11
+ - **`client.members`** `MembersModule`: workspace member + invitation
12
+ lifecycle against `/v1/tenants/{id}/members` and `/v1/tenants/{id}/invitations`.
13
+ Methods: `list`, `add`, `updateRole`, `remove`, `listInvitations`, `invite`,
14
+ `cancelInvitation`. Creating an invitation also sends the invite email
15
+ server-side (no separate resend endpoint re-create to resend).
16
+ - **`TenantMember`**, **`TenantInvitation`** types, exported from the package root.
17
+ - **`auth.signup()`** — self-serve first-workspace provisioning
18
+ (`POST /v1/auth/signup`). Idempotent: returns the existing personal workspace
19
+ (200) when one already exists, 201 on creation. Call when `listTenants()` is
20
+ empty after first sign-in.
21
+ - **`auth.joinTenant(tenantId)`** — open-enrollment self-join
22
+ (`POST /v1/tenants/{id}/join`). Any authenticated user may join a tenant that
23
+ has opted into `tenants.auto_enroll_role`; idempotent. No `X-Tenant-ID` header.
24
+ - **`TenantMembership`** type, exported from the package root.
21
25
 
22
- ### Fixed
26
+ ## 0.4.0 — 2026-06-18
23
27
 
24
- - Removed unused `ApiError` import in `src/cards.ts`. Consumers with
25
- `noUnusedLocals: true` no longer fail the type-check on the SDK's
26
- source.
28
+ Chat file attachments (route-agnostic multimodal).
27
29
 
28
- ### Internal
30
+ ### Added
29
31
 
30
- - Added `noUnusedLocals: true` + `noUnusedParameters: true` to the
31
- SDK's own `tsconfig.json` so future bumps can't ship the same class
32
- of issue. CI catches it before publish.
32
+ - **`SendChatRequest.attachments`** optional `ChatAttachmentRef[]`
33
+ (`{ file_breadcrumb_id, mime_type? }`). Upload via `client.files.upload(...)`
34
+ first, then pass the returned `FileRecord.id` as `file_breadcrumb_id`. The
35
+ server resolves the `gs://` URI + mime type and delivers the file to a
36
+ file-capable agent. Omitting it leaves the text-only body unchanged.
37
+ - **`ChatAttachmentRef`** type, exported from the package root.
33
38
 
34
- ## [0.1.0] — 2026-04-30
39
+ ## 0.3.0 — 2026-06-17
35
40
 
36
- First public release. Drops the `-alpha.1` suffix; signals the SDK is
37
- ready for frontend consumption (canonical RCRT mobile + web template
38
- imports it directly).
41
+ The unified LLM model addressing release. LLM selection is now a single
42
+ canonical `route/provider/model` address stored in the agent breadcrumb's
43
+ `engine_config.model` (see `docs/design/LLM_PROVIDER_MODEL_RESOLUTION.md`).
44
+ There is no runtime tier indirection, no org override layer, and no fallbacks.
39
45
 
40
- ### Added — modules
46
+ ### Removed (breaking)
41
47
 
42
- - **`FilesModule`** (`client.files`) multipart upload, list, get,
43
- signed-URL download, server-side text extraction, soft delete.
44
- Defaults to `tenant` scope; supports `org` / `user` for cross-
45
- workspace assets.
46
- - **`SessionsModule`** (`client.sessions`) `getConstellation()`
47
- graph view of related sessions, `listParticipants` /
48
- `addParticipant` / `removeParticipant` for multi-agent sessions.
49
- - **`CapabilitiesModule`** (`client.capabilities`) `list(type)` for
50
- tools / agents / services / knowledge, plus `getChattableAgents()`
51
- helper that filters `interpret:promptable` to entries tagged
52
- `interface:chat` / `interface:chat-default`.
53
- - **`IdentityModule.getUserProfile` / `updateUserProfile`** workspace-
54
- scoped editable profile breadcrumb. Returns `null` (instead of
55
- throwing) when no profile has been written yet.
56
- - **`GrantsModule.resolveService(name)`** — server-side credential
57
- resolution for tools that need to act on behalf of a workspace's
58
- active service grant.
48
+ - **`org.getLLMTiers` / `org.upsertLLMTier` / `org.deleteLLMTier`** and the
49
+ `OrgLLMTier` type — the `/v1/orgs/{org}/llm-tiers` endpoints and the
50
+ `org_intelligence_tier_overrides` layer are gone. Tiers are authoring sugar
51
+ expanded to a concrete address at publish time; there is no org tier override.
52
+ - **`OrgLLMProvider.fallback_for_id`** `org_llm_providers` is now purely a
53
+ credential store keyed by route. `is_default` is retained only as a
54
+ convenience flag for credential listing and does NOT influence resolution.
55
+ There is no `fallback` role and no fallback behaviour (fail loudly instead).
56
+
57
+ ### Added
58
+
59
+ - **`EngineConfig` / `ModelAddress`** types plus `parseModelAddress`,
60
+ `formatModelAddress`, `isTierAddress`, and `ROUTE_TIER` the typed mirror of
61
+ the Go `pkg/rcrt/types.go` `EngineConfig` and `internal/llm/address.go`
62
+ `ModelAddress`.
63
+ - **`breadcrumbs.updateAgentEngineConfig(id, modelAddress, version)`** typed
64
+ convenience that PATCHes an agent breadcrumb's `engine_config.model` to a
65
+ canonical address while preserving the rest of `content`.
66
+
67
+ ### Migration
68
+
69
+ - `org.listLLMProviders` is unchanged (credential listing).
70
+ - Replace any tier reads/writes with the per-agent breadcrumb address via
71
+ `breadcrumbs.updateAgentEngineConfig`.
72
+
73
+ ## 0.2.0 — 2026-06-12
74
+
75
+ The convergence release: everything the MSP console reached past the SDK for is now typed surface. `@possibl/rcrt-api` consumers should migrate to this package — the legacy package is being deprecated.
76
+
77
+ ### Added
78
+
79
+ - **`client.request(path, options)`** — public, typed escape hatch with the client's auth + tenant headers, 401/409/429 retry handling and error envelope. Apps are never blocked on an SDK release for a new gateway endpoint. `options.body` takes a plain object (SDK serialises); `options.rawBody` takes FormData/binary; `options.query` appends search params.
80
+ - **`client.forTenant(tenantId)`** — cached, permanently-bound per-tenant client instances for parallel fleet fan-out. Replaces the mutate-one-client `setTenantId` pattern for multi-workspace reads (no shared-client races, no request serialisation).
81
+ - **`client.marketplace`** — bundle catalogue + install lifecycle: `listBundles`, `getBundle`, `bundleStatus`, `listInstalledBundles`, `installBundle`, `updateBundle`, `repairBundle`, `pruneBundle`, `uninstallBundle`. Typed `BundleStatus` / `BundleItemResult` wire shapes matching the gateway's `/v1/marketplace/*` routes.
82
+ - **`client.org`** — org admin surface: `get`, `listMembers`, `listTenants`, `createTenant`, `createCustomerOrg`, plus intelligence-tier overrides (`getLLMTiers`, `upsertLLMTier`, `deleteLLMTier`, `listLLMProviders`).
83
+ - **`client.files`** — multipart `upload` (isomorphic `FormData`, no deps), `list`, `get`, `downloadUrl`, `getText`, `delete`.
84
+ - **`client.chat`** — session utilities: `sessionBreadcrumbs`, `sessionParticipants`, `addSessionParticipant`, `removeSessionParticipant`; and `listAgents()` agent discovery (`interpret:promptable` + `interface:chat` filtering).
85
+ - Internal fetch: `rawBody` request option for non-JSON payloads.
59
86
 
60
87
  ### Notes
61
88
 
62
- - `IdentityModule.listPendingInvitations` / `acceptInvitation` /
63
- `declineInvitation` were already shipped in the alpha — no change.
64
- - Global-events SSE (`/v1/events`) is exposed as `chat.globalStream`
65
- rather than its own module an extra module would have meant a
66
- duplicate config builder.
67
- - Tenant admin endpoints (members, resource overrides) deliberately
68
- out of scope for `0.1.0`. Will land in a `0.2.x` once a consumer
69
- needs them.
89
+ - `chat.send` already supported `extra_tags` + `target_agent` — no change needed for App Control grounding tags.
90
+ - No breaking changes; 0.1.x consumers upgrade cleanly.
91
+
92
+ ## 0.1.0-alpha.12026-04-23
93
+
94
+ First publish. Breaking changes are expected during the `alpha` line — pin exact versions. Do not use in production until `0.1.0` stable.
95
+
96
+ ### Included
97
+
98
+ - `RcrtClient` with auth, chat (send + stream), breadcrumbs (CRUD + tag query + semantic search), service grants, cards, identity modules.
99
+ - Pluggable `TokenProvider` interface — SDK never touches Firebase / Auth0 / Clerk directly.
100
+ - Pluggable `EventSource` — React Native consumers inject `react-native-sse`; browser + Node 18+ use native.
101
+ - Typed error envelope via `ApiError.detail.code` / `KnownErrorCode`. Accepts both the canonical `{error: {code, message, details}}` and legacy `{error: "string"}` shapes during the migration window.
102
+ - Domain types (`Card`, `Breadcrumb`, `Row`, `ChartSpec`, ...) under the main export.
103
+ - OpenAPI-generated types (`paths`, `operations`, `components`) under `@possibl/rcrt-sdk/generated` for raw fetch-level type safety.
70
104
 
71
- ### Dependencies
105
+ ### Known gaps
72
106
 
73
- - No runtime deps. The SDK is fetch + EventSource + plain TS. Bring
74
- your own `EventSource` polyfill on React Native (see README).
107
+ - No integration test suite against a live backend yet. Smoke tests (9/9) cover the mock surface. Integration harness is queued.
108
+ - The backend's error-envelope migration is in progress (tracked in `packages/docs/guides/07-error-handling.md`). Any handler still returning `{error: "string"}` is normalised by the SDK's `parseErrorBody`.
109
+ - React hooks / a card renderer are deliberately out of scope. See `packages/docs/recipes/react.md` for copy-pasteable patterns.
75
110
 
76
- ### Compatibility
111
+ ### Support matrix
77
112
 
78
- - Node 18+ (native fetch + WHATWG URL).
79
- - Browsers with `fetch`, `URL`, `URLSearchParams`, `EventSource`.
80
- - React Native pass an `eventSource` constructor in
81
- `chat.sessionStream({ eventSource })` and bring a fetch polyfill if
82
- using a runtime older than RN 0.74.
113
+ | Runtime | Supported |
114
+ | --- | --- |
115
+ | Browser (Chrome 103+, Firefox 102+, Safari 16+) | Yes |
116
+ | Node 18+ | Yes |
117
+ | React Native + `react-native-sse` polyfill | Yes |
118
+ | Deno 1.40+ | Untested, should work |
119
+ | Bun 1.0+ | Untested, should work |
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Possibl AI Ltd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -87,33 +87,47 @@ const stream = rcrt.chat.sessionStream(sessionId, handlers, {
87
87
  });
88
88
  ```
89
89
 
90
+ ## Fleet fan-out (multi-workspace apps)
91
+
92
+ A console that reads dozens of workspaces in parallel should never
93
+ mutate one client's tenant. Get a cheap, cached, permanently-bound
94
+ client per workspace instead:
95
+
96
+ ```ts
97
+ const reports = await Promise.all(
98
+ workspaces.map(async (ws) => {
99
+ const c = rcrt.forTenant(ws.tenantId);
100
+ return c.marketplace.listInstalledBundles();
101
+ }),
102
+ );
103
+ ```
104
+
105
+ ## Escape hatch
106
+
107
+ Any gateway endpoint the SDK hasn't wrapped yet is one call away —
108
+ with the same auth, tenant header, retries and error envelope:
109
+
110
+ ```ts
111
+ const res = await rcrt.request<{ items: unknown[] }>('/v1/some/new/endpoint', {
112
+ method: 'POST',
113
+ body: { plain: 'object' }, // SDK serialises JSON
114
+ query: { limit: 50 },
115
+ });
116
+ ```
117
+
90
118
  ## What's exported
91
119
 
92
120
  | Symbol | Purpose |
93
121
  | --- | --- |
94
- | `RcrtClient` | The client. One instance per workspace session. |
122
+ | `RcrtClient` | The client. One instance per workspace session; `forTenant()` for fan-out. |
95
123
  | `TokenProvider` | Interface — wire any IdP. |
96
124
  | `staticTokenProvider` | Static-token convenience (tests, `tk_*` workspace keys). |
97
125
  | `ApiError`, `SdkError` | Error classes. Typed + discriminable by `.status` and `.detail.code`. |
126
+ | `MarketplaceModule`, `OrgModule`, `FilesModule` | Bundle lifecycle, org admin + LLM tiers, file upload. |
98
127
  | `Breadcrumb`, `Card`, `Row`, `ChartSpec`, ... | Every public type from the RCRT contract. Re-exportable as your UI types. |
99
128
 
100
129
  Browse `src/index.ts` for the full re-export list.
101
130
 
102
- ### Module surface on `RcrtClient`
103
-
104
- ```ts
105
- const rcrt = new RcrtClient({ apiUrl, tokenProvider });
106
-
107
- rcrt.auth // identity, tenants, invitations, user profile
108
- rcrt.breadcrumbs // CRUD + tag query + semantic search
109
- rcrt.chat // send + sessionStream (per-session SSE) + globalStream (/v1/events)
110
- rcrt.cards // JIT-UI card resolve + helpers
111
- rcrt.grants // OAuth grants + initiate + resolveService
112
- rcrt.files // upload / list / get / signed download / text extract / delete
113
- rcrt.sessions // constellation + participants
114
- rcrt.capabilities // tools / agents / services / knowledge discovery
115
- ```
116
-
117
131
  ## Design notes
118
132
 
119
133
  - **Isomorphic**: zero DOM or Node-only APIs in the core. The only
@@ -142,11 +156,11 @@ Full narrative + concepts + operations playbook in
142
156
 
143
157
  ## Status
144
158
 
145
- **0.1.0** — first public release. Used by `possibl-ai/rcrt-template-mobile`
146
- and downstream apps (e.g. `possibl-ai/ritual-app`) as their canonical
147
- RCRT client. Surface stable; minor adjustments possible before 1.0.
148
-
149
- See [`CHANGELOG.md`](./CHANGELOG.md) for what's new in this release.
159
+ **0.2.0** — the convergence release: marketplace/org/files modules,
160
+ `forTenant()` fan-out and the `request()` escape hatch cover the full
161
+ surface the production MSP console uses. `@possibl/rcrt-api` is
162
+ deprecated in favour of this package. Shape is stable for the
163
+ documented surface; minor adjustments possible before 1.0.
150
164
 
151
165
  ## Licence
152
166
 
package/dist/auth.d.ts ADDED
@@ -0,0 +1,45 @@
1
+ /**
2
+ * TokenProvider — the seam between RCRT and your identity source.
3
+ *
4
+ * The SDK never talks to Firebase / Auth0 / Clerk directly. You wire
5
+ * up whichever IdP you use by implementing this interface once. On
6
+ * every request the SDK calls `getIdToken()` to get a fresh bearer.
7
+ *
8
+ * The provider owns refresh. If your IdP supports it (Firebase does),
9
+ * it should return a token that's valid for at least the next ~60s;
10
+ * the SDK doesn't track expiry itself.
11
+ *
12
+ * Example Firebase (web):
13
+ *
14
+ * import { getAuth } from 'firebase/auth';
15
+ * const auth = getAuth();
16
+ * const tokenProvider: TokenProvider = {
17
+ * async getIdToken() {
18
+ * const u = auth.currentUser;
19
+ * if (!u) throw new SdkError('NOT_SIGNED_IN', 'Sign in first');
20
+ * return u.getIdToken(false);
21
+ * },
22
+ * };
23
+ *
24
+ * Example test stub:
25
+ *
26
+ * const tokenProvider: TokenProvider = {
27
+ * async getIdToken() { return 'tk_test_workspace_api_key'; },
28
+ * };
29
+ */
30
+ export interface TokenProvider {
31
+ /**
32
+ * Return a bearer token for the next request. Throws or rejects if
33
+ * the user isn't signed in / the key is missing.
34
+ *
35
+ * Implementations SHOULD refresh transparently if they know the
36
+ * token is close to expiry. The SDK calls this per-request and
37
+ * doesn't cache.
38
+ */
39
+ getIdToken(): Promise<string>;
40
+ /** Optional hook: called on 401. Implementations can force a refresh. */
41
+ onUnauthorized?(): Promise<void>;
42
+ }
43
+ /** Convenience: a provider that returns a static string. For workspace API keys or tests. */
44
+ export declare function staticTokenProvider(token: string): TokenProvider;
45
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,MAAM,WAAW,aAAa;IAC5B;;;;;;;OAOG;IACH,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAE9B,yEAAyE;IACzE,cAAc,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAED,6FAA6F;AAC7F,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAOhE"}
@@ -27,30 +27,15 @@
27
27
  * async getIdToken() { return 'tk_test_workspace_api_key'; },
28
28
  * };
29
29
  */
30
-
31
30
  import { SdkError } from './errors.js';
32
-
33
- export interface TokenProvider {
34
- /**
35
- * Return a bearer token for the next request. Throws or rejects if
36
- * the user isn't signed in / the key is missing.
37
- *
38
- * Implementations SHOULD refresh transparently if they know the
39
- * token is close to expiry. The SDK calls this per-request and
40
- * doesn't cache.
41
- */
42
- getIdToken(): Promise<string>;
43
-
44
- /** Optional hook: called on 401. Implementations can force a refresh. */
45
- onUnauthorized?(): Promise<void>;
46
- }
47
-
48
31
  /** Convenience: a provider that returns a static string. For workspace API keys or tests. */
49
- export function staticTokenProvider(token: string): TokenProvider {
50
- if (!token) throw new SdkError('EMPTY_TOKEN', 'staticTokenProvider called with empty token');
51
- return {
52
- async getIdToken() {
53
- return token;
54
- },
55
- };
32
+ export function staticTokenProvider(token) {
33
+ if (!token)
34
+ throw new SdkError('EMPTY_TOKEN', 'staticTokenProvider called with empty token');
35
+ return {
36
+ async getIdToken() {
37
+ return token;
38
+ },
39
+ };
56
40
  }
41
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAiBvC,6FAA6F;AAC7F,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,QAAQ,CAAC,aAAa,EAAE,6CAA6C,CAAC,CAAC;IAC7F,OAAO;QACL,KAAK,CAAC,UAAU;YACd,OAAO,KAAK,CAAC;QACf,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Identity module — `/v1/auth/*` endpoints.
3
+ *
4
+ * Distinct from the TokenProvider abstraction: this is the server-side
5
+ * identity surface that runs _after_ you've presented a bearer token.
6
+ * See `packages/docs/guides/02-auth.md`.
7
+ */
8
+ import type { FetchContext } from './internal/fetch.js';
9
+ export interface MeResponse {
10
+ user: {
11
+ id: string;
12
+ email: string;
13
+ name?: string;
14
+ picture?: string;
15
+ };
16
+ is_platform_admin: boolean;
17
+ organizations: Array<{
18
+ id: string;
19
+ name: string;
20
+ role: string;
21
+ }>;
22
+ tenants: Array<{
23
+ id: string;
24
+ name: string;
25
+ role: string;
26
+ }>;
27
+ active_tenant?: {
28
+ id: string;
29
+ name: string;
30
+ role: string;
31
+ };
32
+ permissions?: string[];
33
+ grants?: unknown[];
34
+ }
35
+ export interface Tenant {
36
+ id: string;
37
+ name: string;
38
+ org_id?: string;
39
+ role?: string;
40
+ }
41
+ export interface TenantMembership {
42
+ user_id: string;
43
+ tenant_id: string;
44
+ role: string;
45
+ }
46
+ export interface PendingInvitation {
47
+ id: string;
48
+ org_id?: string | null;
49
+ tenant_id?: string | null;
50
+ email: string;
51
+ role: string;
52
+ status: 'pending' | 'accepted' | 'declined' | 'expired' | 'cancelled';
53
+ expires_at: string;
54
+ invited_by?: {
55
+ id: string;
56
+ name?: string;
57
+ };
58
+ }
59
+ export declare class IdentityModule {
60
+ private readonly ctx;
61
+ constructor(ctx: FetchContext);
62
+ /**
63
+ * `GET /v1/auth/me` — returns identity + all accessible workspaces.
64
+ *
65
+ * Creates the user row on first sign-in. Call this before any other
66
+ * RCRT endpoint for brand-new Firebase users.
67
+ */
68
+ me(): Promise<MeResponse>;
69
+ /** List workspaces the current user is a member of. */
70
+ listTenants(): Promise<Tenant[]>;
71
+ /**
72
+ * `POST /v1/auth/signup` — self-serve first-workspace provisioning.
73
+ *
74
+ * Creates a personal organisation + "Personal" workspace for an
75
+ * authenticated user with zero org memberships. Idempotent: returns
76
+ * the existing personal workspace (200) when one already exists,
77
+ * 201 on creation. Call when `listTenants()` comes back empty after
78
+ * first sign-in.
79
+ */
80
+ signup(): Promise<{
81
+ org_id: string;
82
+ tenant_id: string;
83
+ }>;
84
+ /**
85
+ * `POST /v1/tenants/{id}/join` — self-enroll into a tenant that has
86
+ * opted into open enrollment via `tenants.auto_enroll_role`.
87
+ *
88
+ * Unlike member management this needs NO existing membership: any
89
+ * authenticated user may join, and the gateway enrolls them with the
90
+ * tenant's configured read-only role. Idempotent — calling it again
91
+ * (or when already a member) is a no-op that returns the current
92
+ * role. The tenant id is in the URL, so no X-Tenant-ID header is
93
+ * sent.
94
+ */
95
+ joinTenant(tenantId: string): Promise<TenantMembership>;
96
+ /** Confirm workspace membership. Client code is still responsible for setting X-Tenant-ID. */
97
+ selectTenant(tenantId: string): Promise<void>;
98
+ /** `POST /v1/auth/logout` — server-side no-op; included for symmetry + audit. */
99
+ logout(): Promise<void>;
100
+ /**
101
+ * `POST /v1/auth/delete-account` — cascading account deletion.
102
+ *
103
+ * **NOT YET ON `development`** at publish time — pending a follow-up
104
+ * PR. The SDK method exists so consumer code can compile against the
105
+ * intended shape; invoking it against a gateway that lacks the
106
+ * handler returns 404.
107
+ */
108
+ deleteAccount(): Promise<void>;
109
+ /** Pending invitations keyed by the caller's email. */
110
+ listPendingInvitations(): Promise<PendingInvitation[]>;
111
+ acceptInvitation(token: string): Promise<void>;
112
+ declineInvitation(invitationId: string): Promise<void>;
113
+ }
114
+ //# sourceMappingURL=authn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authn.d.ts","sourceRoot":"","sources":["../src/authn.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,iBAAiB,EAAE,OAAO,CAAC;IAC3B,aAAa,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjE,OAAO,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3D,aAAa,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,WAAW,CAAC;IACtE,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5C;AAED,qBAAa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,YAAY;IAE9C;;;;;OAKG;IACG,EAAE,IAAI,OAAO,CAAC,UAAU,CAAC;IAI/B,uDAAuD;IACjD,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAOtC;;;;;;;;OAQG;IACG,MAAM,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAO9D;;;;;;;;;;OAUG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAO7D,8FAA8F;IACxF,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOnD,iFAAiF;IAC3E,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAO7B;;;;;;;OAOG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAOpC,uDAAuD;IACjD,sBAAsB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAOtD,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO9C,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAK7D"}
package/dist/authn.js ADDED
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Identity module — `/v1/auth/*` endpoints.
3
+ *
4
+ * Distinct from the TokenProvider abstraction: this is the server-side
5
+ * identity surface that runs _after_ you've presented a bearer token.
6
+ * See `packages/docs/guides/02-auth.md`.
7
+ */
8
+ import { request } from './internal/fetch.js';
9
+ export class IdentityModule {
10
+ ctx;
11
+ constructor(ctx) {
12
+ this.ctx = ctx;
13
+ }
14
+ /**
15
+ * `GET /v1/auth/me` — returns identity + all accessible workspaces.
16
+ *
17
+ * Creates the user row on first sign-in. Call this before any other
18
+ * RCRT endpoint for brand-new Firebase users.
19
+ */
20
+ async me() {
21
+ return request(this.ctx, '/v1/auth/me', { skipTenant: true });
22
+ }
23
+ /** List workspaces the current user is a member of. */
24
+ async listTenants() {
25
+ const res = await request(this.ctx, '/v1/auth/tenants', {
26
+ skipTenant: true,
27
+ });
28
+ return Array.isArray(res) ? res : (res.tenants ?? []);
29
+ }
30
+ /**
31
+ * `POST /v1/auth/signup` — self-serve first-workspace provisioning.
32
+ *
33
+ * Creates a personal organisation + "Personal" workspace for an
34
+ * authenticated user with zero org memberships. Idempotent: returns
35
+ * the existing personal workspace (200) when one already exists,
36
+ * 201 on creation. Call when `listTenants()` comes back empty after
37
+ * first sign-in.
38
+ */
39
+ async signup() {
40
+ return request(this.ctx, '/v1/auth/signup', {
41
+ method: 'POST',
42
+ skipTenant: true,
43
+ });
44
+ }
45
+ /**
46
+ * `POST /v1/tenants/{id}/join` — self-enroll into a tenant that has
47
+ * opted into open enrollment via `tenants.auto_enroll_role`.
48
+ *
49
+ * Unlike member management this needs NO existing membership: any
50
+ * authenticated user may join, and the gateway enrolls them with the
51
+ * tenant's configured read-only role. Idempotent — calling it again
52
+ * (or when already a member) is a no-op that returns the current
53
+ * role. The tenant id is in the URL, so no X-Tenant-ID header is
54
+ * sent.
55
+ */
56
+ async joinTenant(tenantId) {
57
+ return request(this.ctx, `/v1/tenants/${tenantId}/join`, {
58
+ method: 'POST',
59
+ skipTenant: true,
60
+ });
61
+ }
62
+ /** Confirm workspace membership. Client code is still responsible for setting X-Tenant-ID. */
63
+ async selectTenant(tenantId) {
64
+ await request(this.ctx, `/v1/auth/tenants/${tenantId}/select`, {
65
+ method: 'POST',
66
+ skipTenant: true,
67
+ });
68
+ }
69
+ /** `POST /v1/auth/logout` — server-side no-op; included for symmetry + audit. */
70
+ async logout() {
71
+ await request(this.ctx, '/v1/auth/logout', {
72
+ method: 'POST',
73
+ skipTenant: true,
74
+ });
75
+ }
76
+ /**
77
+ * `POST /v1/auth/delete-account` — cascading account deletion.
78
+ *
79
+ * **NOT YET ON `development`** at publish time — pending a follow-up
80
+ * PR. The SDK method exists so consumer code can compile against the
81
+ * intended shape; invoking it against a gateway that lacks the
82
+ * handler returns 404.
83
+ */
84
+ async deleteAccount() {
85
+ await request(this.ctx, '/v1/auth/delete-account', {
86
+ method: 'POST',
87
+ skipTenant: true,
88
+ });
89
+ }
90
+ /** Pending invitations keyed by the caller's email. */
91
+ async listPendingInvitations() {
92
+ const res = await request(this.ctx, '/v1/invitations/pending');
93
+ return Array.isArray(res) ? res : (res.invitations ?? []);
94
+ }
95
+ async acceptInvitation(token) {
96
+ await request(this.ctx, '/v1/invitations/accept', {
97
+ method: 'POST',
98
+ body: { token },
99
+ });
100
+ }
101
+ async declineInvitation(invitationId) {
102
+ await request(this.ctx, `/v1/invitations/${invitationId}/decline`, {
103
+ method: 'POST',
104
+ });
105
+ }
106
+ }
107
+ //# sourceMappingURL=authn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authn.js","sourceRoot":"","sources":["../src/authn.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAyC9C,MAAM,OAAO,cAAc;IACI;IAA7B,YAA6B,GAAiB;QAAjB,QAAG,GAAH,GAAG,CAAc;IAAG,CAAC;IAElD;;;;;OAKG;IACH,KAAK,CAAC,EAAE;QACN,OAAO,OAAO,CAAa,IAAI,CAAC,GAAG,EAAE,aAAa,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,uDAAuD;IACvD,KAAK,CAAC,WAAW;QACf,MAAM,GAAG,GAAG,MAAM,OAAO,CAAmC,IAAI,CAAC,GAAG,EAAE,kBAAkB,EAAE;YACxF,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;QACH,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IACxD,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,MAAM;QACV,OAAO,OAAO,CAAwC,IAAI,CAAC,GAAG,EAAE,iBAAiB,EAAE;YACjF,MAAM,EAAE,MAAM;YACd,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;;;OAUG;IACH,KAAK,CAAC,UAAU,CAAC,QAAgB;QAC/B,OAAO,OAAO,CAAmB,IAAI,CAAC,GAAG,EAAE,eAAe,QAAQ,OAAO,EAAE;YACzE,MAAM,EAAE,MAAM;YACd,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;IACL,CAAC;IAED,8FAA8F;IAC9F,KAAK,CAAC,YAAY,CAAC,QAAgB;QACjC,MAAM,OAAO,CAAO,IAAI,CAAC,GAAG,EAAE,oBAAoB,QAAQ,SAAS,EAAE;YACnE,MAAM,EAAE,MAAM;YACd,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;IACL,CAAC;IAED,iFAAiF;IACjF,KAAK,CAAC,MAAM;QACV,MAAM,OAAO,CAAO,IAAI,CAAC,GAAG,EAAE,iBAAiB,EAAE;YAC/C,MAAM,EAAE,MAAM;YACd,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,aAAa;QACjB,MAAM,OAAO,CAAO,IAAI,CAAC,GAAG,EAAE,yBAAyB,EAAE;YACvD,MAAM,EAAE,MAAM;YACd,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;IACL,CAAC;IAED,uDAAuD;IACvD,KAAK,CAAC,sBAAsB;QAC1B,MAAM,GAAG,GAAG,MAAM,OAAO,CAEvB,IAAI,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAC;QACvC,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,KAAa;QAClC,MAAM,OAAO,CAAO,IAAI,CAAC,GAAG,EAAE,wBAAwB,EAAE;YACtD,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,EAAE,KAAK,EAAE;SAChB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,YAAoB;QAC1C,MAAM,OAAO,CAAO,IAAI,CAAC,GAAG,EAAE,mBAAmB,YAAY,UAAU,EAAE;YACvE,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;IACL,CAAC;CACF"}