@topolo/sdk 0.1.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.
@@ -0,0 +1,277 @@
1
+ /**
2
+ * Auth credential types used by the SDK.
3
+ *
4
+ * Phase 1 supports two modes:
5
+ * - `api_key`: a single platform API key (service-scoped at issuance).
6
+ * Preferred for long-lived agent installs.
7
+ * - `access_token`: a short-lived JWT from the interactive login flow.
8
+ * Used by TopoloCli after `topolo auth login`.
9
+ *
10
+ * Phase 2 will add OAuth access tokens issued by the authorization-code +
11
+ * PKCE flow in TopoloAuth. Those will plug in as a third variant.
12
+ */
13
+ type TopoloCredential = {
14
+ kind: 'api_key';
15
+ apiKey: string;
16
+ } | {
17
+ kind: 'access_token';
18
+ accessToken: string;
19
+ refreshToken?: string;
20
+ expiresAt?: number;
21
+ };
22
+ interface AgentIdentity {
23
+ /** Display name of the agent or tool making requests. Included in audit headers. */
24
+ clientName: string;
25
+ /** Semver of the client. Included in audit headers. */
26
+ clientVersion: string;
27
+ /**
28
+ * Optional agent-assigned label (e.g. "claude-code", "codex-cli") used for
29
+ * multi-tenant audit attribution when the client is used by another agent.
30
+ */
31
+ agentName?: string;
32
+ }
33
+
34
+ /**
35
+ * Service registry — base URLs for each Topolo platform service.
36
+ *
37
+ * Defaults point at production. Any entry can be overridden per-call via
38
+ * `TopoloClient` options, or globally via env vars like `TOPOLO_SERVICE_URL_CRM`.
39
+ *
40
+ * IMPORTANT: The SDK NEVER accepts `orgId` as a parameter. Every request is
41
+ * scoped to the organization embedded in the auth credential (JWT claim or
42
+ * API-key binding). This is the load-bearing cross-org isolation guarantee.
43
+ */
44
+ type ServiceId = 'auth' | 'crm';
45
+ declare const DEFAULT_SERVICE_URLS: Record<ServiceId, string>;
46
+ /**
47
+ * Service IDs as registered in TopoloAuth's service catalog. These are sent in
48
+ * the `X-Service-ID` header when the platform needs to route an API-key
49
+ * introspection or permission check.
50
+ */
51
+ declare const PLATFORM_SERVICE_IDS: Partial<Record<ServiceId, string>>;
52
+ declare function resolveServiceUrl(service: ServiceId, overrides?: Partial<Record<ServiceId, string>>): string;
53
+
54
+ interface TopoloClientOptions {
55
+ credential: TopoloCredential;
56
+ agent: AgentIdentity;
57
+ /** Per-service URL overrides. Useful for staging/dev. */
58
+ serviceUrls?: Partial<Record<ServiceId, string>>;
59
+ /**
60
+ * When true, mutating helpers (POST/PUT/PATCH/DELETE) require callers to pass
61
+ * `{ confirm: true }`. Defaults to true. Set false only for trusted surfaces.
62
+ */
63
+ requireConfirmForWrites?: boolean;
64
+ /** HTTP request timeout (ms). Default 30s. */
65
+ timeoutMs?: number;
66
+ /** Injected fetch, for testing. Defaults to global fetch. */
67
+ fetch?: typeof fetch;
68
+ }
69
+ interface RequestOptions {
70
+ service: ServiceId;
71
+ path: string;
72
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
73
+ query?: Record<string, string | number | boolean | undefined>;
74
+ body?: unknown;
75
+ /** Explicit write-acknowledgement for mutating calls. */
76
+ confirm?: boolean;
77
+ /** Extra headers appended after auth/audit headers. */
78
+ headers?: Record<string, string>;
79
+ signal?: AbortSignal;
80
+ }
81
+ declare class TopoloClient {
82
+ private readonly credential;
83
+ private readonly agent;
84
+ private readonly serviceUrls?;
85
+ private readonly requireConfirmForWrites;
86
+ private readonly timeoutMs;
87
+ private readonly fetchImpl;
88
+ constructor(options: TopoloClientOptions);
89
+ /**
90
+ * Low-level JSON request. Prefer the typed module helpers (identity, crm, ...)
91
+ * for anything a caller would reach for; this stays exported for escape-hatch
92
+ * use and for the generic `topolo api` / MCP passthrough tool.
93
+ */
94
+ request<T = unknown>(opts: RequestOptions): Promise<T>;
95
+ /**
96
+ * Introspect the current credential. Returns the caller's resolved identity,
97
+ * organization, and permission set. Used by CLI `whoami` and by MCP to gate
98
+ * advertised tools by scope.
99
+ *
100
+ * Both JWT access tokens and platform API keys are resolved via the unified
101
+ * `GET /api/auth/me` endpoint on TopoloAuth, which does not require service
102
+ * credentials. The caller's possession of the credential secret is proof.
103
+ */
104
+ introspect(): Promise<CredentialIntrospection>;
105
+ }
106
+ interface CredentialIntrospection {
107
+ kind: 'api_key' | 'access_token';
108
+ user: {
109
+ id: string;
110
+ email: string;
111
+ name: string | null;
112
+ role: string;
113
+ permissions: string[];
114
+ };
115
+ organization: {
116
+ id: string;
117
+ slug: string;
118
+ name: string;
119
+ } | null;
120
+ }
121
+
122
+ declare class TopoloSdkError extends Error {
123
+ readonly code: string;
124
+ constructor(code: string, message: string);
125
+ }
126
+ declare class TopoloAuthError extends TopoloSdkError {
127
+ constructor(message: string, code?: string);
128
+ }
129
+ declare class TopoloPermissionError extends TopoloSdkError {
130
+ readonly required: string[];
131
+ constructor(message: string, required: string[]);
132
+ }
133
+ declare class TopoloHttpError extends TopoloSdkError {
134
+ readonly status: number;
135
+ readonly body: unknown;
136
+ readonly service: string;
137
+ readonly path: string;
138
+ constructor(service: string, path: string, status: number, body: unknown, message?: string);
139
+ }
140
+
141
+ interface CrmContactSummary {
142
+ id: string;
143
+ firstName: string | null;
144
+ lastName: string | null;
145
+ email: string | null;
146
+ phone: string | null;
147
+ company: string | null;
148
+ leadStatus: string | null;
149
+ lifecycleStage: string | null;
150
+ updatedAt: string | null;
151
+ }
152
+ interface ListContactsOptions {
153
+ /** Full-text search string. */
154
+ q?: string;
155
+ /** Page number, 1-indexed. */
156
+ page?: number;
157
+ /** Page size. */
158
+ pageSize?: number;
159
+ }
160
+ interface ListContactsResult {
161
+ contacts: CrmContactSummary[];
162
+ total: number;
163
+ page: number;
164
+ pageSize: number;
165
+ }
166
+ /**
167
+ * CRM module — thin typed wrapper over the TopoloCRM HTTP API.
168
+ *
169
+ * Cross-org isolation: there is intentionally NO `orgId` parameter anywhere in
170
+ * this module. The organization is derived entirely from the auth credential
171
+ * by the CRM backend, which scopes all SQL queries by `org_slug`/`org_id`.
172
+ */
173
+ declare class CrmModule {
174
+ private readonly client;
175
+ constructor(client: TopoloClient);
176
+ listContacts(options?: ListContactsOptions): Promise<ListContactsResult>;
177
+ getContact(contactId: string): Promise<CrmContactSummary | null>;
178
+ }
179
+
180
+ /**
181
+ * Identity module — wraps TopoloAuth's caller-identity endpoints.
182
+ *
183
+ * All responses are scoped to the organization the credential was issued for;
184
+ * there is no way to ask this module about a different org.
185
+ */
186
+ declare class IdentityModule {
187
+ private readonly client;
188
+ constructor(client: TopoloClient);
189
+ /** Returns the resolved user, organization, and permission set for the
190
+ * current credential. Equivalent to `TopoloClient.introspect()` — exposed
191
+ * on this module for symmetry with the other domain modules. */
192
+ whoami(): Promise<CredentialIntrospection>;
193
+ }
194
+
195
+ interface DeviceAuthorizationResponse {
196
+ device_code: string;
197
+ user_code: string;
198
+ verification_uri: string;
199
+ verification_uri_complete?: string;
200
+ expires_in: number;
201
+ interval: number;
202
+ }
203
+ interface TokenResponse {
204
+ access_token: string;
205
+ token_type: 'Bearer';
206
+ expires_in: number;
207
+ refresh_token?: string;
208
+ scope?: string;
209
+ }
210
+ interface OAuthHelperOptions {
211
+ /** Overrides for TopoloAuth URL. Staging/dev should pass a mapping. */
212
+ serviceUrls?: Partial<Record<ServiceId, string>>;
213
+ /** Injected fetch. Defaults to global fetch. */
214
+ fetch?: typeof fetch;
215
+ }
216
+ /**
217
+ * Thin client over the Topolo OAuth 2.1 authorization server. Intended for
218
+ * first-party tooling (the CLI) and registered third-party agents. Issued
219
+ * access tokens plug into `createTopolo({ credential: { kind: 'access_token', ... } })`.
220
+ */
221
+ declare class TopoloOAuth {
222
+ #private;
223
+ private readonly baseUrl;
224
+ private readonly fetchImpl;
225
+ constructor(options?: OAuthHelperOptions);
226
+ /**
227
+ * RFC 8628 device authorization grant — starts the flow by asking the server
228
+ * for a device_code + user_code. The caller prints the user_code +
229
+ * verification_uri and then polls `pollDeviceToken` until approval.
230
+ */
231
+ requestDeviceCode(params: {
232
+ clientId: string;
233
+ scope?: string | string[];
234
+ }): Promise<DeviceAuthorizationResponse>;
235
+ /**
236
+ * Polls the token endpoint once. Returns the token pair on success, or
237
+ * throws a `TopoloAuthError` whose `code` is one of the RFC 8628 poll
238
+ * states: `authorization_pending`, `slow_down`, `access_denied`,
239
+ * `expired_token`. Callers should back off on `slow_down`, wait at least
240
+ * one `interval` on `authorization_pending`, and give up on the rest.
241
+ */
242
+ pollDeviceToken(params: {
243
+ clientId: string;
244
+ deviceCode: string;
245
+ clientSecret?: string;
246
+ }): Promise<TokenResponse>;
247
+ /** Exchange an authorization code (with PKCE) for tokens. */
248
+ exchangeAuthorizationCode(params: {
249
+ clientId: string;
250
+ code: string;
251
+ redirectUri: string;
252
+ codeVerifier?: string;
253
+ clientSecret?: string;
254
+ }): Promise<TokenResponse>;
255
+ /** Rotate a refresh token for a new token pair. */
256
+ refreshToken(params: {
257
+ clientId: string;
258
+ refreshToken: string;
259
+ clientSecret?: string;
260
+ }): Promise<TokenResponse>;
261
+ }
262
+
263
+ /**
264
+ * Convenience factory that returns a ready-to-use typed Topolo client plus
265
+ * domain module surfaces (identity, crm, ...).
266
+ *
267
+ * Phase 1 exposes identity + crm; subsequent phases add more modules
268
+ * incrementally as each app's API contract stabilizes.
269
+ */
270
+ declare function createTopolo(options: TopoloClientOptions): {
271
+ client: TopoloClient;
272
+ identity: IdentityModule;
273
+ crm: CrmModule;
274
+ };
275
+ type Topolo = ReturnType<typeof createTopolo>;
276
+
277
+ export { type AgentIdentity, type CredentialIntrospection, type CrmContactSummary, CrmModule, DEFAULT_SERVICE_URLS, type DeviceAuthorizationResponse, IdentityModule, type ListContactsOptions, type ListContactsResult, type OAuthHelperOptions, PLATFORM_SERVICE_IDS, type RequestOptions, type ServiceId, type TokenResponse, type Topolo, TopoloAuthError, TopoloClient, type TopoloClientOptions, type TopoloCredential, TopoloHttpError, TopoloOAuth, TopoloPermissionError, TopoloSdkError, createTopolo, resolveServiceUrl };
@@ -0,0 +1,277 @@
1
+ /**
2
+ * Auth credential types used by the SDK.
3
+ *
4
+ * Phase 1 supports two modes:
5
+ * - `api_key`: a single platform API key (service-scoped at issuance).
6
+ * Preferred for long-lived agent installs.
7
+ * - `access_token`: a short-lived JWT from the interactive login flow.
8
+ * Used by TopoloCli after `topolo auth login`.
9
+ *
10
+ * Phase 2 will add OAuth access tokens issued by the authorization-code +
11
+ * PKCE flow in TopoloAuth. Those will plug in as a third variant.
12
+ */
13
+ type TopoloCredential = {
14
+ kind: 'api_key';
15
+ apiKey: string;
16
+ } | {
17
+ kind: 'access_token';
18
+ accessToken: string;
19
+ refreshToken?: string;
20
+ expiresAt?: number;
21
+ };
22
+ interface AgentIdentity {
23
+ /** Display name of the agent or tool making requests. Included in audit headers. */
24
+ clientName: string;
25
+ /** Semver of the client. Included in audit headers. */
26
+ clientVersion: string;
27
+ /**
28
+ * Optional agent-assigned label (e.g. "claude-code", "codex-cli") used for
29
+ * multi-tenant audit attribution when the client is used by another agent.
30
+ */
31
+ agentName?: string;
32
+ }
33
+
34
+ /**
35
+ * Service registry — base URLs for each Topolo platform service.
36
+ *
37
+ * Defaults point at production. Any entry can be overridden per-call via
38
+ * `TopoloClient` options, or globally via env vars like `TOPOLO_SERVICE_URL_CRM`.
39
+ *
40
+ * IMPORTANT: The SDK NEVER accepts `orgId` as a parameter. Every request is
41
+ * scoped to the organization embedded in the auth credential (JWT claim or
42
+ * API-key binding). This is the load-bearing cross-org isolation guarantee.
43
+ */
44
+ type ServiceId = 'auth' | 'crm';
45
+ declare const DEFAULT_SERVICE_URLS: Record<ServiceId, string>;
46
+ /**
47
+ * Service IDs as registered in TopoloAuth's service catalog. These are sent in
48
+ * the `X-Service-ID` header when the platform needs to route an API-key
49
+ * introspection or permission check.
50
+ */
51
+ declare const PLATFORM_SERVICE_IDS: Partial<Record<ServiceId, string>>;
52
+ declare function resolveServiceUrl(service: ServiceId, overrides?: Partial<Record<ServiceId, string>>): string;
53
+
54
+ interface TopoloClientOptions {
55
+ credential: TopoloCredential;
56
+ agent: AgentIdentity;
57
+ /** Per-service URL overrides. Useful for staging/dev. */
58
+ serviceUrls?: Partial<Record<ServiceId, string>>;
59
+ /**
60
+ * When true, mutating helpers (POST/PUT/PATCH/DELETE) require callers to pass
61
+ * `{ confirm: true }`. Defaults to true. Set false only for trusted surfaces.
62
+ */
63
+ requireConfirmForWrites?: boolean;
64
+ /** HTTP request timeout (ms). Default 30s. */
65
+ timeoutMs?: number;
66
+ /** Injected fetch, for testing. Defaults to global fetch. */
67
+ fetch?: typeof fetch;
68
+ }
69
+ interface RequestOptions {
70
+ service: ServiceId;
71
+ path: string;
72
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
73
+ query?: Record<string, string | number | boolean | undefined>;
74
+ body?: unknown;
75
+ /** Explicit write-acknowledgement for mutating calls. */
76
+ confirm?: boolean;
77
+ /** Extra headers appended after auth/audit headers. */
78
+ headers?: Record<string, string>;
79
+ signal?: AbortSignal;
80
+ }
81
+ declare class TopoloClient {
82
+ private readonly credential;
83
+ private readonly agent;
84
+ private readonly serviceUrls?;
85
+ private readonly requireConfirmForWrites;
86
+ private readonly timeoutMs;
87
+ private readonly fetchImpl;
88
+ constructor(options: TopoloClientOptions);
89
+ /**
90
+ * Low-level JSON request. Prefer the typed module helpers (identity, crm, ...)
91
+ * for anything a caller would reach for; this stays exported for escape-hatch
92
+ * use and for the generic `topolo api` / MCP passthrough tool.
93
+ */
94
+ request<T = unknown>(opts: RequestOptions): Promise<T>;
95
+ /**
96
+ * Introspect the current credential. Returns the caller's resolved identity,
97
+ * organization, and permission set. Used by CLI `whoami` and by MCP to gate
98
+ * advertised tools by scope.
99
+ *
100
+ * Both JWT access tokens and platform API keys are resolved via the unified
101
+ * `GET /api/auth/me` endpoint on TopoloAuth, which does not require service
102
+ * credentials. The caller's possession of the credential secret is proof.
103
+ */
104
+ introspect(): Promise<CredentialIntrospection>;
105
+ }
106
+ interface CredentialIntrospection {
107
+ kind: 'api_key' | 'access_token';
108
+ user: {
109
+ id: string;
110
+ email: string;
111
+ name: string | null;
112
+ role: string;
113
+ permissions: string[];
114
+ };
115
+ organization: {
116
+ id: string;
117
+ slug: string;
118
+ name: string;
119
+ } | null;
120
+ }
121
+
122
+ declare class TopoloSdkError extends Error {
123
+ readonly code: string;
124
+ constructor(code: string, message: string);
125
+ }
126
+ declare class TopoloAuthError extends TopoloSdkError {
127
+ constructor(message: string, code?: string);
128
+ }
129
+ declare class TopoloPermissionError extends TopoloSdkError {
130
+ readonly required: string[];
131
+ constructor(message: string, required: string[]);
132
+ }
133
+ declare class TopoloHttpError extends TopoloSdkError {
134
+ readonly status: number;
135
+ readonly body: unknown;
136
+ readonly service: string;
137
+ readonly path: string;
138
+ constructor(service: string, path: string, status: number, body: unknown, message?: string);
139
+ }
140
+
141
+ interface CrmContactSummary {
142
+ id: string;
143
+ firstName: string | null;
144
+ lastName: string | null;
145
+ email: string | null;
146
+ phone: string | null;
147
+ company: string | null;
148
+ leadStatus: string | null;
149
+ lifecycleStage: string | null;
150
+ updatedAt: string | null;
151
+ }
152
+ interface ListContactsOptions {
153
+ /** Full-text search string. */
154
+ q?: string;
155
+ /** Page number, 1-indexed. */
156
+ page?: number;
157
+ /** Page size. */
158
+ pageSize?: number;
159
+ }
160
+ interface ListContactsResult {
161
+ contacts: CrmContactSummary[];
162
+ total: number;
163
+ page: number;
164
+ pageSize: number;
165
+ }
166
+ /**
167
+ * CRM module — thin typed wrapper over the TopoloCRM HTTP API.
168
+ *
169
+ * Cross-org isolation: there is intentionally NO `orgId` parameter anywhere in
170
+ * this module. The organization is derived entirely from the auth credential
171
+ * by the CRM backend, which scopes all SQL queries by `org_slug`/`org_id`.
172
+ */
173
+ declare class CrmModule {
174
+ private readonly client;
175
+ constructor(client: TopoloClient);
176
+ listContacts(options?: ListContactsOptions): Promise<ListContactsResult>;
177
+ getContact(contactId: string): Promise<CrmContactSummary | null>;
178
+ }
179
+
180
+ /**
181
+ * Identity module — wraps TopoloAuth's caller-identity endpoints.
182
+ *
183
+ * All responses are scoped to the organization the credential was issued for;
184
+ * there is no way to ask this module about a different org.
185
+ */
186
+ declare class IdentityModule {
187
+ private readonly client;
188
+ constructor(client: TopoloClient);
189
+ /** Returns the resolved user, organization, and permission set for the
190
+ * current credential. Equivalent to `TopoloClient.introspect()` — exposed
191
+ * on this module for symmetry with the other domain modules. */
192
+ whoami(): Promise<CredentialIntrospection>;
193
+ }
194
+
195
+ interface DeviceAuthorizationResponse {
196
+ device_code: string;
197
+ user_code: string;
198
+ verification_uri: string;
199
+ verification_uri_complete?: string;
200
+ expires_in: number;
201
+ interval: number;
202
+ }
203
+ interface TokenResponse {
204
+ access_token: string;
205
+ token_type: 'Bearer';
206
+ expires_in: number;
207
+ refresh_token?: string;
208
+ scope?: string;
209
+ }
210
+ interface OAuthHelperOptions {
211
+ /** Overrides for TopoloAuth URL. Staging/dev should pass a mapping. */
212
+ serviceUrls?: Partial<Record<ServiceId, string>>;
213
+ /** Injected fetch. Defaults to global fetch. */
214
+ fetch?: typeof fetch;
215
+ }
216
+ /**
217
+ * Thin client over the Topolo OAuth 2.1 authorization server. Intended for
218
+ * first-party tooling (the CLI) and registered third-party agents. Issued
219
+ * access tokens plug into `createTopolo({ credential: { kind: 'access_token', ... } })`.
220
+ */
221
+ declare class TopoloOAuth {
222
+ #private;
223
+ private readonly baseUrl;
224
+ private readonly fetchImpl;
225
+ constructor(options?: OAuthHelperOptions);
226
+ /**
227
+ * RFC 8628 device authorization grant — starts the flow by asking the server
228
+ * for a device_code + user_code. The caller prints the user_code +
229
+ * verification_uri and then polls `pollDeviceToken` until approval.
230
+ */
231
+ requestDeviceCode(params: {
232
+ clientId: string;
233
+ scope?: string | string[];
234
+ }): Promise<DeviceAuthorizationResponse>;
235
+ /**
236
+ * Polls the token endpoint once. Returns the token pair on success, or
237
+ * throws a `TopoloAuthError` whose `code` is one of the RFC 8628 poll
238
+ * states: `authorization_pending`, `slow_down`, `access_denied`,
239
+ * `expired_token`. Callers should back off on `slow_down`, wait at least
240
+ * one `interval` on `authorization_pending`, and give up on the rest.
241
+ */
242
+ pollDeviceToken(params: {
243
+ clientId: string;
244
+ deviceCode: string;
245
+ clientSecret?: string;
246
+ }): Promise<TokenResponse>;
247
+ /** Exchange an authorization code (with PKCE) for tokens. */
248
+ exchangeAuthorizationCode(params: {
249
+ clientId: string;
250
+ code: string;
251
+ redirectUri: string;
252
+ codeVerifier?: string;
253
+ clientSecret?: string;
254
+ }): Promise<TokenResponse>;
255
+ /** Rotate a refresh token for a new token pair. */
256
+ refreshToken(params: {
257
+ clientId: string;
258
+ refreshToken: string;
259
+ clientSecret?: string;
260
+ }): Promise<TokenResponse>;
261
+ }
262
+
263
+ /**
264
+ * Convenience factory that returns a ready-to-use typed Topolo client plus
265
+ * domain module surfaces (identity, crm, ...).
266
+ *
267
+ * Phase 1 exposes identity + crm; subsequent phases add more modules
268
+ * incrementally as each app's API contract stabilizes.
269
+ */
270
+ declare function createTopolo(options: TopoloClientOptions): {
271
+ client: TopoloClient;
272
+ identity: IdentityModule;
273
+ crm: CrmModule;
274
+ };
275
+ type Topolo = ReturnType<typeof createTopolo>;
276
+
277
+ export { type AgentIdentity, type CredentialIntrospection, type CrmContactSummary, CrmModule, DEFAULT_SERVICE_URLS, type DeviceAuthorizationResponse, IdentityModule, type ListContactsOptions, type ListContactsResult, type OAuthHelperOptions, PLATFORM_SERVICE_IDS, type RequestOptions, type ServiceId, type TokenResponse, type Topolo, TopoloAuthError, TopoloClient, type TopoloClientOptions, type TopoloCredential, TopoloHttpError, TopoloOAuth, TopoloPermissionError, TopoloSdkError, createTopolo, resolveServiceUrl };