@revturbine/cli 0.16.4 → 0.17.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/cli.js +70 -35
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@ Commands that read a config name the version explicitly — there is no default:
|
|
|
69
69
|
| `whoami` | The resolved instance, tenant, credentials source, and whether the stored token works. |
|
|
70
70
|
| `schema` | Emit the bundled `RevTurbineConfig` JSON schema (for agents to author against). |
|
|
71
71
|
| `docs` | Print the canonical documentation URL. |
|
|
72
|
-
| `download` | Fetch a config version (`--live` / `--draft` / `--release <id>`); `--save
|
|
72
|
+
| `download` | Fetch a config version (`--live` / `--draft` / `--release <id>`); `--save`. |
|
|
73
73
|
| `validate` | Offline schema validation of a `<file>`, or the full server catalog against the open draft (`--draft`). |
|
|
74
74
|
| `diff` | Compare any two versions (dry-run, no writes). A file vs `--draft`/`--live`/`--release` previews the launch — the server side is the base, so `+`/`-` read as created/pruned on launch. |
|
|
75
75
|
| `show <kind>` | Summary tables: `plans` · `entitlements` · `segments` · `placements` · `trials` for any version. |
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import { createRequire } from "module";
|
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
8
|
import { createInterface } from "readline/promises";
|
|
9
9
|
import { spawn as spawn2, spawnSync } from "child_process";
|
|
10
|
-
import { Command, CommanderError
|
|
10
|
+
import { Command, CommanderError } from "commander";
|
|
11
11
|
import { z as z29 } from "zod";
|
|
12
12
|
|
|
13
13
|
// src/schema/exported-config.snapshot.mjs
|
|
@@ -9405,6 +9405,34 @@ async function resolveActiveDraft(baseUrl, headers, fetchImpl = fetch) {
|
|
|
9405
9405
|
return { ok: res.ok, status: res.status, draft: res.ok ? json.active ?? null : null };
|
|
9406
9406
|
}
|
|
9407
9407
|
|
|
9408
|
+
// src/lib/evaluate-local.ts
|
|
9409
|
+
import { initRevTurbine } from "@revturbine/sdk/headless";
|
|
9410
|
+
async function evaluateLocal(config, input) {
|
|
9411
|
+
const user = { id: input.userId };
|
|
9412
|
+
if (input.planHandle) user.plan_handle = input.planHandle;
|
|
9413
|
+
if (input.traits && Object.keys(input.traits).length > 0) user.custom = input.traits;
|
|
9414
|
+
const session = await initRevTurbine({
|
|
9415
|
+
user,
|
|
9416
|
+
localRuntime: { playbook: config }
|
|
9417
|
+
});
|
|
9418
|
+
const entitlements = {};
|
|
9419
|
+
for (const handle of input.entitlementHandles ?? []) {
|
|
9420
|
+
entitlements[handle] = await session.checkEntitlement(handle);
|
|
9421
|
+
}
|
|
9422
|
+
const decisions = [];
|
|
9423
|
+
for (const placementId of input.placementIds ?? []) {
|
|
9424
|
+
const controller = session.placement({ placement: { name: placementId } });
|
|
9425
|
+
await controller.load();
|
|
9426
|
+
decisions.push(controller.state.decision);
|
|
9427
|
+
}
|
|
9428
|
+
const placement = input.slot && (input.slot.slotId || input.slot.surfaceType) ? await session.getPlacement({
|
|
9429
|
+
...input.slot.slotId ? { slotId: input.slot.slotId } : {},
|
|
9430
|
+
...input.slot.surfaceType ? { surfaceType: input.slot.surfaceType } : {},
|
|
9431
|
+
...input.planHandle ? { planHandle: input.planHandle } : {}
|
|
9432
|
+
}) : null;
|
|
9433
|
+
return { decisions, entitlements, placement };
|
|
9434
|
+
}
|
|
9435
|
+
|
|
9408
9436
|
// src/lib/ingest-keys.ts
|
|
9409
9437
|
async function readJson(res) {
|
|
9410
9438
|
return await res.json().catch(() => ({}));
|
|
@@ -10652,27 +10680,11 @@ program.command("docs").description("Print the canonical documentation URL (and
|
|
|
10652
10680
|
}
|
|
10653
10681
|
}
|
|
10654
10682
|
});
|
|
10655
|
-
program.command("download").description("Fetch a config version from the server. Requires --live, --draft, or --release <id>.").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option("--draft", "The tenant's open draft (rendered on demand)").option("--live", "The current live Release").option("--release <id>", "A specific playbook version / Release").
|
|
10683
|
+
program.command("download").description("Fetch a config version from the server. Requires --live, --draft, or --release <id>.").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option("--draft", "The tenant's open draft (rendered on demand)").option("--live", "The current live Release").option("--release <id>", "A specific playbook version / Release").option("--save <file>", "Write to <file> instead of stdout").action(
|
|
10656
10684
|
async (opts) => {
|
|
10657
10685
|
const [sel] = requireSelectors(opts, [], { count: 1, allowed: ["draft", "live", "release"], command: "download" });
|
|
10658
10686
|
const conn = connect(opts.url, opts.tenantId);
|
|
10659
10687
|
const playbookVersionId = sel.kind === "release" ? sel.id : sel.kind === "draft" ? await requireOpenDraft(conn) : void 0;
|
|
10660
|
-
if (opts.format === "flatbuffer") {
|
|
10661
|
-
const qs = playbookVersionId ? `?playbookVersionId=${encodeURIComponent(playbookVersionId)}` : "";
|
|
10662
|
-
const res = await request(conn, `/api/config/bundle${qs}`);
|
|
10663
|
-
if (!res.ok) httpFail(conn, "bundle download", res.status);
|
|
10664
|
-
const bytes = Buffer.from(await res.arrayBuffer());
|
|
10665
|
-
if (opts.save) {
|
|
10666
|
-
const out2 = path2.resolve(opts.save);
|
|
10667
|
-
mkdirSync2(path2.dirname(out2), { recursive: true });
|
|
10668
|
-
writeFileSync2(out2, bytes);
|
|
10669
|
-
diag(`\u2713 Wrote ${bytes.length} bytes \u2192 ${out2}`);
|
|
10670
|
-
} else {
|
|
10671
|
-
process.stdout.write(`${bytes.toString("base64")}
|
|
10672
|
-
`);
|
|
10673
|
-
}
|
|
10674
|
-
return;
|
|
10675
|
-
}
|
|
10676
10688
|
const config = await downloadConfig(conn, playbookVersionId);
|
|
10677
10689
|
diag(`Downloaded: ${describePlaybookHeader(readPlaybookHeader(config))}`);
|
|
10678
10690
|
const out = `${JSON.stringify(config, null, 2)}
|
|
@@ -10882,7 +10894,9 @@ program.command("preview").description("The open draft's Runtime Impact summary
|
|
|
10882
10894
|
].join("\n");
|
|
10883
10895
|
emit(data, Boolean(opts.json), text);
|
|
10884
10896
|
});
|
|
10885
|
-
program.command("evaluate").description(
|
|
10897
|
+
program.command("evaluate").description(
|
|
10898
|
+
"Run placement/entitlement decisions for a user context against a config version \u2014 evaluated LOCALLY: the CLI fetches the version's Playbook and runs the SDK engine in-process (plan 192; clean-room, no suppression/cap history). Requires --live, --draft, or --release <id>."
|
|
10899
|
+
).option("--live", "Evaluate the live (deployed) configuration").option("--draft", "Evaluate the tenant's open draft").option("--release <id>", "Evaluate a past release (from its frozen snapshot)").option("--entitlement <handle>", "Check one entitlement (the checkEntitlement result)").option("--slot <id>", "Evaluate one surface slot (the getPlacement decision for that slot)").option("--surface-type <type>", "Disambiguate a slot that can render more than one surface (with --slot), or resolve by surface type alone").option("--plan-handle <handle>", "Evaluate as if the user were on this plan (overrides the ctx file)").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).requiredOption("--user <file>", "JSON file: { user_id, customer_id?, plan_handle?, traits?, now_iso? }").option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").action(
|
|
10886
10900
|
async (opts) => {
|
|
10887
10901
|
const [sel] = requireSelectors(opts, [], { count: 1, allowed: ["draft", "live", "release"], command: "evaluate" });
|
|
10888
10902
|
const conn = connect(opts.url, opts.tenantId);
|
|
@@ -10893,27 +10907,48 @@ program.command("evaluate").description("Run placement/entitlement decisions for
|
|
|
10893
10907
|
} catch (err) {
|
|
10894
10908
|
fail(EXIT.VALIDATION, `invalid JSON in ${opts.user}: ${err.message}`);
|
|
10895
10909
|
}
|
|
10896
|
-
const body = { ...ctx };
|
|
10897
|
-
if (opts.planHandle) body.plan_handle = opts.planHandle;
|
|
10898
10910
|
const wantsSlot = Boolean(opts.slot || opts.surfaceType);
|
|
10899
10911
|
if (opts.entitlement && wantsSlot) {
|
|
10900
10912
|
fail(EXIT.USAGE, "pass exactly one of --entitlement or --slot/--surface-type (or neither for the ctx file lists).");
|
|
10901
10913
|
}
|
|
10902
|
-
|
|
10903
|
-
|
|
10904
|
-
|
|
10914
|
+
const userId = typeof ctx.user_id === "string" && ctx.user_id.length > 0 ? ctx.user_id : void 0;
|
|
10915
|
+
if (!userId) fail(EXIT.VALIDATION, `user file must carry a non-empty string "user_id" (${opts.user})`);
|
|
10916
|
+
if (typeof ctx.now_iso === "string") {
|
|
10917
|
+
diag("WARNING: now_iso is ignored \u2014 local evaluation uses the real clock.");
|
|
10905
10918
|
}
|
|
10906
|
-
if (
|
|
10907
|
-
|
|
10908
|
-
|
|
10909
|
-
|
|
10910
|
-
body.entitlement_handles = [];
|
|
10919
|
+
if (typeof ctx.customer_id === "string") {
|
|
10920
|
+
diag(
|
|
10921
|
+
"WARNING: customer_id is ignored \u2014 supply plan_handle in the ctx file (or --plan-handle). The server-side customer\u2192plan lookup was retired with the decision endpoint (plan 192)."
|
|
10922
|
+
);
|
|
10911
10923
|
}
|
|
10912
|
-
|
|
10913
|
-
if (sel.kind === "
|
|
10914
|
-
|
|
10915
|
-
|
|
10916
|
-
|
|
10924
|
+
let playbookVersionId;
|
|
10925
|
+
if (sel.kind === "release") playbookVersionId = sel.id;
|
|
10926
|
+
if (sel.kind === "draft") playbookVersionId = await requireOpenDraft(conn);
|
|
10927
|
+
const config = await downloadConfig(conn, playbookVersionId);
|
|
10928
|
+
const planHandle = opts.planHandle ?? (typeof ctx.plan_handle === "string" ? ctx.plan_handle : void 0);
|
|
10929
|
+
const bulk = !opts.entitlement && !wantsSlot;
|
|
10930
|
+
const asStringArray2 = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
|
|
10931
|
+
const traits = typeof ctx.traits === "object" && ctx.traits !== null && !Array.isArray(ctx.traits) ? ctx.traits : void 0;
|
|
10932
|
+
const result = await evaluateLocal(config, {
|
|
10933
|
+
userId,
|
|
10934
|
+
planHandle,
|
|
10935
|
+
traits,
|
|
10936
|
+
entitlementHandles: opts.entitlement ? [opts.entitlement] : bulk ? asStringArray2(ctx.entitlement_handles) : [],
|
|
10937
|
+
placementIds: bulk ? asStringArray2(ctx.placement_ids) : [],
|
|
10938
|
+
slot: wantsSlot ? { slotId: opts.slot, surfaceType: opts.surfaceType } : void 0
|
|
10939
|
+
});
|
|
10940
|
+
emit(
|
|
10941
|
+
{
|
|
10942
|
+
tenant_id: conn.tenantId,
|
|
10943
|
+
user_id: userId,
|
|
10944
|
+
...playbookVersionId ? { playbook_version_id: playbookVersionId } : {},
|
|
10945
|
+
evaluated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10946
|
+
// Provenance: decided in-process by the SDK engine, not by a server.
|
|
10947
|
+
evaluation: "local",
|
|
10948
|
+
...result
|
|
10949
|
+
},
|
|
10950
|
+
true
|
|
10951
|
+
);
|
|
10917
10952
|
}
|
|
10918
10953
|
);
|
|
10919
10954
|
var generateCmd = program.command("generate").description("Code generation from a config version (see: generate types).");
|
|
@@ -10991,7 +11026,7 @@ var COMMAND_EXAMPLES = {
|
|
|
10991
11026
|
"Examples:",
|
|
10992
11027
|
" revturbine download --live --save ./revturbine.playbook.json The live config \u2192 file",
|
|
10993
11028
|
" revturbine download --draft The open draft (rendered on demand) \u2192 stdout",
|
|
10994
|
-
" revturbine download --release cs_1a2b3c --
|
|
11029
|
+
" revturbine download --release cs_1a2b3c --save ./release.playbook.json"
|
|
10995
11030
|
].join("\n"),
|
|
10996
11031
|
validate: [
|
|
10997
11032
|
"",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@revturbine/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "revturbine — validate RevTurbine Playbooks and ship them to a RevTurbine instance through the playbook-version lifecycle (draft → Release).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "tsup",
|
|
25
25
|
"generate:schema": "node scripts/generate-schema-snapshot.mjs",
|
|
26
|
-
"revt:sync": "node scripts/sync-revt-deps.mjs",
|
|
27
26
|
"typecheck": "tsc --noEmit",
|
|
28
27
|
"test": "vitest run",
|
|
29
28
|
"lint": "eslint .",
|
|
@@ -31,6 +30,7 @@
|
|
|
31
30
|
"verify": "npm run typecheck && npm run lint && npm run check:schema && npm run test"
|
|
32
31
|
},
|
|
33
32
|
"dependencies": {
|
|
33
|
+
"@revturbine/sdk": "^0.2.88",
|
|
34
34
|
"commander": "^15.0.0",
|
|
35
35
|
"zod": "4.4.3"
|
|
36
36
|
},
|