@myapihq/sdk 1.3.13 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth.d.ts +58 -0
- package/dist/auth.js +76 -0
- package/dist/client.d.ts +6 -0
- package/dist/client.js +16 -3
- package/dist/config.d.ts +1 -0
- package/dist/config.js +2 -1
- package/dist/container.d.ts +8 -0
- package/dist/container.js +26 -2
- package/dist/crm.d.ts +1 -1
- package/dist/crm.js +4 -1
- package/dist/database.js +4 -1
- package/dist/domain.d.ts +1 -1
- package/dist/email.d.ts +0 -74
- package/dist/email.js +1 -70
- package/dist/function.d.ts +4 -0
- package/dist/function.js +8 -0
- package/dist/funnel.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/dist/payments.d.ts +1 -0
- package/dist/payments.js +1 -1
- package/dist/queue.d.ts +1 -0
- package/dist/queue.js +5 -0
- package/dist/services.js +15 -6
- package/dist/storage.d.ts +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/webhook.d.ts +4 -0
- package/dist/workflow.d.ts +11 -1
- package/dist/workflow.js +12 -2
- package/package.json +1 -1
- package/src/auth.ts +129 -0
- package/src/client.ts +36 -4
- package/src/config.ts +1 -0
- package/src/container.ts +53 -3
- package/src/crm.ts +7 -3
- package/src/database.ts +5 -2
- package/src/domain.ts +1 -1
- package/src/email.ts +1 -78
- package/src/function.ts +12 -0
- package/src/funnel.ts +1 -1
- package/src/index.ts +1 -0
- package/src/payments.ts +2 -1
- package/src/queue.ts +5 -0
- package/src/services.ts +15 -6
- package/src/storage.ts +2 -0
- package/src/types.ts +3 -1
- package/src/webhook.ts +10 -0
- package/src/workflow.ts +34 -3
package/dist/services.js
CHANGED
|
@@ -28,6 +28,15 @@ exports.SERVICES = [
|
|
|
28
28
|
status: 'ga',
|
|
29
29
|
keywords: k('auth', 'identity', 'billing', 'organization'),
|
|
30
30
|
},
|
|
31
|
+
{
|
|
32
|
+
module: 'auth',
|
|
33
|
+
skill: 'my-auth-api',
|
|
34
|
+
domain: 'myauthapi.com',
|
|
35
|
+
description: 'Managed OIDC identity provider for the end users of apps built on MyAPI. Per-org auth tenant + OIDC clients, RS256/JWKS, hosted login, managed Google sign-in. A Kinde alternative.',
|
|
36
|
+
category: 'identity',
|
|
37
|
+
status: 'ga',
|
|
38
|
+
keywords: k('auth', 'oidc', 'oauth', 'login', 'identity', 'sso'),
|
|
39
|
+
},
|
|
31
40
|
{
|
|
32
41
|
module: 'domain',
|
|
33
42
|
skill: 'my-domain-api',
|
|
@@ -46,16 +55,16 @@ exports.SERVICES = [
|
|
|
46
55
|
module: 'email',
|
|
47
56
|
skill: 'my-email-api',
|
|
48
57
|
domain: 'myemailapi.com',
|
|
49
|
-
description: 'Send transactional and bulk email from your own domain. Mailboxes, AI templates,
|
|
58
|
+
description: 'Send transactional and bulk email from your own domain. Mailboxes, AI templates, and warmup.',
|
|
50
59
|
category: 'send',
|
|
51
60
|
status: 'ga',
|
|
52
|
-
keywords: k('email', 'transactional', '
|
|
61
|
+
keywords: k('email', 'transactional', 'smtp', 'mailbox'),
|
|
53
62
|
},
|
|
54
63
|
{
|
|
55
64
|
module: 'email',
|
|
56
65
|
skill: 'my-email-verify-api',
|
|
57
66
|
domain: 'myemailapi.com',
|
|
58
|
-
description: 'Sync single-address email verification — syntax + DNS + Microsoft probe. Pre-send quality gate for
|
|
67
|
+
description: 'Sync single-address email verification — syntax + DNS + Microsoft probe. Pre-send quality gate for outbound.',
|
|
59
68
|
category: 'send',
|
|
60
69
|
status: 'ga',
|
|
61
70
|
keywords: k('email', 'verification', 'deliverability', 'syntax', 'dns'),
|
|
@@ -175,10 +184,10 @@ exports.SERVICES = [
|
|
|
175
184
|
module: 'llm',
|
|
176
185
|
skill: 'my-llm-api',
|
|
177
186
|
domain: 'myllmapi.com',
|
|
178
|
-
description: '
|
|
187
|
+
description: 'Self-hosted open-source LLM: raw chat completions (you pick the model) + objective verbs (classify/extract/summarize/draft). Billed in cents per 1M tokens from your balance. Use in workflow steps, not for your own reasoning.',
|
|
179
188
|
category: 'compute',
|
|
180
189
|
status: 'ga',
|
|
181
|
-
keywords: k('llm', 'completion', '
|
|
190
|
+
keywords: k('llm', 'completion', 'qwen', 'classify'),
|
|
182
191
|
},
|
|
183
192
|
{
|
|
184
193
|
// CLI top-level command is `fn`; SDK namespace is `fn`; skill directory is
|
|
@@ -236,7 +245,7 @@ exports.SERVICES = [
|
|
|
236
245
|
domain: 'mygitapi.com',
|
|
237
246
|
description: 'Hosted git repositories over HTTP — create repos, commit files, manage branches and tags, read trees, blobs, history, and diffs. No local clone required.',
|
|
238
247
|
category: 'store',
|
|
239
|
-
status: '
|
|
248
|
+
status: 'ga',
|
|
240
249
|
keywords: k('git', 'repository', 'version-control', 'commit', 'scm'),
|
|
241
250
|
},
|
|
242
251
|
// ── orchestrate ─────────────────────────────────────────────────────
|
package/dist/storage.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface Asset {
|
|
|
7
7
|
created_at: string;
|
|
8
8
|
}
|
|
9
9
|
export declare function ingestAsset(apiKey: string, orgId: string, url: string, name?: string): Promise<Asset>;
|
|
10
|
-
export type UploadContentType = 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp' | 'video/mp4' | 'video/webm';
|
|
10
|
+
export type UploadContentType = 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp' | 'image/svg+xml' | 'application/pdf' | 'video/mp4' | 'video/webm';
|
|
11
11
|
export declare function uploadAsset(apiKey: string, orgId: string, file: Blob | Buffer, contentType: UploadContentType, name?: string): Promise<Asset>;
|
|
12
12
|
export declare function listAssets(apiKey: string, orgId: string): Promise<Asset[]>;
|
|
13
13
|
export declare function deleteAsset(apiKey: string, orgId: string, assetId: string): Promise<void>;
|
package/dist/types.d.ts
CHANGED
package/dist/webhook.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export interface WebhookEndpoint {
|
|
|
8
8
|
slug: string;
|
|
9
9
|
url: string;
|
|
10
10
|
crm_email_path?: string;
|
|
11
|
+
forward_url?: string;
|
|
11
12
|
created_at: string;
|
|
12
13
|
}
|
|
13
14
|
export interface Delivery {
|
|
@@ -15,6 +16,9 @@ export interface Delivery {
|
|
|
15
16
|
endpoint_id: string;
|
|
16
17
|
payload: unknown;
|
|
17
18
|
received_at: string;
|
|
19
|
+
forward_status?: number;
|
|
20
|
+
last_forward_at?: string;
|
|
21
|
+
last_forward_error?: string;
|
|
18
22
|
}
|
|
19
23
|
export interface CreateEndpointOptions {
|
|
20
24
|
description?: string;
|
package/dist/workflow.d.ts
CHANGED
|
@@ -58,6 +58,16 @@ export interface WorkflowRun {
|
|
|
58
58
|
started_at?: string;
|
|
59
59
|
finished_at?: string;
|
|
60
60
|
created_at: string;
|
|
61
|
+
step_outputs?: unknown[];
|
|
62
|
+
}
|
|
63
|
+
export interface ListRunsOptions {
|
|
64
|
+
limit?: number;
|
|
65
|
+
cursor?: string;
|
|
66
|
+
}
|
|
67
|
+
export interface WorkflowRunsPage {
|
|
68
|
+
runs: WorkflowRun[];
|
|
69
|
+
next_cursor?: string;
|
|
70
|
+
has_more?: boolean;
|
|
61
71
|
}
|
|
62
72
|
export declare function createWorkflow(apiKey: string, orgId: string, payload: {
|
|
63
73
|
name: string;
|
|
@@ -78,5 +88,5 @@ export declare function updateWorkflow(apiKey: string, orgId: string, workflowId
|
|
|
78
88
|
export declare function enableWorkflow(apiKey: string, orgId: string, workflowId: string): Promise<void>;
|
|
79
89
|
export declare function disableWorkflow(apiKey: string, orgId: string, workflowId: string): Promise<void>;
|
|
80
90
|
export declare function deleteWorkflow(apiKey: string, orgId: string, workflowId: string): Promise<void>;
|
|
81
|
-
export declare function listWorkflowRuns(apiKey: string, orgId: string, workflowId: string): Promise<
|
|
91
|
+
export declare function listWorkflowRuns(apiKey: string, orgId: string, workflowId: string, opts?: ListRunsOptions): Promise<WorkflowRunsPage>;
|
|
82
92
|
export declare function getWorkflowRun(apiKey: string, orgId: string, runId: string): Promise<WorkflowRun>;
|
package/dist/workflow.js
CHANGED
|
@@ -72,8 +72,18 @@ async function disableWorkflow(apiKey, orgId, workflowId) {
|
|
|
72
72
|
async function deleteWorkflow(apiKey, orgId, workflowId) {
|
|
73
73
|
return (0, client_1.request)('DELETE', `${config_1.WORKFLOW_BASE}/workflow/orgs/${encodeURIComponent(orgId)}/workflows/${encodeURIComponent(workflowId)}`, apiKey);
|
|
74
74
|
}
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
// listWorkflowRuns returns recent runs newest-first. The backend wraps this
|
|
76
|
+
// in a keyset-pagination envelope (limit 1-500 + cursor), so we use
|
|
77
|
+
// requestPage to preserve next_cursor — a plain `request` would drop it.
|
|
78
|
+
async function listWorkflowRuns(apiKey, orgId, workflowId, opts = {}) {
|
|
79
|
+
const qs = new URLSearchParams();
|
|
80
|
+
if (opts.limit !== undefined)
|
|
81
|
+
qs.set('limit', String(opts.limit));
|
|
82
|
+
if (opts.cursor)
|
|
83
|
+
qs.set('cursor', opts.cursor);
|
|
84
|
+
const query = qs.toString() ? `?${qs.toString()}` : '';
|
|
85
|
+
const page = await (0, client_1.requestPage)('GET', `${config_1.WORKFLOW_BASE}/workflow/orgs/${encodeURIComponent(orgId)}/workflows/${encodeURIComponent(workflowId)}/runs${query}`, apiKey);
|
|
86
|
+
return { runs: page.data, next_cursor: page.next_cursor, has_more: page.has_more };
|
|
77
87
|
}
|
|
78
88
|
async function getWorkflowRun(apiKey, orgId, runId) {
|
|
79
89
|
return (0, client_1.request)('GET', `${config_1.WORKFLOW_BASE}/workflow/orgs/${encodeURIComponent(orgId)}/runs/${encodeURIComponent(runId)}`, apiKey);
|
package/package.json
CHANGED
package/src/auth.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { request } from './client';
|
|
2
|
+
import { AUTH_BASE as BASE_URL } from './config';
|
|
3
|
+
import type { Exposes } from './exposes';
|
|
4
|
+
|
|
5
|
+
// The end-user auth product (my-auth-api): a managed multi-tenant OIDC
|
|
6
|
+
// Identity Provider for the END USERS of apps built on MyAPI. Distinct from
|
|
7
|
+
// operator/account auth (hq/account/*). Each org gets one auth tenant; OIDC
|
|
8
|
+
// clients (apps) register against it. End users sign in with managed Google,
|
|
9
|
+
// email/password, or magic links (per the tenant's `connections`). Tokens are
|
|
10
|
+
// RS256, verified via the tenant's JWKS.
|
|
11
|
+
//
|
|
12
|
+
// The browser/app-facing OIDC + sign-in flows — discovery, jwks, authorize,
|
|
13
|
+
// token, userinfo, password/magic/google login, verify-email, password reset,
|
|
14
|
+
// the hosted login page — are machine/end-user surfaces consumed by apps, the
|
|
15
|
+
// JS SDK, and browsers, NOT management calls; they are not CLI/SDK verbs. This
|
|
16
|
+
// module covers only the org-scoped MANAGEMENT surface: tenant, clients, MAU
|
|
17
|
+
// usage, and the custom auth domain.
|
|
18
|
+
export const EXPOSES: Exposes = [
|
|
19
|
+
'POST /auth/orgs/{org_id}/tenant',
|
|
20
|
+
'GET /auth/orgs/{org_id}/tenant',
|
|
21
|
+
'POST /auth/orgs/{org_id}/clients',
|
|
22
|
+
'GET /auth/orgs/{org_id}/clients',
|
|
23
|
+
'GET /auth/orgs/{org_id}/usage',
|
|
24
|
+
'POST /auth/orgs/{org_id}/domain',
|
|
25
|
+
'GET /auth/orgs/{org_id}/domain',
|
|
26
|
+
'DELETE /auth/orgs/{org_id}/domain',
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
// Sign-in methods a tenant offers on its hosted login page.
|
|
30
|
+
export type AuthConnection = 'google' | 'password' | 'magic';
|
|
31
|
+
|
|
32
|
+
export interface Tenant {
|
|
33
|
+
tenant_id: string;
|
|
34
|
+
issuer: string; // OIDC issuer: <base>/<tenant_id>
|
|
35
|
+
login_url: string; // hosted authorize endpoint
|
|
36
|
+
connections?: AuthConnection[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface CreateTenantInput {
|
|
40
|
+
// Sign-in methods to enable. Defaults to ['google'] when omitted.
|
|
41
|
+
connections?: AuthConnection[];
|
|
42
|
+
// Opaque JSON for the hosted login page (branding).
|
|
43
|
+
theme?: unknown;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface AuthClient {
|
|
47
|
+
client_id: string;
|
|
48
|
+
name?: string;
|
|
49
|
+
type: 'spa' | 'web';
|
|
50
|
+
redirect_uris: string[];
|
|
51
|
+
issuer?: string;
|
|
52
|
+
// Returned exactly ONCE, on create, for confidential ('web') clients.
|
|
53
|
+
// SPA clients are public and have no secret.
|
|
54
|
+
client_secret?: string;
|
|
55
|
+
created_at?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface ListClientsResponse {
|
|
59
|
+
clients: AuthClient[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface CreateClientInput {
|
|
63
|
+
name: string;
|
|
64
|
+
type: 'spa' | 'web';
|
|
65
|
+
redirect_uris: string[];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Monthly-active-user metering for the tenant (auth is billed per MAU).
|
|
69
|
+
export interface AuthUsage {
|
|
70
|
+
period: string; // 'YYYY-MM'
|
|
71
|
+
active_users: number;
|
|
72
|
+
price_cents_each: number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// A custom auth domain (e.g. auth.acme.com) for the tenant's hosted login + issuer.
|
|
76
|
+
export interface AuthDomain {
|
|
77
|
+
domain: string;
|
|
78
|
+
status: 'pending' | 'active';
|
|
79
|
+
dns: { type: string; name: string; value: string };
|
|
80
|
+
next?: string;
|
|
81
|
+
issuer?: string; // present once active
|
|
82
|
+
login_url?: string; // present once active
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Create (or update) the org's auth tenant. Idempotent. `connections` selects
|
|
86
|
+
// the sign-in methods (subset of google/password/magic; defaults to google);
|
|
87
|
+
// `theme` is opaque JSON for the hosted login page.
|
|
88
|
+
export async function createTenant(apiKey: string, orgId: string, input: CreateTenantInput = {}): Promise<Tenant> {
|
|
89
|
+
const body: Record<string, unknown> = {};
|
|
90
|
+
if (input.connections !== undefined) body.connections = input.connections;
|
|
91
|
+
if (input.theme !== undefined) body.theme = input.theme;
|
|
92
|
+
return request('POST', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/tenant`, apiKey, body);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Fetch the org's auth tenant. Rejects with TENANT_NOT_FOUND if not created.
|
|
96
|
+
export async function getTenant(apiKey: string, orgId: string): Promise<Tenant> {
|
|
97
|
+
return request('GET', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/tenant`, apiKey);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Register an OIDC client (an app) under the org's tenant. Auto-provisions the
|
|
101
|
+
// tenant if absent. For type 'web' the response carries `client_secret` once.
|
|
102
|
+
export async function createClient(apiKey: string, orgId: string, input: CreateClientInput): Promise<AuthClient> {
|
|
103
|
+
return request('POST', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/clients`, apiKey, input);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function listClients(apiKey: string, orgId: string): Promise<ListClientsResponse> {
|
|
107
|
+
return request('GET', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/clients`, apiKey);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Monthly-active-user usage for the current period (auth is billed per MAU).
|
|
111
|
+
export async function getUsage(apiKey: string, orgId: string): Promise<AuthUsage> {
|
|
112
|
+
return request('GET', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/usage`, apiKey);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Set a custom auth domain (e.g. auth.acme.com). Returns the DNS record to
|
|
116
|
+
// create; TLS provisions automatically and the domain becomes the issuer once
|
|
117
|
+
// active. One custom domain per org.
|
|
118
|
+
export async function registerDomain(apiKey: string, orgId: string, domain: string): Promise<AuthDomain> {
|
|
119
|
+
return request('POST', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/domain`, apiKey, { domain });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Fetch the org's custom auth domain. Rejects with NO_DOMAIN if none set.
|
|
123
|
+
export async function getDomain(apiKey: string, orgId: string): Promise<AuthDomain> {
|
|
124
|
+
return request('GET', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/domain`, apiKey);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export async function deleteDomain(apiKey: string, orgId: string, domain: string): Promise<{ domain: string; status: string }> {
|
|
128
|
+
return request('DELETE', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/domain`, apiKey, { domain });
|
|
129
|
+
}
|
package/src/client.ts
CHANGED
|
@@ -12,13 +12,13 @@ export class MyApiError extends Error {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
async function requestFull<T>(
|
|
16
16
|
method: string,
|
|
17
17
|
url: string,
|
|
18
18
|
apiKey?: string,
|
|
19
19
|
body?: unknown,
|
|
20
20
|
extraHeaders?: Record<string, string>,
|
|
21
|
-
): Promise<T
|
|
21
|
+
): Promise<ApiResponse<T>> {
|
|
22
22
|
const headers: Record<string, string> = {};
|
|
23
23
|
|
|
24
24
|
if (apiKey) {
|
|
@@ -48,7 +48,7 @@ export async function request<T>(
|
|
|
48
48
|
const response = await fetch(url, options);
|
|
49
49
|
|
|
50
50
|
if (response.status === 204) {
|
|
51
|
-
return
|
|
51
|
+
return { success: true, data: null, error: null, meta: {} as ApiResponse<T>['meta'] };
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
// Read the body as text first so we can include a snippet in errors when
|
|
@@ -85,5 +85,37 @@ export async function request<T>(
|
|
|
85
85
|
throw new MyApiError(code, response.status, detail, errBody);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
return apiResponse
|
|
88
|
+
return apiResponse;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Returns the unwrapped `data` payload — the common case.
|
|
92
|
+
export async function request<T>(
|
|
93
|
+
method: string,
|
|
94
|
+
url: string,
|
|
95
|
+
apiKey?: string,
|
|
96
|
+
body?: unknown,
|
|
97
|
+
extraHeaders?: Record<string, string>,
|
|
98
|
+
): Promise<T> {
|
|
99
|
+
return (await requestFull<T>(method, url, apiKey, body, extraHeaders)).data as T;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface Page<T> {
|
|
103
|
+
data: T[];
|
|
104
|
+
next_cursor?: string;
|
|
105
|
+
has_more?: boolean;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// For keyset-paginated endpoints (backend `envelope.WrapPage`): `data` is a
|
|
109
|
+
// bare array and the cursor/has_more live under `meta`. `request` would drop
|
|
110
|
+
// the cursor, so list endpoints with pagination must use this.
|
|
111
|
+
export async function requestPage<T>(
|
|
112
|
+
method: string,
|
|
113
|
+
url: string,
|
|
114
|
+
apiKey?: string,
|
|
115
|
+
body?: unknown,
|
|
116
|
+
extraHeaders?: Record<string, string>,
|
|
117
|
+
): Promise<Page<T>> {
|
|
118
|
+
const r = await requestFull<T[]>(method, url, apiKey, body, extraHeaders);
|
|
119
|
+
const meta = (r.meta ?? {}) as { next_cursor?: string; has_more?: boolean };
|
|
120
|
+
return { data: (r.data ?? []) as T[], next_cursor: meta.next_cursor, has_more: meta.has_more };
|
|
89
121
|
}
|
package/src/config.ts
CHANGED
|
@@ -36,3 +36,4 @@ export const AUDIENCE_BASE= process.env.MYAPI_AUDIENCE_URL?? GATEWAY;
|
|
|
36
36
|
export const LLM_BASE = process.env.MYAPI_LLM_URL ?? GATEWAY;
|
|
37
37
|
export const DATABASE_BASE= process.env.MYAPI_DATABASE_URL?? GATEWAY;
|
|
38
38
|
export const CRM_BASE = process.env.MYAPI_CRM_URL ?? GATEWAY;
|
|
39
|
+
export const AUTH_BASE = process.env.MYAPI_AUTH_URL ?? GATEWAY;
|
package/src/container.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { request } from './client';
|
|
1
|
+
import { request, MyApiError } from './client';
|
|
2
|
+
import { ApiResponse } from './types';
|
|
2
3
|
import { CONTAINER_BASE as BASE_URL } from './config';
|
|
3
4
|
import type { Exposes } from './exposes';
|
|
4
5
|
|
|
@@ -39,6 +40,9 @@ export interface Container {
|
|
|
39
40
|
// Origin Rule that rewrites Host + SNI to the Cloud Run hostname.
|
|
40
41
|
custom_domain?: string;
|
|
41
42
|
status: string;
|
|
43
|
+
// Egress mode for the container's outbound traffic (backend returns it on
|
|
44
|
+
// every row, container/crud.go). Static-IP vs. default depending on config.
|
|
45
|
+
egress: string;
|
|
42
46
|
created_at: string;
|
|
43
47
|
updated_at: string;
|
|
44
48
|
}
|
|
@@ -89,12 +93,58 @@ export async function deleteContainer(apiKey: string, orgId: string, containerId
|
|
|
89
93
|
return request('DELETE', `${BASE_URL}/container/orgs/${encodeURIComponent(orgId)}/containers/${encodeURIComponent(containerId)}`, apiKey);
|
|
90
94
|
}
|
|
91
95
|
|
|
92
|
-
// deployContainer ships a revision from a pre-built image ref to Cloud Run
|
|
93
|
-
//
|
|
96
|
+
// deployContainer ships a revision from a pre-built image ref to Cloud Run.
|
|
97
|
+
// Synchronous — returns status='active' with a rotated scoped key.
|
|
94
98
|
export async function deployContainer(apiKey: string, orgId: string, containerId: string, image: string): Promise<DeployResponse> {
|
|
95
99
|
return request('POST', `${BASE_URL}/container/orgs/${encodeURIComponent(orgId)}/containers/${encodeURIComponent(containerId)}/deploy`, apiKey, { image });
|
|
96
100
|
}
|
|
97
101
|
|
|
102
|
+
// BuildDeployResponse — the async source-build path. The backend records a
|
|
103
|
+
// `building` revision, kicks off Cloud Build on a background goroutine, and
|
|
104
|
+
// returns immediately. The scoped key is minted later (once the image
|
|
105
|
+
// exists), so it is NOT in this response. Poll getContainer until
|
|
106
|
+
// status='active' (or 'build_error'/'deploy_error').
|
|
107
|
+
export interface BuildDeployResponse {
|
|
108
|
+
container_id: string;
|
|
109
|
+
revision_id: string;
|
|
110
|
+
status: string; // 'building'
|
|
111
|
+
message?: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// deployContainerSource uploads a build-context tarball via multipart `source`
|
|
115
|
+
// (backend deploy.go builds it via Cloud Build → Artifact Registry, then
|
|
116
|
+
// deploys). Asynchronous: returns status='building' to poll. The tarball is
|
|
117
|
+
// capped at 100MB server-side. Mirrors the multipart upload in storage.ts.
|
|
118
|
+
export async function deployContainerSource(
|
|
119
|
+
apiKey: string,
|
|
120
|
+
orgId: string,
|
|
121
|
+
containerId: string,
|
|
122
|
+
tarball: Blob | Buffer,
|
|
123
|
+
filename = 'source.tar.gz',
|
|
124
|
+
): Promise<BuildDeployResponse> {
|
|
125
|
+
const formData = new FormData();
|
|
126
|
+
formData.append('source', new Blob([tarball as any], { type: 'application/gzip' }), filename);
|
|
127
|
+
|
|
128
|
+
const response = await fetch(
|
|
129
|
+
`${BASE_URL}/container/orgs/${encodeURIComponent(orgId)}/containers/${encodeURIComponent(containerId)}/deploy`,
|
|
130
|
+
{ method: 'POST', headers: { Authorization: `Bearer ${apiKey}` }, body: formData as any },
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
let result: any;
|
|
134
|
+
try {
|
|
135
|
+
result = await response.json();
|
|
136
|
+
} catch {
|
|
137
|
+
throw new MyApiError('invalid_json_response', response.status);
|
|
138
|
+
}
|
|
139
|
+
if (!response.ok || !result?.success) {
|
|
140
|
+
const err = result?.error;
|
|
141
|
+
const code = typeof err === 'object' ? (err?.code || 'unknown_error') : (err || 'unknown_error');
|
|
142
|
+
const detail = typeof err === 'object' ? err?.message : undefined;
|
|
143
|
+
throw new MyApiError(code, response.status, detail, typeof err === 'object' ? err : undefined);
|
|
144
|
+
}
|
|
145
|
+
return (result as ApiResponse<BuildDeployResponse>).data as BuildDeployResponse;
|
|
146
|
+
}
|
|
147
|
+
|
|
98
148
|
// One runtime log line from the container's Cloud Run resource.
|
|
99
149
|
export interface LogEntry {
|
|
100
150
|
timestamp: string;
|
package/src/crm.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { request } from './client';
|
|
1
|
+
import { request, requestPage } from './client';
|
|
2
2
|
import { CRM_BASE as BASE_URL } from './config';
|
|
3
3
|
import type { Exposes } from './exposes';
|
|
4
4
|
|
|
@@ -46,7 +46,8 @@ export type EventKind =
|
|
|
46
46
|
| 'email_clicked'
|
|
47
47
|
| 'email_replied'
|
|
48
48
|
| 'pixel_visit'
|
|
49
|
-
| 'webhook_received'
|
|
49
|
+
| 'webhook_received'
|
|
50
|
+
| 'payment';
|
|
50
51
|
|
|
51
52
|
// Goldfox enrichment is a live join — the response embeds the current
|
|
52
53
|
// Goldfox row when goldfox_person_id is set. v1: returns null until the
|
|
@@ -204,7 +205,10 @@ export async function getContactEvents(apiKey: string, orgId: string, id: string
|
|
|
204
205
|
if (opts.kind != null) qs.append('kind', opts.kind);
|
|
205
206
|
const q = qs.toString();
|
|
206
207
|
const url = `${BASE_URL}/crm/orgs/${encodeURIComponent(orgId)}/contacts/${encodeURIComponent(id)}/events${q ? `?${q}` : ''}`;
|
|
207
|
-
|
|
208
|
+
// Paginated (WrapPage): events are the bare `data` array with the cursor
|
|
209
|
+
// under `meta` — use requestPage so `next_cursor` survives.
|
|
210
|
+
const page = await requestPage<ContactEvent>('GET', url, apiKey);
|
|
211
|
+
return { events: page.data, next_cursor: page.next_cursor };
|
|
208
212
|
}
|
|
209
213
|
|
|
210
214
|
// ── Companies ─────────────────────────────────────────────────────────────
|
package/src/database.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { request } from './client';
|
|
1
|
+
import { request, requestPage } from './client';
|
|
2
2
|
import { DATABASE_BASE as BASE_URL } from './config';
|
|
3
3
|
import type { Exposes } from './exposes';
|
|
4
4
|
|
|
@@ -71,7 +71,10 @@ export async function listKeys(apiKey: string, orgId: string, ns: string, opts:
|
|
|
71
71
|
if (opts.values) qs.append('values', 'true');
|
|
72
72
|
const q = qs.toString();
|
|
73
73
|
const url = `${BASE_URL}/database/orgs/${encodeURIComponent(orgId)}/namespaces/${encodeURIComponent(ns)}/keys${q ? `?${q}` : ''}`;
|
|
74
|
-
|
|
74
|
+
// Paginated (WrapPage): keys come back as the bare `data` array with the
|
|
75
|
+
// cursor under `meta` — must use requestPage or the cursor is lost.
|
|
76
|
+
const page = await requestPage<KeyEntry>('GET', url, apiKey);
|
|
77
|
+
return { keys: page.data, next_cursor: page.next_cursor };
|
|
75
78
|
}
|
|
76
79
|
|
|
77
80
|
export async function getKey(apiKey: string, orgId: string, ns: string, key: string): Promise<KeyEntry> {
|
package/src/domain.ts
CHANGED
|
@@ -211,7 +211,7 @@ export async function deleteDnsRecord(apiKey: string, orgId: string, domain: str
|
|
|
211
211
|
export interface EmailInfraResponse {
|
|
212
212
|
domain: string;
|
|
213
213
|
email_infra: 'pending' | 'ready' | 'error';
|
|
214
|
-
|
|
214
|
+
subdomain: string;
|
|
215
215
|
next_step?: string;
|
|
216
216
|
}
|
|
217
217
|
|
package/src/email.ts
CHANGED
|
@@ -31,18 +31,6 @@ export const EXPOSES: Exposes = [
|
|
|
31
31
|
'POST /email/orgs/{org_id}/templates/{template_id}/send-test',
|
|
32
32
|
'DELETE /email/orgs/{org_id}/templates/{template_id}',
|
|
33
33
|
'GET /email/templates/{template_id}/preview',
|
|
34
|
-
// Campaigns
|
|
35
|
-
'POST /email/orgs/{org_id}/campaigns',
|
|
36
|
-
'GET /email/orgs/{org_id}/campaigns',
|
|
37
|
-
'GET /email/orgs/{org_id}/campaigns/{campaign_id}',
|
|
38
|
-
'PATCH /email/orgs/{org_id}/campaigns/{campaign_id}',
|
|
39
|
-
'POST /email/orgs/{org_id}/campaigns/{campaign_id}/contacts/upload-list',
|
|
40
|
-
'POST /email/orgs/{org_id}/campaigns/{campaign_id}/contacts/upload-file',
|
|
41
|
-
'GET /email/orgs/{org_id}/campaigns/{campaign_id}/contacts/status',
|
|
42
|
-
'POST /email/orgs/{org_id}/campaigns/{campaign_id}/start',
|
|
43
|
-
'POST /email/orgs/{org_id}/campaigns/{campaign_id}/pause',
|
|
44
|
-
'POST /email/orgs/{org_id}/campaigns/{campaign_id}/resume',
|
|
45
|
-
'GET /email/orgs/{org_id}/campaigns/{campaign_id}/stats',
|
|
46
34
|
// Verify
|
|
47
35
|
'POST /email/orgs/{org_id}/verify',
|
|
48
36
|
'POST /email/orgs/{org_id}/verify-bulk',
|
|
@@ -51,14 +39,6 @@ export const EXPOSES: Exposes = [
|
|
|
51
39
|
|
|
52
40
|
export interface EmailMessage { message_id: string; from: string; subject: string; body?: string; received_at: string }
|
|
53
41
|
export interface EmailTemplate { id: string; name: string; subject: string; preview_url: string; created_at: string; updated_at: string }
|
|
54
|
-
export interface WarmupConfig { enabled: boolean; start: number; increment: number; max: number; }
|
|
55
|
-
export interface Campaign { id: string; name: string; status: string; template_id: string; from_address: string; per_day_limit: number; warmup_config?: WarmupConfig; created_at: string }
|
|
56
|
-
export interface CampaignStats {
|
|
57
|
-
campaign: Campaign;
|
|
58
|
-
validation: { total: number; valid: number; invalid: number; pending: number; list_status: string };
|
|
59
|
-
dispatch: { sent: number; remaining: number; sent_this_hour: number; hour_limit: number; last_dispatch_at: string };
|
|
60
|
-
engagement: { sent: number; opens: number; clicks: number; page_visits: number };
|
|
61
|
-
}
|
|
62
42
|
|
|
63
43
|
// ── Email verification ──────────────────────────────────────────────────────
|
|
64
44
|
//
|
|
@@ -138,8 +118,7 @@ export async function createMailbox(apiKey: string, domain: string, username: st
|
|
|
138
118
|
}
|
|
139
119
|
|
|
140
120
|
// Backend (2026-06-06): idempotent — 204 whether the mailbox existed or not.
|
|
141
|
-
//
|
|
142
|
-
// this address; pause/delete the campaign first.
|
|
121
|
+
// May return 409 MAILBOX_IN_USE if the address is still referenced server-side.
|
|
143
122
|
export async function deleteMailbox(apiKey: string, address: string): Promise<void> {
|
|
144
123
|
return request('DELETE', `${BASE_URL}/email/mailboxes/${encodeURIComponent(address)}`, apiKey);
|
|
145
124
|
}
|
|
@@ -264,59 +243,3 @@ export async function listTemplates(apiKey: string, orgId: string): Promise<Emai
|
|
|
264
243
|
export async function deleteTemplate(apiKey: string, orgId: string, templateId: string): Promise<void> {
|
|
265
244
|
return request('DELETE', `${BASE_URL}/email/orgs/${encodeURIComponent(orgId)}/templates/${encodeURIComponent(templateId)}`, apiKey);
|
|
266
245
|
}
|
|
267
|
-
|
|
268
|
-
// ── Campaigns (org-scoped) ───────────────────────────────────────────────────
|
|
269
|
-
|
|
270
|
-
export async function createCampaign(apiKey: string, orgId: string, payload: { name: string; template_id: string; from_address: string; per_day_limit?: number; warmup_config?: WarmupConfig | null; }): Promise<Campaign> {
|
|
271
|
-
return request('POST', `${BASE_URL}/email/orgs/${encodeURIComponent(orgId)}/campaigns`, apiKey, payload);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
export async function uploadContactList(apiKey: string, orgId: string, campaignId: string, emails: string[]): Promise<{ list_id: string; total: number; status: string }> {
|
|
275
|
-
return request('POST', `${BASE_URL}/email/orgs/${encodeURIComponent(orgId)}/campaigns/${encodeURIComponent(campaignId)}/contacts/upload-list`, apiKey, { emails });
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
export async function uploadContactsFile(apiKey: string, orgId: string, campaignId: string, file: Blob | ArrayBuffer | Uint8Array, filename: string = 'contacts.csv'): Promise<{ list_id: string; total: number; status: string }> {
|
|
279
|
-
const form = new FormData();
|
|
280
|
-
const blob = file instanceof Blob ? file : new Blob([file as BlobPart]);
|
|
281
|
-
form.append('file', blob, filename);
|
|
282
|
-
const res = await fetch(`${BASE_URL}/email/orgs/${encodeURIComponent(orgId)}/campaigns/${encodeURIComponent(campaignId)}/contacts/upload-file`, {
|
|
283
|
-
method: 'POST',
|
|
284
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
285
|
-
body: form,
|
|
286
|
-
});
|
|
287
|
-
const json = await res.json();
|
|
288
|
-
if (!res.ok || !json.success) throw new Error(json.error?.code || json.error || 'upload_failed');
|
|
289
|
-
return json.data;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
export async function getContactUploadStatus(apiKey: string, orgId: string, campaignId: string): Promise<{ upload_status: 'ready' | 'validating' | 'failed'; total: number; valid_count: number; invalid_count: number }> {
|
|
293
|
-
return request('GET', `${BASE_URL}/email/orgs/${encodeURIComponent(orgId)}/campaigns/${encodeURIComponent(campaignId)}/contacts/status`, apiKey);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
export async function startCampaign(apiKey: string, orgId: string, campaignId: string): Promise<{ status: string }> {
|
|
297
|
-
return request('POST', `${BASE_URL}/email/orgs/${encodeURIComponent(orgId)}/campaigns/${encodeURIComponent(campaignId)}/start`, apiKey);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
export async function pauseCampaign(apiKey: string, orgId: string, campaignId: string): Promise<void> {
|
|
301
|
-
return request('POST', `${BASE_URL}/email/orgs/${encodeURIComponent(orgId)}/campaigns/${encodeURIComponent(campaignId)}/pause`, apiKey);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
export async function resumeCampaign(apiKey: string, orgId: string, campaignId: string): Promise<void> {
|
|
305
|
-
return request('POST', `${BASE_URL}/email/orgs/${encodeURIComponent(orgId)}/campaigns/${encodeURIComponent(campaignId)}/resume`, apiKey);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
export async function getCampaignStats(apiKey: string, orgId: string, campaignId: string): Promise<CampaignStats> {
|
|
309
|
-
return request('GET', `${BASE_URL}/email/orgs/${encodeURIComponent(orgId)}/campaigns/${encodeURIComponent(campaignId)}/stats`, apiKey);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
export async function listCampaigns(apiKey: string, orgId: string): Promise<Campaign[]> {
|
|
313
|
-
return request('GET', `${BASE_URL}/email/orgs/${encodeURIComponent(orgId)}/campaigns`, apiKey);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
export async function getCampaign(apiKey: string, orgId: string, campaignId: string): Promise<Campaign> {
|
|
317
|
-
return request('GET', `${BASE_URL}/email/orgs/${encodeURIComponent(orgId)}/campaigns/${encodeURIComponent(campaignId)}`, apiKey);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
export async function updateCampaign(apiKey: string, orgId: string, campaignId: string, payload: { name?: string; per_day_limit?: number }): Promise<Campaign> {
|
|
321
|
-
return request('PATCH', `${BASE_URL}/email/orgs/${encodeURIComponent(orgId)}/campaigns/${encodeURIComponent(campaignId)}`, apiKey, payload);
|
|
322
|
-
}
|
package/src/function.ts
CHANGED
|
@@ -35,6 +35,10 @@ export interface CreatePayload {
|
|
|
35
35
|
name: string; // required; ^[a-z0-9][a-z0-9-]{0,49}$
|
|
36
36
|
trigger_type?: TriggerType; // optional, defaults to 'http' server-side
|
|
37
37
|
cron_schedule?: string; // required if trigger_type === 'cron'
|
|
38
|
+
// Optional slot allow-list narrowing the minted function key's grants
|
|
39
|
+
// (backend crud.go: CreateFunction `scopes`). Empty/omitted inherits the
|
|
40
|
+
// deploying caller's grants (legacy). Grants can never exceed the caller.
|
|
41
|
+
scopes?: string[];
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
// POST response envelope includes the scoped API key — returned ONCE. The
|
|
@@ -122,6 +126,14 @@ export async function setFunctionEnv(apiKey: string, orgId: string, fnId: string
|
|
|
122
126
|
return request('POST', `${BASE_URL}/function/orgs/${encodeURIComponent(orgId)}/functions/${encodeURIComponent(fnId)}/env`, apiKey, { name, value });
|
|
123
127
|
}
|
|
124
128
|
|
|
129
|
+
// setFunctionEnvBulk writes multiple Worker Secrets in one call. The backend
|
|
130
|
+
// (env.go) loops, calling Cloudflare once per secret, and returns `{set:N}`.
|
|
131
|
+
// The single-secret `setFunctionEnv` above stays the canonical one-off path;
|
|
132
|
+
// this is the bulk variant. The function must already be deployed.
|
|
133
|
+
export async function setFunctionEnvBulk(apiKey: string, orgId: string, fnId: string, env: Record<string, string>): Promise<{ set: number }> {
|
|
134
|
+
return request('POST', `${BASE_URL}/function/orgs/${encodeURIComponent(orgId)}/functions/${encodeURIComponent(fnId)}/env`, apiKey, { env });
|
|
135
|
+
}
|
|
136
|
+
|
|
125
137
|
// listFunctionRuns returns recent invocation records, most recent first
|
|
126
138
|
// (Story 4). Capped at 100 server-side.
|
|
127
139
|
export async function listFunctionRuns(apiKey: string, orgId: string, fnId: string): Promise<FunctionRun[]> {
|
package/src/funnel.ts
CHANGED
|
@@ -62,7 +62,7 @@ export interface VerifyResult {
|
|
|
62
62
|
pages?: Array<{ slug: string; tests: VerifyTest[] }>;
|
|
63
63
|
tests?: VerifyTest[];
|
|
64
64
|
}
|
|
65
|
-
export interface VerifyTest { type: 'link' | 'webhook'; url: string; passed: boolean; details: string }
|
|
65
|
+
export interface VerifyTest { type: 'link' | 'image' | 'webhook'; url: string; passed: boolean; details: string }
|
|
66
66
|
|
|
67
67
|
export async function createFunnel(apiKey: string, orgId: string, opts?: { name?: string }): Promise<CreateFunnelResponse> {
|
|
68
68
|
// v2: accept optional name for N-funnels-per-org. Backend rejects names
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from './client';
|
|
|
7
7
|
// CJS-compiled SDK. If you need a base URL in a downstream package, mirror
|
|
8
8
|
// the constant locally — it's one line and the comment makes it obvious.
|
|
9
9
|
export * as hq from './hq';
|
|
10
|
+
export * as auth from './auth';
|
|
10
11
|
export * as domain from './domain';
|
|
11
12
|
export * as email from './email';
|
|
12
13
|
export * as funnel from './funnel';
|