@revturbine/cli 0.5.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +73 -16
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3979,6 +3979,13 @@ var RevTurbineConfigSchema = z19.object({
|
|
|
3979
3979
|
// The change set this export represents: the active change set by default,
|
|
3980
3980
|
// or a specific change set when one is requested. Null for an unscoped export.
|
|
3981
3981
|
change_set_id: z19.string().nullable().default(null).meta({ ...Unrestricted16, readOnly: true }),
|
|
3982
|
+
// Origin target identity (plan 131 TASK-10, cli.md "Target-aware, portable"):
|
|
3983
|
+
// stamped by the server on export so a downloaded Config File records where
|
|
3984
|
+
// it came from; upload tooling targets these by default and flags a tenant
|
|
3985
|
+
// mismatch against the session before sending. Optional — hand-authored and
|
|
3986
|
+
// pre-existing configs carry no target.
|
|
3987
|
+
tenant_id: z19.string().optional().meta({ ...Unrestricted16, readOnly: true }),
|
|
3988
|
+
environment_id: z19.string().optional().meta({ ...Unrestricted16, readOnly: true }),
|
|
3982
3989
|
plans: z19.array(RevTurbineConfigPlansItemSchema).meta(Unrestricted16),
|
|
3983
3990
|
// Optional for back-compat: pre-plan-88 configs (and the live export until web
|
|
3984
3991
|
// adopts the new @revt-eng/schema) omit it. Add-on definitions only; pricing
|
|
@@ -5562,7 +5569,7 @@ function evaluate(graph, opts = {}) {
|
|
|
5562
5569
|
}
|
|
5563
5570
|
|
|
5564
5571
|
// src/schema/version.ts
|
|
5565
|
-
var SCHEMA_VERSION = "0.1.
|
|
5572
|
+
var SCHEMA_VERSION = "0.1.108";
|
|
5566
5573
|
|
|
5567
5574
|
// src/lib/credentials.ts
|
|
5568
5575
|
import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
|
|
@@ -6024,6 +6031,26 @@ function requireSelectors(opts, positionalFiles, {
|
|
|
6024
6031
|
return found;
|
|
6025
6032
|
}
|
|
6026
6033
|
|
|
6034
|
+
// src/lib/target.ts
|
|
6035
|
+
function resolveUploadTarget(params) {
|
|
6036
|
+
const embedded = params.embedded ?? void 0;
|
|
6037
|
+
const explicit = params.explicit ?? void 0;
|
|
6038
|
+
if (explicit) {
|
|
6039
|
+
return {
|
|
6040
|
+
ok: true,
|
|
6041
|
+
tenantId: explicit,
|
|
6042
|
+
note: embedded && embedded !== explicit ? `retargeting deliberately: config was exported from ${embedded}, sending to ${explicit} (-t)` : void 0
|
|
6043
|
+
};
|
|
6044
|
+
}
|
|
6045
|
+
if (!embedded || embedded === params.session) {
|
|
6046
|
+
return { ok: true, tenantId: params.session };
|
|
6047
|
+
}
|
|
6048
|
+
return {
|
|
6049
|
+
ok: false,
|
|
6050
|
+
error: `this config was exported from tenant ${embedded}, but the session targets ${params.session}. Pass -t ${embedded} to send it back where it came from, or -t ${params.session} to retarget it deliberately.`
|
|
6051
|
+
};
|
|
6052
|
+
}
|
|
6053
|
+
|
|
6027
6054
|
// src/lib/version-trail.ts
|
|
6028
6055
|
function serverSchemaIsNewer(config, bundledVersion) {
|
|
6029
6056
|
const server = config?.schema_version;
|
|
@@ -6086,6 +6113,17 @@ function connect(rawUrl, explicitTenantId) {
|
|
|
6086
6113
|
}
|
|
6087
6114
|
};
|
|
6088
6115
|
}
|
|
6116
|
+
function uploadTenantFor(rawUrl, config, explicit) {
|
|
6117
|
+
const cred = getCredential(normalizeBaseUrl(rawUrl));
|
|
6118
|
+
const target = resolveUploadTarget({
|
|
6119
|
+
embedded: config?.tenant_id,
|
|
6120
|
+
explicit,
|
|
6121
|
+
session: cred?.tenant_id ?? "dev-tenant-001"
|
|
6122
|
+
});
|
|
6123
|
+
if (!target.ok) fail(EXIT.VALIDATION, target.error);
|
|
6124
|
+
if (target.note) diag(target.note);
|
|
6125
|
+
return target.tenantId;
|
|
6126
|
+
}
|
|
6089
6127
|
function authHint(url, status) {
|
|
6090
6128
|
if (status === 401 || status === 403) {
|
|
6091
6129
|
diag(`Authentication required for ${url}. Log in with:
|
|
@@ -6526,7 +6564,7 @@ program.command("show").description(`Render a summary view of a config version.
|
|
|
6526
6564
|
program.command("upload").description("Stage a Config File as the open draft (POST /api/config/import).").argument("<config>", "Path to a Config File").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").action(async (configFile2, opts) => {
|
|
6527
6565
|
const config = verifyConfig(configFile2);
|
|
6528
6566
|
if (config === null) fail(EXIT.VALIDATION, `Fix the issues above in ${configFile2}, then re-run.`);
|
|
6529
|
-
const conn = connect(opts.url, opts.tenantId);
|
|
6567
|
+
const conn = connect(opts.url, uploadTenantFor(opts.url, config, opts.tenantId));
|
|
6530
6568
|
diag(`Staging ${configFile2} as the open draft (${conn.url}/api/config/import) \u2026`);
|
|
6531
6569
|
const { res, json } = await postJson(conn, "/api/config/import", config);
|
|
6532
6570
|
if (!res.ok) {
|
|
@@ -6546,11 +6584,12 @@ program.command("upload").description("Stage a Config File as the open draft (PO
|
|
|
6546
6584
|
});
|
|
6547
6585
|
program.command("launch").description("Take a config live as a new Release: validate (launch gate), then submit \u2192 approve \u2192 deploy. Synchronous.").argument("[file]", "Config File to upload and launch directly").option("--draft", "Launch the already-open draft").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option("--yes", "Accepted for parity with discard/restore; launch has no confirmation prompt").action(async (file, opts) => {
|
|
6548
6586
|
const [sel] = requireSelectors(opts, file ? [file] : [], { count: 1, allowed: ["file", "draft"], command: "launch" });
|
|
6549
|
-
|
|
6587
|
+
let conn;
|
|
6550
6588
|
let playbookVersionId;
|
|
6551
6589
|
if (sel.kind === "file") {
|
|
6552
6590
|
const config = verifyConfig(sel.path);
|
|
6553
6591
|
if (config === null) fail(EXIT.VALIDATION, `Fix the issues above in ${sel.path}, then re-run.`);
|
|
6592
|
+
conn = connect(opts.url, uploadTenantFor(opts.url, config, opts.tenantId));
|
|
6554
6593
|
diag(`Staging ${sel.path} as the open draft \u2026`);
|
|
6555
6594
|
const { res, json } = await postJson(conn, "/api/config/import", config);
|
|
6556
6595
|
if (!res.ok) {
|
|
@@ -6561,6 +6600,7 @@ program.command("launch").description("Take a config live as a new Release: vali
|
|
|
6561
6600
|
if (!playbookVersionId) fail(EXIT.SERVER, "No playbook_version_id returned by the import \u2014 cannot launch.");
|
|
6562
6601
|
diag(`\u2713 Staged (${playbookVersionId})`);
|
|
6563
6602
|
} else {
|
|
6603
|
+
conn = connect(opts.url, opts.tenantId);
|
|
6564
6604
|
playbookVersionId = await requireOpenDraft(conn);
|
|
6565
6605
|
}
|
|
6566
6606
|
await launchDraft(conn, playbookVersionId);
|
|
@@ -6638,19 +6678,35 @@ program.command("preview").description("The open draft's staged changes (runtime
|
|
|
6638
6678
|
if (!res.ok) httpFail(conn, "preview", res.status, json);
|
|
6639
6679
|
emit(json, true);
|
|
6640
6680
|
});
|
|
6641
|
-
program.command("evaluate").description("Run the live
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6681
|
+
program.command("evaluate").description("Run placement/entitlement decisions for a user context against a config version. Requires --live, --draft, or --release <id>.").option("--live", "Evaluate the live configuration").option("--draft", "Evaluate the tenant's open draft (clean-room: no suppression/cap history)").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 placement/slot (the getPlacement decision)").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(
|
|
6682
|
+
async (opts) => {
|
|
6683
|
+
const [sel] = requireSelectors(opts, [], { count: 1, allowed: ["draft", "live", "release"], command: "evaluate" });
|
|
6684
|
+
const conn = connect(opts.url, opts.tenantId);
|
|
6685
|
+
if (!existsSync2(opts.user)) fail(EXIT.USAGE, `user file not found: ${opts.user}`);
|
|
6686
|
+
let ctx;
|
|
6687
|
+
try {
|
|
6688
|
+
ctx = JSON.parse(readFileSync2(opts.user, "utf8"));
|
|
6689
|
+
} catch (err) {
|
|
6690
|
+
fail(EXIT.VALIDATION, `invalid JSON in ${opts.user}: ${err.message}`);
|
|
6691
|
+
}
|
|
6692
|
+
const body = { ...ctx };
|
|
6693
|
+
if (opts.planHandle) body.plan_handle = opts.planHandle;
|
|
6694
|
+
if (opts.entitlement && opts.slot) fail(EXIT.USAGE, "pass exactly one of --entitlement or --slot (or neither for the ctx file lists).");
|
|
6695
|
+
if (opts.entitlement) {
|
|
6696
|
+
body.entitlement_handles = [opts.entitlement];
|
|
6697
|
+
body.placement_ids = [];
|
|
6698
|
+
}
|
|
6699
|
+
if (opts.slot) {
|
|
6700
|
+
body.placement_ids = [opts.slot];
|
|
6701
|
+
body.entitlement_handles = [];
|
|
6702
|
+
}
|
|
6703
|
+
if (sel.kind === "release") body.playbook_version_id = sel.id;
|
|
6704
|
+
if (sel.kind === "draft") body.playbook_version_id = await requireOpenDraft(conn);
|
|
6705
|
+
const { res, json } = await postJson(conn, "/api/sdk/evaluate", body);
|
|
6706
|
+
if (!res.ok) httpFail(conn, "evaluate", res.status, json);
|
|
6707
|
+
emit(json, true);
|
|
6649
6708
|
}
|
|
6650
|
-
|
|
6651
|
-
if (!res.ok) httpFail(conn, "evaluate", res.status, json);
|
|
6652
|
-
emit(json, true);
|
|
6653
|
-
});
|
|
6709
|
+
);
|
|
6654
6710
|
var COMMAND_EXAMPLES = {
|
|
6655
6711
|
download: [
|
|
6656
6712
|
"",
|
|
@@ -6682,7 +6738,8 @@ var COMMAND_EXAMPLES = {
|
|
|
6682
6738
|
evaluate: [
|
|
6683
6739
|
"",
|
|
6684
6740
|
"Example:",
|
|
6685
|
-
" revturbine evaluate --
|
|
6741
|
+
" revturbine evaluate --live --user ./ctx.json --entitlement seats",
|
|
6742
|
+
" revturbine evaluate --draft --user ./ctx.json --slot upgrade_banner --plan-handle pro",
|
|
6686
6743
|
' ctx.json: { "user_id": "u1", "plan_handle": "pro", "entitlement_handles": ["seats"] }'
|
|
6687
6744
|
].join("\n")
|
|
6688
6745
|
};
|
package/package.json
CHANGED