@vxil/sdk 0.1.1 → 0.3.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 CHANGED
@@ -19,7 +19,7 @@ const items = await vx.from('tasks').list({ status: 'open' });
19
19
  - Zero dependencies; works in Node ≥18, browsers, and edge runtimes.
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
22
- project-exact client with `npx vxil gen`.
22
+ project-exact client with `npx @vxil/cli gen`.
23
23
 
24
24
  Docs: [vxil.com](https://vxil.com) · Dashboard: [vxil.com/dashboard](https://vxil.com/dashboard) · Terms: [vxil.com/terms](https://vxil.com/terms)
25
25
 
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ export type ApiVersion = 'v1';
8
8
  * (`/v1/<namespace>/…`). Matches the segment `versionedPath` rewrites, so an
9
9
  * override changes exactly that namespace's paths. Renamed client accessors map
10
10
  * onto their real namespaces (`vx.search` → `search`, `vx.feeds` → `feeds`). */
11
- export type FeatureKey = 'users' | 'notifications' | 'config' | 'features' | 'audit' | 'jobs' | 'auth' | 'rate-limits' | 'files' | 'cms' | 'comments' | 'dm' | 'feeds' | 'realtime' | 'orgs' | 'webhooks' | 'search' | 'ai' | 'rag' | 'payments' | 'fn' | 'copilot';
11
+ export type FeatureKey = 'users' | 'notifications' | 'config' | 'features' | 'audit' | 'usage' | 'jobs' | 'auth' | 'rate-limits' | 'files' | 'cms' | 'comments' | 'dm' | 'feeds' | 'realtime' | 'orgs' | 'webhooks' | 'search' | 'ai' | 'rag' | 'payments' | 'fn' | 'copilot';
12
12
  export interface VxilOptions {
13
13
  apiKey: string;
14
14
  /** defaults to the production edge */
@@ -17,7 +17,7 @@ export interface VxilOptions {
17
17
  /** An end-user's vxil-`auth` **session token** (the ES256 JWT `auth` mints).
18
18
  * When set, every request additionally carries it as the `X-Vxil-End-User`
19
19
  * header — the edge verifies it and threads a **verified `end_user_id`** into
20
- * the inner JWT, putting the request in **end-user mode** (owner-scoped reads/
20
+ * the request, putting it in **end-user mode** (owner-scoped reads/
21
21
  * writes on owned collections; default-safe). This is the thin-client shape:
22
22
  * a mobile/SPA holds a public/thin-client key + the signed-in user's session
23
23
  * token and calls the edge directly, safely scoped to that user. Absent ⇒
@@ -84,6 +84,80 @@ export interface FeatureConfig {
84
84
  version: number;
85
85
  manifest: Record<string, unknown>;
86
86
  }
87
+ /** An advisory plan from the planner (POST /v1/plan) — a proposal, not applied. */
88
+ export interface VxilPlan {
89
+ app: {
90
+ name: string;
91
+ slug: string;
92
+ summary: string;
93
+ prompt: string;
94
+ };
95
+ /** features to enable, each with the capability that pulled it in. */
96
+ features: Array<{
97
+ feature: string;
98
+ why: string;
99
+ }>;
100
+ /** the raw features[] list for a quickstart body. */
101
+ enable: string[];
102
+ /** feature → materialized config manifest. */
103
+ config: Record<string, unknown>;
104
+ /** truly-unique server logic that needs a tenant function. */
105
+ functions: Array<{
106
+ why_unique: string;
107
+ }>;
108
+ /** cms collection drafts (AI tier; [] on the deterministic floor). */
109
+ collections: Array<{
110
+ name: string;
111
+ public?: boolean;
112
+ ownerField?: string;
113
+ fields: Array<{
114
+ name: string;
115
+ type: string;
116
+ required?: boolean;
117
+ unique?: boolean;
118
+ indexSlot?: string;
119
+ relationTo?: string;
120
+ onDelete?: string;
121
+ }>;
122
+ }>;
123
+ /** deny-clamped deploy-key scope union (informational). */
124
+ scopes: string[];
125
+ /** 1 = fully declarative; < 1 = some logic needs functions. */
126
+ thinness: number;
127
+ notes: string[];
128
+ /** a ready-to-review vxil.config.ts draft. */
129
+ config_draft: string;
130
+ rationale: string;
131
+ /** which brain produced the plan. */
132
+ source: 'ai' | 'deterministic';
133
+ }
134
+ /** Current-month usage projection (GET /v1/usage) — the same meter the
135
+ * platform bills and enforces request quotas from. */
136
+ export interface UsageCurrent {
137
+ /** the current UTC month, 'YYYY-MM'. */
138
+ period: string;
139
+ /** the plan tier the quota is derived from. */
140
+ tier: string;
141
+ requests: {
142
+ /** requests recorded this month (0 until the meter has rows). */
143
+ used: number;
144
+ /** the plan's included monthly requests; null = no fixed cap. */
145
+ quota: number | null;
146
+ /** max(quota − used, 0); null when quota is null. */
147
+ remaining: number | null;
148
+ /** the PLAN's hard-cap policy — true only on the free tier (which is cut
149
+ * off with 429 quota_exceeded when exhausted); paid tiers are never
150
+ * interrupted mid-month. Not a live "the next request will 429" signal. */
151
+ enforced: boolean;
152
+ };
153
+ /** month-to-date per-feature metered detail. Aggregated periodically — it
154
+ * may lag and may be empty. */
155
+ features: Array<{
156
+ feature: string;
157
+ metric: string;
158
+ quantity: number;
159
+ }>;
160
+ }
87
161
  export interface DeadLetter {
88
162
  delivery_id: string;
89
163
  template_id: string;
@@ -164,7 +238,7 @@ export interface PaymentsWebhookEvent {
164
238
  processed_at: string | null;
165
239
  reprocess_count: number;
166
240
  reprocessed_at: string | null;
167
- /** detail-only: the verified provider payload (jsonb; '{}' for failed rows). */
241
+ /** detail-only: the verified provider payload (JSON; '{}' for failed rows). */
168
242
  payload?: unknown;
169
243
  /** detail-only: the raw body of a sig_failed/parse_failed delivery (≤64KB). */
170
244
  raw_body?: string | null;
@@ -304,6 +378,12 @@ export interface AiGeneration {
304
378
  usage: AiUsage;
305
379
  finish: string;
306
380
  tool_calls?: AiToolCall[];
381
+ /** the parsed JSON output — present when the request (or its template)
382
+ * carried a response schema; guaranteed to satisfy it (a 200 never violates
383
+ * the schema — final-invalid is a 422 output_schema_mismatch). */
384
+ output?: unknown;
385
+ /** true when the output only validated after the single repair pass. */
386
+ repaired?: boolean;
307
387
  cached: boolean;
308
388
  }
309
389
  /** A streamed generation handle: open the realtime channel for token frames. */
@@ -315,7 +395,7 @@ export interface AiStreamHandle {
315
395
  connect_path?: string;
316
396
  resume_path: string;
317
397
  }
318
- /** Frames delivered over the ai:<generation_id> realtime channel and the
398
+ /** Frames delivered over the ai-<generation_id> realtime channel and the
319
399
  * ?since= replay buffer (the WS envelope is { event, data } — `event` is the
320
400
  * frame type; replayed frames carry it as `type`). `title` arrives early when
321
401
  * the stream was requested with title/title_template; only the terminal usage
@@ -670,6 +750,53 @@ export interface DmConfigState {
670
750
  version: number;
671
751
  configured: boolean;
672
752
  }
753
+ /** The safe query subset the keyless cms public lane admits (roadmap §4.4): the
754
+ * same bounded filter/sort/limit/cursor as the authored list, minus anything
755
+ * owner- or lifecycle-scoped (the lane FORCES status='published'). `filter` is a
756
+ * JSON object, serialized to the wire `filter=` param. */
757
+ export interface CmsPublicQuery {
758
+ filter?: Record<string, unknown>;
759
+ sort?: string;
760
+ /** capped at 100 by the public lane regardless of the value sent. */
761
+ limit?: number;
762
+ cursor?: string;
763
+ }
764
+ /** One row on the keyless public lane: the item id + its authored data + the
765
+ * public lifecycle timestamps. NO internal/system columns and NEVER the
766
+ * owner_field (stripped server-side). */
767
+ export interface CmsPublicRow {
768
+ item_id: string;
769
+ data: Record<string, unknown>;
770
+ created_at: string;
771
+ updated_at: string;
772
+ published_at: string | null;
773
+ }
774
+ /** Build the anonymous, KEYLESS public-delivery URL for a collection's PUBLISHED
775
+ * items (roadmap §4.4) — `{base}/v1/cms/public/:tenantId/:collection[?…]`. This is
776
+ * the reader path a public blog/storefront/docs site hits with NO api key: the
777
+ * edge mints a restricted read-only inner token bound to the URL tenant, forces
778
+ * `status='published'`, and strips the collection's owner_field. PURE (no
779
+ * network) — pass the result to `fetch`, an `<img>`/link, or `listCmsPublic`.
780
+ * `baseUrl` defaults to `VXIL_BASE_URL` (Node) or the production edge, exactly
781
+ * like the client constructor. */
782
+ export declare function cmsPublicUrl(tenantId: string, collection: string, query?: CmsPublicQuery, opts?: {
783
+ baseUrl?: string;
784
+ }): string;
785
+ /** Fetch a page of a collection's PUBLISHED items over the KEYLESS public lane
786
+ * (roadmap §4.4) — NO api key, no `Vxil` client, no auth of any kind. This is the
787
+ * anonymous reader path (a blog/storefront/docs front-end). Returns the same
788
+ * `{ items, next_cursor }` envelope the authed list does, but rows are stripped
789
+ * to the public surface (`CmsPublicRow`) — drafts and the owner_field are never
790
+ * present. A non-2xx throws `VxilError` carrying the envelope, as everywhere else.
791
+ * The collection must be marked public (`collections.setPublic(collection, true)`
792
+ * or `{ public: true }` on create) or the lane 404s (no not-public oracle). */
793
+ export declare function listCmsPublic(tenantId: string, collection: string, query?: CmsPublicQuery, opts?: {
794
+ baseUrl?: string;
795
+ fetch?: typeof fetch;
796
+ }): Promise<{
797
+ items: CmsPublicRow[];
798
+ next_cursor: string | null;
799
+ }>;
673
800
  /** The structural shape a `vxil gen`-generated `VxilSchema` satisfies. The base
674
801
  * `Vxil` class is generic over it (`new Vxil<VxilSchema>(...)`), exactly the
675
802
  * `createClient<Database>()` move — types are layered on; the runtime is
@@ -1111,6 +1238,14 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
1111
1238
  };
1112
1239
  }>;
1113
1240
  };
1241
+ /** The planner (config-architect): describe an app in plain English and get an
1242
+ * ADVISORY vxil plan — which features to enable, a materialized `config_draft`,
1243
+ * and which truly-unique logic needs a tenant function. Deterministic + pure;
1244
+ * it PROPOSES a plan, it never applies anything (review it, then push the
1245
+ * config). Needs `features:read`. (POST /v1/plan; docs/planner-feature-design.md) */
1246
+ readonly planner: {
1247
+ create: (description: string, name?: string) => Promise<VxilPlan>;
1248
+ };
1114
1249
  readonly audit: {
1115
1250
  list: (q?: {
1116
1251
  since?: string;
@@ -1147,6 +1282,13 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
1147
1282
  }>;
1148
1283
  }>;
1149
1284
  };
1285
+ /** Current-month usage: requests used vs the plan's included quota, plus
1286
+ * month-to-date per-feature metered detail. Read-only (needs `usage:read`
1287
+ * or `features:read`). Agents: call this before a bulk run to self-check
1288
+ * remaining quota. */
1289
+ readonly usage: {
1290
+ current: () => Promise<UsageCurrent>;
1291
+ };
1150
1292
  readonly jobs: {
1151
1293
  /** Enqueue a one-off job; Vxil POSTs a signed callback to target_url with
1152
1294
  * retries. deliver_after (ISO) / delay_seconds (≤ 30 d, at most one of the
@@ -1400,7 +1542,7 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
1400
1542
  };
1401
1543
  /** End-user administration (server/function surface, auth:write). */
1402
1544
  users: {
1403
- /** GDPR PII erasure: anonymize the end-user's auth + tenant_users record
1545
+ /** GDPR PII erasure: anonymize the end-user's auth + end-user identity record
1404
1546
  * (email → tombstone, password/name/avatar cleared) and revoke every
1405
1547
  * live session. Idempotent — a second call on an already-erased user is a
1406
1548
  * no-op. Meant to be called from a "delete my account" server route or
@@ -1568,6 +1710,12 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
1568
1710
  * end-user principal (default-deny) in end-user mode; a no-op in
1569
1711
  * server-caller mode. Omit for shared/reference collections. */
1570
1712
  owner_field?: string;
1713
+ /** Public delivery (roadmap §4.4): when true, this collection's PUBLISHED
1714
+ * items become KEYLESS-readable through the anonymous public lane —
1715
+ * `GET {base}/v1/cms/public/:tenantId/:collection` with NO api key. Drafts
1716
+ * and the owner_field are never exposed. Optional; defaults false. Use the
1717
+ * top-level `cmsPublicUrl` / `listCmsPublic` helpers for the reader side. */
1718
+ public?: boolean;
1571
1719
  fields?: Array<{
1572
1720
  field: string;
1573
1721
  type: "string" | "text" | "int" | "float" | "bool" | "datetime" | "json" | "relation" | "file";
@@ -1591,6 +1739,7 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
1591
1739
  collection: string;
1592
1740
  fields: number;
1593
1741
  owner_field?: string;
1742
+ public?: boolean;
1594
1743
  }>;
1595
1744
  list: () => Promise<Array<{
1596
1745
  collection: string;
@@ -1602,6 +1751,12 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
1602
1751
  /** Set (or clear, with `null`) the collection's end-user owner-scope flag
1603
1752
  * (design §5.1). Names an existing `string` field that holds the owner id. */
1604
1753
  setOwnerField: (collection: string, ownerField: string | null) => Promise<void>;
1754
+ /** Toggle the collection's PUBLIC-delivery flag (roadmap §4.4). When `true`,
1755
+ * its PUBLISHED items become KEYLESS-readable via the anonymous public lane
1756
+ * (`GET {base}/v1/cms/public/:tenantId/:collection` — no api key); drafts and
1757
+ * the owner_field are never exposed. `false` closes the lane (and purges the
1758
+ * edge cache). Read the public side with `cmsPublicUrl` / `listCmsPublic`. */
1759
+ setPublic: (collection: string, isPublic: boolean) => Promise<void>;
1605
1760
  };
1606
1761
  items: {
1607
1762
  /** `lock` serializes same-key writers (per-tenant advisory lock); `guard`
@@ -1621,11 +1776,11 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
1621
1776
  get: (collection: string, itemId: string) => Promise<Record<string, unknown>>;
1622
1777
  /**
1623
1778
  * The bounded query DSL. filter ops: $eq $ne $gt $gte $lt $lte $in
1624
- * $contains $startsWith (LIKE-escaped; trigram-indexed on s*-slotted
1625
- * fields) $arrayContains/$anyOf (json/relation array containment via the
1626
- * JSONB GIN); range/sort needs slot-indexed fields. This is the
1779
+ * $contains $startsWith (LIKE-escaped; substring-searchable on s*-slotted
1780
+ * fields) $arrayContains/$anyOf (json/relation array containment, indexed);
1781
+ * range/sort needs slot-indexed fields. This is the
1627
1782
  * DELIBERATELY-UNTYPED escape hatch: it accepts everything the server
1628
- * admits, including unslotted range ops (served by unindexed JSONB
1783
+ * admits, including unslotted range ops (served by unindexed
1629
1784
  * scans, bounded only by the statement timeout) that the generated
1630
1785
  * per-field `Filterable` unions on `vx.from(...).query` exclude.
1631
1786
  */
@@ -2270,7 +2425,7 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
2270
2425
  settings?: Record<string, unknown>;
2271
2426
  role?: string | null;
2272
2427
  }>;
2273
- /** Update a workspace's name / `settings` jsonb. */
2428
+ /** Update a workspace's name / `settings` JSON. */
2274
2429
  update: (orgId: string, patch: {
2275
2430
  name?: string;
2276
2431
  settings?: Record<string, unknown>;
@@ -2521,7 +2676,7 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
2521
2676
  };
2522
2677
  /**
2523
2678
  * Managed hybrid search (the `vector-search` feature): store documents → chunk →
2524
- * embed → index, then retrieve top-k via hybrid (BM25 ∪ vector, RRF-fused),
2679
+ * embed → index, then retrieve top-k via hybrid (keyword ∪ vector, RRF-fused),
2525
2680
  * vector, or keyword. The embedder is config: 'mock' (deterministic default),
2526
2681
  * 'byov' (you supply vectors), or a BYO-key provider (openai/cohere).
2527
2682
  */
@@ -2615,7 +2770,10 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
2615
2770
  readonly ai: {
2616
2771
  templates: {
2617
2772
  /** Store a tenant-authored prompt template; versions are monotonic per name.
2618
- * `{{var}}` placeholders are filled from `generate`'s `input`. */
2773
+ * `{{var}}` placeholders are filled from `generate`'s `input`.
2774
+ * `schema` (a JSON Schema) is now ENFORCED at generate time on sync/job
2775
+ * requests — the output is validated (with one repair pass) against it;
2776
+ * a per-request `response_schema` overrides it. Ignored on streams. */
2619
2777
  put: (input: {
2620
2778
  template: string;
2621
2779
  user: string;
@@ -2657,6 +2815,13 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
2657
2815
  * role:'tool' results) — vxil relays; you execute the tools. */
2658
2816
  messages?: AiChatMessage[];
2659
2817
  json_mode?: boolean;
2818
+ /** JSON Schema the completion must satisfy (schema-guaranteed structured
2819
+ * output): enforced natively where the provider supports strict
2820
+ * structured output, else via one validate-and-repair pass; the parsed
2821
+ * object lands in `output` (+`repaired` when the repair pass fired). A
2822
+ * still-invalid result is a 422 `output_schema_mismatch` (billed). Wins
2823
+ * over the template's stored schema. */
2824
+ response_schema?: Record<string, unknown>;
2660
2825
  images?: string[];
2661
2826
  user_id?: string;
2662
2827
  }) => Promise<AiGeneration>;
@@ -2708,6 +2873,11 @@ export declare class Vxil<S extends VxilSchemaShape = VxilSchemaShape> {
2708
2873
  }>;
2709
2874
  messages?: AiChatMessage[];
2710
2875
  json_mode?: boolean;
2876
+ /** JSON Schema the completion must satisfy — same contract as
2877
+ * `generate`; the validated answer lands in the replay buffer (a
2878
+ * schema-mismatch delivers finish:'schema_mismatch' on the terminal
2879
+ * usage frame). */
2880
+ response_schema?: Record<string, unknown>;
2711
2881
  images?: string[];
2712
2882
  user_id?: string;
2713
2883
  }) => Promise<AiJobHandle>;
package/dist/index.js CHANGED
@@ -43,6 +43,57 @@ function resolveBase(explicit) {
43
43
  const envBase = g.process?.env?.VXIL_BASE_URL;
44
44
  return (explicit ?? envBase ?? DEFAULT_BASE).replace(/\/$/, '');
45
45
  }
46
+ /** Build the anonymous, KEYLESS public-delivery URL for a collection's PUBLISHED
47
+ * items (roadmap §4.4) — `{base}/v1/cms/public/:tenantId/:collection[?…]`. This is
48
+ * the reader path a public blog/storefront/docs site hits with NO api key: the
49
+ * edge mints a restricted read-only inner token bound to the URL tenant, forces
50
+ * `status='published'`, and strips the collection's owner_field. PURE (no
51
+ * network) — pass the result to `fetch`, an `<img>`/link, or `listCmsPublic`.
52
+ * `baseUrl` defaults to `VXIL_BASE_URL` (Node) or the production edge, exactly
53
+ * like the client constructor. */
54
+ export function cmsPublicUrl(tenantId, collection, query, opts) {
55
+ const base = resolveBase(opts?.baseUrl);
56
+ const path = `/v1/cms/public/${encodeURIComponent(tenantId)}/${encodeURIComponent(collection)}`;
57
+ const qs = new URLSearchParams();
58
+ if (query?.filter)
59
+ qs.set('filter', JSON.stringify(query.filter));
60
+ if (query?.sort)
61
+ qs.set('sort', query.sort);
62
+ if (query?.limit !== undefined)
63
+ qs.set('limit', String(query.limit));
64
+ if (query?.cursor)
65
+ qs.set('cursor', query.cursor);
66
+ const s = qs.toString();
67
+ return `${base}${path}${s ? `?${s}` : ''}`;
68
+ }
69
+ /** Fetch a page of a collection's PUBLISHED items over the KEYLESS public lane
70
+ * (roadmap §4.4) — NO api key, no `Vxil` client, no auth of any kind. This is the
71
+ * anonymous reader path (a blog/storefront/docs front-end). Returns the same
72
+ * `{ items, next_cursor }` envelope the authed list does, but rows are stripped
73
+ * to the public surface (`CmsPublicRow`) — drafts and the owner_field are never
74
+ * present. A non-2xx throws `VxilError` carrying the envelope, as everywhere else.
75
+ * The collection must be marked public (`collections.setPublic(collection, true)`
76
+ * or `{ public: true }` on create) or the lane 404s (no not-public oracle). */
77
+ export async function listCmsPublic(tenantId, collection, query, opts) {
78
+ const url = cmsPublicUrl(tenantId, collection, query, opts);
79
+ const fetchImpl = opts?.fetch ?? fetch;
80
+ const res = await fetchImpl(url, { method: 'GET' });
81
+ const text = await res.text();
82
+ let parsed = {};
83
+ if (text.length > 0) {
84
+ try {
85
+ parsed = JSON.parse(text);
86
+ }
87
+ catch {
88
+ throw new VxilError(res.status, `http_${res.status}`, text.slice(0, 200));
89
+ }
90
+ }
91
+ if (!res.ok || parsed.error) {
92
+ const e = parsed.error ?? { code: `http_${res.status}`, message: text.slice(0, 200) };
93
+ throw new VxilError(res.status, e.code, e.message, e.hint, e.fixUrl, parsed.meta?.request_id);
94
+ }
95
+ return parsed.data ?? { items: [], next_cursor: null };
96
+ }
46
97
  export class Vxil {
47
98
  base;
48
99
  key;
@@ -302,6 +353,17 @@ export class Vxil {
302
353
  * stable flat-array shorthand. */
303
354
  summary: async () => (await this.call('GET', '/v1/features')).data,
304
355
  };
356
+ /** The planner (config-architect): describe an app in plain English and get an
357
+ * ADVISORY vxil plan — which features to enable, a materialized `config_draft`,
358
+ * and which truly-unique logic needs a tenant function. Deterministic + pure;
359
+ * it PROPOSES a plan, it never applies anything (review it, then push the
360
+ * config). Needs `features:read`. (POST /v1/plan; docs/planner-feature-design.md) */
361
+ planner = {
362
+ create: async (description, name) => (await this.call('POST', '/v1/plan', {
363
+ description,
364
+ ...(name ? { name } : {}),
365
+ })).data,
366
+ };
305
367
  audit = {
306
368
  list: async (q) => {
307
369
  const qs = new URLSearchParams();
@@ -367,6 +429,13 @@ export class Vxil {
367
429
  };
368
430
  },
369
431
  };
432
+ /** Current-month usage: requests used vs the plan's included quota, plus
433
+ * month-to-date per-feature metered detail. Read-only (needs `usage:read`
434
+ * or `features:read`). Agents: call this before a bulk run to self-check
435
+ * remaining quota. */
436
+ usage = {
437
+ current: async () => (await this.call('GET', '/v1/usage')).data,
438
+ };
370
439
  jobs = {
371
440
  /** Enqueue a one-off job; Vxil POSTs a signed callback to target_url with
372
441
  * retries. deliver_after (ISO) / delay_seconds (≤ 30 d, at most one of the
@@ -481,7 +550,7 @@ export class Vxil {
481
550
  },
482
551
  /** End-user administration (server/function surface, auth:write). */
483
552
  users: {
484
- /** GDPR PII erasure: anonymize the end-user's auth + tenant_users record
553
+ /** GDPR PII erasure: anonymize the end-user's auth + end-user identity record
485
554
  * (email → tombstone, password/name/avatar cleared) and revoke every
486
555
  * live session. Idempotent — a second call on an already-erased user is a
487
556
  * no-op. Meant to be called from a "delete my account" server route or
@@ -574,6 +643,14 @@ export class Vxil {
574
643
  setOwnerField: async (collection, ownerField) => {
575
644
  await this.call('POST', `/v1/cms/collections/${encodeURIComponent(collection)}/fields`, { owner_field: ownerField });
576
645
  },
646
+ /** Toggle the collection's PUBLIC-delivery flag (roadmap §4.4). When `true`,
647
+ * its PUBLISHED items become KEYLESS-readable via the anonymous public lane
648
+ * (`GET {base}/v1/cms/public/:tenantId/:collection` — no api key); drafts and
649
+ * the owner_field are never exposed. `false` closes the lane (and purges the
650
+ * edge cache). Read the public side with `cmsPublicUrl` / `listCmsPublic`. */
651
+ setPublic: async (collection, isPublic) => {
652
+ await this.call('POST', `/v1/cms/collections/${encodeURIComponent(collection)}/fields`, { public: isPublic });
653
+ },
577
654
  },
578
655
  items: {
579
656
  /** `lock` serializes same-key writers (per-tenant advisory lock); `guard`
@@ -582,11 +659,11 @@ export class Vxil {
582
659
  get: async (collection, itemId) => (await this.call('GET', `/v1/cms/items/${encodeURIComponent(collection)}/${encodeURIComponent(itemId)}`)).data,
583
660
  /**
584
661
  * The bounded query DSL. filter ops: $eq $ne $gt $gte $lt $lte $in
585
- * $contains $startsWith (LIKE-escaped; trigram-indexed on s*-slotted
586
- * fields) $arrayContains/$anyOf (json/relation array containment via the
587
- * JSONB GIN); range/sort needs slot-indexed fields. This is the
662
+ * $contains $startsWith (LIKE-escaped; substring-searchable on s*-slotted
663
+ * fields) $arrayContains/$anyOf (json/relation array containment, indexed);
664
+ * range/sort needs slot-indexed fields. This is the
588
665
  * DELIBERATELY-UNTYPED escape hatch: it accepts everything the server
589
- * admits, including unslotted range ops (served by unindexed JSONB
666
+ * admits, including unslotted range ops (served by unindexed
590
667
  * scans, bounded only by the statement timeout) that the generated
591
668
  * per-field `Filterable` unions on `vx.from(...).query` exclude.
592
669
  */
@@ -937,7 +1014,7 @@ export class Vxil {
937
1014
  sessionClaims: async (userId) => (await this.call('GET', `/v1/orgs/session-claims?user_id=${encodeURIComponent(userId)}`)).data,
938
1015
  /** Read one workspace (incl. `settings`). */
939
1016
  get: async (orgId) => (await this.call('GET', `/v1/orgs/${encodeURIComponent(orgId)}`)).data,
940
- /** Update a workspace's name / `settings` jsonb. */
1017
+ /** Update a workspace's name / `settings` JSON. */
941
1018
  update: async (orgId, patch) => (await this.call('PATCH', `/v1/orgs/${encodeURIComponent(orgId)}`, patch)).data,
942
1019
  members: {
943
1020
  add: async (orgId, userId, role) => {
@@ -1060,7 +1137,7 @@ export class Vxil {
1060
1137
  };
1061
1138
  /**
1062
1139
  * Managed hybrid search (the `vector-search` feature): store documents → chunk →
1063
- * embed → index, then retrieve top-k via hybrid (BM25 ∪ vector, RRF-fused),
1140
+ * embed → index, then retrieve top-k via hybrid (keyword ∪ vector, RRF-fused),
1064
1141
  * vector, or keyword. The embedder is config: 'mock' (deterministic default),
1065
1142
  * 'byov' (you supply vectors), or a BYO-key provider (openai/cohere).
1066
1143
  */
@@ -1120,7 +1197,10 @@ export class Vxil {
1120
1197
  ai = {
1121
1198
  templates: {
1122
1199
  /** Store a tenant-authored prompt template; versions are monotonic per name.
1123
- * `{{var}}` placeholders are filled from `generate`'s `input`. */
1200
+ * `{{var}}` placeholders are filled from `generate`'s `input`.
1201
+ * `schema` (a JSON Schema) is now ENFORCED at generate time on sync/job
1202
+ * requests — the output is validated (with one repair pass) against it;
1203
+ * a per-request `response_schema` overrides it. Ignored on streams. */
1124
1204
  put: async (input) => (await this.call('POST', '/v1/ai/templates', input)).data,
1125
1205
  /** List stored templates (each name + its latest version). */
1126
1206
  list: async () => (await this.call('GET', '/v1/ai/templates')).data.templates,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vxil/sdk",
3
- "version": "0.1.1",
3
+ "version": "0.3.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).",
@@ -28,11 +28,10 @@
28
28
  "files": [
29
29
  "dist"
30
30
  ],
31
- "scripts": {
32
- "build": "tsc -p tsconfig.build.json",
33
- "prepublishOnly": "pnpm run build"
34
- },
35
31
  "publishConfig": {
36
32
  "access": "public"
33
+ },
34
+ "scripts": {
35
+ "build": "tsc -p tsconfig.build.json"
37
36
  }
38
37
  }