@myapihq/sdk 2.4.0 → 2.4.2
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/client.d.ts +23 -2
- package/dist/client.js +174 -22
- package/dist/crm.d.ts +2 -0
- package/dist/crm.js +15 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +11 -0
- package/package.json +11 -2
- package/scripts/scaffold-sdk.js +0 -501
- package/src/audience.ts +0 -162
- package/src/auth.ts +0 -161
- package/src/client.ts +0 -121
- package/src/company.ts +0 -48
- package/src/config.ts +0 -39
- package/src/container.ts +0 -187
- package/src/crm.ts +0 -239
- package/src/database.ts +0 -106
- package/src/domain.ts +0 -246
- package/src/email.ts +0 -247
- package/src/exposes.ts +0 -16
- package/src/function.ts +0 -141
- package/src/funds.ts +0 -66
- package/src/funnel.ts +0 -203
- package/src/git.ts +0 -171
- package/src/hq.ts +0 -393
- package/src/image.ts +0 -67
- package/src/index.ts +0 -32
- package/src/llm.ts +0 -189
- package/src/payments.ts +0 -98
- package/src/people.ts +0 -84
- package/src/pixel.ts +0 -80
- package/src/queue.ts +0 -115
- package/src/services.ts +0 -323
- package/src/storage.ts +0 -80
- package/src/task.ts +0 -198
- package/src/types.ts +0 -15
- package/src/url.ts +0 -18
- package/src/webhook.ts +0 -88
- package/src/workflow.ts +0 -162
- package/tsconfig.json +0 -14
package/src/auth.ts
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
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
|
-
'DELETE /auth/orgs/{org_id}/clients/{client_id}',
|
|
24
|
-
'POST /auth/orgs/{org_id}/clients/{client_id}/rotate',
|
|
25
|
-
'GET /auth/orgs/{org_id}/usage',
|
|
26
|
-
'POST /auth/orgs/{org_id}/domain',
|
|
27
|
-
'GET /auth/orgs/{org_id}/domain',
|
|
28
|
-
'POST /auth/orgs/{org_id}/domain/verify',
|
|
29
|
-
'DELETE /auth/orgs/{org_id}/domain',
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
// Sign-in methods a tenant offers on its hosted login page.
|
|
33
|
-
export type AuthConnection = 'google' | 'password' | 'magic';
|
|
34
|
-
|
|
35
|
-
export interface Tenant {
|
|
36
|
-
tenant_id: string;
|
|
37
|
-
issuer: string; // OIDC issuer: <base>/<tenant_id>
|
|
38
|
-
login_url: string; // hosted authorize endpoint
|
|
39
|
-
connections?: AuthConnection[];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface CreateTenantInput {
|
|
43
|
-
// Sign-in methods to enable. Defaults to ['google'] when omitted.
|
|
44
|
-
connections?: AuthConnection[];
|
|
45
|
-
// Opaque JSON for the hosted login page (branding).
|
|
46
|
-
theme?: unknown;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface AuthClient {
|
|
50
|
-
client_id: string;
|
|
51
|
-
name?: string;
|
|
52
|
-
type: 'spa' | 'web';
|
|
53
|
-
redirect_uris: string[];
|
|
54
|
-
issuer?: string;
|
|
55
|
-
// Returned exactly ONCE, on create, for confidential ('web') clients.
|
|
56
|
-
// SPA clients are public and have no secret.
|
|
57
|
-
client_secret?: string;
|
|
58
|
-
created_at?: string;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export interface ListClientsResponse {
|
|
62
|
-
clients: AuthClient[];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export interface CreateClientInput {
|
|
66
|
-
name: string;
|
|
67
|
-
type: 'spa' | 'web';
|
|
68
|
-
redirect_uris: string[];
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Monthly-active-user metering for the tenant (auth is billed per MAU).
|
|
72
|
-
export interface AuthUsage {
|
|
73
|
-
period: string; // 'YYYY-MM'
|
|
74
|
-
active_users: number;
|
|
75
|
-
price_cents_each: number;
|
|
76
|
-
as_of?: string; // RFC3339 freshness marker — MAU is aggregated, not real-time
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// A custom auth domain (e.g. auth.acme.com) for the tenant's hosted login +
|
|
80
|
-
// issuer. Lifecycle: awaiting_verification (publish the TXT challenge, then
|
|
81
|
-
// `verify`) → pending (publish the A record, TLS provisions) → active.
|
|
82
|
-
export interface AuthDomain {
|
|
83
|
-
domain: string;
|
|
84
|
-
status: 'awaiting_verification' | 'pending' | 'active';
|
|
85
|
-
// Present while `awaiting_verification` — the ownership-challenge TXT record
|
|
86
|
-
// to publish, then call verifyDomain().
|
|
87
|
-
verification?: { type: string; name: string; value: string };
|
|
88
|
-
// Present once `pending`/`active` — the A record to publish (withheld until
|
|
89
|
-
// ownership is verified).
|
|
90
|
-
dns?: { type: string; name: string; value: string };
|
|
91
|
-
next?: string;
|
|
92
|
-
issuer?: string; // present once active
|
|
93
|
-
login_url?: string; // present once active
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Create (or update) the org's auth tenant. Idempotent. `connections` selects
|
|
97
|
-
// the sign-in methods (subset of google/password/magic; defaults to google);
|
|
98
|
-
// `theme` is opaque JSON for the hosted login page.
|
|
99
|
-
export async function createTenant(apiKey: string, orgId: string, input: CreateTenantInput = {}): Promise<Tenant> {
|
|
100
|
-
const body: Record<string, unknown> = {};
|
|
101
|
-
if (input.connections !== undefined) body.connections = input.connections;
|
|
102
|
-
if (input.theme !== undefined) body.theme = input.theme;
|
|
103
|
-
return request('POST', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/tenant`, apiKey, body);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Fetch the org's auth tenant. Rejects with TENANT_NOT_FOUND if not created.
|
|
107
|
-
export async function getTenant(apiKey: string, orgId: string): Promise<Tenant> {
|
|
108
|
-
return request('GET', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/tenant`, apiKey);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Register an OIDC client (an app) under the org's tenant. Auto-provisions the
|
|
112
|
-
// tenant if absent. For type 'web' the response carries `client_secret` once.
|
|
113
|
-
export async function createClient(apiKey: string, orgId: string, input: CreateClientInput): Promise<AuthClient> {
|
|
114
|
-
return request('POST', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/clients`, apiKey, input);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export async function listClients(apiKey: string, orgId: string): Promise<ListClientsResponse> {
|
|
118
|
-
return request('GET', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/clients`, apiKey);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Delete (revoke) an OIDC client. Irreversible — the client_id stops
|
|
122
|
-
// authenticating immediately.
|
|
123
|
-
export async function deleteClient(apiKey: string, orgId: string, clientId: string): Promise<{ client_id: string; deleted: boolean }> {
|
|
124
|
-
return request('DELETE', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/clients/${encodeURIComponent(clientId)}`, apiKey);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Rotate a confidential ('web') client's secret. Returns the NEW secret exactly
|
|
128
|
-
// once; the previous secret stops working immediately. (SPA clients have no
|
|
129
|
-
// secret — rotating one is rejected by the backend.)
|
|
130
|
-
export async function rotateClient(apiKey: string, orgId: string, clientId: string): Promise<AuthClient> {
|
|
131
|
-
return request('POST', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/clients/${encodeURIComponent(clientId)}/rotate`, apiKey);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Monthly-active-user usage for the current period (auth is billed per MAU).
|
|
135
|
-
export async function getUsage(apiKey: string, orgId: string): Promise<AuthUsage> {
|
|
136
|
-
return request('GET', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/usage`, apiKey);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Set a custom auth domain (e.g. auth.acme.com). Returns the DNS record to
|
|
140
|
-
// create; TLS provisions automatically and the domain becomes the issuer once
|
|
141
|
-
// active. One custom domain per org.
|
|
142
|
-
export async function registerDomain(apiKey: string, orgId: string, domain: string): Promise<AuthDomain> {
|
|
143
|
-
return request('POST', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/domain`, apiKey, { domain });
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Fetch the org's custom auth domain. Rejects with NO_DOMAIN if none set.
|
|
147
|
-
export async function getDomain(apiKey: string, orgId: string): Promise<AuthDomain> {
|
|
148
|
-
return request('GET', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/domain`, apiKey);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Verify ownership of the org's custom auth domain by checking the published
|
|
152
|
-
// TXT challenge. On success the domain advances awaiting_verification → pending
|
|
153
|
-
// (and the response carries the A record). Rejects with 422 DOMAIN_NOT_VERIFIED
|
|
154
|
-
// if the TXT record isn't found yet. Idempotent once already pending/active.
|
|
155
|
-
export async function verifyDomain(apiKey: string, orgId: string): Promise<AuthDomain> {
|
|
156
|
-
return request('POST', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/domain/verify`, apiKey);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export async function deleteDomain(apiKey: string, orgId: string, domain: string): Promise<{ domain: string; status: string }> {
|
|
160
|
-
return request('DELETE', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/domain`, apiKey, { domain });
|
|
161
|
-
}
|
package/src/client.ts
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { ApiResponse } from './types';
|
|
2
|
-
|
|
3
|
-
export class MyApiError extends Error {
|
|
4
|
-
// `body` is the full error object as returned by the backend — preserved so
|
|
5
|
-
// callers can read service-specific fields the SDK doesn't otherwise model
|
|
6
|
-
// (e.g. CF_API_ERROR carries `cf_message` / `cf_status`).
|
|
7
|
-
constructor(public code: string, public status: number, public detail?: string, public body?: Record<string, unknown>) {
|
|
8
|
-
// Surface the detail in the message so console.error / err.message
|
|
9
|
-
// shows useful info without callers having to look at err.detail.
|
|
10
|
-
super(detail ? `${code} (${status}): ${detail}` : code);
|
|
11
|
-
this.name = 'MyApiError';
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async function requestFull<T>(
|
|
16
|
-
method: string,
|
|
17
|
-
url: string,
|
|
18
|
-
apiKey?: string,
|
|
19
|
-
body?: unknown,
|
|
20
|
-
extraHeaders?: Record<string, string>,
|
|
21
|
-
): Promise<ApiResponse<T>> {
|
|
22
|
-
const headers: Record<string, string> = {};
|
|
23
|
-
|
|
24
|
-
if (apiKey) {
|
|
25
|
-
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (body !== undefined) {
|
|
29
|
-
headers['Content-Type'] = 'application/json';
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Caller-supplied headers (e.g. CAS via If-Match) win over the defaults
|
|
33
|
-
// above. Keep this near the top so route logic later doesn't accidentally
|
|
34
|
-
// overwrite something the caller passed in.
|
|
35
|
-
if (extraHeaders) {
|
|
36
|
-
for (const [k, v] of Object.entries(extraHeaders)) headers[k] = v;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const options: RequestInit = {
|
|
40
|
-
method,
|
|
41
|
-
headers,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
if (body !== undefined) {
|
|
45
|
-
options.body = JSON.stringify(body);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const response = await fetch(url, options);
|
|
49
|
-
|
|
50
|
-
if (response.status === 204) {
|
|
51
|
-
return { success: true, data: null, error: null, meta: {} as ApiResponse<T>['meta'] };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Read the body as text first so we can include a snippet in errors when
|
|
55
|
-
// it isn't valid JSON (HTML error pages from edge proxies, plaintext
|
|
56
|
-
// 5xx, etc.). One read; we re-parse manually below.
|
|
57
|
-
const raw = await response.text();
|
|
58
|
-
|
|
59
|
-
let result: any;
|
|
60
|
-
try {
|
|
61
|
-
result = raw ? JSON.parse(raw) : {};
|
|
62
|
-
} catch {
|
|
63
|
-
const snippet = raw.slice(0, 200).replace(/\s+/g, ' ').trim();
|
|
64
|
-
throw new MyApiError(
|
|
65
|
-
'invalid_json_response',
|
|
66
|
-
response.status,
|
|
67
|
-
`${method} ${url} returned non-JSON (status ${response.status}): ${snippet}`,
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (!response.ok) {
|
|
72
|
-
const err = result?.error;
|
|
73
|
-
const code = typeof err === 'object' ? (err?.code || 'unknown_error') : (err || 'unknown_error');
|
|
74
|
-
const detail = typeof err === 'object' ? (err?.message || undefined) : undefined;
|
|
75
|
-
const errBody = typeof err === 'object' ? err : undefined;
|
|
76
|
-
throw new MyApiError(code, response.status, detail, errBody);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const apiResponse = result as ApiResponse<T>;
|
|
80
|
-
if (!apiResponse.success) {
|
|
81
|
-
const err = apiResponse.error as any;
|
|
82
|
-
const code = typeof err === 'object' ? (err?.code || 'unknown_error') : (err || 'unknown_error');
|
|
83
|
-
const detail = typeof err === 'object' ? (err?.message || undefined) : undefined;
|
|
84
|
-
const errBody = typeof err === 'object' ? err : undefined;
|
|
85
|
-
throw new MyApiError(code, response.status, detail, errBody);
|
|
86
|
-
}
|
|
87
|
-
|
|
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 };
|
|
121
|
-
}
|
package/src/company.ts
DELETED
|
@@ -1,48 +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 { COMPANY_BASE as BASE_URL } from './config';
|
|
9
|
-
import type { Exposes } from './exposes';
|
|
10
|
-
|
|
11
|
-
import type { SearchFilter } from './audience';
|
|
12
|
-
import type { Person } from './people';
|
|
13
|
-
|
|
14
|
-
export const EXPOSES: Exposes = [
|
|
15
|
-
'POST /company/orgs/{org_id}/search',
|
|
16
|
-
'GET /company/orgs/{org_id}/{company_id}',
|
|
17
|
-
];
|
|
18
|
-
|
|
19
|
-
import type { Location, PersonCompany } from './people';
|
|
20
|
-
|
|
21
|
-
// Company is the same shape as PersonCompany (a person's embedded company)
|
|
22
|
-
// with an optional embedded people array when include_people=N is passed.
|
|
23
|
-
export interface Company extends PersonCompany {
|
|
24
|
-
people?: Person[]; // present when include_people=N is passed
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface CompanySearchResult {
|
|
28
|
-
companies: Company[];
|
|
29
|
-
total: number;
|
|
30
|
-
limit: number;
|
|
31
|
-
offset: number;
|
|
32
|
-
has_more: boolean;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface CompanySearchOptions extends SearchFilter {
|
|
36
|
-
include_people?: number; // 0..10; embed up to N people per company
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export async function searchCompanies(apiKey: string, orgId: string, options: CompanySearchOptions): Promise<CompanySearchResult> {
|
|
40
|
-
return request('POST', `${BASE_URL}/company/orgs/${encodeURIComponent(orgId)}/search`, apiKey, options);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export async function getCompany(apiKey: string, orgId: string, companyId: string, include_people?: number): Promise<Company> {
|
|
44
|
-
const _url = include_people != null
|
|
45
|
-
? `${BASE_URL}/company/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(companyId)}?include_people=${encodeURIComponent(String(include_people))}`
|
|
46
|
-
: `${BASE_URL}/company/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(companyId)}`;
|
|
47
|
-
return request('GET', _url, apiKey);
|
|
48
|
-
}
|
package/src/config.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// Service base URLs. Today's reality: all primitives are served from the
|
|
2
|
-
// single `api.myapihq.com` gateway; per-service brand hosts
|
|
3
|
-
// (e.g. api.myllmapi.com) are aspirational and only some are wired in DNS
|
|
4
|
-
// + routed correctly. We default to the working hosts where they exist,
|
|
5
|
-
// and to the gateway where they don't (LLM, DATABASE, CRM, PEOPLE,
|
|
6
|
-
// COMPANY, AUDIENCE). The MYAPI_*_URL env vars stay as overrides for
|
|
7
|
-
// local dev — `source scripts/local-env.sh` still points everything at
|
|
8
|
-
// localhost:8080 regardless of the prod default.
|
|
9
|
-
|
|
10
|
-
const GATEWAY = 'https://api.myapihq.com';
|
|
11
|
-
|
|
12
|
-
export const HQ_BASE = process.env.MYAPI_HQ_URL ?? process.env.MYAPI_API_BASE ?? GATEWAY;
|
|
13
|
-
export const DOMAIN_BASE = process.env.MYAPI_DOMAIN_URL ?? 'https://api.mydomainapi.com';
|
|
14
|
-
export const FUNNEL_BASE = process.env.MYAPI_FUNNEL_URL ?? 'https://api.myfunnelapi.com';
|
|
15
|
-
// New slots — routed via the gateway until brand hosts are DNS-wired
|
|
16
|
-
// (mirrors the LLM/DATABASE/CRM pattern below).
|
|
17
|
-
export const FUNCTION_BASE= process.env.MYAPI_FUNCTION_URL?? GATEWAY;
|
|
18
|
-
export const PAYMENTS_BASE= process.env.MYAPI_PAYMENTS_URL?? GATEWAY;
|
|
19
|
-
export const CONTAINER_BASE=process.env.MYAPI_CONTAINER_URL?? GATEWAY;
|
|
20
|
-
export const GIT_BASE = process.env.MYAPI_GIT_URL ?? GATEWAY;
|
|
21
|
-
export const QUEUE_BASE = process.env.MYAPI_QUEUE_URL ?? GATEWAY;
|
|
22
|
-
export const TASK_BASE = process.env.MYAPI_TASK_URL ?? GATEWAY;
|
|
23
|
-
export const IMAGE_BASE = process.env.MYAPI_IMAGE_URL ?? 'https://api.myimageapi.com';
|
|
24
|
-
export const WEBHOOK_BASE = process.env.MYAPI_WEBHOOK_URL ?? 'https://api.mywebhookapi.com';
|
|
25
|
-
export const WORKFLOW_BASE= process.env.MYAPI_WORKFLOW_URL?? 'https://api.myworkflowapi.com';
|
|
26
|
-
export const EMAIL_BASE = process.env.MYAPI_EMAIL_URL ?? 'https://api.myemailapi.com';
|
|
27
|
-
export const STORAGE_BASE = process.env.MYAPI_STORAGE_URL ?? 'https://api.mystorageapi.com';
|
|
28
|
-
export const URL_BASE = process.env.MYAPI_URL_URL ?? 'https://api.myurlto.com';
|
|
29
|
-
export const PIXEL_BASE = process.env.MYAPI_PIXEL_URL ?? 'https://api.mypixelapi.com';
|
|
30
|
-
// Per-service hosts below either don't resolve in DNS or 404 the routes;
|
|
31
|
-
// served via the gateway until backend wires the brand routing. Flip back
|
|
32
|
-
// to per-service hosts when DNS is sound and routes verified there.
|
|
33
|
-
export const PEOPLE_BASE = process.env.MYAPI_PEOPLE_URL ?? GATEWAY;
|
|
34
|
-
export const COMPANY_BASE = process.env.MYAPI_COMPANY_URL ?? GATEWAY;
|
|
35
|
-
export const AUDIENCE_BASE= process.env.MYAPI_AUDIENCE_URL?? GATEWAY;
|
|
36
|
-
export const LLM_BASE = process.env.MYAPI_LLM_URL ?? GATEWAY;
|
|
37
|
-
export const DATABASE_BASE= process.env.MYAPI_DATABASE_URL?? GATEWAY;
|
|
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
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import { request, MyApiError } from './client';
|
|
2
|
-
import { ApiResponse } from './types';
|
|
3
|
-
import { CONTAINER_BASE as BASE_URL } from './config';
|
|
4
|
-
import type { Exposes } from './exposes';
|
|
5
|
-
|
|
6
|
-
// Backend: my-container-api per myapi-hq/internal/routes/container/. Phase 1
|
|
7
|
-
// is metadata + scoped API key issuance; the Cloud Run build/deploy pipeline
|
|
8
|
-
// is Phase 2 — `deployContainer` returns RUNTIME_UNAVAILABLE until it lands.
|
|
9
|
-
export const EXPOSES: Exposes = [
|
|
10
|
-
'POST /container/orgs/{org_id}/containers',
|
|
11
|
-
'GET /container/orgs/{org_id}/containers',
|
|
12
|
-
'GET /container/orgs/{org_id}/containers/{id}',
|
|
13
|
-
'DELETE /container/orgs/{org_id}/containers/{id}',
|
|
14
|
-
'POST /container/orgs/{org_id}/containers/{id}/deploy',
|
|
15
|
-
'GET /container/orgs/{org_id}/containers/{id}/logs',
|
|
16
|
-
'POST /container/orgs/{org_id}/containers/{id}/domain',
|
|
17
|
-
'DELETE /container/orgs/{org_id}/containers/{id}/domain',
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
// service = HTTP server, worker = always-on background process, job = runs
|
|
21
|
-
// to completion (the only type that accepts a cron_schedule).
|
|
22
|
-
export type ContainerType = 'service' | 'worker' | 'job';
|
|
23
|
-
|
|
24
|
-
// Mirrors the backend's `Container` struct in crud.go. `url` is empty until
|
|
25
|
-
// the container is deployed.
|
|
26
|
-
export interface Container {
|
|
27
|
-
id: string;
|
|
28
|
-
org_id: string;
|
|
29
|
-
name: string;
|
|
30
|
-
type: ContainerType;
|
|
31
|
-
cron_schedule?: string;
|
|
32
|
-
env: Record<string, unknown>;
|
|
33
|
-
cpu: string;
|
|
34
|
-
memory: string;
|
|
35
|
-
min_instances: number;
|
|
36
|
-
max_instances: number;
|
|
37
|
-
port: number;
|
|
38
|
-
url?: string;
|
|
39
|
-
// Custom domain bound to this container, if any. Served via a Cloudflare
|
|
40
|
-
// Origin Rule that rewrites Host + SNI to the Cloud Run hostname.
|
|
41
|
-
custom_domain?: string;
|
|
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;
|
|
46
|
-
created_at: string;
|
|
47
|
-
updated_at: string;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface CreatePayload {
|
|
51
|
-
name: string; // required; ^[a-z0-9][a-z0-9-]{0,49}$
|
|
52
|
-
type?: ContainerType; // defaults to 'service' server-side
|
|
53
|
-
cron_schedule?: string; // only valid when type is 'job'
|
|
54
|
-
env?: Record<string, unknown>;
|
|
55
|
-
cpu?: string; // defaults to '1'
|
|
56
|
-
memory?: string; // defaults to '512Mi'
|
|
57
|
-
min_instances?: number; // defaults to 0 ('worker' is forced to >= 1)
|
|
58
|
-
max_instances?: number; // defaults to 3
|
|
59
|
-
port?: number;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// POST response — the scoped API key is returned ONCE. It is delivered to
|
|
63
|
-
// the running container as the MYAPI_KEY environment variable.
|
|
64
|
-
export interface CreateResponse {
|
|
65
|
-
container: Container;
|
|
66
|
-
scoped_api_key: string;
|
|
67
|
-
scoped_api_key_id: string;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// deployContainer response. The scoped key is ROTATED on every deploy — the
|
|
71
|
-
// value here is fresh and the only time it is knowable.
|
|
72
|
-
export interface DeployResponse {
|
|
73
|
-
container_id: string;
|
|
74
|
-
revision_id: string;
|
|
75
|
-
url: string;
|
|
76
|
-
status: string;
|
|
77
|
-
scoped_api_key: string;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export async function createContainer(apiKey: string, orgId: string, payload: CreatePayload): Promise<CreateResponse> {
|
|
81
|
-
return request('POST', `${BASE_URL}/container/orgs/${encodeURIComponent(orgId)}/containers`, apiKey, payload);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export async function listContainers(apiKey: string, orgId: string): Promise<Container[]> {
|
|
85
|
-
return request('GET', `${BASE_URL}/container/orgs/${encodeURIComponent(orgId)}/containers`, apiKey);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export async function getContainer(apiKey: string, orgId: string, containerId: string): Promise<Container> {
|
|
89
|
-
return request('GET', `${BASE_URL}/container/orgs/${encodeURIComponent(orgId)}/containers/${encodeURIComponent(containerId)}`, apiKey);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export async function deleteContainer(apiKey: string, orgId: string, containerId: string): Promise<void> {
|
|
93
|
-
return request('DELETE', `${BASE_URL}/container/orgs/${encodeURIComponent(orgId)}/containers/${encodeURIComponent(containerId)}`, apiKey);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// deployContainer ships a revision from a pre-built image ref to Cloud Run.
|
|
97
|
-
// Synchronous — returns status='active' with a rotated scoped key.
|
|
98
|
-
export async function deployContainer(apiKey: string, orgId: string, containerId: string, image: string): Promise<DeployResponse> {
|
|
99
|
-
return request('POST', `${BASE_URL}/container/orgs/${encodeURIComponent(orgId)}/containers/${encodeURIComponent(containerId)}/deploy`, apiKey, { image });
|
|
100
|
-
}
|
|
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
|
-
|
|
148
|
-
// One runtime log line from the container's Cloud Run resource.
|
|
149
|
-
export interface LogEntry {
|
|
150
|
-
timestamp: string;
|
|
151
|
-
severity: string;
|
|
152
|
-
text: string;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// getContainerLogs returns recent runtime log entries, newest first.
|
|
156
|
-
// `tail` caps the count (default 100, max 1000 server-side). An
|
|
157
|
-
// undeployed container returns an empty array.
|
|
158
|
-
export async function getContainerLogs(apiKey: string, orgId: string, containerId: string, tail?: number): Promise<LogEntry[]> {
|
|
159
|
-
const query = tail ? `?tail=${encodeURIComponent(String(tail))}` : '';
|
|
160
|
-
return request('GET', `${BASE_URL}/container/orgs/${encodeURIComponent(orgId)}/containers/${encodeURIComponent(containerId)}/logs${query}`, apiKey);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// bindDomain response. `origin` is the Cloud Run hostname the custom domain
|
|
164
|
-
// is fronted onto via a Cloudflare Origin Rule (Host + SNI rewrite).
|
|
165
|
-
export interface DomainBinding {
|
|
166
|
-
container_id: string;
|
|
167
|
-
custom_domain: string;
|
|
168
|
-
origin: string;
|
|
169
|
-
status: string;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// bindDomain points a custom domain at a deployed container. The parent
|
|
173
|
-
// domain must be MyAPI-managed. Errors: 422 (container not deployed, or no
|
|
174
|
-
// MyAPI-managed parent domain), 409 (container already has a domain, or the
|
|
175
|
-
// hostname is taken).
|
|
176
|
-
function domainUrl(orgId: string, containerId: string): string {
|
|
177
|
-
return `${BASE_URL}/container/orgs/${encodeURIComponent(orgId)}/containers/${encodeURIComponent(containerId)}/domain`;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
export async function bindDomain(apiKey: string, orgId: string, containerId: string, domain: string): Promise<DomainBinding> {
|
|
181
|
-
return request('POST', domainUrl(orgId, containerId), apiKey, { domain });
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// unbindDomain removes the custom domain from a container.
|
|
185
|
-
export async function unbindDomain(apiKey: string, orgId: string, containerId: string): Promise<void> {
|
|
186
|
-
return request('DELETE', domainUrl(orgId, containerId), apiKey);
|
|
187
|
-
}
|