@myapihq/sdk 2.4.1 → 2.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/hq.ts DELETED
@@ -1,393 +0,0 @@
1
- import { request } from './client';
2
- import { HQ_BASE as BASE_URL } from './config';
3
- import type { Exposes } from './exposes';
4
-
5
- export const EXPOSES: Exposes = [
6
- 'POST /hq/account/anonymous',
7
- 'POST /hq/account/send-code',
8
- 'POST /hq/account/verify-code',
9
- 'PATCH /hq/account/upgrade',
10
- 'GET /hq/account/me',
11
- 'GET /hq/account/free-tier',
12
- 'POST /hq/account/create/key',
13
- 'GET /hq/account/keys',
14
- 'POST /hq/account/keys/revoke-all',
15
- 'DELETE /hq/account/delete/key/{key_id}',
16
- 'PATCH /hq/account/spend-cap',
17
- // Backend (2026-06-06): account-scoped mailing address — required for
18
- // transactional email per CAN-SPAM. PATCH sets, GET reads (null when
19
- // unset). Hard-gates POST /email/send with MAILING_ADDRESS_REQUIRED.
20
- 'PATCH /hq/account/mailing-address',
21
- 'GET /hq/account/mailing-address',
22
- 'POST /hq/orgs',
23
- 'GET /hq/orgs',
24
- 'GET /hq/orgs/{org_id}',
25
- 'PATCH /hq/orgs/{org_id}',
26
- 'DELETE /hq/orgs/{org_id}',
27
- 'POST /hq/org-imports',
28
- 'GET /hq/org-imports/{import_id}',
29
- 'POST /hq/org-imports/{import_id}/confirm',
30
- 'GET /hq/billing/balance',
31
- 'GET /hq/billing/history',
32
- 'GET /hq/billing/usage',
33
- 'POST /hq/billing/setup-payment',
34
- 'POST /hq/billing/topup',
35
- 'GET /hq/billing/auto-recharge',
36
- 'PUT /hq/billing/auto-recharge',
37
- 'DELETE /hq/billing/auto-recharge',
38
- 'GET /hq/orgs/{org_id}/doctor',
39
- ];
40
-
41
- export interface Org {
42
- id: string;
43
- name: string;
44
- tagline?: string;
45
- description?: string;
46
- business_sector?: string;
47
- logo_url?: string;
48
- favicon_url?: string;
49
- og_image_url?: string;
50
- color_palette?: Record<string, string>;
51
- font_family?: string;
52
- imagery_style?: string;
53
- headline?: string;
54
- subheadline?: string;
55
- cta_text?: string;
56
- value_propositions?: string[];
57
- social_links?: Record<string, string>;
58
- canonical_url?: string;
59
- privacy_policy_url?: string;
60
- cookie_policy_url?: string;
61
- terms_url?: string;
62
- gdpr_enabled?: boolean;
63
- default_language?: string;
64
- tracking?: Record<string, unknown>;
65
- preview_subdomain?: string;
66
- created_at: string;
67
- updated_at: string;
68
- }
69
-
70
- export type OrgPayload = Omit<Org, 'id' | 'created_at' | 'updated_at'>;
71
-
72
- export interface AuthResult {
73
- api_key: string;
74
- account_id: string;
75
- default_org: string;
76
- default_funnel: string;
77
- }
78
-
79
- export interface AccountInfo {
80
- account_id: string;
81
- email?: string;
82
- is_anonymous?: boolean;
83
- // Account-level spend cap (IAM "Layer 2"). Present only when a cap is set.
84
- spend_cap_cents?: number;
85
- current_period_spend_cents?: number;
86
- }
87
-
88
- // ── Capability IAM (design-iam-capability-keys-2026-05-15) ───────────────────
89
-
90
- export type GrantAccess = 'read' | 'write';
91
-
92
- // A key's slot grants: slot name (or "*" wildcard) → access level.
93
- // `write` implies `read`. A slot absent from the map = no access.
94
- export type Grants = Record<string, GrantAccess>;
95
-
96
- // The closed grantable-slot vocabulary. Mirrors `iam.GrantableSlots` in the
97
- // backend — keep in sync. Used to validate `--grant` client-side before the
98
- // network call. "*" is also valid in a Grants map but is not a slot name.
99
- export const GRANTABLE_SLOTS = [
100
- 'domain', 'email', 'funnel', 'storage', 'image', 'webhook', 'workflow',
101
- 'function', 'url', 'people', 'company', 'audience', 'llm', 'database', 'crm',
102
- ] as const;
103
-
104
- export type KeyKind = 'account' | 'function' | 'manual';
105
- export type SpendCapPeriod = 'month' | 'day' | 'none';
106
-
107
- // Mirrors the backend `keyView`. `api_key` is present ONLY in the create
108
- // response (returned once). `current_period_spend_cents` is present on list
109
- // (metered), absent on create (a new key has no spend yet).
110
- export interface ApiKey {
111
- id: string;
112
- name: string;
113
- prefix: string;
114
- kind: KeyKind;
115
- org_id: string | null;
116
- grants: Grants;
117
- spend_cap_cents: number | null;
118
- spend_cap_period: string;
119
- current_period_spend_cents?: number;
120
- api_key?: string;
121
- }
122
-
123
- export async function createAnonymousAccount(): Promise<AuthResult & { subdomain_url: string }> {
124
- return request('POST', `${BASE_URL}/hq/account/anonymous`, undefined, {});
125
- }
126
-
127
- export async function sendCode(email: string): Promise<void> {
128
- return request('POST', `${BASE_URL}/hq/account/send-code`, undefined, { email });
129
- }
130
-
131
- export async function verifyCode(email: string, code: string): Promise<AuthResult> {
132
- return request('POST', `${BASE_URL}/hq/account/verify-code`, undefined, { email, code });
133
- }
134
-
135
- export async function upgradeAccount(apiKey: string, email: string): Promise<AuthResult> {
136
- return request('PATCH', `${BASE_URL}/hq/account/upgrade`, apiKey, { email });
137
- }
138
-
139
- // The backend returns an array of per-service free-tier rows, or null if the
140
- // account has no free-tier allocations. (Previously typed as a single scalar
141
- // object — that was incorrect and surfaced as `undefined/undefined` in the
142
- // CLI's whoami output.)
143
- export type FreeTierEntry = {
144
- service: string;
145
- used: number;
146
- allowance: number;
147
- remaining?: number;
148
- reset_at?: string;
149
- };
150
-
151
- export async function getFreeTier(apiKey: string): Promise<FreeTierEntry[] | null> {
152
- return request('GET', `${BASE_URL}/hq/account/free-tier`, apiKey);
153
- }
154
-
155
- export async function getAccount(apiKey: string): Promise<AccountInfo> {
156
- return request('GET', `${BASE_URL}/hq/account/me`, apiKey);
157
- }
158
-
159
- // Mailing address — required for transactional `email send` (CAN-SPAM).
160
- // `getMailingAddress` returns `null` for `mailing_address` when unset.
161
- export async function getMailingAddress(apiKey: string): Promise<{ mailing_address: string | null }> {
162
- return request('GET', `${BASE_URL}/hq/account/mailing-address`, apiKey);
163
- }
164
-
165
- export async function setMailingAddress(apiKey: string, mailingAddress: string): Promise<{ mailing_address: string }> {
166
- return request('PATCH', `${BASE_URL}/hq/account/mailing-address`, apiKey, { mailing_address: mailingAddress });
167
- }
168
-
169
- export interface CreateApiKeyOptions {
170
- // Slot grants. Omitted → backend defaults to unrestricted ({"*":"write"}).
171
- grants?: Grants;
172
- // Lock the key to a single org. Omitted → account-wide.
173
- orgId?: string;
174
- // Per-key spend ceiling, in cents. Omitted → no per-key cap.
175
- spendCapCents?: number;
176
- }
177
-
178
- // Mints a manual API key. The requested (grants, org, spend cap) must be a
179
- // subset of the calling key's authority — the backend rejects escalation
180
- // with 403 SCOPE_FORBIDDEN. The full `api_key` is in the response ONCE.
181
- export async function createApiKey(apiKey: string, name: string, opts: CreateApiKeyOptions = {}): Promise<ApiKey> {
182
- const body: Record<string, unknown> = { name };
183
- if (opts.grants) body.grants = opts.grants;
184
- if (opts.orgId) body.org_id = opts.orgId;
185
- if (opts.spendCapCents != null) body.spend_cap_cents = opts.spendCapCents;
186
- return request('POST', `${BASE_URL}/hq/account/create/key`, apiKey, body);
187
- }
188
-
189
- export async function listApiKeys(apiKey: string): Promise<ApiKey[]> {
190
- return request('GET', `${BASE_URL}/hq/account/keys`, apiKey);
191
- }
192
-
193
- export async function revokeApiKey(apiKey: string, keyId: string): Promise<void> {
194
- return request('DELETE', `${BASE_URL}/hq/account/delete/key/${encodeURIComponent(keyId)}`, apiKey);
195
- }
196
-
197
- // The kill switch. With no `kind`, revokes every active key in the account —
198
- // including the caller's own; recovery is re-auth via login. A `kind` narrows
199
- // it to one provenance class.
200
- export async function revokeAllKeys(apiKey: string, kind?: KeyKind): Promise<{ revoked: number }> {
201
- return request('POST', `${BASE_URL}/hq/account/keys/revoke-all`, apiKey, kind ? { kind } : {});
202
- }
203
-
204
- // Sets or clears the account-level spend ceiling (IAM "Layer 2"). Pass
205
- // `null` to clear. `period` defaults to 'month' server-side.
206
- export async function setAccountSpendCap(
207
- apiKey: string,
208
- spendCapCents: number | null,
209
- period?: SpendCapPeriod,
210
- ): Promise<{ spend_cap_cents: number | null; spend_cap_period: string }> {
211
- const body: Record<string, unknown> = { spend_cap_cents: spendCapCents };
212
- if (period) body.period = period;
213
- return request('PATCH', `${BASE_URL}/hq/account/spend-cap`, apiKey, body);
214
- }
215
-
216
- export async function createOrg(apiKey: string, payload: OrgPayload): Promise<Org> {
217
- return request('POST', `${BASE_URL}/hq/orgs`, apiKey, payload);
218
- }
219
-
220
- export async function importOrg(apiKey: string, orgId: string, domain: string, autoAccept?: boolean): Promise<{ job_id: string; status: string }> {
221
- return request('POST', `${BASE_URL}/hq/org-imports`, apiKey, { domain, org_id: orgId, auto_accept: autoAccept });
222
- }
223
-
224
- export async function getOrgImportStatus(apiKey: string, jobId: string): Promise<{ status: string; brand_preview: unknown }> {
225
- return request('GET', `${BASE_URL}/hq/org-imports/${encodeURIComponent(jobId)}`, apiKey);
226
- }
227
-
228
- export async function confirmOrgImport(apiKey: string, jobId: string, overrides?: Partial<OrgPayload>): Promise<Org> {
229
- return request('POST', `${BASE_URL}/hq/org-imports/${encodeURIComponent(jobId)}/confirm`, apiKey, overrides);
230
- }
231
-
232
- export async function listOrgs(apiKey: string): Promise<Org[]> {
233
- return request('GET', `${BASE_URL}/hq/orgs`, apiKey);
234
- }
235
-
236
- export async function getOrg(apiKey: string, orgId: string): Promise<Org> {
237
- return request('GET', `${BASE_URL}/hq/orgs/${encodeURIComponent(orgId)}`, apiKey);
238
- }
239
-
240
- export async function updateOrg(apiKey: string, orgId: string, payload: Partial<OrgPayload>): Promise<Org> {
241
- return request('PATCH', `${BASE_URL}/hq/orgs/${encodeURIComponent(orgId)}`, apiKey, payload);
242
- }
243
-
244
- export async function deleteOrg(apiKey: string, orgId: string): Promise<void> {
245
- return request('DELETE', `${BASE_URL}/hq/orgs/${encodeURIComponent(orgId)}`, apiKey);
246
- }
247
-
248
- export async function getBalance(apiKey: string): Promise<{ balance_display: string; credits_display: string; has_payment_method: boolean }> {
249
- return request('GET', `${BASE_URL}/hq/billing/balance`, apiKey);
250
- }
251
-
252
- export async function getBillingHistory(apiKey: string): Promise<{ type: string; amount_display: string; status: string; created_at: string; }[]> {
253
- return request('GET', `${BASE_URL}/hq/billing/history`, apiKey);
254
- }
255
-
256
- // One service's rolled-up spend over the usage window.
257
- export interface ServiceUsage {
258
- service: string;
259
- requests: number;
260
- cost_display: string;
261
- }
262
-
263
- // Spend rolled up by service — the accurate "where is my money going" view.
264
- // Aggregates every billing event, unlike the flat/capped history log.
265
- export interface BillingUsage {
266
- period: 'month' | '30d';
267
- since: string;
268
- services: ServiceUsage[];
269
- total_display: string;
270
- }
271
-
272
- // getBillingUsage rolls up spend by service for a window: the current
273
- // calendar month (default) or the trailing 30 days ('30d').
274
- export async function getBillingUsage(apiKey: string, period?: 'month' | '30d'): Promise<BillingUsage> {
275
- const query = period ? `?period=${encodeURIComponent(period)}` : '';
276
- return request('GET', `${BASE_URL}/hq/billing/usage${query}`, apiKey);
277
- }
278
-
279
- export async function setupPayment(apiKey: string): Promise<{ url: string }> {
280
- return request('POST', `${BASE_URL}/hq/billing/setup-payment`, apiKey);
281
- }
282
-
283
- export async function topUp(apiKey: string, amountDollars: number): Promise<{ new_balance_display: string }> {
284
- return request('POST', `${BASE_URL}/hq/billing/topup`, apiKey, { amount_dollars: amountDollars });
285
- }
286
-
287
- // ── Auto-recharge ──────────────────────────────────────────────────────
288
- // Keeps the prepaid wallet funded without a human in the loop: when the
289
- // balance drops below `threshold_cents`, the backend charges the saved card
290
- // `amount_cents` off-session, bounded by `monthly_cap_cents`. Opt-in, off by
291
- // default; enabling requires a saved payment method.
292
-
293
- export type RechargeStatus = 'succeeded' | 'failed' | 'capped' | 'no_pm' | 'pending';
294
-
295
- export interface AutoRechargeConfig {
296
- enabled: boolean;
297
- threshold_cents: number | null;
298
- amount_cents: number | null;
299
- monthly_cap_cents: number | null;
300
- // Auto-recharge dollars charged this calendar month (vs the cap).
301
- month_to_date_recharged_cents: number;
302
- has_payment_method: boolean;
303
- last_recharge_status: RechargeStatus | null;
304
- last_recharge_attempt_at: string | null;
305
- }
306
-
307
- export async function getAutoRecharge(apiKey: string): Promise<AutoRechargeConfig> {
308
- return request('GET', `${BASE_URL}/hq/billing/auto-recharge`, apiKey);
309
- }
310
-
311
- export interface SetAutoRechargeInput {
312
- enabled: boolean;
313
- threshold_cents?: number;
314
- amount_cents?: number;
315
- monthly_cap_cents?: number;
316
- }
317
-
318
- // Enable/update auto-recharge. The backend validates invariants and rejects
319
- // with a 400 (`MISSING_FIELDS`, `AMOUNT_BELOW_FLOOR` [$5 floor],
320
- // `AMOUNT_LT_THRESHOLD`, `CAP_LT_AMOUNT`, `NO_PAYMENT_METHOD`) — surfaced as a
321
- // MyApiError. Returns the full config on success.
322
- export async function setAutoRecharge(apiKey: string, input: SetAutoRechargeInput): Promise<AutoRechargeConfig> {
323
- return request('PUT', `${BASE_URL}/hq/billing/auto-recharge`, apiKey, input);
324
- }
325
-
326
- // Disable auto-recharge (the threshold/amount/cap are preserved for easy
327
- // re-enable). Backend returns 204 No Content.
328
- export async function disableAutoRecharge(apiKey: string): Promise<void> {
329
- return request('DELETE', `${BASE_URL}/hq/billing/auto-recharge`, apiKey);
330
- }
331
-
332
- // ── Org doctor ─────────────────────────────────────────────────────────
333
- // Aggregated consistency report across slots. The endpoint resolves the
334
- // reported org from the API key's binding, so the `{org_id}` path param
335
- // is currently positional/cosmetic (pass your default — the response will
336
- // indicate which org was actually scored under `report.org_id`).
337
-
338
- export type DoctorSeverity = 'ok' | 'warn' | 'crit';
339
-
340
- export interface DoctorEntityRef {
341
- slot: string; // 'funnel' | 'webhook' | 'workflow' | 'container' | 'domain' | …
342
- id: string;
343
- name?: string;
344
- }
345
-
346
- export interface DoctorIssue {
347
- id: string; // stable across runs, dedupable
348
- severity: DoctorSeverity;
349
- scope: string; // logical scope, e.g. 'funnel/<slug>'
350
- entity?: DoctorEntityRef;
351
- // Open string — do not exhaustively switch on it. Backend categories,
352
- // per the OpenAPI schema: 'reference_integrity' | 'orphan' | 'activity' |
353
- // 'security' | 'billing' | 'internal'. The backend may also emit
354
- // slash-namespaced subcategories (e.g. 'security/spf', 'security/dmarc');
355
- // if you branch on category, match the parent by splitting on the first
356
- // '/'. The CLI's local augmentation adds one more category not in the
357
- // schema: 'network' (the DNS + HTTP reachability probes).
358
- category?: string;
359
- message: string;
360
- hint?: string;
361
- // True when the issue is platform-side and NOT actionable by the customer
362
- // (e.g. a degraded internal dependency the MyAPI team owns). The backend
363
- // sets this together with `category: 'internal'` and a customer-appropriate
364
- // message; the operator detail is routed to an internal sink, not here.
365
- // Consumers should surface these for transparency but must NOT count them
366
- // as customer-actionable failures (they don't fail `doctor`'s exit code).
367
- operator_only?: boolean;
368
- }
369
-
370
- export interface DoctorSection {
371
- name: string;
372
- summary: string;
373
- issues: DoctorIssue[];
374
- // Authoritative count of resources of this section's kind in the org,
375
- // independent of how many issues were emitted. Lets a consumer tell
376
- // "zero resources" apart from "resources present, all healthy" without
377
- // inferring it from `issues.length` (which breaks the moment the backend
378
- // stops emitting an `ok` row per healthy resource). Optional: older
379
- // backends omit it, and consumers must fall back to the issue-count proxy.
380
- resource_count?: number;
381
- }
382
-
383
- export interface DoctorReport {
384
- org_id: string;
385
- generated_at: string; // RFC3339
386
- cache_ttl_seconds?: number;
387
- sections: DoctorSection[];
388
- totals: { ok: number; warn: number; crit: number };
389
- }
390
-
391
- export async function getDoctor(apiKey: string, orgId: string): Promise<DoctorReport> {
392
- return request('GET', `${BASE_URL}/hq/orgs/${encodeURIComponent(orgId)}/doctor`, apiKey);
393
- }
package/src/image.ts DELETED
@@ -1,67 +0,0 @@
1
- import { request } from './client';
2
- import { IMAGE_BASE as BASE_URL } from './config';
3
- import type { Exposes } from './exposes';
4
-
5
- export const EXPOSES: Exposes = [
6
- 'POST /image/orgs/{org_id}/generate',
7
- 'GET /image/orgs/{org_id}/jobs/{job_id}',
8
- 'GET /image/orgs/{org_id}/list',
9
- 'GET /image/orgs/{org_id}/models',
10
- 'DELETE /image/orgs/{org_id}/images/{image_id}',
11
- ];
12
-
13
- // Catalog row from GET /image/.../models. Mirrors the shape of the LLM
14
- // model catalog but with per-image pricing (image gen is priced per
15
- // image, not per token).
16
- export interface ImageModel {
17
- id: string;
18
- kind: 'image';
19
- aspect_ratios: string[]; // e.g. ['1:1', '16:9', '9:16', '4:3', '3:4']
20
- default_size: string; // e.g. '1024x1024'
21
- cost_per_image_usd: number;
22
- }
23
-
24
- export interface ImageModelsResponse {
25
- models: ImageModel[];
26
- }
27
-
28
-
29
-
30
- export interface ImageJob {
31
- job_id: string;
32
- status: 'pending' | 'processing' | 'completed' | 'failed';
33
- url?: string;
34
- error?: string;
35
- prompt: string;
36
- aspect_ratio: string;
37
- created_at: string;
38
- }
39
-
40
- export async function generateImage(apiKey: string, orgId: string, payload: {
41
- prompt: string;
42
- // Optional model id; must be in GET /image/.../models. Defaults
43
- // server-side to gemini-2.5-flash-image.
44
- model?: string;
45
- aspect_ratio?: '1:1' | '16:9' | '9:16' | '4:3' | '3:4';
46
- style?: string;
47
- colors?: string;
48
- has_text?: boolean;
49
- }): Promise<{ job_id: string; status: string }> {
50
- return request('POST', `${BASE_URL}/image/orgs/${encodeURIComponent(orgId)}/generate`, apiKey, payload);
51
- }
52
-
53
- export async function getImageJob(apiKey: string, orgId: string, jobId: string): Promise<ImageJob> {
54
- return request('GET', `${BASE_URL}/image/orgs/${encodeURIComponent(orgId)}/jobs/${encodeURIComponent(jobId)}`, apiKey);
55
- }
56
-
57
- export async function listImages(apiKey: string, orgId: string): Promise<ImageJob[]> {
58
- return request('GET', `${BASE_URL}/image/orgs/${encodeURIComponent(orgId)}/list`, apiKey);
59
- }
60
-
61
- export async function deleteImage(apiKey: string, orgId: string, jobId: string): Promise<void> {
62
- return request('DELETE', `${BASE_URL}/image/orgs/${encodeURIComponent(orgId)}/images/${encodeURIComponent(jobId)}`, apiKey);
63
- }
64
-
65
- export async function listModels(apiKey: string, orgId: string): Promise<ImageModelsResponse> {
66
- return request('GET', `${BASE_URL}/image/orgs/${encodeURIComponent(orgId)}/models`, apiKey);
67
- }
package/src/index.ts DELETED
@@ -1,32 +0,0 @@
1
- export * from './types';
2
- export * from './client';
3
- export * from './funds';
4
- // Note: config constants (STORAGE_BASE etc.) are NOT re-exported from the
5
- // barrel. TypeScript compiles `export * from './config'` to a runtime
6
- // `__exportStar` call that Node's cjs-module-lexer can't see through, so
7
- // downstream ESM consumers fail with "Named export not found" against the
8
- // CJS-compiled SDK. If you need a base URL in a downstream package, mirror
9
- // the constant locally — it's one line and the comment makes it obvious.
10
- export * as hq from './hq';
11
- export * as auth from './auth';
12
- export * as domain from './domain';
13
- export * as email from './email';
14
- export * as funnel from './funnel';
15
- export * as image from './image';
16
- export * as pixel from './pixel';
17
- export * as storage from './storage';
18
- export * as webhook from './webhook';
19
- export * as workflow from './workflow';
20
- export * as url from './url';
21
- export * as people from './people';
22
- export * as company from './company';
23
- export * as audience from './audience';
24
- export * as llm from './llm';
25
- export * as database from './database';
26
- export * as crm from './crm';
27
- export * as fn from './function';
28
- export * as payments from './payments';
29
- export * as container from './container';
30
- export * as git from './git';
31
- export * as queue from './queue';
32
- export * as task from './task';
package/src/llm.ts DELETED
@@ -1,189 +0,0 @@
1
- // Scaffolded from schema by scripts/scaffold-sdk.js — DO NOT remove the
2
- // EXPOSES array (the coverage tool verifies it against the live schema).
3
- // Types and function names ARE editable; re-running the scaffolder against
4
- // a service whose config has been changed will diff vs. existing file
5
- // (use --dry-run to inspect).
6
-
7
- import { request } from './client';
8
- import { LLM_BASE as BASE_URL } from './config';
9
- import type { Exposes } from './exposes';
10
-
11
- export const EXPOSES: Exposes = [
12
- 'POST /llm/orgs/{org_id}/complete',
13
- 'POST /llm/orgs/{org_id}/embed',
14
- 'GET /llm/orgs/{org_id}/models',
15
- 'POST /llm/orgs/{org_id}/tasks/{verb}',
16
- ];
17
-
18
- // ── Raw surface ──────────────────────────────────────────────────────────
19
- // Backed *exclusively* by self-hosted open-source models on our own TPU.
20
- // The customer picks the model id from the catalog. Proprietary models are
21
- // not callable here — that rule is what keeps the LLM slot off the
22
- // reseller framing.
23
-
24
- export type Role = 'system' | 'user' | 'assistant';
25
-
26
- export interface Message {
27
- role: Role;
28
- content: string;
29
- }
30
-
31
- export interface CompleteRequest {
32
- model: string;
33
- messages: Message[];
34
- max_tokens?: number;
35
- temperature?: number;
36
- stop?: string[];
37
- }
38
-
39
- export interface RawUsage {
40
- input_tokens: number;
41
- output_tokens?: number;
42
- cost_cents: number;
43
- }
44
-
45
- export interface CompleteResponse {
46
- model: string;
47
- content: string;
48
- finish_reason: string; // 'stop' | 'length' | 'filter'
49
- usage: RawUsage;
50
- }
51
-
52
- export interface EmbedRequest {
53
- model: string;
54
- input: string | string[]; // single string or batch
55
- }
56
-
57
- // Always returned as an array — even for a single-string input you get a
58
- // one-element array. Each element is the dense vector as a flat number[].
59
- export interface EmbedResponse {
60
- model: string;
61
- embeddings: number[][];
62
- usage: RawUsage;
63
- }
64
-
65
- // Catalog row. `chat` models have context_window + output_cost_per_1m_cents;
66
- // `embed` models have dimensions instead. Pricing is in cents per 1M tokens
67
- // (the platform's native unit — cost_cents everywhere).
68
- export interface Model {
69
- id: string;
70
- kind: 'chat' | 'embed';
71
- context_window?: number;
72
- dimensions?: number;
73
- input_cost_per_1m_cents: number;
74
- output_cost_per_1m_cents?: number;
75
- }
76
-
77
- export interface ModelsResponse {
78
- models: Model[];
79
- }
80
-
81
- export async function complete(apiKey: string, orgId: string, req: CompleteRequest): Promise<CompleteResponse> {
82
- return request('POST', `${BASE_URL}/llm/orgs/${encodeURIComponent(orgId)}/complete`, apiKey, req);
83
- }
84
-
85
- export async function embed(apiKey: string, orgId: string, req: EmbedRequest): Promise<EmbedResponse> {
86
- return request('POST', `${BASE_URL}/llm/orgs/${encodeURIComponent(orgId)}/embed`, apiKey, req);
87
- }
88
-
89
- export async function listModels(apiKey: string, orgId: string): Promise<ModelsResponse> {
90
- return request('GET', `${BASE_URL}/llm/orgs/${encodeURIComponent(orgId)}/models`, apiKey);
91
- }
92
-
93
- // ── Verb surface — objective-based completion ────────────────────────────
94
- // The customer asks for a task done (classify / extract / summarize /
95
- // draft); the model is implementation detail and is never named in the
96
- // response. This is where any future proprietary model lives — wrapped, not
97
- // resold.
98
-
99
- export type Verb = 'classify' | 'extract' | 'summarize' | 'draft';
100
-
101
- /** Routing hint; opaque — the server picks the model. With one served
102
- * model today every tier resolves to it. `tier_used` echoes back what was
103
- * actually selected. */
104
- export type Tier = 'fast' | 'reasoning' | 'cheap';
105
-
106
- export interface VerbUsage {
107
- tier_used: Tier;
108
- tokens_in: number;
109
- tokens_out: number;
110
- cost_cents: number;
111
- }
112
-
113
- interface VerbBase {
114
- /** Routing hint. Defaults to 'fast'. */
115
- tier?: Tier;
116
- }
117
-
118
- export interface ClassifyRequest extends VerbBase {
119
- input: string;
120
- /** Candidate labels — pick from this set. */
121
- labels: string[];
122
- /** When true, multiple labels may apply; response carries `labels[]`. */
123
- multi?: boolean;
124
- }
125
-
126
- export interface ClassifyResponse {
127
- data: { label?: string; labels?: string[] };
128
- usage: VerbUsage;
129
- }
130
-
131
- export interface ExtractRequest extends VerbBase {
132
- input: string;
133
- /** A JSON Schema (object) the extracted data must conform to. */
134
- schema: Record<string, unknown>;
135
- }
136
-
137
- export interface ExtractResponse {
138
- data: { data: Record<string, unknown> };
139
- usage: VerbUsage;
140
- }
141
-
142
- export interface SummarizeRequest extends VerbBase {
143
- input: string;
144
- /** `brief` (1-2 sentences), `exec` (3-4 decision-maker sentences), or
145
- * `bullet` (short bulleted list). Default `brief`. */
146
- style?: 'brief' | 'exec' | 'bullet';
147
- }
148
-
149
- export interface SummarizeResponse {
150
- data: { summary: string };
151
- usage: VerbUsage;
152
- }
153
-
154
- export interface DraftRequest extends VerbBase {
155
- /** Optional source content — may be empty when `context`/`prompt` carry
156
- * the brief. */
157
- input?: string;
158
- /** What to write — `email`, `message`, `reply`, etc. */
159
- kind: string;
160
- /** Free-form structured context (recipient, tone, facts). */
161
- context?: Record<string, unknown>;
162
- /** Free-text instructions to the writer. */
163
- prompt?: string;
164
- }
165
-
166
- export interface DraftResponse {
167
- data: { text: string };
168
- usage: VerbUsage;
169
- }
170
-
171
- function verbUrl(orgId: string, verb: Verb): string {
172
- return `${BASE_URL}/llm/orgs/${encodeURIComponent(orgId)}/tasks/${verb}`;
173
- }
174
-
175
- export async function classify(apiKey: string, orgId: string, req: ClassifyRequest): Promise<ClassifyResponse> {
176
- return request('POST', verbUrl(orgId, 'classify'), apiKey, req);
177
- }
178
-
179
- export async function extract(apiKey: string, orgId: string, req: ExtractRequest): Promise<ExtractResponse> {
180
- return request('POST', verbUrl(orgId, 'extract'), apiKey, req);
181
- }
182
-
183
- export async function summarize(apiKey: string, orgId: string, req: SummarizeRequest): Promise<SummarizeResponse> {
184
- return request('POST', verbUrl(orgId, 'summarize'), apiKey, req);
185
- }
186
-
187
- export async function draft(apiKey: string, orgId: string, req: DraftRequest): Promise<DraftResponse> {
188
- return request('POST', verbUrl(orgId, 'draft'), apiKey, req);
189
- }