@vxil/cli 0.20.2 → 0.21.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.
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const AI_THINKING_LEVELS: readonly ["minimal", "low", "medium", "high"];
|
|
2
|
+
export type GeminiThinkingLevel = (typeof AI_THINKING_LEVELS)[number];
|
|
3
|
+
/** The lowest thinking level the model accepts. 3.x thinks by default; the
|
|
4
|
+
* 2.5-era `thinkingBudget:0` is INVALID on 3.x, and `thinkingLevel:'minimal'`
|
|
5
|
+
* (which zeroes thought tokens) is accepted by gemini-3-flash … 3.6-flash and
|
|
6
|
+
* every flash-lite, but REJECTED with a 400 ("Thinking level MINIMAL is not
|
|
7
|
+
* supported for this model") by gemini-3.7-flash and gemini-3.8-flash
|
|
8
|
+
* (measured 2026-10-03 on the staging key). Unknown model shapes (incl. the
|
|
9
|
+
* Pro models) get 'low': it is the one level measured as accepted on every
|
|
10
|
+
* 3.x model tried. */
|
|
11
|
+
export declare function minimalThinkingLevel(model: string): GeminiThinkingLevel;
|
|
12
|
+
/** Null when `model` is not known to reject `level`, else the reason. Only
|
|
13
|
+
* 'minimal' is checked (per the table above). Measured: 'low' on every 3.x
|
|
14
|
+
* model tried; medium/high are NOT verified per model — a model that rejects
|
|
15
|
+
* one fails as a provider 4xx after the reserve (refunded), not here. */
|
|
16
|
+
export declare function geminiThinkingLevelError(model: string, level: GeminiThinkingLevel): string | null;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Static, type TSchema } from '@sinclair/typebox';
|
|
2
|
+
export { AI_THINKING_LEVELS, geminiThinkingLevelError, minimalThinkingLevel, type GeminiThinkingLevel } from './_vxil-feature-configs-geminiThinking.js';
|
|
2
3
|
export * from './_vxil-feature-configs-hooks.js';
|
|
3
4
|
export * from './_vxil-feature-configs-readmodels.js';
|
|
4
5
|
export * from './_vxil-feature-configs-apiState.js';
|
|
@@ -605,11 +606,12 @@ export declare const AiConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
605
606
|
openaiBaseUrl: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
606
607
|
}>>;
|
|
607
608
|
}>;
|
|
608
|
-
defaults: import("@sinclair/typebox").TObject<{
|
|
609
|
+
defaults: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
609
610
|
model: import("@sinclair/typebox").TString;
|
|
610
611
|
maxTokens: import("@sinclair/typebox").TInteger;
|
|
611
612
|
temperature: import("@sinclair/typebox").TNumber;
|
|
612
|
-
|
|
613
|
+
thinkingLevel: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<"minimal" | "low" | "medium" | "high">[]>>;
|
|
614
|
+
}>>;
|
|
613
615
|
cache: import("@sinclair/typebox").TObject<{
|
|
614
616
|
ttlSeconds: import("@sinclair/typebox").TInteger;
|
|
615
617
|
}>;
|
package/dist/vxil.js
CHANGED
|
@@ -3383,6 +3383,9 @@ var PublicAssetsConfigSchema = Type.Object({
|
|
|
3383
3383
|
))
|
|
3384
3384
|
});
|
|
3385
3385
|
|
|
3386
|
+
// ../feature-configs/src/geminiThinking.ts
|
|
3387
|
+
var AI_THINKING_LEVELS = ["minimal", "low", "medium", "high"];
|
|
3388
|
+
|
|
3386
3389
|
// ../feature-configs/src/canonicalJson.ts
|
|
3387
3390
|
function canonicalJson(v) {
|
|
3388
3391
|
if (Array.isArray(v)) return `[${v.map((x) => canonicalJson(x) ?? "null").join(",")}]`;
|
|
@@ -3963,8 +3966,8 @@ var FilesConfigSchema = Type.Object({
|
|
|
3963
3966
|
// (pricing review 2026-07-10). 5 GiB/object, 1 TiB/tenant absolute.
|
|
3964
3967
|
maxObjectBytes: Type.Integer({ default: 100 * 1024 * 1024, minimum: 1, maximum: 5 * 1024 * 1024 * 1024 }),
|
|
3965
3968
|
// NO default (2026-10-04): UNSET means "the plan's storage ceiling"
|
|
3966
|
-
// (control-plane plans.ts FILES_MAX_TOTAL_BYTES_BY_TIER — Free
|
|
3967
|
-
//
|
|
3969
|
+
// (control-plane plans.ts FILES_MAX_TOTAL_BYTES_BY_TIER — Free 10 GiB,
|
|
3970
|
+
// Developer 25 GiB, Team 150 GiB, Business 256 GiB, Enterprise 1 TiB), resolved by
|
|
3968
3971
|
// files-v1 where the quota is enforced, so a plan change applies at once.
|
|
3969
3972
|
// A value is the project's own LOWER cap (above the plan → 422 at config
|
|
3970
3973
|
// write; a downgrade clamps it). It used to default to 10 GiB on every
|
|
@@ -4469,11 +4472,18 @@ var AiConfigSchema = Type.Object({
|
|
|
4469
4472
|
openaiBaseUrl: Type.Optional(Type.String({ maxLength: 512 }))
|
|
4470
4473
|
}))
|
|
4471
4474
|
}, { default: {} }),
|
|
4472
|
-
defaults
|
|
4475
|
+
// `defaults` became an OPTIONAL bag (3 leaves → 1, the `streaming` trick) on
|
|
4476
|
+
// 2026-10-10 to carry `thinkingLevel` (AI-THINKING-LEVEL). It KEEPS
|
|
4477
|
+
// `default: {}`, so Value.Default still materializes
|
|
4478
|
+
// defaults.{model,maxTokens,temperature} exactly as before; `thinkingLevel`
|
|
4479
|
+
// has NO default (absent = the model's own default), so an existing manifest
|
|
4480
|
+
// stays byte-identical. Sent to Gemini only; validated per model below.
|
|
4481
|
+
defaults: Type.Optional(Type.Object({
|
|
4473
4482
|
model: Type.String({ default: "mock-1" }),
|
|
4474
4483
|
maxTokens: Type.Integer({ default: 1024, minimum: 1, maximum: 2e5 }),
|
|
4475
|
-
temperature: Type.Number({ default: 0.7, minimum: 0, maximum: 2 })
|
|
4476
|
-
|
|
4484
|
+
temperature: Type.Number({ default: 0.7, minimum: 0, maximum: 2 }),
|
|
4485
|
+
thinkingLevel: Type.Optional(Type.Union(AI_THINKING_LEVELS.map((l) => Type.Literal(l))))
|
|
4486
|
+
}, { default: {} })),
|
|
4477
4487
|
cache: Type.Object({ ttlSeconds: Type.Integer({ default: 0, minimum: 0 }) }, { default: {} }),
|
|
4478
4488
|
// 0 = off
|
|
4479
4489
|
limits: Type.Object({
|
|
@@ -5065,14 +5075,16 @@ function lowerAllTriggerBindings(def, name) {
|
|
|
5065
5075
|
return bindings;
|
|
5066
5076
|
}
|
|
5067
5077
|
var CONFIG_FILENAMES = ["vxil.config.ts", "vxil.config.mjs", "vxil.config.js"];
|
|
5068
|
-
var VXIL_CONFIG_PKG_VERSION = "0.
|
|
5069
|
-
var VXIL_SDK_PKG_VERSION = "0.
|
|
5070
|
-
var VXIL_CLI_PKG_VERSION = "0.
|
|
5078
|
+
var VXIL_CONFIG_PKG_VERSION = "0.14.0";
|
|
5079
|
+
var VXIL_SDK_PKG_VERSION = "0.21.0";
|
|
5080
|
+
var VXIL_CLI_PKG_VERSION = "0.21.0";
|
|
5071
5081
|
function ensureScaffoldPackageJson(cwd, opts = {}) {
|
|
5072
5082
|
const file = resolve(cwd, "package.json");
|
|
5073
5083
|
const wanted = {
|
|
5074
5084
|
"@vxil/config": `^${VXIL_CONFIG_PKG_VERSION}`,
|
|
5075
|
-
...opts.sdkTypes ? { "@vxil/sdk": `^${VXIL_SDK_PKG_VERSION}` } : {}
|
|
5085
|
+
...opts.sdkTypes ? { "@vxil/sdk": `^${VXIL_SDK_PKG_VERSION}` } : {},
|
|
5086
|
+
// the scaffolded tsconfig.json names `types: ["node"]` (CLI-TS-SCAFFOLD)
|
|
5087
|
+
...opts.nodeTypes ? { "@types/node": SCAFFOLD_NODE_TYPES_RANGE } : {}
|
|
5076
5088
|
};
|
|
5077
5089
|
if (!existsSync(file)) {
|
|
5078
5090
|
writeFileSync(file, JSON.stringify({
|
|
@@ -5102,6 +5114,24 @@ function ensureScaffoldPackageJson(cwd, opts = {}) {
|
|
|
5102
5114
|
writeFileSync(file, JSON.stringify(pkg, null, 2) + "\n");
|
|
5103
5115
|
return { action: "added" };
|
|
5104
5116
|
}
|
|
5117
|
+
var SCAFFOLD_NODE_TYPES_RANGE = "^24.0.0";
|
|
5118
|
+
var SCAFFOLD_TSCONFIG = {
|
|
5119
|
+
compilerOptions: {
|
|
5120
|
+
target: "ES2022",
|
|
5121
|
+
module: "ESNext",
|
|
5122
|
+
moduleResolution: "bundler",
|
|
5123
|
+
strict: true,
|
|
5124
|
+
skipLibCheck: true,
|
|
5125
|
+
noEmit: true,
|
|
5126
|
+
types: ["node"]
|
|
5127
|
+
}
|
|
5128
|
+
};
|
|
5129
|
+
function ensureScaffoldTsconfig(cwd) {
|
|
5130
|
+
const file = resolve(cwd, "tsconfig.json");
|
|
5131
|
+
if (existsSync(file)) return "exists";
|
|
5132
|
+
writeFileSync(file, JSON.stringify(SCAFFOLD_TSCONFIG, null, 2) + "\n");
|
|
5133
|
+
return "created";
|
|
5134
|
+
}
|
|
5105
5135
|
function apiErrText(r) {
|
|
5106
5136
|
const e = r.body.error;
|
|
5107
5137
|
if (!e) return `${r.status}`;
|
|
@@ -5353,6 +5383,49 @@ async function serverApply({ api, bundle, resume, allowDestructive, onPlan }) {
|
|
|
5353
5383
|
);
|
|
5354
5384
|
}
|
|
5355
5385
|
var PUSH_READ_CONCURRENCY = 5;
|
|
5386
|
+
var CMS_ENABLE_MANIFEST = { enabled: true };
|
|
5387
|
+
function cmsEnableManifest(declared) {
|
|
5388
|
+
return declared.limits !== void 0 ? { ...CMS_ENABLE_MANIFEST, limits: declared.limits } : { ...CMS_ENABLE_MANIFEST };
|
|
5389
|
+
}
|
|
5390
|
+
async function enableCmsBeforeSchema({ api, cfg }) {
|
|
5391
|
+
const declared = cfg.features?.cms;
|
|
5392
|
+
if (!Object.keys(cfg.cms?.collections ?? {}).length || !declared || declared.enabled === false) return null;
|
|
5393
|
+
const cur = await api("GET", "/v1/config/cms");
|
|
5394
|
+
if (classifyRead(cur.status, cur.body?.error?.code) !== "empty") return null;
|
|
5395
|
+
const dry = await api("PUT", "/v1/config/cms?dry_run=true", declared);
|
|
5396
|
+
if (dry.status !== 200) {
|
|
5397
|
+
const e = dry.body.error ?? {};
|
|
5398
|
+
throw new Error(`invalid config for cms: ${e.code ?? dry.status} ${e.message ?? ""} ${e.hint ?? ""}`.trim() + "\n(nothing was pushed)");
|
|
5399
|
+
}
|
|
5400
|
+
const put = await api("PUT", "/v1/config/cms", cmsEnableManifest(declared));
|
|
5401
|
+
if (put.status !== 200) {
|
|
5402
|
+
const e = put.body.error ?? {};
|
|
5403
|
+
throw new Error(`cms: could not enable cms before its schema: ${e.code ?? put.status} ${e.message ?? ""} ${e.hint ?? ""}`.trim());
|
|
5404
|
+
}
|
|
5405
|
+
return put.body.data?.version ?? 0;
|
|
5406
|
+
}
|
|
5407
|
+
var CMS_PROPAGATION_ATTEMPTS = 4;
|
|
5408
|
+
var CMS_PROPAGATION_DELAY_MS = 1e4;
|
|
5409
|
+
function retryWhileCmsPropagates(api, opts = {}) {
|
|
5410
|
+
const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
5411
|
+
const attempts = opts.attempts ?? CMS_PROPAGATION_ATTEMPTS;
|
|
5412
|
+
const delayMs = opts.delayMs ?? CMS_PROPAGATION_DELAY_MS;
|
|
5413
|
+
let waited = 0;
|
|
5414
|
+
return async (method, path, body) => {
|
|
5415
|
+
for (; ; ) {
|
|
5416
|
+
const res = await api(method, path, body);
|
|
5417
|
+
const propagating = path.startsWith("/v1/cms/") && res.status === 501 && (res.body?.error?.code ?? "capability_not_enabled") === "capability_not_enabled";
|
|
5418
|
+
if (!propagating || waited >= attempts) return res;
|
|
5419
|
+
waited++;
|
|
5420
|
+
opts.onWait?.(` \u2026 cms config propagating (just enabled) \u2014 retrying in ${Math.round(delayMs / 1e3)} s (${waited}/${attempts})`);
|
|
5421
|
+
await sleep(delayMs);
|
|
5422
|
+
}
|
|
5423
|
+
};
|
|
5424
|
+
}
|
|
5425
|
+
function onlyPropagationDeferred(steps) {
|
|
5426
|
+
const errored = steps.filter((st) => st.status === "pending" && st.error);
|
|
5427
|
+
return errored.length > 0 && errored.every((st) => (st.kind === "api-state" || st.kind === "cms.collection" || st.kind === "cms.field") && (st.error ?? "").startsWith("deferred:"));
|
|
5428
|
+
}
|
|
5356
5429
|
async function planOrPush({ api, features, apply, allowDestructive }) {
|
|
5357
5430
|
const results = [];
|
|
5358
5431
|
const destructiveQs = allowDestructive ? "&allow_destructive=true" : "";
|
|
@@ -9561,7 +9634,7 @@ var API_CHEATSHEET = [
|
|
|
9561
9634
|
{ feature: "orgs", what: "add / remove a member", cmd: `vxil api POST /v1/orgs/<org_id>/members '{"user_id":"<id>","role":"member"}' \xB7 vxil api DELETE /v1/orgs/<org_id>/members/<user_id>` },
|
|
9562
9635
|
{ feature: "orgs", what: "invite by e-mail (the token is returned once) / revoke", cmd: `vxil api POST /v1/orgs/<org_id>/invitations '{"email":"a@example.com","role":"member"}' \xB7 vxil api DELETE /v1/orgs/invitations/<invite_id>` },
|
|
9563
9636
|
// feeds
|
|
9564
|
-
{ feature: "activity-feed", what: "block or mute a feed for a user", cmd: `vxil api POST /v1/feeds/blocks '{"owner":"
|
|
9637
|
+
{ feature: "activity-feed", what: "block or mute a feed for a user", cmd: `vxil api POST /v1/feeds/blocks '{"owner":"timeline:<reader_id>","blocked":"user:<author_id>","mode":"block"}'` },
|
|
9565
9638
|
{ feature: "activity-feed", what: "remove an activity", cmd: "vxil api DELETE /v1/feeds/<group>/<feed_id>/activities/<ref>" }
|
|
9566
9639
|
];
|
|
9567
9640
|
function apiCheatSheet() {
|
|
@@ -11876,7 +11949,9 @@ function genInputFromConfig(cfg) {
|
|
|
11876
11949
|
}
|
|
11877
11950
|
function typesCheckBody(src) {
|
|
11878
11951
|
const i = src.indexOf("export interface VxilSchema");
|
|
11879
|
-
|
|
11952
|
+
const body = i >= 0 ? src.slice(i) : src;
|
|
11953
|
+
const usage = body.indexOf("\n// Usage \u2014 ");
|
|
11954
|
+
return usage >= 0 ? body.slice(0, usage) : body;
|
|
11880
11955
|
}
|
|
11881
11956
|
function checkGeneratedTypes(existing, generated, opts) {
|
|
11882
11957
|
let have = typesCheckBody(withCurrentApiVersionsHead(existing));
|
|
@@ -11947,7 +12022,7 @@ export type VxilFilterJson = string | number | boolean | { $eq?: string | number
|
|
|
11947
12022
|
// Usage \u2014 narrow the client to THIS tenant's enabled features (a disabled feature
|
|
11948
12023
|
// becomes a compile error):
|
|
11949
12024
|
// import { Vxil } from '@vxil/sdk';
|
|
11950
|
-
// import type { VxilSchema } from './vxil.types';
|
|
12025
|
+
// import type { VxilSchema } from './vxil.types.js';
|
|
11951
12026
|
// const vx = Vxil.connect<VxilSchema>({ apiKey: process.env.VXIL_API_KEY! });
|
|
11952
12027
|
// await vx.from('${colls[0]?.collection ?? "collection"}').create({ /* field-typed */ });
|
|
11953
12028
|
`;
|
|
@@ -13552,7 +13627,7 @@ var TOOLS = [
|
|
|
13552
13627
|
{
|
|
13553
13628
|
name: "search_query",
|
|
13554
13629
|
feature: "vector-search",
|
|
13555
|
-
description: "Retrieve the top-k most relevant chunks from a vector-search collection. mode 'hybrid' (default) fuses semantic + keyword via RRF; 'vector' is pure semantic; 'keyword' is lexical. filter is a JSON object of equality constraints over chunk metadata. Returns ranked results with document/chunk attribution: score is the fused rank value (orders the hits, not a similarity); similarity is the cosine similarity to the query (-1..1, null in keyword mode) \u2014 threshold on similarity.",
|
|
13630
|
+
description: "Retrieve the top-k most relevant chunks from a vector-search collection. mode 'hybrid' (default) fuses semantic + keyword via RRF; 'vector' is pure semantic; 'keyword' is lexical. filter is a JSON object of equality constraints over chunk metadata. Returns ranked results with document/chunk attribution: score is the fused rank value (orders the hits, not a similarity); similarity is the cosine similarity to the query (-1..1, null in keyword mode) \u2014 threshold on similarity. embed_provider names the collection's embedder; 'mock' is the deterministic test embedder (ranks by a hash, not meaning), so tell the user search is not semantic until a real provider is set.",
|
|
13556
13631
|
inputSchema: {
|
|
13557
13632
|
type: "object",
|
|
13558
13633
|
properties: {
|
|
@@ -13582,6 +13657,11 @@ var TOOLS = [
|
|
|
13582
13657
|
model: { type: "string" },
|
|
13583
13658
|
max_tokens: { type: "number", minimum: 1, maximum: 2e5 },
|
|
13584
13659
|
temperature: { type: "number", minimum: 0, maximum: 2 },
|
|
13660
|
+
thinking_level: {
|
|
13661
|
+
type: "string",
|
|
13662
|
+
enum: ["minimal", "low", "medium", "high"],
|
|
13663
|
+
description: "Gemini thinking depth (ignored by other providers). Omitted: the tenant's defaults.thinkingLevel, then the model default. 'minimal' on a model that rejects it is a 422 thinking_level_unsupported. Thinking tokens bill as output."
|
|
13664
|
+
},
|
|
13585
13665
|
json_mode: { type: "boolean", description: "Constrain the output to JSON (no schema \u2014 use response_schema for a guaranteed shape)." },
|
|
13586
13666
|
response_schema: {
|
|
13587
13667
|
type: "object",
|
|
@@ -13712,7 +13792,7 @@ var TOOLS = [
|
|
|
13712
13792
|
{
|
|
13713
13793
|
name: "rag_answer",
|
|
13714
13794
|
feature: "rag",
|
|
13715
|
-
description: "Answer a question grounded in a vector-search collection: retrieve \u2192 budget context \u2192 generate \u2192 cite. Defaults the collection/template/top_k/mode from the rag config; override per call. Returns the answer plus citations to the source chunks that grounded it.",
|
|
13795
|
+
description: "Answer a question grounded in a vector-search collection: retrieve \u2192 budget context \u2192 generate \u2192 cite. Defaults the collection/template/top_k/mode from the rag config; override per call. Returns the answer plus citations to the source chunks that grounded it, and embed_provider (the collection's embedder; 'mock' = the deterministic test embedder, so retrieval is not semantic yet). The collection must exist first (POST /v1/search/collections, SDK vx.search.createCollection); a missing one fails retrieval with upstream collection_not_found.",
|
|
13716
13796
|
inputSchema: {
|
|
13717
13797
|
type: "object",
|
|
13718
13798
|
properties: {
|
|
@@ -13736,7 +13816,7 @@ var TOOLS = [
|
|
|
13736
13816
|
{
|
|
13737
13817
|
name: "rag_search",
|
|
13738
13818
|
feature: "rag",
|
|
13739
|
-
description: "Retrieval-only grounding preview: the exact chunks rag_answer would ground on, with rerank + metadata boosts applied \u2014 no generation, no token spend. Use it to inspect/tune retrieval (boost weights, rerank, min_similarity) before paying for an answer.",
|
|
13819
|
+
description: "Retrieval-only grounding preview: the exact chunks rag_answer would ground on, with rerank + metadata boosts applied \u2014 no generation, no token spend. Use it to inspect/tune retrieval (boost weights, rerank, min_similarity) before paying for an answer. embed_provider names the collection's embedder (mock = the deterministic test embedder, not semantic).",
|
|
13740
13820
|
inputSchema: {
|
|
13741
13821
|
type: "object",
|
|
13742
13822
|
properties: {
|
|
@@ -13882,7 +13962,7 @@ var TOOLS = [
|
|
|
13882
13962
|
// Substrate tool (no `feature` — always-on like users_upsert): usage is a
|
|
13883
13963
|
// platform property, not a feature you enable.
|
|
13884
13964
|
name: "usage_current",
|
|
13885
|
-
description: "Current-month usage for this project: requests used, the plan's included quota, remaining (null when the plan has no fixed cap), month-to-date per-feature metered detail (may lag \u2014 it is aggregated periodically), and `routes` \u2014 the same request count split by the surface that served it (the resolved upstream; /v1/fn/* reports as 'fn-invoke', busiest first, observational only). Read-only. Call this before a bulk run to self-check remaining quota: the Free tier is hard-capped (429 quota_exceeded when exhausted); paid tiers are never cut off mid-month. Needs usage:read or features:read.",
|
|
13965
|
+
description: "Current-month usage for this project: requests used, the plan's included quota, remaining (null when the plan has no fixed cap), month-to-date per-feature metered detail (may lag \u2014 it is aggregated periodically), and `routes` \u2014 the same request count split by the surface that served it (the resolved upstream; /v1/fn/* reports as 'fn-invoke', busiest first, observational only), `files` (live private objects and bytes vs the storage `limit` uploads are held to \u2014 422 quota_exceeded at it) and `functions` (today's UTC call count vs the plan's `limit_per_day`; a snapshot that can trail by a minute or two, null when there is no snapshot for today). Read-only. Call this before a bulk run to self-check remaining quota: the Free tier is hard-capped (429 quota_exceeded when exhausted); paid tiers are never cut off mid-month. Needs usage:read or features:read.",
|
|
13886
13966
|
inputSchema: { type: "object", properties: {} },
|
|
13887
13967
|
method: "GET",
|
|
13888
13968
|
path: "/v1/usage"
|
|
@@ -14440,6 +14520,21 @@ function notificationsProviderChecks(manifest) {
|
|
|
14440
14520
|
}
|
|
14441
14521
|
return [{ name: "notifications provider", ok: true, warn: true, detail: MOCK_PROVIDER_NOTE }];
|
|
14442
14522
|
}
|
|
14523
|
+
var EMBED_MOCK_NOTE = "vector-search embed provider is 'mock': the deterministic TEST embedder ranks by a hash of the text, not by meaning, so search and rag results look real but are not semantic (responses carry embed_provider: 'mock'). Store a provider key (`vxil secrets set vector-search/<name>`) and set embed: { provider: 'openai' | 'cohere', apiKeyRef } in your vector-search config (or 'byov' to supply your own vectors) before you ship";
|
|
14524
|
+
function embedProviderChecks(manifest, collections) {
|
|
14525
|
+
if (!manifest || manifest.enabled === false) return [];
|
|
14526
|
+
const def = manifest.embed?.provider ?? "mock";
|
|
14527
|
+
const mockCols = (collections ?? []).filter((c) => (c.provider ?? def) === "mock").map((c) => c.collection);
|
|
14528
|
+
if (def !== "mock" && !mockCols.length) {
|
|
14529
|
+
return [{ name: "vector-search embed provider", ok: true, detail: def }];
|
|
14530
|
+
}
|
|
14531
|
+
return [{
|
|
14532
|
+
name: "vector-search embed provider",
|
|
14533
|
+
ok: true,
|
|
14534
|
+
warn: true,
|
|
14535
|
+
detail: EMBED_MOCK_NOTE + (mockCols.length ? `. Collections on 'mock': ${mockCols.join(", ")} \u2014 a collection keeps the provider it was created with: create it again with the real embed provider (same name and dimensions) and re-ingest its documents` : "")
|
|
14536
|
+
}];
|
|
14537
|
+
}
|
|
14443
14538
|
var PAYMENTS_WEBHOOK_SECRET_NOTE = "payments provider is real but no per-tenant webhook secret is declared: every inbound provider delivery will be rejected 401 bad_signature and recorded as a sig_failed row (those rows can never be reprocessed, so the money events behind them are lost). There is NO platform-wide fallback. Declare <provider>.webhookSecretRef in your payments config and store the value with `vxil secrets set payments/<name>`";
|
|
14444
14539
|
function paymentsWebhookSecretChecks(manifest) {
|
|
14445
14540
|
if (!manifest || manifest.enabled === false) return [];
|
|
@@ -15219,10 +15314,10 @@ function renderTryReport(result, opts) {
|
|
|
15219
15314
|
if (genOk) {
|
|
15220
15315
|
lines.push("\n try it (types were generated in ./vxil.types.ts):");
|
|
15221
15316
|
lines.push(" import { Vxil } from '@vxil/sdk';");
|
|
15222
|
-
lines.push(" import type { VxilSchema } from './vxil.types';");
|
|
15317
|
+
lines.push(" import type { VxilSchema } from './vxil.types.js';");
|
|
15223
15318
|
lines.push(` const vx = Vxil.connect<VxilSchema>({ apiKey: '${result.api_key}', baseUrl: '${opts.base}' });`);
|
|
15224
15319
|
} else {
|
|
15225
|
-
lines.push("\n try it (typed client skipped \u2014 `npm i -D vxil && npm i @vxil/sdk`, then `npx vxil gen`):");
|
|
15320
|
+
lines.push("\n try it (typed client skipped \u2014 `npm i -D @vxil/cli && npm i @vxil/sdk`, then `npx vxil gen`):");
|
|
15226
15321
|
lines.push(" import { Vxil } from '@vxil/sdk';");
|
|
15227
15322
|
lines.push(` const vx = new Vxil({ apiKey: '${result.api_key}', baseUrl: '${opts.base}' });`);
|
|
15228
15323
|
}
|
|
@@ -21455,10 +21550,19 @@ async function runApply(apply, sel = "prod", opts = {}) {
|
|
|
21455
21550
|
report.errors++;
|
|
21456
21551
|
}
|
|
21457
21552
|
};
|
|
21553
|
+
let cmsApi = api;
|
|
21554
|
+
if (apply) {
|
|
21555
|
+
const enabledAt = await enableCmsBeforeSchema({ api, cfg });
|
|
21556
|
+
if (enabledAt !== null) {
|
|
21557
|
+
console.log(`
|
|
21558
|
+
cms: enabled (v${enabledAt}) before its schema \u2014 the declared cms config follows in the config step`);
|
|
21559
|
+
cmsApi = retryWhileCmsPropagates(api, { onWait: (line) => console.log(line) });
|
|
21560
|
+
}
|
|
21561
|
+
}
|
|
21458
21562
|
await runSection("cms.schema", async () => {
|
|
21459
21563
|
if (!cfg.cms?.collections || !Object.keys(cfg.cms.collections).length) return;
|
|
21460
21564
|
const r = await planCmsSchema({
|
|
21461
|
-
api,
|
|
21565
|
+
api: cmsApi,
|
|
21462
21566
|
collections: cfg.cms.collections,
|
|
21463
21567
|
apply,
|
|
21464
21568
|
allowDestructive: hasFlag("allow-destructive"),
|
|
@@ -21873,15 +21977,11 @@ ${formatTriggerWiring(w).join("\n")}`);
|
|
|
21873
21977
|
say("\napplying server-side\u2026");
|
|
21874
21978
|
}
|
|
21875
21979
|
});
|
|
21876
|
-
|
|
21877
|
-
|
|
21878
|
-
|
|
21879
|
-
|
|
21880
|
-
|
|
21881
|
-
say(" \u2026 declared API state deferred (the feature config is still propagating) \u2014 waiting 30 s, then resuming the same apply");
|
|
21882
|
-
await new Promise((r) => setTimeout(r, 3e4));
|
|
21883
|
-
done = await serverApply({ api, resume: done.apply_id });
|
|
21884
|
-
}
|
|
21980
|
+
for (let attempt = 1; done.status === "running" && onlyPropagationDeferred(done.steps) && attempt <= CMS_PROPAGATION_ATTEMPTS; attempt++) {
|
|
21981
|
+
if (attempt === 1) report(done, false);
|
|
21982
|
+
say(` \u2026 deferred: the feature config is still propagating \u2014 waiting ${CMS_PROPAGATION_DELAY_MS / 1e3} s, then resuming the same apply (${attempt}/${CMS_PROPAGATION_ATTEMPTS})`);
|
|
21983
|
+
await new Promise((r) => setTimeout(r, CMS_PROPAGATION_DELAY_MS));
|
|
21984
|
+
done = await serverApply({ api, resume: done.apply_id });
|
|
21885
21985
|
}
|
|
21886
21986
|
report(done, true);
|
|
21887
21987
|
const problems = { hard: [], strict: [] };
|
|
@@ -22142,6 +22242,16 @@ async function runDoctor() {
|
|
|
22142
22242
|
));
|
|
22143
22243
|
}
|
|
22144
22244
|
}
|
|
22245
|
+
if (live.has("vector-search")) {
|
|
22246
|
+
const vres = await api("GET", "/v1/config/vector-search");
|
|
22247
|
+
if (vres.status === 200) {
|
|
22248
|
+
const cres = await api("GET", "/v1/search/collections");
|
|
22249
|
+
checks.push(...embedProviderChecks(
|
|
22250
|
+
vres.body.data?.manifest,
|
|
22251
|
+
cres.status === 200 ? cres.body.data?.collections ?? [] : null
|
|
22252
|
+
));
|
|
22253
|
+
}
|
|
22254
|
+
}
|
|
22145
22255
|
if (live.has("payments")) {
|
|
22146
22256
|
const pres = await api("GET", "/v1/config/payments");
|
|
22147
22257
|
if (pres.status === 200) {
|
|
@@ -22279,11 +22389,19 @@ function scaffoldProject(configSrc, fns) {
|
|
|
22279
22389
|
const gi = resolve7(cwd, ".gitignore");
|
|
22280
22390
|
const ignore = ["", "# vxil", ".vxil/", ".env.local", ""].join("\n");
|
|
22281
22391
|
if (!existsSync8(gi) || !readFileSync8(gi, "utf8").includes(".vxil/")) appendFileSync2(gi, ignore);
|
|
22282
|
-
const
|
|
22392
|
+
const freshFolder = !existsSync8(resolve7(cwd, "package.json"));
|
|
22393
|
+
const tsconfig = freshFolder ? ensureScaffoldTsconfig(cwd) : existsSync8(resolve7(cwd, "tsconfig.json")) ? "exists" : "suggested";
|
|
22394
|
+
const pkg = ensureScaffoldPackageJson(cwd, {
|
|
22395
|
+
sdkTypes: entries.some(([, src]) => src.includes("from '@vxil/sdk'")),
|
|
22396
|
+
nodeTypes: tsconfig === "created"
|
|
22397
|
+
});
|
|
22283
22398
|
if (pkg.action === "skipped") console.log(` ! package.json not patched: ${pkg.reason}`);
|
|
22284
|
-
|
|
22399
|
+
if (tsconfig === "suggested") {
|
|
22400
|
+
console.log(' (no tsconfig.json written into an existing app \u2014 to type-check vxil.config.ts / functions, use moduleResolution "bundler" and add @types/node)');
|
|
22401
|
+
}
|
|
22402
|
+
return { ...pkg, tsconfig };
|
|
22285
22403
|
}
|
|
22286
|
-
function initProject() {
|
|
22404
|
+
function initProject(opts = {}) {
|
|
22287
22405
|
const cwd = process.cwd();
|
|
22288
22406
|
const cfgPath = resolve7(cwd, "vxil.config.ts");
|
|
22289
22407
|
if (existsSync8(cfgPath) && !hasFlag("force")) fail("vxil.config.ts already exists (use --force to overwrite)");
|
|
@@ -22302,8 +22420,10 @@ function initProject() {
|
|
|
22302
22420
|
const sdk = Object.values(tpl.functions ?? {}).some((src) => src.includes("from '@vxil/sdk'"));
|
|
22303
22421
|
console.log(` package.json # declares @vxil/config (the config import)${sdk ? " + @vxil/sdk (the functions' envelope types)" : ""}`);
|
|
22304
22422
|
}
|
|
22423
|
+
if (pkgRes.tsconfig === "created") console.log(' tsconfig.json # minimal: moduleResolution bundler, types ["node"]');
|
|
22424
|
+
if (opts.fromQuickstart) return;
|
|
22305
22425
|
console.log("\nnext: `vxil quickstart` (new tenant) or `vxil link <slug>` (existing), then `vxil push`.");
|
|
22306
|
-
console.log(" (
|
|
22426
|
+
console.log(" (the CLI runs vxil.config.ts and bundles functions without `npm install`; run it to type-check \u2014 tsc and your editor need the installed types.)");
|
|
22307
22427
|
}
|
|
22308
22428
|
function listTemplates() {
|
|
22309
22429
|
console.log("vxil templates \u2014 the Blueprint Gallery. Scaffold one with `vxil init --template <id>`.\n");
|
|
@@ -22381,7 +22501,7 @@ try {
|
|
|
22381
22501
|
saveProject(hasFlag("dev") ? { ...merged, dev: { tenant_id: slot.tenant_id, slug: slot.slug, edge_url: slot.edge_url } } : merged);
|
|
22382
22502
|
const envNotice = quickstartEnvNotice(flag("env"), base2, d.app_slug, { dev: hasFlag("dev") });
|
|
22383
22503
|
if (envNotice) console.error(`vxil: ${envNotice}`);
|
|
22384
|
-
if (!existsSync8(resolve7(process.cwd(), "vxil.config.ts"))) initProject();
|
|
22504
|
+
if (!existsSync8(resolve7(process.cwd(), "vxil.config.ts"))) initProject({ fromQuickstart: true });
|
|
22385
22505
|
console.log(`
|
|
22386
22506
|
\u2713 tenant '${d.app_slug}' created \xB7 enabled: ${(d.enabled ?? []).join(", ")}`);
|
|
22387
22507
|
if (d.expires_at) console.log(` dev tenant \u2014 expires ${d.expires_at} (the nightly reaper tears it down; extend by recreating)`);
|
|
@@ -22411,11 +22531,12 @@ try {
|
|
|
22411
22531
|
console.log(` types: ./vxil.types.ts (generated \u2014 re-run \`vxil gen\` after a schema change)`);
|
|
22412
22532
|
console.log(` try it:`);
|
|
22413
22533
|
console.log(` import { Vxil } from '@vxil/sdk';`);
|
|
22414
|
-
console.log(` import type { VxilSchema } from './vxil.types';`);
|
|
22415
|
-
console.log(` const vx = Vxil.connect<VxilSchema>({ apiKey }); // narrows to YOUR enabled features`);
|
|
22534
|
+
console.log(` import type { VxilSchema } from './vxil.types.js';`);
|
|
22535
|
+
console.log(` const vx = Vxil.connect<VxilSchema>({ apiKey: process.env.VXIL_API_KEY! }); // narrows to YOUR enabled features`);
|
|
22416
22536
|
console.log(` await vx.from('${firstCollection}').create({ /* field-typed */ });`);
|
|
22417
22537
|
console.log(`
|
|
22418
22538
|
key stored in ~/.vxil/credentials.json \xB7 project bound in .vxil/project.json`);
|
|
22539
|
+
console.log(" next: `vxil env pull` writes VXIL_API_KEY (+ VXIL_BASE_URL, VXIL_TENANT_ID) to .env.local for the snippet above");
|
|
22419
22540
|
break;
|
|
22420
22541
|
}
|
|
22421
22542
|
case "try": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vxil/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The vxil CLI — init, quickstart, push, gen, secrets, keys, migrate, doctor, diff, functions, payments; installs the `vxil` command (npm i -g @vxil/cli).",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@vxil/config": "0.
|
|
49
|
-
"@vxil/feature-configs": "0.
|
|
48
|
+
"@vxil/config": "0.14.0",
|
|
49
|
+
"@vxil/feature-configs": "0.12.0",
|
|
50
50
|
"@vxil/runtime": "0.0.1",
|
|
51
51
|
"@vxil/types": "0.0.1",
|
|
52
52
|
"miniflare": "^4.20260730.0"
|