@myapihq/sdk 2.0.1 → 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/funds.d.ts +11 -0
- package/dist/funds.js +55 -0
- package/dist/hq.d.ts +22 -0
- package/dist/hq.js +18 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/services.js +2 -2
- package/package.json +1 -1
- package/src/funds.ts +66 -0
- package/src/hq.ts +59 -0
- package/src/index.ts +1 -0
- package/src/services.ts +2 -2
package/dist/funds.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { MyApiError } from './client';
|
|
2
|
+
export type AutoRechargeState = 'in_flight' | 'disabled' | 'capped' | 'no_pm' | 'failed';
|
|
3
|
+
export declare function isInsufficientFunds(err: unknown): err is MyApiError;
|
|
4
|
+
export declare function isSpendCapExceeded(err: unknown): err is MyApiError;
|
|
5
|
+
export declare function autoRechargeState(err: unknown): AutoRechargeState | undefined;
|
|
6
|
+
export interface FundsRetryOptions {
|
|
7
|
+
maxRetries?: number;
|
|
8
|
+
sleep?: (ms: number) => Promise<void>;
|
|
9
|
+
onRetry?: (attempt: number, waitSeconds: number) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function withFundsRetry<T>(call: () => Promise<T>, opts?: FundsRetryOptions): Promise<T>;
|
package/dist/funds.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isInsufficientFunds = isInsufficientFunds;
|
|
4
|
+
exports.isSpendCapExceeded = isSpendCapExceeded;
|
|
5
|
+
exports.autoRechargeState = autoRechargeState;
|
|
6
|
+
exports.withFundsRetry = withFundsRetry;
|
|
7
|
+
const client_1 = require("./client");
|
|
8
|
+
// Wallet empty. Matches the unified `INSUFFICIENT_FUNDS` and the legacy
|
|
9
|
+
// `INSUFFICIENT_BALANCE` (still emitted by imagegen's free-tier path until the
|
|
10
|
+
// backend finishes the split — tolerate both).
|
|
11
|
+
function isInsufficientFunds(err) {
|
|
12
|
+
return err instanceof client_1.MyApiError &&
|
|
13
|
+
(err.code === 'INSUFFICIENT_FUNDS' || err.code === 'INSUFFICIENT_BALANCE');
|
|
14
|
+
}
|
|
15
|
+
// Hard account spend ceiling hit (distinct from an empty wallet). Always needs
|
|
16
|
+
// a human — raising/clearing the cap — never a retry or top-up.
|
|
17
|
+
function isSpendCapExceeded(err) {
|
|
18
|
+
return err instanceof client_1.MyApiError && err.code === 'SPEND_CAP_EXCEEDED';
|
|
19
|
+
}
|
|
20
|
+
// The auto_recharge state on an INSUFFICIENT_FUNDS error, if the backend set it.
|
|
21
|
+
function autoRechargeState(err) {
|
|
22
|
+
if (!(err instanceof client_1.MyApiError))
|
|
23
|
+
return undefined;
|
|
24
|
+
const s = err.body?.auto_recharge;
|
|
25
|
+
return typeof s === 'string' ? s : undefined;
|
|
26
|
+
}
|
|
27
|
+
const defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
28
|
+
// Wrap a billable call so an empty wallet with an auto-recharge IN FLIGHT is
|
|
29
|
+
// polled-and-retried instead of failing — the whole point of the 402 split,
|
|
30
|
+
// so an autonomous agent doesn't escalate to a human for a refill that's
|
|
31
|
+
// already coming. Retries ONLY on `INSUFFICIENT_FUNDS` + `auto_recharge ===
|
|
32
|
+
// 'in_flight'`, sleeping the server-provided `retry_after_seconds` (fallback
|
|
33
|
+
// 5s), up to maxRetries. Every other case — capped / no_pm / failed /
|
|
34
|
+
// disabled, or a `SPEND_CAP_EXCEEDED` hard ceiling — rethrows immediately:
|
|
35
|
+
// those need a human (top up, fix the card, raise the cap), not a retry.
|
|
36
|
+
async function withFundsRetry(call, opts = {}) {
|
|
37
|
+
const maxRetries = opts.maxRetries ?? 3;
|
|
38
|
+
const sleep = opts.sleep ?? defaultSleep;
|
|
39
|
+
let attempt = 0;
|
|
40
|
+
for (;;) {
|
|
41
|
+
try {
|
|
42
|
+
return await call();
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
if (attempt >= maxRetries || !isInsufficientFunds(err) || autoRechargeState(err) !== 'in_flight') {
|
|
46
|
+
throw err;
|
|
47
|
+
}
|
|
48
|
+
attempt++;
|
|
49
|
+
const hinted = err.body?.retry_after_seconds;
|
|
50
|
+
const waitSec = typeof hinted === 'number' && hinted > 0 ? hinted : 5;
|
|
51
|
+
opts.onRetry?.(attempt, waitSec);
|
|
52
|
+
await sleep(waitSec * 1000);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
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
|
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
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
|
package/dist/services.js
CHANGED
|
@@ -48,8 +48,8 @@ exports.SERVICES = [
|
|
|
48
48
|
},
|
|
49
49
|
// ── send ────────────────────────────────────────────────────────────
|
|
50
50
|
{
|
|
51
|
-
// Customer-facing email surface — mailboxes,
|
|
52
|
-
// 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
|
|
53
53
|
// (the [disabled, pre-launch] / 503 SERVICE_NOT_LAUNCHED state is gone).
|
|
54
54
|
// Sibling `my-email-verify-api` is the sync single-address verifier.
|
|
55
55
|
module: 'email',
|
package/package.json
CHANGED
package/src/funds.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { MyApiError } from './client';
|
|
2
|
+
|
|
3
|
+
// The auto-recharge state the backend carries on an `INSUFFICIENT_FUNDS` 402.
|
|
4
|
+
// `in_flight` = a refill was triggered and is on its way (retry); everything
|
|
5
|
+
// else needs a human action, not a retry.
|
|
6
|
+
export type AutoRechargeState = 'in_flight' | 'disabled' | 'capped' | 'no_pm' | 'failed';
|
|
7
|
+
|
|
8
|
+
// Wallet empty. Matches the unified `INSUFFICIENT_FUNDS` and the legacy
|
|
9
|
+
// `INSUFFICIENT_BALANCE` (still emitted by imagegen's free-tier path until the
|
|
10
|
+
// backend finishes the split — tolerate both).
|
|
11
|
+
export function isInsufficientFunds(err: unknown): err is MyApiError {
|
|
12
|
+
return err instanceof MyApiError &&
|
|
13
|
+
(err.code === 'INSUFFICIENT_FUNDS' || err.code === 'INSUFFICIENT_BALANCE');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Hard account spend ceiling hit (distinct from an empty wallet). Always needs
|
|
17
|
+
// a human — raising/clearing the cap — never a retry or top-up.
|
|
18
|
+
export function isSpendCapExceeded(err: unknown): err is MyApiError {
|
|
19
|
+
return err instanceof MyApiError && err.code === 'SPEND_CAP_EXCEEDED';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// The auto_recharge state on an INSUFFICIENT_FUNDS error, if the backend set it.
|
|
23
|
+
export function autoRechargeState(err: unknown): AutoRechargeState | undefined {
|
|
24
|
+
if (!(err instanceof MyApiError)) return undefined;
|
|
25
|
+
const s = err.body?.auto_recharge;
|
|
26
|
+
return typeof s === 'string' ? (s as AutoRechargeState) : undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface FundsRetryOptions {
|
|
30
|
+
// Max poll-and-retry attempts on an in-flight refill (default 3).
|
|
31
|
+
maxRetries?: number;
|
|
32
|
+
// Injectable sleep (tests pass a no-op; default is real setTimeout).
|
|
33
|
+
sleep?: (ms: number) => Promise<void>;
|
|
34
|
+
// Called before each wait — e.g. to log "balance low, refill in flight…".
|
|
35
|
+
onRetry?: (attempt: number, waitSeconds: number) => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const defaultSleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));
|
|
39
|
+
|
|
40
|
+
// Wrap a billable call so an empty wallet with an auto-recharge IN FLIGHT is
|
|
41
|
+
// polled-and-retried instead of failing — the whole point of the 402 split,
|
|
42
|
+
// so an autonomous agent doesn't escalate to a human for a refill that's
|
|
43
|
+
// already coming. Retries ONLY on `INSUFFICIENT_FUNDS` + `auto_recharge ===
|
|
44
|
+
// 'in_flight'`, sleeping the server-provided `retry_after_seconds` (fallback
|
|
45
|
+
// 5s), up to maxRetries. Every other case — capped / no_pm / failed /
|
|
46
|
+
// disabled, or a `SPEND_CAP_EXCEEDED` hard ceiling — rethrows immediately:
|
|
47
|
+
// those need a human (top up, fix the card, raise the cap), not a retry.
|
|
48
|
+
export async function withFundsRetry<T>(call: () => Promise<T>, opts: FundsRetryOptions = {}): Promise<T> {
|
|
49
|
+
const maxRetries = opts.maxRetries ?? 3;
|
|
50
|
+
const sleep = opts.sleep ?? defaultSleep;
|
|
51
|
+
let attempt = 0;
|
|
52
|
+
for (;;) {
|
|
53
|
+
try {
|
|
54
|
+
return await call();
|
|
55
|
+
} catch (err) {
|
|
56
|
+
if (attempt >= maxRetries || !isInsufficientFunds(err) || autoRechargeState(err) !== 'in_flight') {
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
attempt++;
|
|
60
|
+
const hinted = (err as MyApiError).body?.retry_after_seconds;
|
|
61
|
+
const waitSec = typeof hinted === 'number' && hinted > 0 ? hinted : 5;
|
|
62
|
+
opts.onRetry?.(attempt, waitSec);
|
|
63
|
+
await sleep(waitSec * 1000);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
package/src/hq.ts
CHANGED
|
@@ -281,6 +281,51 @@ export async function topUp(apiKey: string, amountDollars: number): Promise<{ ne
|
|
|
281
281
|
return request('POST', `${BASE_URL}/hq/billing/topup`, apiKey, { amount_dollars: amountDollars });
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
+
// ── Auto-recharge ──────────────────────────────────────────────────────
|
|
285
|
+
// Keeps the prepaid wallet funded without a human in the loop: when the
|
|
286
|
+
// balance drops below `threshold_cents`, the backend charges the saved card
|
|
287
|
+
// `amount_cents` off-session, bounded by `monthly_cap_cents`. Opt-in, off by
|
|
288
|
+
// default; enabling requires a saved payment method.
|
|
289
|
+
|
|
290
|
+
export type RechargeStatus = 'succeeded' | 'failed' | 'capped' | 'no_pm' | 'pending';
|
|
291
|
+
|
|
292
|
+
export interface AutoRechargeConfig {
|
|
293
|
+
enabled: boolean;
|
|
294
|
+
threshold_cents: number | null;
|
|
295
|
+
amount_cents: number | null;
|
|
296
|
+
monthly_cap_cents: number | null;
|
|
297
|
+
// Auto-recharge dollars charged this calendar month (vs the cap).
|
|
298
|
+
month_to_date_recharged_cents: number;
|
|
299
|
+
has_payment_method: boolean;
|
|
300
|
+
last_recharge_status: RechargeStatus | null;
|
|
301
|
+
last_recharge_attempt_at: string | null;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export async function getAutoRecharge(apiKey: string): Promise<AutoRechargeConfig> {
|
|
305
|
+
return request('GET', `${BASE_URL}/hq/billing/auto-recharge`, apiKey);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export interface SetAutoRechargeInput {
|
|
309
|
+
enabled: boolean;
|
|
310
|
+
threshold_cents?: number;
|
|
311
|
+
amount_cents?: number;
|
|
312
|
+
monthly_cap_cents?: number;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Enable/update auto-recharge. The backend validates invariants and rejects
|
|
316
|
+
// with a 400 (`MISSING_FIELDS`, `AMOUNT_BELOW_FLOOR` [$5 floor],
|
|
317
|
+
// `AMOUNT_LT_THRESHOLD`, `CAP_LT_AMOUNT`, `NO_PAYMENT_METHOD`) — surfaced as a
|
|
318
|
+
// MyApiError. Returns the full config on success.
|
|
319
|
+
export async function setAutoRecharge(apiKey: string, input: SetAutoRechargeInput): Promise<AutoRechargeConfig> {
|
|
320
|
+
return request('PUT', `${BASE_URL}/hq/billing/auto-recharge`, apiKey, input);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Disable auto-recharge (the threshold/amount/cap are preserved for easy
|
|
324
|
+
// re-enable). Backend returns 204 No Content.
|
|
325
|
+
export async function disableAutoRecharge(apiKey: string): Promise<void> {
|
|
326
|
+
return request('DELETE', `${BASE_URL}/hq/billing/auto-recharge`, apiKey);
|
|
327
|
+
}
|
|
328
|
+
|
|
284
329
|
// ── Org doctor ─────────────────────────────────────────────────────────
|
|
285
330
|
// Aggregated consistency report across slots. The endpoint resolves the
|
|
286
331
|
// reported org from the API key's binding, so the `{org_id}` path param
|
|
@@ -310,12 +355,26 @@ export interface DoctorIssue {
|
|
|
310
355
|
category?: string;
|
|
311
356
|
message: string;
|
|
312
357
|
hint?: string;
|
|
358
|
+
// True when the issue is platform-side and NOT actionable by the customer
|
|
359
|
+
// (e.g. a degraded internal dependency the MyAPI team owns). The backend
|
|
360
|
+
// sets this together with `category: 'internal'` and a customer-appropriate
|
|
361
|
+
// message; the operator detail is routed to an internal sink, not here.
|
|
362
|
+
// Consumers should surface these for transparency but must NOT count them
|
|
363
|
+
// as customer-actionable failures (they don't fail `doctor`'s exit code).
|
|
364
|
+
operator_only?: boolean;
|
|
313
365
|
}
|
|
314
366
|
|
|
315
367
|
export interface DoctorSection {
|
|
316
368
|
name: string;
|
|
317
369
|
summary: string;
|
|
318
370
|
issues: DoctorIssue[];
|
|
371
|
+
// Authoritative count of resources of this section's kind in the org,
|
|
372
|
+
// independent of how many issues were emitted. Lets a consumer tell
|
|
373
|
+
// "zero resources" apart from "resources present, all healthy" without
|
|
374
|
+
// inferring it from `issues.length` (which breaks the moment the backend
|
|
375
|
+
// stops emitting an `ok` row per healthy resource). Optional: older
|
|
376
|
+
// backends omit it, and consumers must fall back to the issue-count proxy.
|
|
377
|
+
resource_count?: number;
|
|
319
378
|
}
|
|
320
379
|
|
|
321
380
|
export interface DoctorReport {
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './types';
|
|
2
2
|
export * from './client';
|
|
3
|
+
export * from './funds';
|
|
3
4
|
// Note: config constants (STORAGE_BASE etc.) are NOT re-exported from the
|
|
4
5
|
// barrel. TypeScript compiles `export * from './config'` to a runtime
|
|
5
6
|
// `__exportStar` call that Node's cjs-module-lexer can't see through, so
|
package/src/services.ts
CHANGED
|
@@ -75,8 +75,8 @@ export const SERVICES: readonly ServiceMeta[] = [
|
|
|
75
75
|
|
|
76
76
|
// ── send ────────────────────────────────────────────────────────────
|
|
77
77
|
{
|
|
78
|
-
// Customer-facing email surface — mailboxes,
|
|
79
|
-
// warmup. Launched 2026-05-17: the backend lifted the pre-launch gate
|
|
78
|
+
// Customer-facing email surface — mailboxes, transactional send,
|
|
79
|
+
// templates, warmup. Launched 2026-05-17: the backend lifted the pre-launch gate
|
|
80
80
|
// (the [disabled, pre-launch] / 503 SERVICE_NOT_LAUNCHED state is gone).
|
|
81
81
|
// Sibling `my-email-verify-api` is the sync single-address verifier.
|
|
82
82
|
module: 'email',
|