@myapihq/sdk 1.3.13 → 2.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.
- 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/funds.d.ts +11 -0
- package/dist/funds.js +55 -0
- package/dist/funnel.d.ts +1 -1
- package/dist/hq.d.ts +22 -0
- package/dist/hq.js +18 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -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 +17 -8
- 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/funds.ts +66 -0
- package/src/funnel.ts +1 -1
- package/src/hq.ts +59 -0
- package/src/index.ts +2 -0
- package/src/payments.ts +2 -1
- package/src/queue.ts +5 -0
- package/src/services.ts +17 -8
- 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/hq.d.ts
CHANGED
|
@@ -138,6 +138,26 @@ export declare function setupPayment(apiKey: string): Promise<{
|
|
|
138
138
|
export declare function topUp(apiKey: string, amountDollars: number): Promise<{
|
|
139
139
|
new_balance_display: string;
|
|
140
140
|
}>;
|
|
141
|
+
export type RechargeStatus = 'succeeded' | 'failed' | 'capped' | 'no_pm' | 'pending';
|
|
142
|
+
export interface AutoRechargeConfig {
|
|
143
|
+
enabled: boolean;
|
|
144
|
+
threshold_cents: number | null;
|
|
145
|
+
amount_cents: number | null;
|
|
146
|
+
monthly_cap_cents: number | null;
|
|
147
|
+
month_to_date_recharged_cents: number;
|
|
148
|
+
has_payment_method: boolean;
|
|
149
|
+
last_recharge_status: RechargeStatus | null;
|
|
150
|
+
last_recharge_attempt_at: string | null;
|
|
151
|
+
}
|
|
152
|
+
export declare function getAutoRecharge(apiKey: string): Promise<AutoRechargeConfig>;
|
|
153
|
+
export interface SetAutoRechargeInput {
|
|
154
|
+
enabled: boolean;
|
|
155
|
+
threshold_cents?: number;
|
|
156
|
+
amount_cents?: number;
|
|
157
|
+
monthly_cap_cents?: number;
|
|
158
|
+
}
|
|
159
|
+
export declare function setAutoRecharge(apiKey: string, input: SetAutoRechargeInput): Promise<AutoRechargeConfig>;
|
|
160
|
+
export declare function disableAutoRecharge(apiKey: string): Promise<void>;
|
|
141
161
|
export type DoctorSeverity = 'ok' | 'warn' | 'crit';
|
|
142
162
|
export interface DoctorEntityRef {
|
|
143
163
|
slot: string;
|
|
@@ -152,11 +172,13 @@ export interface DoctorIssue {
|
|
|
152
172
|
category?: string;
|
|
153
173
|
message: string;
|
|
154
174
|
hint?: string;
|
|
175
|
+
operator_only?: boolean;
|
|
155
176
|
}
|
|
156
177
|
export interface DoctorSection {
|
|
157
178
|
name: string;
|
|
158
179
|
summary: string;
|
|
159
180
|
issues: DoctorIssue[];
|
|
181
|
+
resource_count?: number;
|
|
160
182
|
}
|
|
161
183
|
export interface DoctorReport {
|
|
162
184
|
org_id: string;
|
package/dist/hq.js
CHANGED
|
@@ -27,6 +27,9 @@ exports.getBillingHistory = getBillingHistory;
|
|
|
27
27
|
exports.getBillingUsage = getBillingUsage;
|
|
28
28
|
exports.setupPayment = setupPayment;
|
|
29
29
|
exports.topUp = topUp;
|
|
30
|
+
exports.getAutoRecharge = getAutoRecharge;
|
|
31
|
+
exports.setAutoRecharge = setAutoRecharge;
|
|
32
|
+
exports.disableAutoRecharge = disableAutoRecharge;
|
|
30
33
|
exports.getDoctor = getDoctor;
|
|
31
34
|
const client_1 = require("./client");
|
|
32
35
|
const config_1 = require("./config");
|
|
@@ -170,6 +173,21 @@ async function setupPayment(apiKey) {
|
|
|
170
173
|
async function topUp(apiKey, amountDollars) {
|
|
171
174
|
return (0, client_1.request)('POST', `${config_1.HQ_BASE}/hq/billing/topup`, apiKey, { amount_dollars: amountDollars });
|
|
172
175
|
}
|
|
176
|
+
async function getAutoRecharge(apiKey) {
|
|
177
|
+
return (0, client_1.request)('GET', `${config_1.HQ_BASE}/hq/billing/auto-recharge`, apiKey);
|
|
178
|
+
}
|
|
179
|
+
// Enable/update auto-recharge. The backend validates invariants and rejects
|
|
180
|
+
// with a 400 (`MISSING_FIELDS`, `AMOUNT_BELOW_FLOOR` [$5 floor],
|
|
181
|
+
// `AMOUNT_LT_THRESHOLD`, `CAP_LT_AMOUNT`, `NO_PAYMENT_METHOD`) — surfaced as a
|
|
182
|
+
// MyApiError. Returns the full config on success.
|
|
183
|
+
async function setAutoRecharge(apiKey, input) {
|
|
184
|
+
return (0, client_1.request)('PUT', `${config_1.HQ_BASE}/hq/billing/auto-recharge`, apiKey, input);
|
|
185
|
+
}
|
|
186
|
+
// Disable auto-recharge (the threshold/amount/cap are preserved for easy
|
|
187
|
+
// re-enable). Backend returns 204 No Content.
|
|
188
|
+
async function disableAutoRecharge(apiKey) {
|
|
189
|
+
return (0, client_1.request)('DELETE', `${config_1.HQ_BASE}/hq/billing/auto-recharge`, apiKey);
|
|
190
|
+
}
|
|
173
191
|
async function getDoctor(apiKey, orgId) {
|
|
174
192
|
return (0, client_1.request)('GET', `${config_1.HQ_BASE}/hq/orgs/${encodeURIComponent(orgId)}/doctor`, apiKey);
|
|
175
193
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -36,9 +36,10 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
36
36
|
};
|
|
37
37
|
})();
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.task = exports.queue = exports.git = exports.container = exports.payments = exports.fn = exports.crm = exports.database = exports.llm = exports.audience = exports.company = exports.people = exports.url = exports.workflow = exports.webhook = exports.storage = exports.pixel = exports.image = exports.funnel = exports.email = exports.domain = exports.hq = void 0;
|
|
39
|
+
exports.task = exports.queue = exports.git = exports.container = exports.payments = exports.fn = exports.crm = exports.database = exports.llm = exports.audience = exports.company = exports.people = exports.url = exports.workflow = exports.webhook = exports.storage = exports.pixel = exports.image = exports.funnel = exports.email = exports.domain = exports.auth = exports.hq = void 0;
|
|
40
40
|
__exportStar(require("./types"), exports);
|
|
41
41
|
__exportStar(require("./client"), exports);
|
|
42
|
+
__exportStar(require("./funds"), exports);
|
|
42
43
|
// Note: config constants (STORAGE_BASE etc.) are NOT re-exported from the
|
|
43
44
|
// barrel. TypeScript compiles `export * from './config'` to a runtime
|
|
44
45
|
// `__exportStar` call that Node's cjs-module-lexer can't see through, so
|
|
@@ -46,6 +47,7 @@ __exportStar(require("./client"), exports);
|
|
|
46
47
|
// CJS-compiled SDK. If you need a base URL in a downstream package, mirror
|
|
47
48
|
// the constant locally — it's one line and the comment makes it obvious.
|
|
48
49
|
exports.hq = __importStar(require("./hq"));
|
|
50
|
+
exports.auth = __importStar(require("./auth"));
|
|
49
51
|
exports.domain = __importStar(require("./domain"));
|
|
50
52
|
exports.email = __importStar(require("./email"));
|
|
51
53
|
exports.funnel = __importStar(require("./funnel"));
|
package/dist/payments.d.ts
CHANGED
package/dist/payments.js
CHANGED
|
@@ -32,7 +32,7 @@ async function connect(apiKey, orgId, stripeSecretKey, tier = 't0') {
|
|
|
32
32
|
stripe_secret_key: stripeSecretKey,
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
|
-
// getConnect reads the org's connection status.
|
|
35
|
+
// getConnect reads the org's connection status. Returns { connected: false } when no Stripe is linked (no longer a 404).
|
|
36
36
|
// when the org has not connected Stripe.
|
|
37
37
|
async function getConnect(apiKey, orgId) {
|
|
38
38
|
return (0, client_1.request)('GET', `${config_1.PAYMENTS_BASE}/payments/orgs/${encodeURIComponent(orgId)}/connect`, apiKey);
|
package/dist/queue.d.ts
CHANGED
|
@@ -41,6 +41,7 @@ export interface EnqueueOptions {
|
|
|
41
41
|
export declare function createQueue(apiKey: string, orgId: string, opts: CreateQueueOptions): Promise<Queue>;
|
|
42
42
|
export declare function listQueues(apiKey: string, orgId: string): Promise<Queue[]>;
|
|
43
43
|
export declare function getQueue(apiKey: string, orgId: string, name: string): Promise<Queue>;
|
|
44
|
+
export declare function deleteQueue(apiKey: string, orgId: string, name: string): Promise<void>;
|
|
44
45
|
export declare function enqueueJob(apiKey: string, orgId: string, name: string, opts?: EnqueueOptions): Promise<Job>;
|
|
45
46
|
export declare function listJobs(apiKey: string, orgId: string, name: string, opts?: {
|
|
46
47
|
status?: string;
|
package/dist/queue.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.EXPOSES = void 0;
|
|
|
4
4
|
exports.createQueue = createQueue;
|
|
5
5
|
exports.listQueues = listQueues;
|
|
6
6
|
exports.getQueue = getQueue;
|
|
7
|
+
exports.deleteQueue = deleteQueue;
|
|
7
8
|
exports.enqueueJob = enqueueJob;
|
|
8
9
|
exports.listJobs = listJobs;
|
|
9
10
|
exports.getJob = getJob;
|
|
@@ -17,6 +18,7 @@ exports.EXPOSES = [
|
|
|
17
18
|
'POST /queue/orgs/{org_id}/queues',
|
|
18
19
|
'GET /queue/orgs/{org_id}/queues',
|
|
19
20
|
'GET /queue/orgs/{org_id}/queues/{name}',
|
|
21
|
+
'DELETE /queue/orgs/{org_id}/queues/{name}',
|
|
20
22
|
'POST /queue/orgs/{org_id}/queues/{name}/jobs',
|
|
21
23
|
'GET /queue/orgs/{org_id}/queues/{name}/jobs',
|
|
22
24
|
'GET /queue/orgs/{org_id}/jobs/{id}',
|
|
@@ -41,6 +43,9 @@ async function listQueues(apiKey, orgId) {
|
|
|
41
43
|
async function getQueue(apiKey, orgId, name) {
|
|
42
44
|
return (0, client_1.request)('GET', `${queuesBase(orgId)}/${encodeURIComponent(name)}`, apiKey);
|
|
43
45
|
}
|
|
46
|
+
async function deleteQueue(apiKey, orgId, name) {
|
|
47
|
+
return (0, client_1.request)('DELETE', `${queuesBase(orgId)}/${encodeURIComponent(name)}`, apiKey);
|
|
48
|
+
}
|
|
44
49
|
// enqueueJob is idempotent on (queue, dedup_key). A job with unsucceeded
|
|
45
50
|
// depends_on starts blocked.
|
|
46
51
|
async function enqueueJob(apiKey, orgId, name, opts = {}) {
|
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',
|
|
@@ -39,23 +48,23 @@ exports.SERVICES = [
|
|
|
39
48
|
},
|
|
40
49
|
// ── send ────────────────────────────────────────────────────────────
|
|
41
50
|
{
|
|
42
|
-
// Customer-facing email surface — mailboxes,
|
|
43
|
-
// warmup. Launched 2026-05-17: the backend lifted the pre-launch gate
|
|
51
|
+
// Customer-facing email surface — mailboxes, transactional send,
|
|
52
|
+
// templates, warmup. Launched 2026-05-17: the backend lifted the pre-launch gate
|
|
44
53
|
// (the [disabled, pre-launch] / 503 SERVICE_NOT_LAUNCHED state is gone).
|
|
45
54
|
// Sibling `my-email-verify-api` is the sync single-address verifier.
|
|
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> {
|