@vxil/sdk 0.9.0 → 0.11.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/README.md +1 -1
- package/dist/index.d.ts +663 -40
- package/dist/index.js +141 -12
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ await vx.notifications.send({ user_id: 'u_1', template: 'welcome', data: { app_n
|
|
|
16
16
|
const { items } = await vx.from('tasks').query({ filter: { status: 'open' } });
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
- Zero dependencies. Runs anywhere `fetch` exists: Node ≥
|
|
19
|
+
- Zero dependencies. Runs anywhere `fetch` exists: Node ≥ 22, browsers, edge runtimes, and **React Native / Expo** (Hermes) — the SDK uses none of the WHATWG `URL` / `URLSearchParams` surface React Native only partially provides.
|
|
20
20
|
- Defaults to `https://api.vxil.com`; pass `baseUrl` to target another environment.
|
|
21
21
|
- Every feature the tenant has enabled is available as a typed namespace; generate a project-exact client with `npx @vxil/cli gen`.
|
|
22
22
|
- Every non-2xx response throws a `VxilError` carrying the structured error envelope (`status`, `code`, `message`, `hint`, `fixUrl`, `requestId`, and `retryAfter` in seconds when the server sent `Retry-After`).
|
package/dist/index.d.ts
CHANGED
|
@@ -367,6 +367,21 @@ export interface JobRun {
|
|
|
367
367
|
/** the single-run read (`vx.jobs.run` / `waitForRun`) only: your enqueue
|
|
368
368
|
* payload — `{ redacted: true }` for a platform-internal run; never on lists */
|
|
369
369
|
payload_json?: unknown;
|
|
370
|
+
/** a platform webhook delivery run (`job_name: 'webhooks.deliver'`) only:
|
|
371
|
+
* the event type it delivers (e.g. `payments.charge.refunded`) — so a dead
|
|
372
|
+
* delivery names WHICH event was lost while its payload stays redacted;
|
|
373
|
+
* null on every other run (list + single read, since 2026-10-01) */
|
|
374
|
+
webhook_event?: string | null;
|
|
375
|
+
/** a platform webhook delivery run only: the event's id — the `audit_id`
|
|
376
|
+
* the delivery carries to your handler and the row id in `GET /v1/audit`;
|
|
377
|
+
* null on every other run */
|
|
378
|
+
audit_id?: number | null;
|
|
379
|
+
/** the schedule (`sch_…`) that fired this run; null for a run you enqueued */
|
|
380
|
+
schedule_id?: string | null;
|
|
381
|
+
/** the SLOT a schedule-fired run was due for (the schedule's next_run_at at
|
|
382
|
+
* the fire); `started_at − scheduled_for` is how late it started. null for a
|
|
383
|
+
* run you enqueued (and for runs fired before 2026-10-01). */
|
|
384
|
+
scheduled_for?: string | null;
|
|
370
385
|
}
|
|
371
386
|
/** The run states no later write can move — what `waitForRun` and an
|
|
372
387
|
* async+wait invoke resolve `done: true` on. */
|
|
@@ -404,13 +419,35 @@ export interface JobFlowRule {
|
|
|
404
419
|
state: string;
|
|
405
420
|
created_at: string;
|
|
406
421
|
}
|
|
422
|
+
/** A schedule that fired on time but whose run has not STARTED for more than
|
|
423
|
+
* two of the schedule's intervals past its slot — held downstream (a busy
|
|
424
|
+
* queue, your concurrency cap, a flow rule). Announced once as
|
|
425
|
+
* `jobs.schedule.missed` with `reason: 'held'` — `functions.schedule.missed`
|
|
426
|
+
* (with `function`) for a function's cron trigger (`fn-cron:*`). */
|
|
427
|
+
export interface JobScheduleHeld {
|
|
428
|
+
run_id: string;
|
|
429
|
+
scheduled_for: string;
|
|
430
|
+
held_seconds: number;
|
|
431
|
+
}
|
|
407
432
|
export interface JobSchedule {
|
|
408
433
|
schedule_id: string;
|
|
409
434
|
job_name: string;
|
|
410
|
-
cron
|
|
411
|
-
|
|
435
|
+
/** the create echo carries no cron; the list names it `cron_expr` */
|
|
436
|
+
cron?: string | null;
|
|
437
|
+
cron_expr?: string | null;
|
|
438
|
+
run_at?: string | null;
|
|
412
439
|
next_run_at: string | null;
|
|
440
|
+
last_run_at?: string | null;
|
|
413
441
|
state: string;
|
|
442
|
+
/** `'skip'`: a due slot fires nothing while an earlier run of this schedule
|
|
443
|
+
* is still open (queued / running / retrying / waiting / delayed); the slot
|
|
444
|
+
* is counted in `skipped_fires`. `'allow'` (default): every slot fires. */
|
|
445
|
+
overlap?: 'allow' | 'skip';
|
|
446
|
+
/** slots skipped since the schedule last fired a run (list only) */
|
|
447
|
+
skipped_fires?: number;
|
|
448
|
+
last_skipped_at?: string | null;
|
|
449
|
+
/** non-null while the schedule is HELD (list only) */
|
|
450
|
+
held?: JobScheduleHeld | null;
|
|
414
451
|
}
|
|
415
452
|
export interface AuthSession {
|
|
416
453
|
token: string;
|
|
@@ -557,11 +594,42 @@ export interface PaymentsCharge {
|
|
|
557
594
|
/** the one-off product the charge bought (the `ledger.productMap` key), if any */
|
|
558
595
|
product_id: string | null;
|
|
559
596
|
amount_cents: number;
|
|
597
|
+
/** SETTLED refunds only (2026-10-01): a refund still awaiting the provider is
|
|
598
|
+
* in `amount_refund_pending`, and `status` follows the settled total */
|
|
560
599
|
amount_refunded: number;
|
|
600
|
+
/** reserved by outbound refunds the provider has not settled yet (a Paddle
|
|
601
|
+
* adjustment awaiting approval); moves to `amount_refunded` when approved,
|
|
602
|
+
* back to 0 when refused */
|
|
603
|
+
amount_refund_pending: number;
|
|
561
604
|
currency: string;
|
|
605
|
+
/** succeeded | partially_refunded | refunded | disputed | pending | failed —
|
|
606
|
+
* of SETTLED money */
|
|
562
607
|
status: string;
|
|
608
|
+
/** the provider-reported environment of the money (2026-10-01): a store
|
|
609
|
+
* reviewer's sandbox purchase (`revenuecat.sandboxUsers`) is `sandbox` —
|
|
610
|
+
* leave it out of revenue */
|
|
611
|
+
environment: PaymentsEventEnvironment;
|
|
563
612
|
created_at: string;
|
|
564
613
|
}
|
|
614
|
+
/** A refund row as listed by GET /v1/payments/refunds (2026-10-01). */
|
|
615
|
+
export interface PaymentsRefund {
|
|
616
|
+
/** `ref_…` — what `createRefund` returned, and what a 502 / `payments.refund.failed`
|
|
617
|
+
* / `payments.charge.refunded` names */
|
|
618
|
+
refund_id: string;
|
|
619
|
+
charge_id: string;
|
|
620
|
+
provider: string;
|
|
621
|
+
/** the provider's own refund / adjustment id (null until the provider answered) */
|
|
622
|
+
provider_refund_id: string | null;
|
|
623
|
+
amount_cents: number;
|
|
624
|
+
currency: string;
|
|
625
|
+
reason: string | null;
|
|
626
|
+
/** `pending` until the provider settles it (a Paddle adjustment awaiting approval) */
|
|
627
|
+
status: 'pending' | 'succeeded' | 'failed';
|
|
628
|
+
/** `api` = issued through POST /v1/payments/refunds; `webhook` = recorded from the
|
|
629
|
+
* provider (a refund made in the provider's own dashboard) */
|
|
630
|
+
source: 'api' | 'webhook';
|
|
631
|
+
created_at: string | null;
|
|
632
|
+
}
|
|
565
633
|
/** A subscription row as listed by GET /v1/payments/subscriptions (payments.md
|
|
566
634
|
* §3) — provider subscriptions, manual grants and purchase passes alike.
|
|
567
635
|
* `current_period_start` (2026-09-25) is the row's own window start: for a
|
|
@@ -797,8 +865,9 @@ export interface AiTokenExpiringEvent {
|
|
|
797
865
|
remint_path: string;
|
|
798
866
|
}
|
|
799
867
|
/** A job-routed async generation handle (POST /v1/ai/generate with mode:'job').
|
|
800
|
-
* The jobs run drives the provider call; read the answer
|
|
801
|
-
* (`
|
|
868
|
+
* The jobs run drives the provider call; read the settled answer with
|
|
869
|
+
* `ai.getGeneration(generation_id)` (kept 30 days) or via `resume_path`, or
|
|
870
|
+
* track the run through the jobs surface. */
|
|
802
871
|
export interface AiJobHandle {
|
|
803
872
|
generation_id: string;
|
|
804
873
|
run_id: string;
|
|
@@ -806,8 +875,46 @@ export interface AiJobHandle {
|
|
|
806
875
|
* `job.generation.completed|failed` events carry BOTH `generation_id` and
|
|
807
876
|
* `correlation_id` next to `run_id`, so no run→record link is needed. */
|
|
808
877
|
correlation_id?: string | null;
|
|
809
|
-
|
|
878
|
+
/** `pending` on a fresh handle; on a replay (`deduplicated`) the
|
|
879
|
+
* generation's status NOW — `processing`, `completed` or `failed` too */
|
|
880
|
+
status: 'pending' | 'processing' | 'completed' | 'failed';
|
|
881
|
+
/** a /v1 route, stable within the v1 major */
|
|
810
882
|
resume_path: string;
|
|
883
|
+
/** true when this answer is the REPLAY of an earlier request carrying the
|
|
884
|
+
* same `Idempotency-Key` and body (within 24 h) — nothing was enqueued
|
|
885
|
+
* again. The same key with a DIFFERENT body is `422 idempotency_key_reused`. */
|
|
886
|
+
deduplicated?: true;
|
|
887
|
+
}
|
|
888
|
+
/** One generation as `GET /v1/ai/generations/{id}` (and the `?correlation_id=`
|
|
889
|
+
* lookup) reads it. `result` is the settled answer of a JOB-lane generation
|
|
890
|
+
* (`generateAsync`), kept 30 days after it settles; a sync/stream generation
|
|
891
|
+
* returned its answer to you directly and records none. The lookup always
|
|
892
|
+
* carries `result: null` — read the text by id. */
|
|
893
|
+
export interface AiGenerationRecord {
|
|
894
|
+
generation_id: string;
|
|
895
|
+
correlation_id: string | null;
|
|
896
|
+
/** the jobs run carrying a job-lane generation (null on the other lanes) */
|
|
897
|
+
run_id: string | null;
|
|
898
|
+
status: 'pending' | 'processing' | 'completed' | 'failed';
|
|
899
|
+
provider: string;
|
|
900
|
+
model: string;
|
|
901
|
+
template: string | null;
|
|
902
|
+
usage: {
|
|
903
|
+
input_tokens: number;
|
|
904
|
+
output_tokens: number;
|
|
905
|
+
};
|
|
906
|
+
cached: boolean;
|
|
907
|
+
created_at: string;
|
|
908
|
+
settled_at: string | null;
|
|
909
|
+
result: {
|
|
910
|
+
text: string;
|
|
911
|
+
finish: string;
|
|
912
|
+
truncated: boolean;
|
|
913
|
+
} | null;
|
|
914
|
+
/** `stored` (in `result`) · `purged` (past the 30-day retention, or the
|
|
915
|
+
* user was erased) · `not_recorded` (not settled yet, or a sync/stream
|
|
916
|
+
* generation) */
|
|
917
|
+
result_state: 'stored' | 'purged' | 'not_recorded';
|
|
811
918
|
}
|
|
812
919
|
/** The `data` of `job.generation.queued` (jobs.md §11): the run was accepted. */
|
|
813
920
|
export interface JobGenerationQueuedEventPayload {
|
|
@@ -823,15 +930,18 @@ export interface JobGenerationQueuedEventPayload {
|
|
|
823
930
|
/** when the run is failed as expired if the provider never settles it */
|
|
824
931
|
expires_at: string;
|
|
825
932
|
}
|
|
826
|
-
/** The `data` of `job.generation.completed` / `job.generation.failed`.
|
|
933
|
+
/** The `data` of `job.generation.completed` / `job.generation.failed`. Every
|
|
934
|
+
* run that emitted `job.generation.queued` ends with exactly one of the two —
|
|
935
|
+
* a run refused at its credit hold included (`failed`, `error_class:
|
|
936
|
+
* 'ReserveInsufficient'`, beside the older `job.generation.reserve_aborted`). */
|
|
827
937
|
export interface JobGenerationSettledEventPayload {
|
|
828
938
|
run_id: string;
|
|
829
939
|
generation_id: string | null;
|
|
830
940
|
correlation_id: string | null;
|
|
831
941
|
/** `completed` on the healthy terminal, `failed` on the broken one */
|
|
832
942
|
status: 'completed' | 'failed';
|
|
833
|
-
/** the run's SETTLED error class (`provider_error`, `PollExhausted`,
|
|
834
|
-
* always null on `completed` */
|
|
943
|
+
/** the run's SETTLED error class (`provider_error`, `PollExhausted`,
|
|
944
|
+
* `GenerationExpired`, `ReserveInsufficient`, …); always null on `completed` */
|
|
835
945
|
error_class: string | null;
|
|
836
946
|
/** the provider hint alone (`Upstream said: gemini 400: …`); null on
|
|
837
947
|
* `completed` and whenever the failure carried none */
|
|
@@ -859,15 +969,274 @@ export interface JobRunEventPayload {
|
|
|
859
969
|
* exhausted its attempts normally. */
|
|
860
970
|
reason?: 'reaped' | 'queue_backstop';
|
|
861
971
|
}
|
|
862
|
-
/**
|
|
863
|
-
|
|
864
|
-
|
|
972
|
+
/** The provider-reported environment of the money (`production` | `sandbox`). */
|
|
973
|
+
export type PaymentsEventEnvironment = 'production' | 'sandbox';
|
|
974
|
+
/** Which credits a debit spends: ONE `credit_type`, or an ORDERED
|
|
975
|
+
* `credit_types` (1–8 distinct) — the whole amount from the FIRST type whose
|
|
976
|
+
* available balance covers it, never split across types (2026-10-01 W9). */
|
|
977
|
+
export type PaymentsCreditTypeChoice = {
|
|
978
|
+
credit_type: string;
|
|
979
|
+
credit_types?: never;
|
|
980
|
+
} | {
|
|
981
|
+
credit_types: string[];
|
|
982
|
+
credit_type?: never;
|
|
983
|
+
};
|
|
984
|
+
/** `POST /v1/payments/credits/consume` → `data`. */
|
|
985
|
+
export interface PaymentsConsumeResult {
|
|
986
|
+
balance_after: number;
|
|
987
|
+
ledger_entry_id: string | null;
|
|
988
|
+
reversed: boolean;
|
|
989
|
+
/** the credit type the debit came from (the chosen one for an ordered consume) */
|
|
990
|
+
credit_type: string;
|
|
991
|
+
}
|
|
992
|
+
/** `payments.charge.succeeded` (a new charge row) / `payments.charge.completed`
|
|
993
|
+
* (a recorded charge reached the provider's completed state — Paddle's
|
|
994
|
+
* `transaction.completed` after `transaction.paid`). */
|
|
995
|
+
export interface PaymentsChargeEventPayload {
|
|
996
|
+
end_user_id: string;
|
|
997
|
+
provider: string;
|
|
998
|
+
/** the vxil charge id (`chg_…`) */
|
|
999
|
+
charge_id: string | null;
|
|
1000
|
+
/** the provider's own transaction / payment id */
|
|
1001
|
+
provider_charge_id: string | null;
|
|
1002
|
+
amount_cents: number | null;
|
|
1003
|
+
currency: string;
|
|
1004
|
+
product_id: string | null;
|
|
1005
|
+
provider_status: PaymentsProviderChargeStatus | null;
|
|
1006
|
+
environment: PaymentsEventEnvironment;
|
|
1007
|
+
level: 'info';
|
|
1008
|
+
}
|
|
1009
|
+
/** `payments.charge.refunded` / `payments.charge.disputed` — the SAME keys on
|
|
1010
|
+
* the outbound route (`source: 'api'`, emitted when the route fully refunds a
|
|
1011
|
+
* charge) and on a provider delivery (`source: 'webhook'`). */
|
|
1012
|
+
export interface PaymentsChargeRefundedEventPayload {
|
|
1013
|
+
end_user_id: string;
|
|
1014
|
+
provider: string;
|
|
1015
|
+
charge_id: string | null;
|
|
1016
|
+
provider_charge_id: string | null;
|
|
1017
|
+
provider_refund_id: string | null;
|
|
1018
|
+
/** the refunds row (`ref_…`, readable at `GET /v1/payments/refunds?refund_id=`);
|
|
1019
|
+
* null for a dispute */
|
|
1020
|
+
refund_id: string | null;
|
|
1021
|
+
/** what THIS refund moved (a Stripe delivery's own delta, never the running total) */
|
|
1022
|
+
amount_cents: number | null;
|
|
1023
|
+
currency: string;
|
|
1024
|
+
/** the charge is fully refunded (or disputed): what it bought has ended */
|
|
1025
|
+
full: boolean;
|
|
1026
|
+
reason: string | null;
|
|
1027
|
+
/** the credits taken back, per credit type (bounded by what was unspent) */
|
|
1028
|
+
reversed: Array<{
|
|
1029
|
+
credit_type: string;
|
|
1030
|
+
amount: number;
|
|
1031
|
+
}>;
|
|
1032
|
+
source: 'api' | 'webhook';
|
|
1033
|
+
environment: PaymentsEventEnvironment;
|
|
1034
|
+
level: 'info';
|
|
1035
|
+
}
|
|
1036
|
+
/** `payments.refund.failed` — the provider refused (or the call failed) and the
|
|
1037
|
+
* reserve went back onto the charge. */
|
|
1038
|
+
export interface PaymentsRefundFailedEventPayload {
|
|
1039
|
+
refund_id: string;
|
|
1040
|
+
charge_id: string;
|
|
1041
|
+
end_user_id: string | null;
|
|
1042
|
+
provider: string;
|
|
1043
|
+
provider_charge_id: string | null;
|
|
1044
|
+
provider_refund_id: string | null;
|
|
1045
|
+
amount_cents: number;
|
|
1046
|
+
reason: string | null;
|
|
1047
|
+
source: 'api' | 'webhook';
|
|
1048
|
+
environment: PaymentsEventEnvironment;
|
|
1049
|
+
level: 'warn';
|
|
1050
|
+
state: 'broken';
|
|
1051
|
+
}
|
|
1052
|
+
/** `payments.subscription.<status>` (`trialing` | `active` | `past_due` |
|
|
1053
|
+
* `cancelled` | `expired` | `lapsed`). `slack_hours` is set on `lapsed` only
|
|
1054
|
+
* (the period-end sweep's own ending). */
|
|
1055
|
+
export interface PaymentsSubscriptionStatusEventPayload {
|
|
1056
|
+
end_user_id: string;
|
|
1057
|
+
subscription_id: string;
|
|
1058
|
+
provider: string;
|
|
1059
|
+
provider_sub_id: string | null;
|
|
1060
|
+
tier: string | null;
|
|
1061
|
+
status: 'trialing' | 'active' | 'past_due' | 'cancelled' | 'expired' | 'lapsed';
|
|
1062
|
+
current_period_start: string | null;
|
|
1063
|
+
current_period_end: string | null;
|
|
1064
|
+
environment: PaymentsEventEnvironment;
|
|
1065
|
+
/** what wrote it: the provider event type, `subscription.synced` /
|
|
1066
|
+
* `subscription.restored`, or `period_end_enforced` */
|
|
1067
|
+
event_type: string;
|
|
1068
|
+
slack_hours: number | null;
|
|
1069
|
+
level: 'info';
|
|
1070
|
+
state: 'ok';
|
|
1071
|
+
}
|
|
1072
|
+
/** `payments.subscription.granted` — a manual / support grant (`source:
|
|
1073
|
+
* 'manual'`) or a purchase pass the fold wrote (`source: 'purchase'`). */
|
|
1074
|
+
export interface PaymentsSubscriptionGrantedEventPayload {
|
|
1075
|
+
end_user_id: string;
|
|
1076
|
+
subscription_id: string | null;
|
|
1077
|
+
tier: string;
|
|
1078
|
+
/** the window start (a queued pass's first day may be in the future) */
|
|
1079
|
+
since: string;
|
|
1080
|
+
until: string | null;
|
|
1081
|
+
reason: string;
|
|
1082
|
+
source: 'manual' | 'purchase';
|
|
1083
|
+
charge_id: string | null;
|
|
1084
|
+
/** the manual grant's own key; null for a purchase */
|
|
1085
|
+
idempotency_key: string | null;
|
|
1086
|
+
/** the product a purchase bought; null for a manual grant */
|
|
1087
|
+
product_id: string | null;
|
|
1088
|
+
/** the provider of the purchase; null for a manual grant */
|
|
1089
|
+
provider: string | null;
|
|
1090
|
+
environment: PaymentsEventEnvironment | null;
|
|
1091
|
+
level: 'info';
|
|
1092
|
+
state: 'ok';
|
|
1093
|
+
}
|
|
1094
|
+
/** `payments.subscription.revoked` — a row ended by `/revoke` or `DELETE`, or by
|
|
1095
|
+
* its charge's full refund / chargeback. */
|
|
1096
|
+
export interface PaymentsSubscriptionRevokedEventPayload {
|
|
1097
|
+
end_user_id: string;
|
|
1098
|
+
subscription_id: string;
|
|
1099
|
+
tier: string | null;
|
|
1100
|
+
via: 'delete' | 'revoke' | 'refund' | 'chargeback';
|
|
1101
|
+
charge_id: string | null;
|
|
1102
|
+
provider_refund_id: string | null;
|
|
1103
|
+
level: 'info';
|
|
1104
|
+
state: 'ok';
|
|
1105
|
+
}
|
|
1106
|
+
/** `payments.subscription.transferred` — a RevenueCat TRANSFER (`reason:
|
|
1107
|
+
* 'transfer'`) or the account-merge re-key (`reason: 'account_merge'`,
|
|
1108
|
+
* `provider: null`). */
|
|
1109
|
+
export interface PaymentsSubscriptionTransferredEventPayload {
|
|
1110
|
+
end_user_id: string;
|
|
1111
|
+
from_user_ids: string[];
|
|
1112
|
+
moved: number;
|
|
1113
|
+
credits_moved: number;
|
|
1114
|
+
provider: string | null;
|
|
1115
|
+
reason: 'transfer' | 'account_merge';
|
|
1116
|
+
environment: PaymentsEventEnvironment;
|
|
1117
|
+
level: 'info';
|
|
1118
|
+
}
|
|
1119
|
+
/** `payments.entitlement.changed` — the user's resolved snapshot changed. */
|
|
1120
|
+
export interface PaymentsEntitlementChangedEventPayload {
|
|
1121
|
+
end_user_id: string;
|
|
1122
|
+
from_tier: string | null;
|
|
1123
|
+
tier: string;
|
|
1124
|
+
entitlements: string[];
|
|
1125
|
+
quotas: Record<string, number>;
|
|
1126
|
+
source_sub_id: string | null;
|
|
1127
|
+
status: string | null;
|
|
1128
|
+
until: string | null;
|
|
1129
|
+
reason: string;
|
|
1130
|
+
environment: PaymentsEventEnvironment;
|
|
1131
|
+
level: 'info';
|
|
1132
|
+
}
|
|
1133
|
+
/** `payments.entitlement.revoke.failed` — the daily sweep found a refunded or
|
|
1134
|
+
* disputed charge whose access is still live (`refunded_but_entitled`). */
|
|
1135
|
+
export interface PaymentsEntitlementRevokeFailedEventPayload {
|
|
1136
|
+
kind: 'refunded_but_entitled';
|
|
1137
|
+
count: number;
|
|
1138
|
+
subscription_ids: string[];
|
|
1139
|
+
level: 'error';
|
|
1140
|
+
state: 'broken';
|
|
1141
|
+
}
|
|
1142
|
+
/** `payments.grant.failed` — a recurring tier grant could not be written. */
|
|
1143
|
+
export interface PaymentsGrantFailedEventPayload {
|
|
1144
|
+
end_user_id: string;
|
|
1145
|
+
credit_type: string;
|
|
1146
|
+
amount: number;
|
|
1147
|
+
grant_key: string;
|
|
1148
|
+
source: string;
|
|
1149
|
+
error: string;
|
|
1150
|
+
level: 'error';
|
|
1151
|
+
state: 'broken';
|
|
1152
|
+
}
|
|
1153
|
+
/** `payments.webhook.rejected` — a signature / parse failure was persisted. */
|
|
1154
|
+
export interface PaymentsWebhookRejectedEventPayload {
|
|
1155
|
+
event_id: string;
|
|
1156
|
+
provider: string;
|
|
1157
|
+
reason: 'sig_failed' | 'parse_failed';
|
|
1158
|
+
fingerprint: string;
|
|
1159
|
+
level: 'warn';
|
|
1160
|
+
state: 'broken';
|
|
1161
|
+
}
|
|
1162
|
+
/** `payments.webhook_event.failed` — a fold threw or resolved to `error`. */
|
|
1163
|
+
export interface PaymentsWebhookEventFailedEventPayload {
|
|
1164
|
+
/** the delivery-log row (`whe_…`) when known */
|
|
1165
|
+
event_id: string | null;
|
|
1166
|
+
provider: string;
|
|
1167
|
+
/** the provider's own event id when known */
|
|
1168
|
+
provider_evt_id: string | null;
|
|
1169
|
+
event_type: string;
|
|
1170
|
+
error: string;
|
|
1171
|
+
level: 'error';
|
|
1172
|
+
state: 'broken';
|
|
1173
|
+
}
|
|
1174
|
+
/** `payments.webhook_event.reprocessed` — an operator replay ran. */
|
|
1175
|
+
export interface PaymentsWebhookEventReprocessedEventPayload {
|
|
1176
|
+
event_id: string;
|
|
1177
|
+
provider: string;
|
|
1178
|
+
event_type: string;
|
|
1179
|
+
}
|
|
1180
|
+
/** `payments.reconcile.discrepancy` — one per finding kind per daily sweep. */
|
|
1181
|
+
export interface PaymentsReconcileDiscrepancyEventPayload {
|
|
1182
|
+
kind: string;
|
|
1183
|
+
count: number;
|
|
1184
|
+
/** opaque ids only (users / subscriptions / charges / events / refunds) */
|
|
1185
|
+
ids: string[];
|
|
1186
|
+
level: 'error';
|
|
1187
|
+
state: 'broken';
|
|
1188
|
+
}
|
|
1189
|
+
/** `payments.reconcile.ok` — the tenant crossed back to clean. */
|
|
1190
|
+
export interface PaymentsReconcileOkEventPayload {
|
|
1191
|
+
level: 'info';
|
|
1192
|
+
state: 'ok';
|
|
1193
|
+
}
|
|
1194
|
+
/** `payments.conformance` — a lifecycle simulation ran (mock/dev tenants). */
|
|
1195
|
+
export interface PaymentsConformanceEventPayload {
|
|
1196
|
+
scenario: string;
|
|
1197
|
+
run_id: string;
|
|
1198
|
+
end_user_id: string;
|
|
1199
|
+
tier: string;
|
|
1200
|
+
expected: Record<string, unknown>;
|
|
1201
|
+
observed: Record<string, unknown>;
|
|
1202
|
+
pass: boolean;
|
|
1203
|
+
tenant_kind: string;
|
|
1204
|
+
level: 'info' | 'error';
|
|
1205
|
+
state: 'ok' | 'broken';
|
|
1206
|
+
}
|
|
1207
|
+
/** Event name → typed `data`, for the events that settle work you started and
|
|
1208
|
+
* (since 2026-10-01) every `payments.*` event. `VxilEventPayload<'job.generation.failed'>`
|
|
1209
|
+
* names one; everything else on the catalog is `Record<string, unknown>` until
|
|
1210
|
+
* typed here. */
|
|
865
1211
|
export interface VxilEventPayloads {
|
|
866
1212
|
'job.generation.queued': JobGenerationQueuedEventPayload;
|
|
867
1213
|
'job.generation.completed': JobGenerationSettledEventPayload;
|
|
868
1214
|
'job.generation.failed': JobGenerationSettledEventPayload;
|
|
869
1215
|
'job.succeeded': JobRunEventPayload;
|
|
870
1216
|
'job.dead_lettered': JobRunEventPayload;
|
|
1217
|
+
'payments.charge.succeeded': PaymentsChargeEventPayload;
|
|
1218
|
+
'payments.charge.completed': PaymentsChargeEventPayload;
|
|
1219
|
+
'payments.charge.refunded': PaymentsChargeRefundedEventPayload;
|
|
1220
|
+
'payments.charge.disputed': PaymentsChargeRefundedEventPayload;
|
|
1221
|
+
'payments.refund.failed': PaymentsRefundFailedEventPayload;
|
|
1222
|
+
'payments.subscription.trialing': PaymentsSubscriptionStatusEventPayload;
|
|
1223
|
+
'payments.subscription.active': PaymentsSubscriptionStatusEventPayload;
|
|
1224
|
+
'payments.subscription.past_due': PaymentsSubscriptionStatusEventPayload;
|
|
1225
|
+
'payments.subscription.cancelled': PaymentsSubscriptionStatusEventPayload;
|
|
1226
|
+
'payments.subscription.expired': PaymentsSubscriptionStatusEventPayload;
|
|
1227
|
+
'payments.subscription.lapsed': PaymentsSubscriptionStatusEventPayload;
|
|
1228
|
+
'payments.subscription.granted': PaymentsSubscriptionGrantedEventPayload;
|
|
1229
|
+
'payments.subscription.revoked': PaymentsSubscriptionRevokedEventPayload;
|
|
1230
|
+
'payments.subscription.transferred': PaymentsSubscriptionTransferredEventPayload;
|
|
1231
|
+
'payments.entitlement.changed': PaymentsEntitlementChangedEventPayload;
|
|
1232
|
+
'payments.entitlement.revoke.failed': PaymentsEntitlementRevokeFailedEventPayload;
|
|
1233
|
+
'payments.grant.failed': PaymentsGrantFailedEventPayload;
|
|
1234
|
+
'payments.webhook.rejected': PaymentsWebhookRejectedEventPayload;
|
|
1235
|
+
'payments.webhook_event.failed': PaymentsWebhookEventFailedEventPayload;
|
|
1236
|
+
'payments.webhook_event.reprocessed': PaymentsWebhookEventReprocessedEventPayload;
|
|
1237
|
+
'payments.reconcile.discrepancy': PaymentsReconcileDiscrepancyEventPayload;
|
|
1238
|
+
'payments.reconcile.ok': PaymentsReconcileOkEventPayload;
|
|
1239
|
+
'payments.conformance': PaymentsConformanceEventPayload;
|
|
871
1240
|
}
|
|
872
1241
|
export type VxilEventPayload<E extends string> = E extends keyof VxilEventPayloads ? VxilEventPayloads[E] : Record<string, unknown>;
|
|
873
1242
|
/** The `trigger` label on the envelope. The config spells two of them in
|
|
@@ -960,10 +1329,14 @@ export interface QueueFunctionEnvelope<P = unknown> extends FunctionEnvelopeBase
|
|
|
960
1329
|
trigger: 'queue';
|
|
961
1330
|
payload: P;
|
|
962
1331
|
}
|
|
963
|
-
/** A schedule tick carries no data: `payload` is `{}`.
|
|
1332
|
+
/** A schedule tick carries no data: `payload` is `{}`. `scheduled_for` is the
|
|
1333
|
+
* SLOT this tick was due for (ISO, UTC) — stable across a late start and a
|
|
1334
|
+
* re-delivery, so key per-slot work on it (a rollup "for 13:15"). Absent only
|
|
1335
|
+
* on a tick fired before 2026-10-01. */
|
|
964
1336
|
export interface CronFunctionEnvelope extends FunctionEnvelopeBase {
|
|
965
1337
|
trigger: 'cron';
|
|
966
1338
|
payload: Record<string, unknown>;
|
|
1339
|
+
scheduled_for?: string;
|
|
967
1340
|
}
|
|
968
1341
|
export interface WebhookFunctionEnvelope<D = Record<string, unknown>> extends FunctionEnvelopeBase {
|
|
969
1342
|
trigger: 'webhook';
|
|
@@ -984,6 +1357,21 @@ export interface JobDelivery<P = unknown> {
|
|
|
984
1357
|
attempt: number;
|
|
985
1358
|
/** exactly what the run was enqueued with */
|
|
986
1359
|
payload: P;
|
|
1360
|
+
/** a schedule-fired run only: the slot it was due for (ISO, UTC) */
|
|
1361
|
+
scheduled_for?: string;
|
|
1362
|
+
}
|
|
1363
|
+
/** The `?since=` replay read (GET /v1/ai/generations/{id}/stream). */
|
|
1364
|
+
export interface AiResumePage {
|
|
1365
|
+
generation_id: string;
|
|
1366
|
+
correlation_id: string | null;
|
|
1367
|
+
since: number;
|
|
1368
|
+
status: 'pending' | 'processing' | 'completed' | 'failed';
|
|
1369
|
+
frames: Array<Record<string, unknown>>;
|
|
1370
|
+
done: boolean;
|
|
1371
|
+
max_seq: number;
|
|
1372
|
+
/** the generation settled but its frames are gone (the 1 h buffer was
|
|
1373
|
+
* reclaimed, or a job-lane answer passed its 30-day retention) */
|
|
1374
|
+
expired: boolean;
|
|
987
1375
|
}
|
|
988
1376
|
/** A re-minted mid-stream connect token (GET /v1/ai/generations/{id}/token). */
|
|
989
1377
|
export interface AiStreamToken {
|
|
@@ -1424,6 +1812,47 @@ export interface VxilSchemaShape {
|
|
|
1424
1812
|
Output: unknown;
|
|
1425
1813
|
}>;
|
|
1426
1814
|
}
|
|
1815
|
+
/** One deployed tenant function as GET /v1/functions lists it. */
|
|
1816
|
+
export interface DeployedFunction {
|
|
1817
|
+
name: string;
|
|
1818
|
+
/** the content-addressed script name (`fn-<tenant8>-<name>-<sha12>`) */
|
|
1819
|
+
scriptRef: string;
|
|
1820
|
+
/** `/v1/fn/<name>` */
|
|
1821
|
+
endpoint: string;
|
|
1822
|
+
scopes: string[];
|
|
1823
|
+
egressAllow: string[];
|
|
1824
|
+
/** secret REFS only (`secret:<name>`) — never values */
|
|
1825
|
+
secrets: string[];
|
|
1826
|
+
enabled: boolean;
|
|
1827
|
+
trigger: string;
|
|
1828
|
+
bindings: Array<{
|
|
1829
|
+
kind: string;
|
|
1830
|
+
schedule?: string;
|
|
1831
|
+
source?: string;
|
|
1832
|
+
path?: string;
|
|
1833
|
+
collection?: string;
|
|
1834
|
+
event?: string;
|
|
1835
|
+
retry?: {
|
|
1836
|
+
maxAttempts: number;
|
|
1837
|
+
};
|
|
1838
|
+
}>;
|
|
1839
|
+
signature: {
|
|
1840
|
+
input?: unknown;
|
|
1841
|
+
output?: unknown;
|
|
1842
|
+
} | null;
|
|
1843
|
+
limits: {
|
|
1844
|
+
cpuMs?: number;
|
|
1845
|
+
timeoutMs?: number;
|
|
1846
|
+
} | null;
|
|
1847
|
+
/** the runtime settings the deployed script runs under; `cpuMs` = the
|
|
1848
|
+
* per-invocation CPU limit (ms) its script carries — min(limits.cpuMs, the
|
|
1849
|
+
* plan's ceiling). Absent on scripts deployed before it was recorded. */
|
|
1850
|
+
runtime?: {
|
|
1851
|
+
compatibilityDate: string;
|
|
1852
|
+
compatibilityFlags: string[];
|
|
1853
|
+
cpuMs?: number;
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1427
1856
|
/** Per-call options of `vx.fn.<name>(input, opts)`. */
|
|
1428
1857
|
export interface FnInvokeOptions {
|
|
1429
1858
|
/** `true` ⇒ the ASYNC http lane (`x-vxil-async: 1`): the call resolves to the
|
|
@@ -2425,13 +2854,26 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
2425
2854
|
* generation_status onto a tenant record, enforces a built-in timeout, and
|
|
2426
2855
|
* (on failure) fires the payments credit-reversal.
|
|
2427
2856
|
*
|
|
2857
|
+
* The answer's `generation_status` is `pending` on a fresh enqueue; a replay
|
|
2858
|
+
* of the same `idempotency_key` (`deduplicated: true`) carries the run's
|
|
2859
|
+
* status NOW (`processing`, `completed` or `failed` too).
|
|
2860
|
+
*
|
|
2428
2861
|
* `reserve_credits` (§11.8) takes a PROVISIONAL held credit debit at enqueue
|
|
2429
2862
|
* (linked to the run), committed on `completed` and reversed on
|
|
2430
2863
|
* failed/timeout/DLQ. `amount` is positive-only and CLAMPED to the platform
|
|
2431
2864
|
* `config.generation.maxReserveCredits` cap; in end-user mode `user_id` is
|
|
2432
2865
|
* FORCED to the verified end-user (a mismatched user_id → 400). A 402 aborts
|
|
2433
|
-
* the enqueue (insufficient balance
|
|
2434
|
-
*
|
|
2866
|
+
* the enqueue (insufficient balance; the run ends with job.generation.failed
|
|
2867
|
+
* `ReserveInsufficient` and the 402 names its `run_id`); a runaway over the
|
|
2868
|
+
* per-tenant outstanding-holds ceiling → 429 (Retry-After 5).
|
|
2869
|
+
*
|
|
2870
|
+
* Generic completion options for queue-style providers (fal.ai and alike —
|
|
2871
|
+
* no vendor adapter): `completion.callback.query_param` puts the signed
|
|
2872
|
+
* callback URL in the provider URL's query string (`?fal_webhook=…`) and
|
|
2873
|
+
* sends `provider.body` exactly; `completion.status_map` maps up to 8
|
|
2874
|
+
* provider words to completed | failed | processing (`{ OK: 'completed',
|
|
2875
|
+
* ERROR: 'failed' }`); `completion.result_path` names the value the run
|
|
2876
|
+
* settles with — the status mirror receives it as `result`. */
|
|
2435
2877
|
generation: (input: {
|
|
2436
2878
|
job_name: string;
|
|
2437
2879
|
provider: {
|
|
@@ -2449,6 +2891,14 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
2449
2891
|
headers?: Record<string, string>;
|
|
2450
2892
|
interval_ms?: number;
|
|
2451
2893
|
};
|
|
2894
|
+
/** webhook mode: the signed callback URL rides `?<query_param>=` */
|
|
2895
|
+
callback?: {
|
|
2896
|
+
query_param: string;
|
|
2897
|
+
};
|
|
2898
|
+
/** ≤ 8 provider words → the run's terminal (or processing) state */
|
|
2899
|
+
status_map?: Record<string, "completed" | "failed" | "processing">;
|
|
2900
|
+
/** dotted path of the value a completed run settles with (mirrored as `result`) */
|
|
2901
|
+
result_path?: string;
|
|
2452
2902
|
};
|
|
2453
2903
|
status_mirror?: {
|
|
2454
2904
|
feature: string;
|
|
@@ -2467,10 +2917,11 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
2467
2917
|
};
|
|
2468
2918
|
};
|
|
2469
2919
|
};
|
|
2470
|
-
|
|
2920
|
+
/** the held debit; name ONE `credit_type`, or an ordered `credit_types`
|
|
2921
|
+
* (the whole amount is held on the first type that covers it) */
|
|
2922
|
+
reserve_credits?: PaymentsCreditTypeChoice & {
|
|
2471
2923
|
amount: number;
|
|
2472
2924
|
user_id: string;
|
|
2473
|
-
credit_type: string;
|
|
2474
2925
|
reason?: string;
|
|
2475
2926
|
};
|
|
2476
2927
|
payload?: Record<string, unknown>;
|
|
@@ -2532,13 +2983,16 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
2532
2983
|
/** Secret for verifying X-Vxil-Jobs-Signature on your callback endpoints. */
|
|
2533
2984
|
signingSecret: () => Promise<string>;
|
|
2534
2985
|
schedules: {
|
|
2535
|
-
/** Recurring (5-field cron, UTC) or one-shot (run_at). Exactly one of cron/run_at.
|
|
2986
|
+
/** Recurring (5-field cron, UTC) or one-shot (run_at). Exactly one of cron/run_at.
|
|
2987
|
+
* `overlap: 'skip'` (cron only): no new run while the previous one is
|
|
2988
|
+
* still open — a missed window is never caught up either way. */
|
|
2536
2989
|
create: (input: {
|
|
2537
2990
|
job_name: string;
|
|
2538
2991
|
target_url: string;
|
|
2539
2992
|
payload?: Record<string, unknown>;
|
|
2540
2993
|
cron?: string;
|
|
2541
2994
|
run_at?: string;
|
|
2995
|
+
overlap?: "allow" | "skip";
|
|
2542
2996
|
}) => Promise<JobSchedule>;
|
|
2543
2997
|
list: () => Promise<JobSchedule[]>;
|
|
2544
2998
|
delete: (scheduleId: string) => Promise<void>;
|
|
@@ -2791,6 +3245,28 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
2791
3245
|
user_id: string;
|
|
2792
3246
|
erased: boolean;
|
|
2793
3247
|
}>;
|
|
3248
|
+
/** LIVE-APP MIGRATION (server only, `auth:write`; 1–500 ids per call):
|
|
3249
|
+
* give ids already registered through `users.bulk` their sign-in
|
|
3250
|
+
* identity, carrying each user's SOURCE password hash — bcrypt
|
|
3251
|
+
* (`$2a$`/`$2b$`/`$2y$`, cost 4–10; verified once at that user's first
|
|
3252
|
+
* sign-in and replaced there) or a hash from vxil's own owner-only
|
|
3253
|
+
* credential export. An existing identity is updated only while it still
|
|
3254
|
+
* holds an imported hash; one with a vxil password, or a passwordless
|
|
3255
|
+
* (OAuth / magic-link) one, is `kept`, never overwritten. Ids are unique
|
|
3256
|
+
* across every project in an environment: an id another project holds
|
|
3257
|
+
* (e.g. a rehearsal dev backend) is `skipped` / `id_taken`. `oidc_issuer` links each id as that
|
|
3258
|
+
* issuer's subject, so an installed app can trade its old session token
|
|
3259
|
+
* for a vxil one (`auth.oauth.native('oidc', …)`). One result per id:
|
|
3260
|
+
* `created` / `updated` / `kept` / `skipped` + `reason`. `vxil migrate
|
|
3261
|
+
* data --with-password-hashes` drives this for you. */
|
|
3262
|
+
import: (input: {
|
|
3263
|
+
users: Array<{
|
|
3264
|
+
id: string;
|
|
3265
|
+
password_hash?: string | null;
|
|
3266
|
+
email_verified?: boolean;
|
|
3267
|
+
}>;
|
|
3268
|
+
oidc_issuer?: string;
|
|
3269
|
+
}) => Promise<AuthImportResult>;
|
|
2794
3270
|
/** END-USER MODE ONLY (`endUserToken` / `asEndUser`): the signed-in user
|
|
2795
3271
|
* deep-merges a bounded `attributes` object into its OWN shared identity
|
|
2796
3272
|
* row (null deletes a key; ≤8 KB, ≤64 top-level keys, depth ≤8). Profile
|
|
@@ -3184,6 +3660,11 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
3184
3660
|
* admits, including unslotted range ops (served by unindexed
|
|
3185
3661
|
* scans, bounded only by the statement timeout) that the generated
|
|
3186
3662
|
* per-field `Filterable` unions on `vx.from(...).query` exclude.
|
|
3663
|
+
*
|
|
3664
|
+
* Paging: feed `next_cursor` back as `cursor` with the SAME `filter` and
|
|
3665
|
+
* `sort` — it works for the default order and (since 2026-10-01) for any
|
|
3666
|
+
* slot-indexed or `created_at`/`updated_at`/`published_at` sort; a cursor
|
|
3667
|
+
* sent with a different sort is a 422. A joined (dotted) sort has no cursor.
|
|
3187
3668
|
*/
|
|
3188
3669
|
query: (collection: string, q?: {
|
|
3189
3670
|
filter?: Record<string, unknown>;
|
|
@@ -3508,6 +3989,56 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
3508
3989
|
blocked_state: boolean;
|
|
3509
3990
|
}>;
|
|
3510
3991
|
};
|
|
3992
|
+
/**
|
|
3993
|
+
* The deployed tenant functions — the MANAGEMENT surface (`vx.fn.<name>()`
|
|
3994
|
+
* invokes them). Deploying stays on `vxil push` / `vxil functions deploy`
|
|
3995
|
+
* (the CLI bundles the source); this namespace reads and removes.
|
|
3996
|
+
*/
|
|
3997
|
+
readonly functions: {
|
|
3998
|
+
/** The deployed functions (name, endpoint, bindings, scopes, limits, the
|
|
3999
|
+
* runtime each script runs under), `runtime`, the settings a deploy uses
|
|
4000
|
+
* now, and `cpuMsCeiling`, the per-invocation CPU limit (ms) the plan
|
|
4001
|
+
* gives a function now (its own `limits.cpuMs` can only lower it). Any
|
|
4002
|
+
* authenticated key may read it. (GET /v1/functions) */
|
|
4003
|
+
list: () => Promise<{
|
|
4004
|
+
functions: DeployedFunction[];
|
|
4005
|
+
runtime?: {
|
|
4006
|
+
compatibilityDate: string;
|
|
4007
|
+
compatibilityFlags: string[];
|
|
4008
|
+
};
|
|
4009
|
+
cpuMsCeiling?: number;
|
|
4010
|
+
}>;
|
|
4011
|
+
/**
|
|
4012
|
+
* Remove ONE function from the deployed set — a new functions config
|
|
4013
|
+
* version without it (invokes answer 404, its crons and trigger
|
|
4014
|
+
* subscriptions are unbound). The uploaded code is kept until the nightly
|
|
4015
|
+
* cleanup, so a functions config rollback restores it exactly. Pass
|
|
4016
|
+
* `ifMatch` (the active functions config version) to refuse the delete if
|
|
4017
|
+
* someone else changed the set first (409 `config_version_conflict`);
|
|
4018
|
+
* with or without it, a delete that loses a race to a concurrent deploy
|
|
4019
|
+
* is refused the same way and never erases that deploy. A
|
|
4020
|
+
* name that is not deployed is 404 `function_not_found`. Needs
|
|
4021
|
+
* `features:write`. (DELETE /v1/functions/:name)
|
|
4022
|
+
*/
|
|
4023
|
+
delete: (name: string, opts?: {
|
|
4024
|
+
ifMatch?: number;
|
|
4025
|
+
}) => Promise<{
|
|
4026
|
+
deleted: string;
|
|
4027
|
+
version: number;
|
|
4028
|
+
hook_reconcile?: {
|
|
4029
|
+
created: number;
|
|
4030
|
+
updated: number;
|
|
4031
|
+
deleted: number;
|
|
4032
|
+
failed: number;
|
|
4033
|
+
};
|
|
4034
|
+
cron_schedules?: {
|
|
4035
|
+
created: number;
|
|
4036
|
+
deleted: number;
|
|
4037
|
+
failed: number;
|
|
4038
|
+
};
|
|
4039
|
+
warnings?: string[];
|
|
4040
|
+
}>;
|
|
4041
|
+
};
|
|
3511
4042
|
/** The MCP aggregation surface (the `mcp` feature). */
|
|
3512
4043
|
readonly mcp: {
|
|
3513
4044
|
/** Per-tenant secret for verifying the X-Vxil-Mcp-Signature header on custom
|
|
@@ -4108,6 +4639,25 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
4108
4639
|
status: string;
|
|
4109
4640
|
}>;
|
|
4110
4641
|
downloadUrl: (objectId: string) => Promise<string>;
|
|
4642
|
+
/** Mint up to 100 presigned GETs in ONE call (`GET /v1/files/download-urls`)
|
|
4643
|
+
* — a gallery page costs one request, not one per image. `urls[]` keeps
|
|
4644
|
+
* your order (duplicates collapse); an id that is unknown, someone else's
|
|
4645
|
+
* (end-user mode), deleted or still uploading lands in `errors[]` instead of
|
|
4646
|
+
* failing the batch. Raster images (png/jpeg/webp/gif/avif/heic) are served
|
|
4647
|
+
* `inline`. For expo-image, cache on the object id, not the URL:
|
|
4648
|
+
* `<Image source={{ uri: u.download_url, cacheKey: u.object_id }} />`. */
|
|
4649
|
+
downloadUrls: (objectIds: string[]) => Promise<{
|
|
4650
|
+
urls: Array<{
|
|
4651
|
+
object_id: string;
|
|
4652
|
+
download_url: string;
|
|
4653
|
+
content_type: string;
|
|
4654
|
+
}>;
|
|
4655
|
+
errors: Array<{
|
|
4656
|
+
object_id: string;
|
|
4657
|
+
code: "not_found" | "upload_incomplete";
|
|
4658
|
+
}>;
|
|
4659
|
+
expires_in: number;
|
|
4660
|
+
}>;
|
|
4111
4661
|
list: (q?: {
|
|
4112
4662
|
user_id?: string;
|
|
4113
4663
|
cursor?: string;
|
|
@@ -4318,6 +4868,16 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
4318
4868
|
created_at: string;
|
|
4319
4869
|
content_sha256?: string;
|
|
4320
4870
|
}>>;
|
|
4871
|
+
/** Retire a stored template (soft): every live version of the name leaves
|
|
4872
|
+
* the list and can no longer be rendered (by name or by version → 404
|
|
4873
|
+
* `template_not_found`); the rows stay for the generations that used
|
|
4874
|
+
* them, and storing the name again starts a new live version. A name
|
|
4875
|
+
* with no live version is a 404. `vxil push --allow-destructive` retires
|
|
4876
|
+
* the templates a config no longer declares. */
|
|
4877
|
+
retire: (name: string) => Promise<{
|
|
4878
|
+
retired: string;
|
|
4879
|
+
versions: number;
|
|
4880
|
+
}>;
|
|
4321
4881
|
};
|
|
4322
4882
|
/** Provider × capability matrix + the tenant's default provider (the set an
|
|
4323
4883
|
* agent/dashboard reads to know which providers/features are configured). */
|
|
@@ -4413,8 +4973,12 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
4413
4973
|
correlation_id?: string;
|
|
4414
4974
|
}) => Promise<AiStreamHandle>;
|
|
4415
4975
|
/** Job-routed async generation (long/vision/batch): 202 + a jobs run drives
|
|
4416
|
-
* the provider call; the settled answer
|
|
4417
|
-
*
|
|
4976
|
+
* the provider call; the settled answer is stored on the generation (read
|
|
4977
|
+
* it with `getGeneration`, 30 days) and replayed at `resume_path`.
|
|
4978
|
+
* Reserve/settle + credit reversal-on-failure ride the run.
|
|
4979
|
+
* `opts.idempotencyKey` deduplicates for 24 h: a re-send after a timeout
|
|
4980
|
+
* or a lost answer returns the ORIGINAL handle (`deduplicated: true`) and
|
|
4981
|
+
* never pays the provider twice. */
|
|
4418
4982
|
generateAsync: (input: {
|
|
4419
4983
|
template?: string;
|
|
4420
4984
|
template_version?: number;
|
|
@@ -4463,22 +5027,35 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
4463
5027
|
* the row, echoed on the answer and the replay read, and (job mode)
|
|
4464
5028
|
* carried on the `job.generation.*` events with `generation_id`. */
|
|
4465
5029
|
correlation_id?: string;
|
|
5030
|
+
}, opts?: {
|
|
5031
|
+
idempotencyKey?: string;
|
|
4466
5032
|
}) => Promise<AiJobHandle>;
|
|
5033
|
+
/** One generation: status, usage, run, and — for a settled job-lane
|
|
5034
|
+
* generation — the settled answer (`result`, kept 30 days). In end-user
|
|
5035
|
+
* mode only the caller's own generation reads (a foreign id is a 404). */
|
|
5036
|
+
getGeneration: (generationId: string) => Promise<AiGenerationRecord>;
|
|
5037
|
+
/** Find generations by YOUR `correlation_id` (newest first, at most 20) —
|
|
5038
|
+
* the recovery read after a timed-out or lost `generateAsync` answer.
|
|
5039
|
+
* Each carries `result: null` (`result_state` still says whether an answer
|
|
5040
|
+
* is stored): read the text with `getGeneration(generation_id)`. */
|
|
5041
|
+
findGenerations: (q: {
|
|
5042
|
+
correlation_id: string;
|
|
5043
|
+
}) => Promise<{
|
|
5044
|
+
generations: AiGenerationRecord[];
|
|
5045
|
+
}>;
|
|
4467
5046
|
/** Re-mint a fresh connect token for a LIVE stream (a generation that
|
|
4468
5047
|
* outlives the ≤300s realtime token TTL); reconnect with `?since=<seq>`. */
|
|
4469
5048
|
remintToken: (generationId: string) => Promise<AiStreamToken>;
|
|
4470
5049
|
/** Resume a streamed generation after a dropped socket: every recorded frame
|
|
4471
5050
|
* with seq > `since` plus `done` (the §2a replay buffer — plain JSON, not
|
|
4472
|
-
* an SSE stream).
|
|
5051
|
+
* an SSE stream; the buffer lives 1 h). A settled JOB-lane generation is
|
|
5052
|
+
* served from its stored answer instead (30 days). `status` says where the
|
|
5053
|
+
* generation is; `expired: true` means it settled but its frames are gone
|
|
5054
|
+
* (`done` is then true — nothing more will come). The rag-native mirror
|
|
5055
|
+
* is `rag.resume()`. */
|
|
4473
5056
|
resume: (generationId: string, q?: {
|
|
4474
5057
|
since?: number;
|
|
4475
|
-
}) => Promise<
|
|
4476
|
-
generation_id: string;
|
|
4477
|
-
since: number;
|
|
4478
|
-
frames: Array<Record<string, unknown>>;
|
|
4479
|
-
done: boolean;
|
|
4480
|
-
max_seq: number;
|
|
4481
|
-
}>;
|
|
5058
|
+
}) => Promise<AiResumePage>;
|
|
4482
5059
|
/** Classify an input into one of YOUR labels (or, with `multi:true`, every
|
|
4483
5060
|
* label that applies) — a schema-forced verdict over the generate path:
|
|
4484
5061
|
* the allowed labels ride the structured-output schema as an enum, so the
|
|
@@ -4741,21 +5318,22 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
4741
5318
|
* committed): a linked jobs run that terminally fails auto-refunds the hold,
|
|
4742
5319
|
* a success settles it. Throws VxilError(402, 'INSUFFICIENT_CREDITS') when the
|
|
4743
5320
|
* available balance can't cover `amount`.
|
|
5321
|
+
*
|
|
5322
|
+
* Ordered credits: pass `credit_types` (1–8, in spend order — e.g.
|
|
5323
|
+
* `['free', 'subscription', 'topup']`) instead of `credit_type`; the WHOLE
|
|
5324
|
+
* amount comes from the FIRST type whose available balance covers it (one
|
|
5325
|
+
* ledger row, never split across types) and the answer's `credit_type` names
|
|
5326
|
+
* it. A retry with the same key answers the same type.
|
|
4744
5327
|
*/
|
|
4745
|
-
consume: (input: {
|
|
5328
|
+
consume: (input: PaymentsCreditTypeChoice & {
|
|
4746
5329
|
user_id: string;
|
|
4747
|
-
credit_type: string;
|
|
4748
5330
|
amount: number;
|
|
4749
5331
|
/** link this debit to a jobs run_id → provisional hold + auto-reverse. */
|
|
4750
5332
|
job_id?: string;
|
|
4751
5333
|
reason?: string;
|
|
4752
5334
|
}, opts: {
|
|
4753
5335
|
idempotencyKey: string;
|
|
4754
|
-
}) => Promise<
|
|
4755
|
-
balance_after: number;
|
|
4756
|
-
ledger_entry_id: string | null;
|
|
4757
|
-
reversed: boolean;
|
|
4758
|
-
}>;
|
|
5336
|
+
}) => Promise<PaymentsConsumeResult>;
|
|
4759
5337
|
/** Additive grant (recurring/top-up) — idempotent per Idempotency-Key. A grant
|
|
4760
5338
|
* only ADDS; `amount` must be ≥ 1. */
|
|
4761
5339
|
grant: (input: {
|
|
@@ -4870,14 +5448,18 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
4870
5448
|
* approving webhook flips it to `succeeded`. A rejected adjustment flips it
|
|
4871
5449
|
* to `failed`, releases the reserved amount back onto the charge and emits
|
|
4872
5450
|
* `payments.refund.failed`. While a refund is `pending`, the credits the
|
|
4873
|
-
* charge bought are NOT yet reversed
|
|
5451
|
+
* charge bought are NOT yet reversed, and (since 2026-10-01) the charge's
|
|
5452
|
+
* `amount_refunded` / `status` do not move either — the amount shows in its
|
|
5453
|
+
* `amount_refund_pending` until the provider settles it. Stripe and the
|
|
5454
|
+
* mock settle inline. Poll it with `listRefunds({ refund_id })`.
|
|
4874
5455
|
*
|
|
4875
5456
|
* If the provider has nothing left to refund on the charge — e.g. a refund
|
|
4876
5457
|
* was already issued in the provider's own dashboard, which vxil's running
|
|
4877
5458
|
* total cannot see — the call returns 422 `refund_not_allocatable` with the
|
|
4878
5459
|
* precise reason, and nothing is sent to the provider. It is deterministic:
|
|
4879
5460
|
* retrying the same refund fails the same way. A provider call that actually
|
|
4880
|
-
* fails is still 502 `provider_error`.
|
|
5461
|
+
* fails is still 502 `provider_error`. Both failure bodies carry `refund_id`
|
|
5462
|
+
* (the row that went `failed`) so you can tie them to this call.
|
|
4881
5463
|
*/
|
|
4882
5464
|
createRefund: (input: {
|
|
4883
5465
|
charge_id: string;
|
|
@@ -4989,7 +5571,9 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
4989
5571
|
/** The last report-only reconciliation sweep result for this project
|
|
4990
5572
|
* (`run: null` before the first daily tick). Finding kinds:
|
|
4991
5573
|
* `balance_drift`, `entitlement_lag`, `null_tier`, `refund_pending`,
|
|
4992
|
-
* `stranded_webhook`, `terminal_entitled`, `webhook_secret_missing
|
|
5574
|
+
* `stranded_webhook`, `terminal_entitled`, `webhook_secret_missing`,
|
|
5575
|
+
* `refunded_but_entitled`, `paid_without_grant`, `refund_reserve_drift`
|
|
5576
|
+
* (a charge's pending refund amount no pending refund explains).
|
|
4993
5577
|
* SERVER-SIDE KEYS ONLY — a thin-client (`end_user_required`) key gets
|
|
4994
5578
|
* 403 `server_only`; the sweep is a tenant-wide operator surface. */
|
|
4995
5579
|
reconcile: () => Promise<{
|
|
@@ -5010,18 +5594,45 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
5010
5594
|
} | null;
|
|
5011
5595
|
}>;
|
|
5012
5596
|
/** List charges newest-first (the refund enabler — discover the charge_id).
|
|
5013
|
-
* Filters: user_id, status,
|
|
5597
|
+
* Filters: user_id, status, and (2026-10-01) EXACT `charge_id` /
|
|
5598
|
+
* `provider_charge_id` lookups; keyset-paged — pass `cursor` from a prior
|
|
5599
|
+
* page's `next_cursor` (limit clamped 1..100, default 50). An unknown query
|
|
5600
|
+
* parameter is a 422 `unknown_query_param`, never an unfiltered page.
|
|
5014
5601
|
* Each row carries the provider's own charge id (`provider_charge_id`),
|
|
5015
5602
|
* the provider's charge state (`provider_status`: `'paid'` — Paddle's
|
|
5016
5603
|
* money-taken state — or `'completed'`; null on rows recorded before
|
|
5017
5604
|
* 2026-09-25) and the one-off `product_id` it bought, so a function can
|
|
5018
|
-
* match a charge to a provider event without reading the delivery log.
|
|
5605
|
+
* match a charge to a provider event without reading the delivery log.
|
|
5606
|
+
* `amount_refunded` / `status` are SETTLED money; a refund awaiting the
|
|
5607
|
+
* provider shows in `amount_refund_pending`. */
|
|
5019
5608
|
listCharges: (q?: {
|
|
5020
5609
|
user_id?: string;
|
|
5021
|
-
status?: "succeeded" | "failed" | "refunded" | "partially_refunded" | "pending";
|
|
5610
|
+
status?: "succeeded" | "failed" | "refunded" | "partially_refunded" | "pending" | "disputed";
|
|
5611
|
+
/** exact match on the vxil charge id (`chg_…`) */
|
|
5612
|
+
charge_id?: string;
|
|
5613
|
+
/** exact match on the provider's own charge id (a Paddle `txn_…`, a Stripe `pi_…`) */
|
|
5614
|
+
provider_charge_id?: string;
|
|
5615
|
+
/** `production` = revenue; `sandbox` = store-review test purchases */
|
|
5616
|
+
environment?: PaymentsEventEnvironment;
|
|
5617
|
+
cursor?: string;
|
|
5022
5618
|
limit?: number;
|
|
5023
5619
|
}) => Promise<{
|
|
5024
5620
|
charges: PaymentsCharge[];
|
|
5621
|
+
next_cursor: string | null;
|
|
5622
|
+
}>;
|
|
5623
|
+
/** (2026-10-01) The refunds of ONE charge (`charge_id`) or one refund by id
|
|
5624
|
+
* (`refund_id`) — one of the two is required — newest first, with each
|
|
5625
|
+
* refund's `status` (`pending` until the provider settles it). Use it to
|
|
5626
|
+
* tie a `refund_id` from `createRefund`, its 502 / 422 answer or a
|
|
5627
|
+
* `payments.refund.failed` / `payments.charge.refunded` event back to the
|
|
5628
|
+
* money. A thin-client key sees only its own charges' refunds. */
|
|
5629
|
+
listRefunds: (q: {
|
|
5630
|
+
charge_id?: string;
|
|
5631
|
+
refund_id?: string;
|
|
5632
|
+
status?: "pending" | "succeeded" | "failed";
|
|
5633
|
+
limit?: number;
|
|
5634
|
+
}) => Promise<{
|
|
5635
|
+
refunds: PaymentsRefund[];
|
|
5025
5636
|
}>;
|
|
5026
5637
|
/** Provider webhook event log (payments.md §7 "Event log & replay"):
|
|
5027
5638
|
* operator visibility over every delivery — incl. persisted signature
|
|
@@ -5068,3 +5679,15 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
|
|
|
5068
5679
|
};
|
|
5069
5680
|
}
|
|
5070
5681
|
export { withReporting, report, buildEnvelope, parseDsn, exceptionEvent, reportServerErrors, REPORT_TIMEOUT_MS, type ReportingOptions, type ReportEvent, type FetchHandler, } from './reporting.js';
|
|
5682
|
+
/** `auth.users.import` (POST /v1/auth/users/import) — one result per id. */
|
|
5683
|
+
export interface AuthImportResult {
|
|
5684
|
+
results: Array<{
|
|
5685
|
+
id: string;
|
|
5686
|
+
status: 'created' | 'updated' | 'kept' | 'skipped';
|
|
5687
|
+
reason?: 'invalid_id' | 'invalid_hash' | 'duplicate_id' | 'not_registered' | 'no_email' | 'deleted' | 'email_taken' | 'id_taken';
|
|
5688
|
+
}>;
|
|
5689
|
+
created: number;
|
|
5690
|
+
updated: number;
|
|
5691
|
+
kept: number;
|
|
5692
|
+
skipped: number;
|
|
5693
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -662,13 +662,26 @@ export class Vxil {
|
|
|
662
662
|
* generation_status onto a tenant record, enforces a built-in timeout, and
|
|
663
663
|
* (on failure) fires the payments credit-reversal.
|
|
664
664
|
*
|
|
665
|
+
* The answer's `generation_status` is `pending` on a fresh enqueue; a replay
|
|
666
|
+
* of the same `idempotency_key` (`deduplicated: true`) carries the run's
|
|
667
|
+
* status NOW (`processing`, `completed` or `failed` too).
|
|
668
|
+
*
|
|
665
669
|
* `reserve_credits` (§11.8) takes a PROVISIONAL held credit debit at enqueue
|
|
666
670
|
* (linked to the run), committed on `completed` and reversed on
|
|
667
671
|
* failed/timeout/DLQ. `amount` is positive-only and CLAMPED to the platform
|
|
668
672
|
* `config.generation.maxReserveCredits` cap; in end-user mode `user_id` is
|
|
669
673
|
* FORCED to the verified end-user (a mismatched user_id → 400). A 402 aborts
|
|
670
|
-
* the enqueue (insufficient balance
|
|
671
|
-
*
|
|
674
|
+
* the enqueue (insufficient balance; the run ends with job.generation.failed
|
|
675
|
+
* `ReserveInsufficient` and the 402 names its `run_id`); a runaway over the
|
|
676
|
+
* per-tenant outstanding-holds ceiling → 429 (Retry-After 5).
|
|
677
|
+
*
|
|
678
|
+
* Generic completion options for queue-style providers (fal.ai and alike —
|
|
679
|
+
* no vendor adapter): `completion.callback.query_param` puts the signed
|
|
680
|
+
* callback URL in the provider URL's query string (`?fal_webhook=…`) and
|
|
681
|
+
* sends `provider.body` exactly; `completion.status_map` maps up to 8
|
|
682
|
+
* provider words to completed | failed | processing (`{ OK: 'completed',
|
|
683
|
+
* ERROR: 'failed' }`); `completion.result_path` names the value the run
|
|
684
|
+
* settles with — the status mirror receives it as `result`. */
|
|
672
685
|
generation: async (input) => (await this.call('POST', '/v1/jobs/generation', input)).data,
|
|
673
686
|
runs: async (q) => {
|
|
674
687
|
const s = qs({
|
|
@@ -714,7 +727,9 @@ export class Vxil {
|
|
|
714
727
|
/** Secret for verifying X-Vxil-Jobs-Signature on your callback endpoints. */
|
|
715
728
|
signingSecret: async () => (await this.call('GET', '/v1/jobs/signing-secret')).data.signing_secret,
|
|
716
729
|
schedules: {
|
|
717
|
-
/** Recurring (5-field cron, UTC) or one-shot (run_at). Exactly one of cron/run_at.
|
|
730
|
+
/** Recurring (5-field cron, UTC) or one-shot (run_at). Exactly one of cron/run_at.
|
|
731
|
+
* `overlap: 'skip'` (cron only): no new run while the previous one is
|
|
732
|
+
* still open — a missed window is never caught up either way. */
|
|
718
733
|
create: async (input) => (await this.call('POST', '/v1/jobs/schedules', input)).data,
|
|
719
734
|
list: async () => (await this.call('GET', '/v1/jobs/schedules')).data.schedules,
|
|
720
735
|
delete: async (scheduleId) => {
|
|
@@ -856,6 +871,21 @@ export class Vxil {
|
|
|
856
871
|
* is SELF-only: `userId` must be the signed-in user, any other id throws
|
|
857
872
|
* 403 `server_only` (a thin client can never erase someone else). */
|
|
858
873
|
erase: async (userId) => (await this.call('POST', `/v1/auth/users/${encodeURIComponent(userId)}/erase`, {})).data,
|
|
874
|
+
/** LIVE-APP MIGRATION (server only, `auth:write`; 1–500 ids per call):
|
|
875
|
+
* give ids already registered through `users.bulk` their sign-in
|
|
876
|
+
* identity, carrying each user's SOURCE password hash — bcrypt
|
|
877
|
+
* (`$2a$`/`$2b$`/`$2y$`, cost 4–10; verified once at that user's first
|
|
878
|
+
* sign-in and replaced there) or a hash from vxil's own owner-only
|
|
879
|
+
* credential export. An existing identity is updated only while it still
|
|
880
|
+
* holds an imported hash; one with a vxil password, or a passwordless
|
|
881
|
+
* (OAuth / magic-link) one, is `kept`, never overwritten. Ids are unique
|
|
882
|
+
* across every project in an environment: an id another project holds
|
|
883
|
+
* (e.g. a rehearsal dev backend) is `skipped` / `id_taken`. `oidc_issuer` links each id as that
|
|
884
|
+
* issuer's subject, so an installed app can trade its old session token
|
|
885
|
+
* for a vxil one (`auth.oauth.native('oidc', …)`). One result per id:
|
|
886
|
+
* `created` / `updated` / `kept` / `skipped` + `reason`. `vxil migrate
|
|
887
|
+
* data --with-password-hashes` drives this for you. */
|
|
888
|
+
import: async (input) => (await this.call('POST', '/v1/auth/users/import', input)).data,
|
|
859
889
|
/** END-USER MODE ONLY (`endUserToken` / `asEndUser`): the signed-in user
|
|
860
890
|
* deep-merges a bounded `attributes` object into its OWN shared identity
|
|
861
891
|
* row (null deletes a key; ≤8 KB, ≤64 top-level keys, depth ≤8). Profile
|
|
@@ -1152,6 +1182,11 @@ export class Vxil {
|
|
|
1152
1182
|
* admits, including unslotted range ops (served by unindexed
|
|
1153
1183
|
* scans, bounded only by the statement timeout) that the generated
|
|
1154
1184
|
* per-field `Filterable` unions on `vx.from(...).query` exclude.
|
|
1185
|
+
*
|
|
1186
|
+
* Paging: feed `next_cursor` back as `cursor` with the SAME `filter` and
|
|
1187
|
+
* `sort` — it works for the default order and (since 2026-10-01) for any
|
|
1188
|
+
* slot-indexed or `created_at`/`updated_at`/`published_at` sort; a cursor
|
|
1189
|
+
* sent with a different sort is a 422. A joined (dotted) sort has no cursor.
|
|
1155
1190
|
*/
|
|
1156
1191
|
query: async (collection, q) => {
|
|
1157
1192
|
const s = qs({
|
|
@@ -1344,6 +1379,32 @@ export class Vxil {
|
|
|
1344
1379
|
* tenant's `blockingEnabled` DM config (else 403). */
|
|
1345
1380
|
block: async (input) => (await this.call('POST', '/v1/dm/blocks', input)).data,
|
|
1346
1381
|
};
|
|
1382
|
+
/**
|
|
1383
|
+
* The deployed tenant functions — the MANAGEMENT surface (`vx.fn.<name>()`
|
|
1384
|
+
* invokes them). Deploying stays on `vxil push` / `vxil functions deploy`
|
|
1385
|
+
* (the CLI bundles the source); this namespace reads and removes.
|
|
1386
|
+
*/
|
|
1387
|
+
functions = {
|
|
1388
|
+
/** The deployed functions (name, endpoint, bindings, scopes, limits, the
|
|
1389
|
+
* runtime each script runs under), `runtime`, the settings a deploy uses
|
|
1390
|
+
* now, and `cpuMsCeiling`, the per-invocation CPU limit (ms) the plan
|
|
1391
|
+
* gives a function now (its own `limits.cpuMs` can only lower it). Any
|
|
1392
|
+
* authenticated key may read it. (GET /v1/functions) */
|
|
1393
|
+
list: async () => (await this.call('GET', '/v1/functions')).data,
|
|
1394
|
+
/**
|
|
1395
|
+
* Remove ONE function from the deployed set — a new functions config
|
|
1396
|
+
* version without it (invokes answer 404, its crons and trigger
|
|
1397
|
+
* subscriptions are unbound). The uploaded code is kept until the nightly
|
|
1398
|
+
* cleanup, so a functions config rollback restores it exactly. Pass
|
|
1399
|
+
* `ifMatch` (the active functions config version) to refuse the delete if
|
|
1400
|
+
* someone else changed the set first (409 `config_version_conflict`);
|
|
1401
|
+
* with or without it, a delete that loses a race to a concurrent deploy
|
|
1402
|
+
* is refused the same way and never erases that deploy. A
|
|
1403
|
+
* name that is not deployed is 404 `function_not_found`. Needs
|
|
1404
|
+
* `features:write`. (DELETE /v1/functions/:name)
|
|
1405
|
+
*/
|
|
1406
|
+
delete: async (name, opts) => (await this.call('DELETE', `/v1/functions/${encodeURIComponent(name)}`, undefined, opts?.ifMatch !== undefined ? { 'if-match': String(opts.ifMatch) } : {})).data,
|
|
1407
|
+
};
|
|
1347
1408
|
/** The MCP aggregation surface (the `mcp` feature). */
|
|
1348
1409
|
mcp = {
|
|
1349
1410
|
/** Per-tenant secret for verifying the X-Vxil-Mcp-Signature header on custom
|
|
@@ -1624,6 +1685,14 @@ export class Vxil {
|
|
|
1624
1685
|
createUploadUrl: async (input) => (await this.call('POST', '/v1/files/upload-url', input)).data,
|
|
1625
1686
|
complete: async (objectId) => (await this.call('POST', `/v1/files/${encodeURIComponent(objectId)}/complete`)).data,
|
|
1626
1687
|
downloadUrl: async (objectId) => (await this.call('GET', `/v1/files/${encodeURIComponent(objectId)}/download-url`)).data.download_url,
|
|
1688
|
+
/** Mint up to 100 presigned GETs in ONE call (`GET /v1/files/download-urls`)
|
|
1689
|
+
* — a gallery page costs one request, not one per image. `urls[]` keeps
|
|
1690
|
+
* your order (duplicates collapse); an id that is unknown, someone else's
|
|
1691
|
+
* (end-user mode), deleted or still uploading lands in `errors[]` instead of
|
|
1692
|
+
* failing the batch. Raster images (png/jpeg/webp/gif/avif/heic) are served
|
|
1693
|
+
* `inline`. For expo-image, cache on the object id, not the URL:
|
|
1694
|
+
* `<Image source={{ uri: u.download_url, cacheKey: u.object_id }} />`. */
|
|
1695
|
+
downloadUrls: async (objectIds) => (await this.call('GET', `/v1/files/download-urls?object_ids=${encodeURIComponent(objectIds.join(','))}`)).data,
|
|
1627
1696
|
list: async (q) => {
|
|
1628
1697
|
const s = qs({
|
|
1629
1698
|
user_id: q?.user_id || undefined,
|
|
@@ -1741,6 +1810,13 @@ export class Vxil {
|
|
|
1741
1810
|
* declared `ai.templates[]` entry against; absent on a version stored
|
|
1742
1811
|
* before 2026-09-23 (the next push re-puts that template once). */
|
|
1743
1812
|
list: async () => (await this.call('GET', '/v1/ai/templates')).data.templates,
|
|
1813
|
+
/** Retire a stored template (soft): every live version of the name leaves
|
|
1814
|
+
* the list and can no longer be rendered (by name or by version → 404
|
|
1815
|
+
* `template_not_found`); the rows stay for the generations that used
|
|
1816
|
+
* them, and storing the name again starts a new live version. A name
|
|
1817
|
+
* with no live version is a 404. `vxil push --allow-destructive` retires
|
|
1818
|
+
* the templates a config no longer declares. */
|
|
1819
|
+
retire: async (name) => (await this.call('DELETE', `/v1/ai/templates/${encodeURIComponent(name)}`)).data,
|
|
1744
1820
|
},
|
|
1745
1821
|
/** Provider × capability matrix + the tenant's default provider (the set an
|
|
1746
1822
|
* agent/dashboard reads to know which providers/features are configured). */
|
|
@@ -1754,15 +1830,32 @@ export class Vxil {
|
|
|
1754
1830
|
* live-only `token_expiring` events — see AiTokenExpiringEvent. */
|
|
1755
1831
|
stream: async (input) => (await this.call('POST', '/v1/ai/generate', { ...input, stream: true })).data,
|
|
1756
1832
|
/** Job-routed async generation (long/vision/batch): 202 + a jobs run drives
|
|
1757
|
-
* the provider call; the settled answer
|
|
1758
|
-
*
|
|
1759
|
-
|
|
1833
|
+
* the provider call; the settled answer is stored on the generation (read
|
|
1834
|
+
* it with `getGeneration`, 30 days) and replayed at `resume_path`.
|
|
1835
|
+
* Reserve/settle + credit reversal-on-failure ride the run.
|
|
1836
|
+
* `opts.idempotencyKey` deduplicates for 24 h: a re-send after a timeout
|
|
1837
|
+
* or a lost answer returns the ORIGINAL handle (`deduplicated: true`) and
|
|
1838
|
+
* never pays the provider twice. */
|
|
1839
|
+
generateAsync: async (input, opts) => (await this.call('POST', '/v1/ai/generate', { ...input, mode: 'job' }, opts?.idempotencyKey ? { 'idempotency-key': opts.idempotencyKey } : {})).data,
|
|
1840
|
+
/** One generation: status, usage, run, and — for a settled job-lane
|
|
1841
|
+
* generation — the settled answer (`result`, kept 30 days). In end-user
|
|
1842
|
+
* mode only the caller's own generation reads (a foreign id is a 404). */
|
|
1843
|
+
getGeneration: async (generationId) => (await this.call('GET', `/v1/ai/generations/${encodeURIComponent(generationId)}`)).data,
|
|
1844
|
+
/** Find generations by YOUR `correlation_id` (newest first, at most 20) —
|
|
1845
|
+
* the recovery read after a timed-out or lost `generateAsync` answer.
|
|
1846
|
+
* Each carries `result: null` (`result_state` still says whether an answer
|
|
1847
|
+
* is stored): read the text with `getGeneration(generation_id)`. */
|
|
1848
|
+
findGenerations: async (q) => (await this.call('GET', `/v1/ai/generations?correlation_id=${encodeURIComponent(q.correlation_id)}`)).data,
|
|
1760
1849
|
/** Re-mint a fresh connect token for a LIVE stream (a generation that
|
|
1761
1850
|
* outlives the ≤300s realtime token TTL); reconnect with `?since=<seq>`. */
|
|
1762
1851
|
remintToken: async (generationId) => (await this.call('GET', `/v1/ai/generations/${encodeURIComponent(generationId)}/token`)).data,
|
|
1763
1852
|
/** Resume a streamed generation after a dropped socket: every recorded frame
|
|
1764
1853
|
* with seq > `since` plus `done` (the §2a replay buffer — plain JSON, not
|
|
1765
|
-
* an SSE stream).
|
|
1854
|
+
* an SSE stream; the buffer lives 1 h). A settled JOB-lane generation is
|
|
1855
|
+
* served from its stored answer instead (30 days). `status` says where the
|
|
1856
|
+
* generation is; `expired: true` means it settled but its frames are gone
|
|
1857
|
+
* (`done` is then true — nothing more will come). The rag-native mirror
|
|
1858
|
+
* is `rag.resume()`. */
|
|
1766
1859
|
resume: async (generationId, q) => {
|
|
1767
1860
|
const s = q?.since != null ? `?since=${q.since}` : '';
|
|
1768
1861
|
return (await this.call('GET', `/v1/ai/generations/${encodeURIComponent(generationId)}/stream${s}`)).data;
|
|
@@ -1949,6 +2042,12 @@ export class Vxil {
|
|
|
1949
2042
|
* committed): a linked jobs run that terminally fails auto-refunds the hold,
|
|
1950
2043
|
* a success settles it. Throws VxilError(402, 'INSUFFICIENT_CREDITS') when the
|
|
1951
2044
|
* available balance can't cover `amount`.
|
|
2045
|
+
*
|
|
2046
|
+
* Ordered credits: pass `credit_types` (1–8, in spend order — e.g.
|
|
2047
|
+
* `['free', 'subscription', 'topup']`) instead of `credit_type`; the WHOLE
|
|
2048
|
+
* amount comes from the FIRST type whose available balance covers it (one
|
|
2049
|
+
* ledger row, never split across types) and the answer's `credit_type` names
|
|
2050
|
+
* it. A retry with the same key answers the same type.
|
|
1952
2051
|
*/
|
|
1953
2052
|
consume: async (input, opts) => (await this.call('POST', '/v1/payments/credits/consume', input, { 'idempotency-key': opts.idempotencyKey })).data,
|
|
1954
2053
|
/** Additive grant (recurring/top-up) — idempotent per Idempotency-Key. A grant
|
|
@@ -2002,14 +2101,18 @@ export class Vxil {
|
|
|
2002
2101
|
* approving webhook flips it to `succeeded`. A rejected adjustment flips it
|
|
2003
2102
|
* to `failed`, releases the reserved amount back onto the charge and emits
|
|
2004
2103
|
* `payments.refund.failed`. While a refund is `pending`, the credits the
|
|
2005
|
-
* charge bought are NOT yet reversed
|
|
2104
|
+
* charge bought are NOT yet reversed, and (since 2026-10-01) the charge's
|
|
2105
|
+
* `amount_refunded` / `status` do not move either — the amount shows in its
|
|
2106
|
+
* `amount_refund_pending` until the provider settles it. Stripe and the
|
|
2107
|
+
* mock settle inline. Poll it with `listRefunds({ refund_id })`.
|
|
2006
2108
|
*
|
|
2007
2109
|
* If the provider has nothing left to refund on the charge — e.g. a refund
|
|
2008
2110
|
* was already issued in the provider's own dashboard, which vxil's running
|
|
2009
2111
|
* total cannot see — the call returns 422 `refund_not_allocatable` with the
|
|
2010
2112
|
* precise reason, and nothing is sent to the provider. It is deterministic:
|
|
2011
2113
|
* retrying the same refund fails the same way. A provider call that actually
|
|
2012
|
-
* fails is still 502 `provider_error`.
|
|
2114
|
+
* fails is still 502 `provider_error`. Both failure bodies carry `refund_id`
|
|
2115
|
+
* (the row that went `failed`) so you can tie them to this call.
|
|
2013
2116
|
*/
|
|
2014
2117
|
createRefund: async (input, opts) => (await this.call('POST', '/v1/payments/refunds', input, { 'idempotency-key': opts.idempotencyKey })).data,
|
|
2015
2118
|
/** Where the user MANAGES their subscription, resolved by FUNDING SOURCE
|
|
@@ -2062,25 +2165,51 @@ export class Vxil {
|
|
|
2062
2165
|
/** The last report-only reconciliation sweep result for this project
|
|
2063
2166
|
* (`run: null` before the first daily tick). Finding kinds:
|
|
2064
2167
|
* `balance_drift`, `entitlement_lag`, `null_tier`, `refund_pending`,
|
|
2065
|
-
* `stranded_webhook`, `terminal_entitled`, `webhook_secret_missing
|
|
2168
|
+
* `stranded_webhook`, `terminal_entitled`, `webhook_secret_missing`,
|
|
2169
|
+
* `refunded_but_entitled`, `paid_without_grant`, `refund_reserve_drift`
|
|
2170
|
+
* (a charge's pending refund amount no pending refund explains).
|
|
2066
2171
|
* SERVER-SIDE KEYS ONLY — a thin-client (`end_user_required`) key gets
|
|
2067
2172
|
* 403 `server_only`; the sweep is a tenant-wide operator surface. */
|
|
2068
2173
|
reconcile: async () => (await this.call('GET', '/v1/payments/reconcile')).data,
|
|
2069
2174
|
/** List charges newest-first (the refund enabler — discover the charge_id).
|
|
2070
|
-
* Filters: user_id, status,
|
|
2175
|
+
* Filters: user_id, status, and (2026-10-01) EXACT `charge_id` /
|
|
2176
|
+
* `provider_charge_id` lookups; keyset-paged — pass `cursor` from a prior
|
|
2177
|
+
* page's `next_cursor` (limit clamped 1..100, default 50). An unknown query
|
|
2178
|
+
* parameter is a 422 `unknown_query_param`, never an unfiltered page.
|
|
2071
2179
|
* Each row carries the provider's own charge id (`provider_charge_id`),
|
|
2072
2180
|
* the provider's charge state (`provider_status`: `'paid'` — Paddle's
|
|
2073
2181
|
* money-taken state — or `'completed'`; null on rows recorded before
|
|
2074
2182
|
* 2026-09-25) and the one-off `product_id` it bought, so a function can
|
|
2075
|
-
* match a charge to a provider event without reading the delivery log.
|
|
2183
|
+
* match a charge to a provider event without reading the delivery log.
|
|
2184
|
+
* `amount_refunded` / `status` are SETTLED money; a refund awaiting the
|
|
2185
|
+
* provider shows in `amount_refund_pending`. */
|
|
2076
2186
|
listCharges: async (q) => {
|
|
2077
2187
|
const suffix = qs({
|
|
2078
2188
|
user_id: q?.user_id || undefined,
|
|
2079
2189
|
status: q?.status || undefined,
|
|
2190
|
+
charge_id: q?.charge_id || undefined,
|
|
2191
|
+
provider_charge_id: q?.provider_charge_id || undefined,
|
|
2192
|
+
environment: q?.environment || undefined,
|
|
2193
|
+
cursor: q?.cursor || undefined,
|
|
2080
2194
|
limit: q?.limit || undefined,
|
|
2081
2195
|
});
|
|
2082
2196
|
return (await this.call('GET', `/v1/payments/charges${suffix}`)).data;
|
|
2083
2197
|
},
|
|
2198
|
+
/** (2026-10-01) The refunds of ONE charge (`charge_id`) or one refund by id
|
|
2199
|
+
* (`refund_id`) — one of the two is required — newest first, with each
|
|
2200
|
+
* refund's `status` (`pending` until the provider settles it). Use it to
|
|
2201
|
+
* tie a `refund_id` from `createRefund`, its 502 / 422 answer or a
|
|
2202
|
+
* `payments.refund.failed` / `payments.charge.refunded` event back to the
|
|
2203
|
+
* money. A thin-client key sees only its own charges' refunds. */
|
|
2204
|
+
listRefunds: async (q) => {
|
|
2205
|
+
const suffix = qs({
|
|
2206
|
+
charge_id: q.charge_id || undefined,
|
|
2207
|
+
refund_id: q.refund_id || undefined,
|
|
2208
|
+
status: q.status || undefined,
|
|
2209
|
+
limit: q.limit || undefined,
|
|
2210
|
+
});
|
|
2211
|
+
return (await this.call('GET', `/v1/payments/refunds${suffix}`)).data;
|
|
2212
|
+
},
|
|
2084
2213
|
/** Provider webhook event log (payments.md §7 "Event log & replay"):
|
|
2085
2214
|
* operator visibility over every delivery — incl. persisted signature
|
|
2086
2215
|
* failures — plus an idempotent reprocess verb. Needs payments:read
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vxil/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Typed client for the Vxil REST API (notifications, auth, jobs, files, cms, comments, webhooks, realtime, orgs, rate-limits).",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"typed-client"
|
|
14
14
|
],
|
|
15
15
|
"engines": {
|
|
16
|
-
"node": ">=
|
|
16
|
+
"node": ">=22"
|
|
17
17
|
},
|
|
18
18
|
"main": "./dist/index.js",
|
|
19
19
|
"types": "./dist/index.d.ts",
|