@idlekit/cli 0.4.0 → 0.5.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/main.js +401 -106
- package/dist/main.js.map +15 -13
- package/dist/runtime/workers/entry.js +7 -7
- package/dist/runtime/workers/entry.js.map +8 -8
- package/package.json +2 -2
package/dist/main.js
CHANGED
|
@@ -8397,7 +8397,7 @@ var init_validate = __esm(() => {
|
|
|
8397
8397
|
OFFLINE_ACTORS = new Set(["player", "automation"]);
|
|
8398
8398
|
});
|
|
8399
8399
|
|
|
8400
|
-
// ../../node_modules/.bun/typia@
|
|
8400
|
+
// ../../node_modules/.bun/typia@15.1.0+367f7514c87f1df1/node_modules/typia/lib/internal/_validateReport.mjs
|
|
8401
8401
|
var _validateReport = (array) => {
|
|
8402
8402
|
const isAncestor = (ancestor, descendant) => descendant === ancestor || descendant.startsWith(`${ancestor}.`) || descendant.startsWith(`${ancestor}[`);
|
|
8403
8403
|
const reportable = (path) => {
|
|
@@ -8422,7 +8422,7 @@ var _validateReport = (array) => {
|
|
|
8422
8422
|
};
|
|
8423
8423
|
var init__validateReport = () => {};
|
|
8424
8424
|
|
|
8425
|
-
// ../../node_modules/.bun/typia@
|
|
8425
|
+
// ../../node_modules/.bun/typia@15.1.0+367f7514c87f1df1/node_modules/typia/lib/internal/_createStandardSchema.mjs
|
|
8426
8426
|
var _createStandardSchema = (fn) => Object.assign(fn, { "~standard": {
|
|
8427
8427
|
version: 1,
|
|
8428
8428
|
vendor: "typia",
|
|
@@ -18414,7 +18414,7 @@ var init_checks3 = __esm(() => {
|
|
|
18414
18414
|
FAIL_ON = ["error", "warn", "inconclusive", "unsupported"];
|
|
18415
18415
|
});
|
|
18416
18416
|
|
|
18417
|
-
// ../../node_modules/.bun/typia@
|
|
18417
|
+
// ../../node_modules/.bun/typia@15.1.0+367f7514c87f1df1/node_modules/typia/lib/internal/_accessExpressionAsString.mjs
|
|
18418
18418
|
var _accessExpressionAsString = (str) => variable(str) ? `.${str}` : `[${JSON.stringify(str)}]`, variable = (str) => reserved(str) === false && /^[a-zA-Z_$][a-zA-Z_$0-9]*$/g.test(str), reserved = (str) => RESERVED.has(str), RESERVED;
|
|
18419
18419
|
var init__accessExpressionAsString = __esm(() => {
|
|
18420
18420
|
RESERVED = /* @__PURE__ */ new Set([
|
|
@@ -18961,7 +18961,7 @@ var init_judge = __esm(() => {
|
|
|
18961
18961
|
// ../core/dist/sim/analysis/flow.js
|
|
18962
18962
|
var init_flow = () => {};
|
|
18963
18963
|
|
|
18964
|
-
// ../../node_modules/.bun/typia@
|
|
18964
|
+
// ../../node_modules/.bun/typia@15.1.0+367f7514c87f1df1/node_modules/typia/lib/internal/_isTypeUint32.mjs
|
|
18965
18965
|
var _isTypeUint32 = (value) => Math.floor(value) === value && MINIMUM <= value && value <= MAXIMUM, MINIMUM = 0, MAXIMUM;
|
|
18966
18966
|
var init__isTypeUint32 = __esm(() => {
|
|
18967
18967
|
MAXIMUM = 2 ** 32 - 1;
|
|
@@ -19570,7 +19570,7 @@ var init_bun = __esm(() => {
|
|
|
19570
19570
|
var require_package = __commonJS(function(exports, module) {
|
|
19571
19571
|
module.exports = {
|
|
19572
19572
|
name: "@idlekit/cli",
|
|
19573
|
-
version: "0.
|
|
19573
|
+
version: "0.5.0",
|
|
19574
19574
|
description: "Bun-first idk CLI for idle game scenario validation, simulation, tuning, and reporting.",
|
|
19575
19575
|
license: "MIT",
|
|
19576
19576
|
type: "module",
|
|
@@ -19623,7 +19623,7 @@ var require_package = __commonJS(function(exports, module) {
|
|
|
19623
19623
|
"@clack/prompts": "1.8.1",
|
|
19624
19624
|
"@gunshi/plugin-completion": "0.37.3",
|
|
19625
19625
|
"@gunshi/plugin-suggestion": "0.37.3",
|
|
19626
|
-
"@idlekit/core": "0.4.
|
|
19626
|
+
"@idlekit/core": "0.4.1",
|
|
19627
19627
|
ajv: "^8.20.0",
|
|
19628
19628
|
gunshi: "0.37.3",
|
|
19629
19629
|
"proper-lockfile": "4.1.2",
|
|
@@ -35201,8 +35201,10 @@ async function readScenarioFileWithMeta(path) {
|
|
|
35201
35201
|
}
|
|
35202
35202
|
|
|
35203
35203
|
// src/io/writeOutput.ts
|
|
35204
|
+
init_errors3();
|
|
35204
35205
|
init_bun();
|
|
35205
35206
|
import { resolve as resolve5 } from "path";
|
|
35207
|
+
import { realpath, stat } from "fs/promises";
|
|
35206
35208
|
function toCsv(data) {
|
|
35207
35209
|
if (!Array.isArray(data)) {
|
|
35208
35210
|
return `value
|
|
@@ -35276,6 +35278,27 @@ async function writeOutput(args) {
|
|
|
35276
35278
|
return;
|
|
35277
35279
|
}
|
|
35278
35280
|
const abs = resolve5(args.outPath);
|
|
35281
|
+
for (const input of args.protectedInputs ?? []) {
|
|
35282
|
+
const source = resolve5(input);
|
|
35283
|
+
let same = source === abs;
|
|
35284
|
+
if (!same) {
|
|
35285
|
+
try {
|
|
35286
|
+
const [outputReal, inputReal, outputStat, inputStat] = await Promise.all([
|
|
35287
|
+
realpath(abs),
|
|
35288
|
+
realpath(source),
|
|
35289
|
+
stat(abs, { bigint: true }),
|
|
35290
|
+
stat(source, { bigint: true })
|
|
35291
|
+
]);
|
|
35292
|
+
same = outputReal === inputReal || outputStat.ino !== 0n && outputStat.dev === inputStat.dev && outputStat.ino === inputStat.ino;
|
|
35293
|
+
} catch (error) {
|
|
35294
|
+
const code = error.code;
|
|
35295
|
+
if (code !== "ENOENT" && code !== "ENOTDIR")
|
|
35296
|
+
throw error;
|
|
35297
|
+
}
|
|
35298
|
+
}
|
|
35299
|
+
if (same)
|
|
35300
|
+
throw usageError(`Output path must not overwrite an input file: ${input}`);
|
|
35301
|
+
}
|
|
35279
35302
|
await writeTextFile(abs, body);
|
|
35280
35303
|
console.log(`Wrote ${args.format} output to ${abs}`);
|
|
35281
35304
|
}
|
|
@@ -36449,6 +36472,15 @@ init_outputMeta();
|
|
|
36449
36472
|
import { extname as extname3, resolve as resolve8 } from "path";
|
|
36450
36473
|
|
|
36451
36474
|
// src/lib/ltvCalibration.ts
|
|
36475
|
+
function assertFiniteNumbers(value, path) {
|
|
36476
|
+
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
36477
|
+
throw new RangeError(`Calibration requires finite numbers; numeric overflow at ${path}`);
|
|
36478
|
+
}
|
|
36479
|
+
if (typeof value === "object" && value !== null) {
|
|
36480
|
+
for (const [key, child] of Object.entries(value))
|
|
36481
|
+
assertFiniteNumbers(child, `${path}.${key}`);
|
|
36482
|
+
}
|
|
36483
|
+
}
|
|
36452
36484
|
function clamp012(v) {
|
|
36453
36485
|
if (v < 0)
|
|
36454
36486
|
return 0;
|
|
@@ -36521,7 +36553,7 @@ function estimateCorrelation(x, y) {
|
|
|
36521
36553
|
varianceY
|
|
36522
36554
|
};
|
|
36523
36555
|
}
|
|
36524
|
-
const raw = clampCorrelation(cov / Math.sqrt(vx
|
|
36556
|
+
const raw = clampCorrelation(cov / Math.sqrt(vx) / Math.sqrt(vy));
|
|
36525
36557
|
const ci95 = 1.96 / Math.sqrt(Math.max(1, n - 3));
|
|
36526
36558
|
const sampleConfidence = clamp012(1 - ci95);
|
|
36527
36559
|
const varianceConfidenceX = varianceX / (varianceX + 0.01);
|
|
@@ -36642,7 +36674,7 @@ function calibrateMonetization(rows) {
|
|
|
36642
36674
|
conversionAd: estimatedCorrelationPair.conversionAd.confidence,
|
|
36643
36675
|
arppuAd: estimatedCorrelationPair.arppuAd.confidence
|
|
36644
36676
|
};
|
|
36645
|
-
|
|
36677
|
+
const result = {
|
|
36646
36678
|
monetization: {
|
|
36647
36679
|
cohorts: {
|
|
36648
36680
|
baseUsers: cohortSize
|
|
@@ -36650,8 +36682,8 @@ function calibrateMonetization(rows) {
|
|
|
36650
36682
|
retention: {
|
|
36651
36683
|
d1: clamp012(d1),
|
|
36652
36684
|
d7: clamp012(Math.min(d1, d7)),
|
|
36653
|
-
d30: clamp012(Math.min(d7, d30)),
|
|
36654
|
-
d90: clamp012(Math.min(d30, d90)),
|
|
36685
|
+
d30: clamp012(Math.min(d1, d7, d30)),
|
|
36686
|
+
d90: clamp012(Math.min(d1, d7, d30, d90)),
|
|
36655
36687
|
longTailDailyDecay: 0.02
|
|
36656
36688
|
},
|
|
36657
36689
|
revenue: {
|
|
@@ -36710,6 +36742,8 @@ function calibrateMonetization(rows) {
|
|
|
36710
36742
|
correlationDiagnostics: estimatedCorrelationPair
|
|
36711
36743
|
}
|
|
36712
36744
|
};
|
|
36745
|
+
assertFiniteNumbers(result, "calibration");
|
|
36746
|
+
return result;
|
|
36713
36747
|
}
|
|
36714
36748
|
// src/lib/ltvProjection.ts
|
|
36715
36749
|
function clamp013(v) {
|
|
@@ -36992,6 +37026,135 @@ function estimateLtvDistribution(args) {
|
|
|
36992
37026
|
quantiles: byQuantile
|
|
36993
37027
|
};
|
|
36994
37028
|
}
|
|
37029
|
+
// src/lib/cohortObservation.ts
|
|
37030
|
+
var RETENTION_DAYS = [1, 7, 30, 90];
|
|
37031
|
+
function observeCohort(rows, observedThroughDay) {
|
|
37032
|
+
if (!Number.isSafeInteger(observedThroughDay) || observedThroughDay < 0)
|
|
37033
|
+
throw new Error("observed-through-day must be a nonnegative safe integer");
|
|
37034
|
+
if (rows.length === 0)
|
|
37035
|
+
throw new Error("Observed cohort requires at least one install record");
|
|
37036
|
+
const users = new Map;
|
|
37037
|
+
for (const row of rows) {
|
|
37038
|
+
if (!row.userId.trim() || !Number.isSafeInteger(row.day) || row.day < 0 || row.day > observedThroughDay) {
|
|
37039
|
+
throw new Error("Observed cohort requires a user ID and integer days within observed-through-day");
|
|
37040
|
+
}
|
|
37041
|
+
if (![row.iapRevenue, row.adRevenue, ...row.acquisitionCost === undefined ? [] : [row.acquisitionCost]].every((v) => Number.isFinite(v) && v >= 0)) {
|
|
37042
|
+
throw new Error("Observed cohort revenue and acquisition costs must be finite and nonnegative");
|
|
37043
|
+
}
|
|
37044
|
+
if (row.active !== undefined && typeof row.active !== "boolean")
|
|
37045
|
+
throw new Error("Observed cohort active must be boolean");
|
|
37046
|
+
const user = users.get(row.userId) ?? { days: new Set, active: new Set };
|
|
37047
|
+
if (user.days.has(row.day))
|
|
37048
|
+
throw new Error("Observed cohort requires one aggregated row per user and day");
|
|
37049
|
+
user.days.add(row.day);
|
|
37050
|
+
if (row.active ?? true)
|
|
37051
|
+
user.active.add(row.day);
|
|
37052
|
+
users.set(row.userId, user);
|
|
37053
|
+
}
|
|
37054
|
+
for (const user of users.values()) {
|
|
37055
|
+
if (!user.days.has(0))
|
|
37056
|
+
throw new Error("Observed cohort requires a day 0 install record for every user, including inactive users");
|
|
37057
|
+
}
|
|
37058
|
+
const n = users.size;
|
|
37059
|
+
const observation = {
|
|
37060
|
+
observedThroughDay,
|
|
37061
|
+
users: n,
|
|
37062
|
+
retention: RETENTION_DAYS.map((day) => {
|
|
37063
|
+
if (day > observedThroughDay)
|
|
37064
|
+
return { day, status: "not-observed", users: n, activeUsers: null, rate: null, interval95: null };
|
|
37065
|
+
const activeUsers = [...users.values()].filter((user) => user.active.has(day)).length;
|
|
37066
|
+
const rate = activeUsers / n;
|
|
37067
|
+
const z2 = 1.959963984540054 ** 2;
|
|
37068
|
+
const denominator = 1 + z2 / n;
|
|
37069
|
+
const center = (rate + z2 / (2 * n)) / denominator;
|
|
37070
|
+
const radius = Math.sqrt(z2 * (rate * (1 - rate) / n + z2 / (4 * n * n))) / denominator;
|
|
37071
|
+
return { day, status: "observed", users: n, activeUsers, rate, interval95: { min: activeUsers === 0 ? 0 : Math.max(0, center - radius), max: activeUsers === n ? 1 : Math.min(1, center + radius) } };
|
|
37072
|
+
})
|
|
37073
|
+
};
|
|
37074
|
+
assertCohortObservation(observation);
|
|
37075
|
+
return observation;
|
|
37076
|
+
}
|
|
37077
|
+
function assertCohortObservation(observation) {
|
|
37078
|
+
if (!Number.isSafeInteger(observation.users) || observation.users < 1 || !Number.isSafeInteger(observation.observedThroughDay) || observation.observedThroughDay < 0) {
|
|
37079
|
+
throw new Error("Invalid cohort population or observation cutoff");
|
|
37080
|
+
}
|
|
37081
|
+
if (observation.retention.length !== RETENTION_DAYS.length)
|
|
37082
|
+
throw new Error("Cohort retention must contain D1, D7, D30 and D90");
|
|
37083
|
+
for (const [index, point] of observation.retention.entries()) {
|
|
37084
|
+
if (point.day !== RETENTION_DAYS[index] || point.users !== observation.users)
|
|
37085
|
+
throw new Error("Cohort retention days and population must match the observation");
|
|
37086
|
+
const observed = point.day <= observation.observedThroughDay;
|
|
37087
|
+
if (point.status !== (observed ? "observed" : "not-observed"))
|
|
37088
|
+
throw new Error("Cohort retention status must match the observation cutoff");
|
|
37089
|
+
if (!observed) {
|
|
37090
|
+
if (point.activeUsers !== null || point.rate !== null || point.interval95 !== null)
|
|
37091
|
+
throw new Error("Future retention must remain unobserved");
|
|
37092
|
+
continue;
|
|
37093
|
+
}
|
|
37094
|
+
if (point.activeUsers === null || !Number.isSafeInteger(point.activeUsers) || point.activeUsers < 0 || point.activeUsers > point.users || point.rate !== point.activeUsers / point.users) {
|
|
37095
|
+
throw new Error("Retained users and retention rate must match the installed population");
|
|
37096
|
+
}
|
|
37097
|
+
const interval = point.interval95;
|
|
37098
|
+
if (point.rate === null || !interval || !Number.isFinite(interval.min) || !Number.isFinite(interval.max) || interval.min < 0 || interval.max > 1 || interval.min > point.rate || interval.max < point.rate) {
|
|
37099
|
+
throw new Error("Retention confidence interval must be finite and contain the observed rate");
|
|
37100
|
+
}
|
|
37101
|
+
}
|
|
37102
|
+
}
|
|
37103
|
+
function observedRetentionRates(observation) {
|
|
37104
|
+
return Object.fromEntries(observation.retention.filter((row) => row.rate !== null).map((row) => [`d${row.day}`, row.rate]));
|
|
37105
|
+
}
|
|
37106
|
+
function planRetentionPatch(observation, assumptions = {}) {
|
|
37107
|
+
const rates = observedRetentionRates(observation);
|
|
37108
|
+
if (Object.keys(rates).length === 0)
|
|
37109
|
+
return { status: "unavailable", retention: {}, warnings: ["No retention day has been fully observed."] };
|
|
37110
|
+
const merged = { ...assumptions, ...rates };
|
|
37111
|
+
const warnings = [];
|
|
37112
|
+
for (const day of RETENTION_DAYS) {
|
|
37113
|
+
if (merged[`d${day}`] === undefined)
|
|
37114
|
+
warnings.push(`Unobserved D${day} has no explicit retained assumption. An LTV default could conflict with the observations; supply this assumption in --scenario or observe this day before applying a patch.`);
|
|
37115
|
+
}
|
|
37116
|
+
let previous;
|
|
37117
|
+
for (const day of RETENTION_DAYS) {
|
|
37118
|
+
const rate = merged[`d${day}`];
|
|
37119
|
+
if (rate === undefined)
|
|
37120
|
+
continue;
|
|
37121
|
+
if (previous && previous.rate < rate)
|
|
37122
|
+
warnings.push(`D${previous.day}=${previous.rate} is below D${day}=${rate}. LTV requires d1 >= d7 >= d30 >= d90; review observations and retained assumptions before applying a patch.`);
|
|
37123
|
+
previous = { day, rate };
|
|
37124
|
+
}
|
|
37125
|
+
return warnings.length ? { status: "requires-review", retention: {}, warnings } : { status: "ready", retention: rates, warnings };
|
|
37126
|
+
}
|
|
37127
|
+
function compareRetention(observation, assumptions) {
|
|
37128
|
+
return observation.retention.map((row) => {
|
|
37129
|
+
const assumption = assumptions[`d${row.day}`] ?? null;
|
|
37130
|
+
return {
|
|
37131
|
+
day: row.day,
|
|
37132
|
+
assumption,
|
|
37133
|
+
observed: row.rate,
|
|
37134
|
+
differencePercentagePoints: row.rate === null || assumption === null ? null : (row.rate - assumption) * 100,
|
|
37135
|
+
status: row.rate === null ? "not-observed" : assumption === null ? "unconfigured" : "compared"
|
|
37136
|
+
};
|
|
37137
|
+
});
|
|
37138
|
+
}
|
|
37139
|
+
function renderCohortMarkdown(observation, comparison, patch) {
|
|
37140
|
+
const percent = (value) => value === null ? "unavailable" : `${(value * 100).toFixed(2)}%`;
|
|
37141
|
+
const lines = [
|
|
37142
|
+
"# Observed player retention",
|
|
37143
|
+
"",
|
|
37144
|
+
`${observation.users} installed users; complete activity export through day ${observation.observedThroughDay}. Exact-day retention counts every installed user, including those who never return.`,
|
|
37145
|
+
"",
|
|
37146
|
+
"| Day | Retained / installed | Observed rate | Wilson 95% interval | Assumption | Difference (percentage points) | Status |",
|
|
37147
|
+
"| --- | --- | --- | --- | --- | --- | --- |"
|
|
37148
|
+
];
|
|
37149
|
+
for (const [index, row] of observation.retention.entries()) {
|
|
37150
|
+
const match = comparison?.[index];
|
|
37151
|
+
lines.push(`| D${row.day} | ${row.activeUsers === null ? "unavailable" : `${row.activeUsers} / ${row.users}`} | ${percent(row.rate)} | ${row.interval95 ? `${percent(row.interval95.min)} to ${percent(row.interval95.max)}` : "unavailable"} | ${percent(match?.assumption ?? null)} | ${match?.differencePercentagePoints == null ? "unavailable" : match.differencePercentagePoints.toFixed(2)} | ${match?.status ?? row.status} |`);
|
|
37152
|
+
}
|
|
37153
|
+
lines.push("", `Retention patch: **${patch.status}**.`, "", ...patch.warnings.map((warning) => `- ${warning}`), "", "Positive differences mean observed retention exceeds the configured assumption. Only apply a ready patch. Unobserved days are omitted from scenarioPatch; merge its retention fields into the scenario and preserve the remaining assumptions.", "", "Use a separate held-out cohort to validate a fitted model. These observations and intervals describe this cohort; they do not establish fun, causal improvement, or future revenue. Mixed install ages, missing activity exports, and omitted nonreturning users invalidate this comparison.");
|
|
37154
|
+
return lines.join(`
|
|
37155
|
+
`);
|
|
37156
|
+
}
|
|
37157
|
+
|
|
36995
37158
|
// src/commands/calibrate.ts
|
|
36996
37159
|
init_bun();
|
|
36997
37160
|
function parseCsvRows(raw) {
|
|
@@ -37064,21 +37227,46 @@ function parseNumeric(raw, row, field, opts) {
|
|
|
37064
37227
|
}
|
|
37065
37228
|
return n;
|
|
37066
37229
|
}
|
|
37067
|
-
function
|
|
37230
|
+
function parseActive(value, strict) {
|
|
37231
|
+
if (value === undefined)
|
|
37232
|
+
return true;
|
|
37233
|
+
if (typeof value === "boolean")
|
|
37234
|
+
return value;
|
|
37235
|
+
if (typeof value === "number" && (value === 0 || value === 1))
|
|
37236
|
+
return value === 1;
|
|
37237
|
+
if (typeof value === "string") {
|
|
37238
|
+
if (/^(1|true|yes|y)$/i.test(value))
|
|
37239
|
+
return true;
|
|
37240
|
+
if (/^(0|false|no|n)$/i.test(value))
|
|
37241
|
+
return false;
|
|
37242
|
+
}
|
|
37243
|
+
if (strict)
|
|
37244
|
+
throw usageError("Observed cohort active must be true/false, yes/no, or 1/0");
|
|
37245
|
+
return typeof value === "string" ? /^(1|true|yes|y)$/i.test(value) : Boolean(value);
|
|
37246
|
+
}
|
|
37247
|
+
function parseCsvTelemetry(raw, strictCohort = false) {
|
|
37068
37248
|
const rows2d = parseCsvRows(raw);
|
|
37069
37249
|
if (rows2d.length < 2) {
|
|
37070
37250
|
throw cliError("CLI_USAGE", "CSV telemetry requires a header and at least one data row");
|
|
37071
37251
|
}
|
|
37072
37252
|
const headers = (rows2d[0] ?? []).map(normalizeHeader);
|
|
37253
|
+
if (strictCohort && new Set(headers).size !== headers.length)
|
|
37254
|
+
throw usageError("Observed cohort CSV has duplicate headers");
|
|
37073
37255
|
const rows = [];
|
|
37074
37256
|
for (let i = 1;i < rows2d.length; i++) {
|
|
37075
37257
|
const cols = rows2d[i] ?? [];
|
|
37258
|
+
if (strictCohort && cols.length !== headers.length)
|
|
37259
|
+
throw usageError(`Observed cohort CSV row ${i + 1} has the wrong number of columns`);
|
|
37076
37260
|
const rec = {};
|
|
37077
37261
|
headers.forEach((h, idx) => {
|
|
37078
37262
|
rec[h] = cols[idx] ?? "";
|
|
37079
37263
|
});
|
|
37080
|
-
const
|
|
37081
|
-
const
|
|
37264
|
+
const userInput = pick2(rec, ["user_id", "userid", "user", "id"]);
|
|
37265
|
+
const dayInput = pick2(rec, ["day", "day_index", "days_since_install"]);
|
|
37266
|
+
if (strictCohort && (!userInput?.trim() || !dayInput?.trim()))
|
|
37267
|
+
throw usageError("Observed cohort requires explicit user_id and day fields");
|
|
37268
|
+
const userId = userInput ?? `anon-${i}`;
|
|
37269
|
+
const dayRaw = dayInput ?? "0";
|
|
37082
37270
|
const iapRaw = pick2(rec, ["iap_revenue", "revenue", "iap", "purchase_revenue"]) ?? "0";
|
|
37083
37271
|
const adRaw = pick2(rec, ["ad_revenue", "adrevenue", "ads"]) ?? "0";
|
|
37084
37272
|
const activeRaw = pick2(rec, ["active", "is_active"]);
|
|
@@ -37087,7 +37275,7 @@ function parseCsvTelemetry(raw) {
|
|
|
37087
37275
|
const iapRevenue = parseNumeric(iapRaw, i + 1, "revenue");
|
|
37088
37276
|
const adRevenue = parseNumeric(adRaw, i + 1, "ad_revenue");
|
|
37089
37277
|
const acquisitionCost = acqRaw !== undefined ? parseNumeric(acqRaw, i + 1, "acquisition_cost", { allowEmpty: true }) : undefined;
|
|
37090
|
-
const active = activeRaw
|
|
37278
|
+
const active = parseActive(activeRaw, strictCohort);
|
|
37091
37279
|
rows.push({
|
|
37092
37280
|
userId,
|
|
37093
37281
|
day,
|
|
@@ -37099,7 +37287,7 @@ function parseCsvTelemetry(raw) {
|
|
|
37099
37287
|
}
|
|
37100
37288
|
return rows;
|
|
37101
37289
|
}
|
|
37102
|
-
function parseJsonTelemetry(raw) {
|
|
37290
|
+
function parseJsonTelemetry(raw, strictCohort = false) {
|
|
37103
37291
|
const parsed = JSON.parse(raw);
|
|
37104
37292
|
if (!Array.isArray(parsed)) {
|
|
37105
37293
|
throw cliError("CLI_USAGE", "JSON telemetry must be an array");
|
|
@@ -37110,12 +37298,21 @@ function parseJsonTelemetry(raw) {
|
|
|
37110
37298
|
throw cliError("CLI_USAGE", `JSON telemetry row ${idx} must be an object`);
|
|
37111
37299
|
}
|
|
37112
37300
|
const r = row;
|
|
37113
|
-
const
|
|
37114
|
-
const
|
|
37301
|
+
const userInput = r.userId ?? r.user_id ?? r.user ?? r.id;
|
|
37302
|
+
const dayInput = r.day ?? r.dayIndex ?? r.daysSinceInstall;
|
|
37303
|
+
if (strictCohort && (typeof userInput !== "string" || !userInput.trim() || typeof dayInput !== "number"))
|
|
37304
|
+
throw usageError("Observed cohort JSON requires explicit string userId and numeric day fields");
|
|
37305
|
+
if (strictCohort)
|
|
37306
|
+
for (const key of ["iapRevenue", "iap_revenue", "revenue", "adRevenue", "ad_revenue", "acquisitionCost", "acquisition_cost", "cpi"]) {
|
|
37307
|
+
if (r[key] !== undefined && typeof r[key] !== "number")
|
|
37308
|
+
throw usageError(`Observed cohort JSON ${key} must be numeric`);
|
|
37309
|
+
}
|
|
37310
|
+
const userId = String(userInput ?? `anon-${idx}`);
|
|
37311
|
+
const day = Number(dayInput ?? 0);
|
|
37115
37312
|
const iapRevenue = Number(r.iapRevenue ?? r.iap_revenue ?? r.revenue ?? 0);
|
|
37116
37313
|
const adRevenue = Number(r.adRevenue ?? r.ad_revenue ?? 0);
|
|
37117
37314
|
const acquisitionCost = r.acquisitionCost !== undefined || r.acquisition_cost !== undefined || r.cpi !== undefined ? Number(r.acquisitionCost ?? r.acquisition_cost ?? r.cpi) : undefined;
|
|
37118
|
-
const active = r.active
|
|
37315
|
+
const active = parseActive(r.active, strictCohort);
|
|
37119
37316
|
if (!Number.isFinite(day) || !Number.isFinite(iapRevenue) || !Number.isFinite(adRevenue)) {
|
|
37120
37317
|
throw cliError("CLI_USAGE", `Invalid numeric value in json row ${idx}`);
|
|
37121
37318
|
}
|
|
@@ -37137,26 +37334,65 @@ var calibrate_default = defineCommand({
|
|
|
37137
37334
|
"input-format": option(_enum(["auto", "csv", "json"]).default("auto"), {
|
|
37138
37335
|
description: "Telemetry file format"
|
|
37139
37336
|
}),
|
|
37337
|
+
"observed-through-day": option(exports_coerce.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional(), {
|
|
37338
|
+
description: "Complete activity cutoff for one install cohort; requires day 0 records for every installed user"
|
|
37339
|
+
}),
|
|
37340
|
+
scenario: option(string2().optional(), { description: "Compare observed retention with this scenario's explicit assumptions (requires --observed-through-day)" }),
|
|
37140
37341
|
out: option(string2().optional(), { description: "Output path" }),
|
|
37141
37342
|
format: option(_enum(["json", "md", "csv"]).default("json"), { description: "Output format" })
|
|
37142
37343
|
},
|
|
37143
37344
|
async handler({ flags, positional }) {
|
|
37144
37345
|
const telemetryPath = positional[0];
|
|
37145
|
-
if (!telemetryPath) {
|
|
37346
|
+
if (!telemetryPath || positional.length !== 1) {
|
|
37146
37347
|
throw usageError("Usage: idk calibrate <telemetry.csv|json> [--input-format auto|csv|json]");
|
|
37147
37348
|
}
|
|
37349
|
+
if (flags.scenario && flags["observed-through-day"] === undefined)
|
|
37350
|
+
throw usageError("--scenario requires --observed-through-day for a mature cohort comparison");
|
|
37148
37351
|
const abs = resolve8(process.cwd(), telemetryPath);
|
|
37149
37352
|
const raw = await readTextFile(abs);
|
|
37150
37353
|
const format = flags["input-format"] === "auto" ? extname3(abs).toLowerCase() : flags["input-format"];
|
|
37151
|
-
const rows = format === ".csv" || format === "csv" ? parseCsvTelemetry(raw) : parseJsonTelemetry(raw);
|
|
37152
|
-
|
|
37354
|
+
const rows = format === ".csv" || format === "csv" ? parseCsvTelemetry(raw, flags["observed-through-day"] !== undefined) : parseJsonTelemetry(raw, flags["observed-through-day"] !== undefined);
|
|
37355
|
+
let observation;
|
|
37356
|
+
if (flags["observed-through-day"] !== undefined) {
|
|
37357
|
+
try {
|
|
37358
|
+
observation = observeCohort(rows, flags["observed-through-day"]);
|
|
37359
|
+
} catch (error) {
|
|
37360
|
+
throw usageError(error instanceof Error ? error.message : String(error));
|
|
37361
|
+
}
|
|
37362
|
+
}
|
|
37363
|
+
let calibrated;
|
|
37364
|
+
try {
|
|
37365
|
+
calibrated = calibrateMonetization(rows);
|
|
37366
|
+
} catch (error) {
|
|
37367
|
+
if (error instanceof RangeError)
|
|
37368
|
+
throw usageError(error.message);
|
|
37369
|
+
throw error;
|
|
37370
|
+
}
|
|
37371
|
+
const retentionRates = observation ? observedRetentionRates(observation) : undefined;
|
|
37372
|
+
const scenario = flags.scenario ? await readScenarioFile(flags.scenario) : undefined;
|
|
37373
|
+
const rate = number2().min(0).max(1).optional();
|
|
37374
|
+
const parsedScenario = scenario === undefined ? undefined : object({
|
|
37375
|
+
schemaVersion: literal(1),
|
|
37376
|
+
monetization: object({ retention: object({ d1: rate, d7: rate, d30: rate, d90: rate }).passthrough().optional() }).passthrough().optional()
|
|
37377
|
+
}).passthrough().safeParse(scenario);
|
|
37378
|
+
if (parsedScenario && !parsedScenario.success)
|
|
37379
|
+
throw usageError(`Invalid scenario retention assumptions: ${parsedScenario.error.message}`);
|
|
37380
|
+
const comparison = observation && parsedScenario?.success ? compareRetention(observation, parsedScenario.data.monetization?.retention ?? {}) : undefined;
|
|
37381
|
+
const patch = observation ? planRetentionPatch(observation, parsedScenario?.success ? parsedScenario.data.monetization?.retention : undefined) : undefined;
|
|
37382
|
+
const monetization = observation ? {
|
|
37383
|
+
...calibrated.monetization,
|
|
37384
|
+
retention: { longTailDailyDecay: calibrated.monetization?.retention?.longTailDailyDecay, ...patch?.retention }
|
|
37385
|
+
} : calibrated.monetization;
|
|
37153
37386
|
const outputMeta = buildOutputMeta({
|
|
37154
37387
|
command: "calibrate",
|
|
37155
|
-
telemetry: rows
|
|
37388
|
+
telemetry: observation ? { rows, observedThroughDay: observation.observedThroughDay } : rows,
|
|
37389
|
+
scenario,
|
|
37390
|
+
scenarioPath: flags.scenario ? resolve8(flags.scenario) : undefined
|
|
37156
37391
|
});
|
|
37157
37392
|
await writeOutput({
|
|
37158
37393
|
format: flags.format,
|
|
37159
37394
|
outPath: flags.out,
|
|
37395
|
+
protectedInputs: [abs, ...flags.scenario ? [resolve8(flags.scenario)] : []],
|
|
37160
37396
|
data: flags.format === "json" ? {
|
|
37161
37397
|
ok: true,
|
|
37162
37398
|
source: abs,
|
|
@@ -37164,12 +37400,27 @@ var calibrate_default = defineCommand({
|
|
|
37164
37400
|
rows: rows.length,
|
|
37165
37401
|
users: calibrated.diagnostics.users
|
|
37166
37402
|
},
|
|
37167
|
-
monetization
|
|
37168
|
-
diagnostics: calibrated.diagnostics,
|
|
37169
|
-
|
|
37403
|
+
monetization,
|
|
37404
|
+
diagnostics: observation ? { ...calibrated.diagnostics, retention: retentionRates, observedThroughDay: observation.observedThroughDay } : calibrated.diagnostics,
|
|
37405
|
+
...observation ? { observation } : {},
|
|
37406
|
+
...comparison ? { comparison } : {},
|
|
37407
|
+
...patch ? { patchStatus: patch.status, patchWarnings: patch.warnings } : {},
|
|
37408
|
+
scenarioPatch: observation ? { monetization: { retention: patch.retention } } : {
|
|
37170
37409
|
monetization: calibrated.monetization
|
|
37171
37410
|
}
|
|
37172
|
-
} :
|
|
37411
|
+
} : observation ? flags.format === "md" ? renderCohortMarkdown(observation, comparison, patch) : observation.retention.map((row, index) => ({
|
|
37412
|
+
day: row.day,
|
|
37413
|
+
users: row.users,
|
|
37414
|
+
activeUsers: row.activeUsers,
|
|
37415
|
+
observedRate: row.rate,
|
|
37416
|
+
interval95Min: row.interval95?.min ?? null,
|
|
37417
|
+
interval95Max: row.interval95?.max ?? null,
|
|
37418
|
+
assumption: comparison?.[index]?.assumption ?? null,
|
|
37419
|
+
differencePercentagePoints: comparison?.[index]?.differencePercentagePoints ?? null,
|
|
37420
|
+
status: comparison?.[index]?.status ?? row.status,
|
|
37421
|
+
patchStatus: patch.status,
|
|
37422
|
+
patchWarnings: patch.warnings.join(" ")
|
|
37423
|
+
})) : [
|
|
37173
37424
|
{
|
|
37174
37425
|
source: abs,
|
|
37175
37426
|
rows: rows.length,
|
|
@@ -37191,12 +37442,13 @@ var calibrate_default = defineCommand({
|
|
|
37191
37442
|
|
|
37192
37443
|
// src/commands/balance.ts
|
|
37193
37444
|
init_zod();
|
|
37445
|
+
import { resolve as resolve11 } from "path";
|
|
37194
37446
|
|
|
37195
37447
|
// src/balance/workflow.ts
|
|
37196
37448
|
init_dist2();
|
|
37197
37449
|
init_zod();
|
|
37198
37450
|
import { resolve as resolve10, dirname as dirname8 } from "path";
|
|
37199
|
-
import { stat } from "fs/promises";
|
|
37451
|
+
import { stat as stat2 } from "fs/promises";
|
|
37200
37452
|
|
|
37201
37453
|
// src/cliMeta.ts
|
|
37202
37454
|
var import_package2 = __toESM(require_package(), 1);
|
|
@@ -37442,7 +37694,7 @@ async function runPacingChecks(input, evaluator) {
|
|
|
37442
37694
|
// src/balance/sheet.ts
|
|
37443
37695
|
init_bun();
|
|
37444
37696
|
import { constants } from "fs";
|
|
37445
|
-
import { lstat, mkdir, open as open2, realpath, rename, rm } from "fs/promises";
|
|
37697
|
+
import { lstat, mkdir, open as open2, realpath as realpath2, rename, rm } from "fs/promises";
|
|
37446
37698
|
import { dirname as dirname7, isAbsolute as isAbsolute2, join, relative as relative2, resolve as resolve9, sep as sep2 } from "path";
|
|
37447
37699
|
var SHEET_LIMITS = Object.freeze({
|
|
37448
37700
|
maxCsvBytes: 2 * 1024 * 1024,
|
|
@@ -37816,7 +38068,7 @@ async function snapshotInputs(inputFiles, contextValue, limits) {
|
|
|
37816
38068
|
let total = 0;
|
|
37817
38069
|
for (const name of validInputNames(inputFiles, limits)) {
|
|
37818
38070
|
const path = resolve9(inputFiles[name]);
|
|
37819
|
-
const realPath = await
|
|
38071
|
+
const realPath = await realpath2(path);
|
|
37820
38072
|
const bytes = await boundedRead(realPath, Math.min(limits.maxInputBytes, limits.maxTotalInputBytes - total));
|
|
37821
38073
|
total += bytes.length;
|
|
37822
38074
|
if (total > limits.maxTotalInputBytes)
|
|
@@ -37843,7 +38095,7 @@ async function snapshotInputs(inputFiles, contextValue, limits) {
|
|
|
37843
38095
|
async function canonicalOutput(path) {
|
|
37844
38096
|
path = resolve9(path);
|
|
37845
38097
|
try {
|
|
37846
|
-
return await
|
|
38098
|
+
return await realpath2(path);
|
|
37847
38099
|
} catch (error) {
|
|
37848
38100
|
if (error.code !== "ENOENT")
|
|
37849
38101
|
throw error;
|
|
@@ -37880,7 +38132,7 @@ async function refreshSheetBundle(options) {
|
|
|
37880
38132
|
validInputNames(options.inputFiles, limits);
|
|
37881
38133
|
const output = await canonicalOutput(options.outputDir);
|
|
37882
38134
|
for (const [name, path] of Object.entries(options.inputFiles)) {
|
|
37883
|
-
if (isWithin(output, resolve9(path)) || isWithin(output, await
|
|
38135
|
+
if (isWithin(output, resolve9(path)) || isWithin(output, await realpath2(path)))
|
|
37884
38136
|
throw new Error(`Output/input collision: ${name}`);
|
|
37885
38137
|
}
|
|
37886
38138
|
await mkdir(output, { recursive: true });
|
|
@@ -37940,7 +38192,7 @@ async function refreshSheetBundle(options) {
|
|
|
37940
38192
|
if (manifestBytes.length > 1024 * 1024)
|
|
37941
38193
|
throw new Error("Bundle manifest exceeds byte limit");
|
|
37942
38194
|
await mkdir(generations, { recursive: true });
|
|
37943
|
-
if (await
|
|
38195
|
+
if (await realpath2(generations) !== generations)
|
|
37944
38196
|
throw new Error("Bundle generations directory must not be a symlink");
|
|
37945
38197
|
await mkdir(stage);
|
|
37946
38198
|
for (const name of names)
|
|
@@ -38029,10 +38281,10 @@ async function readCurrentBundle(outputDir, inputFiles, options = {}) {
|
|
|
38029
38281
|
if (!isRecord5(pointer) || pointer.version !== 1 || typeof pointer.generation !== "string" || !generationText.test(pointer.generation) || typeof pointer.manifestSha256 !== "string" || !sha256Text2.test(pointer.manifestSha256))
|
|
38030
38282
|
throw new Error("Invalid bundle pointer");
|
|
38031
38283
|
const generationPath = join(output, "generations", pointer.generation);
|
|
38032
|
-
if (await
|
|
38284
|
+
if (await realpath2(generationPath) !== generationPath)
|
|
38033
38285
|
throw new Error("Bundle generation must not be a symlink");
|
|
38034
38286
|
const manifestPath = join(generationPath, "manifest.json");
|
|
38035
|
-
if (await
|
|
38287
|
+
if (await realpath2(manifestPath) !== manifestPath)
|
|
38036
38288
|
throw new Error("Bundle manifest must not be a symlink");
|
|
38037
38289
|
const bytes = await boundedRead(manifestPath, 1024 * 1024);
|
|
38038
38290
|
if (sha256Hex(bytes) !== pointer.manifestSha256)
|
|
@@ -38041,7 +38293,7 @@ async function readCurrentBundle(outputDir, inputFiles, options = {}) {
|
|
|
38041
38293
|
let outputBytesRead = 0;
|
|
38042
38294
|
for (const [name, artifact] of Object.entries(manifest.artifacts)) {
|
|
38043
38295
|
try {
|
|
38044
|
-
if (await
|
|
38296
|
+
if (await realpath2(artifact.path) !== artifact.path)
|
|
38045
38297
|
throw new Error("Artifact is a symlink");
|
|
38046
38298
|
const artifactBytes = await boundedRead(artifact.path, Math.min(limits.maxOutputBytes, limits.maxTotalOutputBytes - outputBytesRead));
|
|
38047
38299
|
outputBytesRead += artifactBytes.length;
|
|
@@ -38141,7 +38393,7 @@ function normalizeRunDocument(document, strategy, horizon) {
|
|
|
38141
38393
|
}
|
|
38142
38394
|
async function refreshBalanceWorkflow(configPath, flags, check = false) {
|
|
38143
38395
|
const absoluteConfig = resolve10(configPath);
|
|
38144
|
-
const configInfo = await
|
|
38396
|
+
const configInfo = await stat2(absoluteConfig);
|
|
38145
38397
|
if (!configInfo.isFile() || configInfo.size > SHEET_LIMITS.maxInputBytes)
|
|
38146
38398
|
throw new Error("Workflow config is not a bounded regular file");
|
|
38147
38399
|
const rawConfig = await readTextFile(absoluteConfig);
|
|
@@ -38337,6 +38589,46 @@ async function refreshBalanceWorkflow(configPath, flags, check = false) {
|
|
|
38337
38589
|
return { ...manifest, outcome };
|
|
38338
38590
|
}
|
|
38339
38591
|
|
|
38592
|
+
// src/balance/report.ts
|
|
38593
|
+
function cell(value) {
|
|
38594
|
+
return String(value).replaceAll("|", "\\|").replace(/[\r\n]/g, " ");
|
|
38595
|
+
}
|
|
38596
|
+
function explain(result) {
|
|
38597
|
+
if (result.status === "breach" && result.value !== null) {
|
|
38598
|
+
if (result.min !== undefined && result.value < result.min)
|
|
38599
|
+
return `below the minimum by ${result.min - result.value} ${result.unit}`;
|
|
38600
|
+
if (result.max !== undefined && result.value > result.max)
|
|
38601
|
+
return `above the maximum by ${result.value - result.max} ${result.unit}`;
|
|
38602
|
+
}
|
|
38603
|
+
if (result.status === "pass")
|
|
38604
|
+
return "within the inclusive target bounds";
|
|
38605
|
+
return result.message ?? (result.status === "unreached" ? "not reached within the horizon" : "measurement unavailable");
|
|
38606
|
+
}
|
|
38607
|
+
function renderBalanceReport(report, generationPath) {
|
|
38608
|
+
const lines = [
|
|
38609
|
+
"# Balance targets",
|
|
38610
|
+
"",
|
|
38611
|
+
`Result: **${report.status}**. Simulated ${report.horizonSec} seconds using strategy ${cell(report.strategy)}; seeds ${report.seeds.join(", ")}.`,
|
|
38612
|
+
"A target is a design constraint on this model under these conditions. End net worth is modeled asset value; it can differ from spendable wallet money.",
|
|
38613
|
+
"",
|
|
38614
|
+
"| Variant | Seed | Metric | Measured | Target | Status | Explanation |",
|
|
38615
|
+
"| --- | --- | --- | --- | --- | --- | --- |"
|
|
38616
|
+
];
|
|
38617
|
+
for (const entry of report.runs)
|
|
38618
|
+
for (const result of entry.results) {
|
|
38619
|
+
const target = [result.min === undefined ? undefined : `minimum ${result.min} ${result.unit}`, result.max === undefined ? undefined : `maximum ${result.max} ${result.unit}`].filter(Boolean).join(", ");
|
|
38620
|
+
const measured = result.value === null ? "unavailable" : `${result.value} ${result.unit}`;
|
|
38621
|
+
lines.push(`| ${cell(entry.run.variantId)} | ${entry.run.seed} | ${cell(result.metric)} | ${cell(measured)} | ${cell(target)} | ${result.status} | ${cell(explain(result))} |`);
|
|
38622
|
+
}
|
|
38623
|
+
lines.push("", report.ok ? "All declared targets passed." : "A target breached, was not reached, or could not be measured. The command exits 1; inspect the rows before changing the design.", "", `Saved generation: ${cell(generationPath)}`, "The generation contains exact values in results.json and reproduction inputs in provenance.json. Passing targets does not establish fun or player retention.");
|
|
38624
|
+
return lines.join(`
|
|
38625
|
+
`);
|
|
38626
|
+
}
|
|
38627
|
+
function renderBalanceFreshness(result) {
|
|
38628
|
+
return ["# Balance result freshness", "", `State: **${result.state}**. This checks saved inputs and artifacts; it does not check target success. Run balance without --check to evaluate the targets.`, ...result.staleReasons.map((reason) => `- ${cell(reason)}`)].join(`
|
|
38629
|
+
`);
|
|
38630
|
+
}
|
|
38631
|
+
|
|
38340
38632
|
// src/commands/balance.ts
|
|
38341
38633
|
init_errors3();
|
|
38342
38634
|
init_bun();
|
|
@@ -38345,13 +38637,15 @@ var balance_default = defineCommand({
|
|
|
38345
38637
|
description: "Refresh a typed CSV balance sheet into scenario, pacing checks, and result sheets",
|
|
38346
38638
|
options: {
|
|
38347
38639
|
...pluginOptions(),
|
|
38348
|
-
check: option(exports_coerce.boolean().default(false), { description: "Check result freshness without rerunning simulations" })
|
|
38640
|
+
check: option(exports_coerce.boolean().default(false), { description: "Check result freshness without rerunning simulations" }),
|
|
38641
|
+
format: option(_enum(["json", "md"]).default("json"), { description: "JSON manifest or human-readable target explanations" })
|
|
38349
38642
|
},
|
|
38350
38643
|
async handler({ flags, positional }) {
|
|
38351
38644
|
if (positional.length !== 1)
|
|
38352
38645
|
throw usageError("Usage: idk balance <workflow.json> [--check] [plugin options]");
|
|
38353
38646
|
const result = await refreshBalanceWorkflow(positional[0], flags, flags.check);
|
|
38354
|
-
|
|
38647
|
+
const body = flags.format === "json" ? JSON.stringify(result, null, 2) : ("state" in result) ? renderBalanceFreshness(result) : renderBalanceReport(JSON.parse(await readTextFile(resolve11(result.generationPath, "results.json"))), result.generationPath);
|
|
38648
|
+
await writeStdout(body + `
|
|
38355
38649
|
`);
|
|
38356
38650
|
if (flags.check && "state" in result && result.state !== "current")
|
|
38357
38651
|
process.exitCode = 1;
|
|
@@ -38363,7 +38657,7 @@ var balance_default = defineCommand({
|
|
|
38363
38657
|
// src/commands/compare.ts
|
|
38364
38658
|
init_dist2();
|
|
38365
38659
|
init_zod();
|
|
38366
|
-
import { resolve as
|
|
38660
|
+
import { resolve as resolve14 } from "path";
|
|
38367
38661
|
|
|
38368
38662
|
// src/commands/_shared/compareEval.ts
|
|
38369
38663
|
function formatEtaLabel(seconds, reached) {
|
|
@@ -38390,12 +38684,12 @@ init_errors3();
|
|
|
38390
38684
|
init_outputMeta();
|
|
38391
38685
|
|
|
38392
38686
|
// src/io/replayPolicy.ts
|
|
38393
|
-
import { resolve as
|
|
38687
|
+
import { resolve as resolve13 } from "path";
|
|
38394
38688
|
|
|
38395
38689
|
// src/io/replayArtifact.ts
|
|
38396
38690
|
init_outputMeta();
|
|
38397
38691
|
init_bun();
|
|
38398
|
-
import { resolve as
|
|
38692
|
+
import { resolve as resolve12 } from "path";
|
|
38399
38693
|
var REDACTED_KEYS = new Set([
|
|
38400
38694
|
"generatedAt",
|
|
38401
38695
|
"scenario",
|
|
@@ -38461,7 +38755,7 @@ function toReplayCommandLine(args) {
|
|
|
38461
38755
|
return `bun run --cwd packages/cli dev -- ${rendered}`;
|
|
38462
38756
|
}
|
|
38463
38757
|
async function writeReplayArtifact(args) {
|
|
38464
|
-
const abs =
|
|
38758
|
+
const abs = resolve12(args.outPath);
|
|
38465
38759
|
const runId = args.meta.runId;
|
|
38466
38760
|
const seed = args.meta.seed;
|
|
38467
38761
|
const scenarioHash = args.meta.scenarioHash;
|
|
@@ -38537,7 +38831,7 @@ var PATH_FLAG_KINDS = {
|
|
|
38537
38831
|
function normalizePathLike(pathLike, cwd) {
|
|
38538
38832
|
if (pathLike.trim().length === 0)
|
|
38539
38833
|
return pathLike;
|
|
38540
|
-
return
|
|
38834
|
+
return resolve13(cwd, pathLike);
|
|
38541
38835
|
}
|
|
38542
38836
|
function normalizeCsvPaths(raw, cwd) {
|
|
38543
38837
|
return raw.split(",").map((x) => x.trim()).filter(Boolean).map((x) => normalizePathLike(x, cwd)).join(",");
|
|
@@ -38612,7 +38906,7 @@ function plain(value) {
|
|
|
38612
38906
|
return "n/a";
|
|
38613
38907
|
return String(value).replace(/[\r\n]+/g, " ");
|
|
38614
38908
|
}
|
|
38615
|
-
function
|
|
38909
|
+
function cell2(value) {
|
|
38616
38910
|
return plain(value).replaceAll("|", "\\|");
|
|
38617
38911
|
}
|
|
38618
38912
|
function inlineCode(value) {
|
|
@@ -38640,9 +38934,9 @@ function renderCompareMarkdown(args) {
|
|
|
38640
38934
|
const higher = ["endMoney", "endNetWorth", "visibleChangesPerMinute"].includes(metric);
|
|
38641
38935
|
const lower = ["droppedRate", "etaToTargetWorth", "timeToMilestone", "maxNoRewardGapSec"].includes(metric);
|
|
38642
38936
|
const seconds = ["etaToTargetWorth", "timeToMilestone", "maxNoRewardGapSec"].includes(metric);
|
|
38643
|
-
const value = (item) => seconds && (typeof item === "number" || typeof item === "string" && item.trim().length > 0) && Number.isFinite(Number(item)) ? `${
|
|
38937
|
+
const value = (item) => seconds && (typeof item === "number" || typeof item === "string" && item.trim().length > 0) && Number.isFinite(Number(item)) ? `${cell2(item)}s` : cell2(item);
|
|
38644
38938
|
const better = result.better === "a" || result.better === "b" ? result.better.toUpperCase() : result.better === "tie" ? "tie" : "undetermined";
|
|
38645
|
-
lines.push(`| ${
|
|
38939
|
+
lines.push(`| ${cell2(metric)} | ${value(a)} | ${value(b)} | ${higher ? "higher" : lower ? "lower" : "n/a"} | ${better} |`);
|
|
38646
38940
|
if (result.better === "a" || result.better === "b")
|
|
38647
38941
|
preferences[result.better].push(metric);
|
|
38648
38942
|
}
|
|
@@ -39319,8 +39613,8 @@ var compare_default = defineCommand({
|
|
|
39319
39613
|
}
|
|
39320
39614
|
} : singleResults[0];
|
|
39321
39615
|
if (flags["artifact-out"]) {
|
|
39322
|
-
const aAbs =
|
|
39323
|
-
const bAbs =
|
|
39616
|
+
const aAbs = resolve14(process.cwd(), aPath);
|
|
39617
|
+
const bAbs = resolve14(process.cwd(), bPath);
|
|
39324
39618
|
await writeCommandReplayArtifact({
|
|
39325
39619
|
outPath: flags["artifact-out"],
|
|
39326
39620
|
command: "compare",
|
|
@@ -39358,7 +39652,7 @@ init_outputMeta();
|
|
|
39358
39652
|
init_errors3();
|
|
39359
39653
|
init_bun();
|
|
39360
39654
|
init_selfCli();
|
|
39361
|
-
import { basename as basename4, dirname as dirname9, relative as relative3, resolve as
|
|
39655
|
+
import { basename as basename4, dirname as dirname9, relative as relative3, resolve as resolve15 } from "path";
|
|
39362
39656
|
var START_MARKER = "# >>> idk completions >>>";
|
|
39363
39657
|
var END_MARKER = "# <<< idk completions <<<";
|
|
39364
39658
|
function userHome() {
|
|
@@ -39384,13 +39678,13 @@ function defaultRcPath(shell) {
|
|
|
39384
39678
|
const home = userHome();
|
|
39385
39679
|
switch (shell) {
|
|
39386
39680
|
case "zsh":
|
|
39387
|
-
return
|
|
39681
|
+
return resolve15(home, ".zshrc");
|
|
39388
39682
|
case "bash":
|
|
39389
|
-
return
|
|
39683
|
+
return resolve15(home, ".bashrc");
|
|
39390
39684
|
case "fish":
|
|
39391
|
-
return
|
|
39685
|
+
return resolve15(home, ".config/fish/config.fish");
|
|
39392
39686
|
case "powershell":
|
|
39393
|
-
return
|
|
39687
|
+
return resolve15(home, "Documents/PowerShell/Microsoft.PowerShell_profile.ps1");
|
|
39394
39688
|
}
|
|
39395
39689
|
}
|
|
39396
39690
|
function completionsSourceLine(shell, executable = "idk") {
|
|
@@ -39423,7 +39717,7 @@ function replaceManagedBlock(existing, block) {
|
|
|
39423
39717
|
}
|
|
39424
39718
|
async function installCompletions(args) {
|
|
39425
39719
|
const shell = detectShell(args.shell);
|
|
39426
|
-
const rcPath =
|
|
39720
|
+
const rcPath = resolve15(args.rcPath ?? defaultRcPath(shell));
|
|
39427
39721
|
const line = completionsSourceLine(shell, args.executable ?? "idk");
|
|
39428
39722
|
const script = runSelfCli(["completions", shell]);
|
|
39429
39723
|
if (script.exitCode !== 0) {
|
|
@@ -39467,7 +39761,7 @@ ${END_MARKER}
|
|
|
39467
39761
|
}
|
|
39468
39762
|
async function readCompletionSetup(args) {
|
|
39469
39763
|
const shell = detectShell(args.shell);
|
|
39470
|
-
const rcPath =
|
|
39764
|
+
const rcPath = resolve15(args.rcPath ?? defaultRcPath(shell));
|
|
39471
39765
|
const line = completionsSourceLine(shell, args.executable ?? "idk");
|
|
39472
39766
|
const exists = await fileExists(rcPath);
|
|
39473
39767
|
if (!exists) {
|
|
@@ -39478,7 +39772,7 @@ async function readCompletionSetup(args) {
|
|
|
39478
39772
|
return { shell, rcPath, installed, line };
|
|
39479
39773
|
}
|
|
39480
39774
|
function commonDir(paths) {
|
|
39481
|
-
const parts = paths.map((value) => dirname9(
|
|
39775
|
+
const parts = paths.map((value) => dirname9(resolve15(value)).split("/").filter(Boolean));
|
|
39482
39776
|
if (parts.length === 0)
|
|
39483
39777
|
return process.cwd();
|
|
39484
39778
|
const first = parts[0] ?? [];
|
|
@@ -39499,13 +39793,13 @@ async function writePluginTrust(args) {
|
|
|
39499
39793
|
if (args.plugins.length === 0) {
|
|
39500
39794
|
throw usageError("setup plugin-trust requires at least one --plugin path.");
|
|
39501
39795
|
}
|
|
39502
|
-
const outPath =
|
|
39796
|
+
const outPath = resolve15(args.outPath);
|
|
39503
39797
|
if (await fileExists(outPath) && !args.force) {
|
|
39504
39798
|
throw usageError(`Output file already exists: ${outPath}`, "Pass --force true to overwrite.");
|
|
39505
39799
|
}
|
|
39506
39800
|
const entries = {};
|
|
39507
39801
|
for (const plugin of args.plugins) {
|
|
39508
|
-
const path =
|
|
39802
|
+
const path = resolve15(plugin);
|
|
39509
39803
|
const digest = sha256Hex(await readTextFile(path));
|
|
39510
39804
|
const key = args.relative === false ? path : relative3(dirname9(outPath), path);
|
|
39511
39805
|
entries[key] = digest;
|
|
@@ -39517,18 +39811,18 @@ async function writePluginTrust(args) {
|
|
|
39517
39811
|
`);
|
|
39518
39812
|
return {
|
|
39519
39813
|
outPath,
|
|
39520
|
-
plugins: args.plugins.map((plugin) =>
|
|
39814
|
+
plugins: args.plugins.map((plugin) => resolve15(plugin)),
|
|
39521
39815
|
pluginCount: args.plugins.length,
|
|
39522
39816
|
pluginEntries: entries,
|
|
39523
|
-
pluginRootSuggestion: commonDir(args.plugins.map((plugin) =>
|
|
39817
|
+
pluginRootSuggestion: commonDir(args.plugins.map((plugin) => resolve15(plugin))),
|
|
39524
39818
|
updated: true
|
|
39525
39819
|
};
|
|
39526
39820
|
}
|
|
39527
39821
|
function parsePluginList(input) {
|
|
39528
|
-
return input.split(",").map((value) => value.trim()).filter(Boolean).map((value) =>
|
|
39822
|
+
return input.split(",").map((value) => value.trim()).filter(Boolean).map((value) => resolve15(value));
|
|
39529
39823
|
}
|
|
39530
39824
|
function suggestedTrustPath(baseDir = process.cwd()) {
|
|
39531
|
-
return
|
|
39825
|
+
return resolve15(baseDir, ".idk/plugin-trust.json");
|
|
39532
39826
|
}
|
|
39533
39827
|
function describePluginTrustFlags(result) {
|
|
39534
39828
|
return [
|
|
@@ -39540,7 +39834,7 @@ function describePluginTrustFlags(result) {
|
|
|
39540
39834
|
}
|
|
39541
39835
|
function inferredTuneWizardPath(scenarioPath) {
|
|
39542
39836
|
const file = basename4(scenarioPath).replace(/\.json$/i, "");
|
|
39543
|
-
return
|
|
39837
|
+
return resolve15(process.cwd(), `${file}-tune.json`);
|
|
39544
39838
|
}
|
|
39545
39839
|
|
|
39546
39840
|
// src/commands/doctor.ts
|
|
@@ -39912,7 +40206,7 @@ var eta_default = defineCommand({
|
|
|
39912
40206
|
// src/commands/experiment.ts
|
|
39913
40207
|
init_dist2();
|
|
39914
40208
|
init_zod();
|
|
39915
|
-
import { resolve as
|
|
40209
|
+
import { resolve as resolve16 } from "path";
|
|
39916
40210
|
init_errors3();
|
|
39917
40211
|
init_outputMeta();
|
|
39918
40212
|
init_bun();
|
|
@@ -39942,7 +40236,7 @@ var experiment_default = defineCommand({
|
|
|
39942
40236
|
const spec = checked.data;
|
|
39943
40237
|
if (spec.scenario.ref.length === 0)
|
|
39944
40238
|
throw usageError("Usage: idk experiment <spec>");
|
|
39945
|
-
const scenarioPath =
|
|
40239
|
+
const scenarioPath = resolve16(spec.scenario.ref);
|
|
39946
40240
|
const scenario = await readScenarioFile(scenarioPath);
|
|
39947
40241
|
const loaded = await loadRegistriesFromFlags(flags);
|
|
39948
40242
|
const valid = validateScenarioV1(scenario, loaded.modelRegistry);
|
|
@@ -39991,12 +40285,12 @@ var experiment_default = defineCommand({
|
|
|
39991
40285
|
// src/commands/evaluate.ts
|
|
39992
40286
|
init_dist2();
|
|
39993
40287
|
init_zod();
|
|
39994
|
-
import { resolve as
|
|
40288
|
+
import { resolve as resolve18 } from "path";
|
|
39995
40289
|
|
|
39996
40290
|
// src/commands/ltv.ts
|
|
39997
40291
|
init_dist2();
|
|
39998
40292
|
init_zod();
|
|
39999
|
-
import { resolve as
|
|
40293
|
+
import { resolve as resolve17 } from "path";
|
|
40000
40294
|
init_errors3();
|
|
40001
40295
|
init_outputMeta();
|
|
40002
40296
|
var strategySchema2 = string2().min(1).optional();
|
|
@@ -40365,7 +40659,7 @@ var ltv_default = defineCommand({
|
|
|
40365
40659
|
};
|
|
40366
40660
|
const output = flags.format === "json" ? jsonOutput : analysis.horizons;
|
|
40367
40661
|
if (flags["artifact-out"]) {
|
|
40368
|
-
const scenarioAbs =
|
|
40662
|
+
const scenarioAbs = resolve17(process.cwd(), scenarioPath);
|
|
40369
40663
|
await writeCommandReplayArtifact({
|
|
40370
40664
|
outPath: flags["artifact-out"],
|
|
40371
40665
|
command: "ltv",
|
|
@@ -40444,7 +40738,7 @@ async function loadBalanceChecks(flags) {
|
|
|
40444
40738
|
if (!failOn) {
|
|
40445
40739
|
throw usageError("--fail-on must list error, warn, inconclusive, or unsupported");
|
|
40446
40740
|
}
|
|
40447
|
-
const path =
|
|
40741
|
+
const path = resolve18(process.cwd(), flags.checks);
|
|
40448
40742
|
let raw;
|
|
40449
40743
|
try {
|
|
40450
40744
|
raw = await readJsonFile(path);
|
|
@@ -40525,7 +40819,7 @@ var evaluate_default = defineCommand({
|
|
|
40525
40819
|
throw scenarioInvalidError(valid.issues);
|
|
40526
40820
|
}
|
|
40527
40821
|
const scenario = valid.scenario;
|
|
40528
|
-
const scenarioAbs =
|
|
40822
|
+
const scenarioAbs = resolve18(process.cwd(), scenarioPath);
|
|
40529
40823
|
const unseeded = prepareResolvedRun({
|
|
40530
40824
|
scenario: valid.scenario,
|
|
40531
40825
|
modelRegistry: loaded.modelRegistry,
|
|
@@ -40702,9 +40996,9 @@ var evaluate_default = defineCommand({
|
|
|
40702
40996
|
stageScope
|
|
40703
40997
|
});
|
|
40704
40998
|
const files = {
|
|
40705
|
-
simulate:
|
|
40706
|
-
experience:
|
|
40707
|
-
ltv:
|
|
40999
|
+
simulate: resolve18(process.cwd(), flags["out-dir"] ?? ".", "simulate.json"),
|
|
41000
|
+
experience: resolve18(process.cwd(), flags["out-dir"] ?? ".", "experience.json"),
|
|
41001
|
+
ltv: resolve18(process.cwd(), flags["out-dir"] ?? ".", "ltv.json")
|
|
40708
41002
|
};
|
|
40709
41003
|
const output = {
|
|
40710
41004
|
ok: balance?.exitCode !== 2,
|
|
@@ -40735,12 +41029,12 @@ var evaluate_default = defineCommand({
|
|
|
40735
41029
|
...balance ? { balanceChecks: balance.exitCode === 2 ? toBalanceCheckError(balance) : balance } : {}
|
|
40736
41030
|
};
|
|
40737
41031
|
if (flags["out-dir"]) {
|
|
40738
|
-
const outDir =
|
|
41032
|
+
const outDir = resolve18(process.cwd(), flags["out-dir"]);
|
|
40739
41033
|
await ensureDir(outDir);
|
|
40740
|
-
files.simulate =
|
|
40741
|
-
files.experience =
|
|
40742
|
-
files.ltv =
|
|
40743
|
-
const summaryPath =
|
|
41034
|
+
files.simulate = resolve18(outDir, "simulate.json");
|
|
41035
|
+
files.experience = resolve18(outDir, "experience.json");
|
|
41036
|
+
files.ltv = resolve18(outDir, "ltv.json");
|
|
41037
|
+
const summaryPath = resolve18(process.cwd(), flags["out-dir"], flags.format === "md" ? "summary.md" : "summary.json");
|
|
40744
41038
|
files.summary = summaryPath;
|
|
40745
41039
|
await writeTextFile(files.simulate, `${JSON.stringify({ ...simulate, _meta: simulateMeta }, null, 2)}
|
|
40746
41040
|
`);
|
|
@@ -41170,7 +41464,7 @@ var report_default = defineCommand({
|
|
|
41170
41464
|
// src/commands/simulate.ts
|
|
41171
41465
|
init_dist2();
|
|
41172
41466
|
init_zod();
|
|
41173
|
-
import { resolve as
|
|
41467
|
+
import { resolve as resolve19 } from "path";
|
|
41174
41468
|
|
|
41175
41469
|
// src/commands/_shared/simulateView.ts
|
|
41176
41470
|
function resolveEventLog(args) {
|
|
@@ -41324,11 +41618,11 @@ var simulate_default = defineCommand({
|
|
|
41324
41618
|
const input = await readScenarioFile(scenarioPath);
|
|
41325
41619
|
const loaded = await loadRegistriesFromFlags(flags);
|
|
41326
41620
|
const scenario = assertValidScenario2(validateScenarioV1(input, loaded.modelRegistry));
|
|
41327
|
-
const resumedJson = flags.resume ? await readJsonFile(
|
|
41621
|
+
const resumedJson = flags.resume ? await readJsonFile(resolve19(process.cwd(), flags.resume)).then((raw) => {
|
|
41328
41622
|
try {
|
|
41329
41623
|
return parseSimStateJSON(raw);
|
|
41330
41624
|
} catch (error) {
|
|
41331
|
-
throw cliError("SIM_STATE_INVALID_JSON", `Invalid resume state json: ${
|
|
41625
|
+
throw cliError("SIM_STATE_INVALID_JSON", `Invalid resume state json: ${resolve19(process.cwd(), flags.resume)}`, {
|
|
41332
41626
|
detail: errorDetail(error),
|
|
41333
41627
|
cause: error
|
|
41334
41628
|
});
|
|
@@ -41336,7 +41630,7 @@ var simulate_default = defineCommand({
|
|
|
41336
41630
|
}).catch((error) => {
|
|
41337
41631
|
if (error instanceof Error && error.name === "CliError")
|
|
41338
41632
|
throw error;
|
|
41339
|
-
throw cliError("SIM_STATE_INVALID_JSON", `Invalid resume state json: ${
|
|
41633
|
+
throw cliError("SIM_STATE_INVALID_JSON", `Invalid resume state json: ${resolve19(process.cwd(), flags.resume)}`, {
|
|
41340
41634
|
detail: errorDetail(error),
|
|
41341
41635
|
cause: error
|
|
41342
41636
|
});
|
|
@@ -41453,7 +41747,7 @@ var simulate_default = defineCommand({
|
|
|
41453
41747
|
const run = runScenario(effectiveScenario);
|
|
41454
41748
|
const netWorth = effectiveScenario.model.netWorth?.(effectiveScenario.ctx, run.end) ?? run.end.wallet.money;
|
|
41455
41749
|
const totalElapsedSec = priorElapsedSec + (offlineRun ? runElapsedSec(offlineRun) : 0) + runElapsedSec(run);
|
|
41456
|
-
const stateOutPath = flags["state-out"] ?
|
|
41750
|
+
const stateOutPath = flags["state-out"] ? resolve19(process.cwd(), flags["state-out"]) : undefined;
|
|
41457
41751
|
const seed = deterministicSeed;
|
|
41458
41752
|
const offlineEndWorth = offlineRun && (runScenarioInput.model.netWorth?.(runScenarioInput.ctx, offlineRun.end) ?? offlineRun.end.wallet.money);
|
|
41459
41753
|
if (stateOutPath) {
|
|
@@ -41517,7 +41811,7 @@ var simulate_default = defineCommand({
|
|
|
41517
41811
|
eventLog: run.eventLog
|
|
41518
41812
|
};
|
|
41519
41813
|
if (flags["artifact-out"]) {
|
|
41520
|
-
const scenarioAbs =
|
|
41814
|
+
const scenarioAbs = resolve19(process.cwd(), scenarioPath);
|
|
41521
41815
|
await writeCommandReplayArtifact({
|
|
41522
41816
|
outPath: flags["artifact-out"],
|
|
41523
41817
|
command: "simulate",
|
|
@@ -41552,7 +41846,7 @@ var simulate_default = defineCommand({
|
|
|
41552
41846
|
// src/commands/tune.ts
|
|
41553
41847
|
init_dist2();
|
|
41554
41848
|
init_zod();
|
|
41555
|
-
import { resolve as
|
|
41849
|
+
import { resolve as resolve21 } from "path";
|
|
41556
41850
|
init_errors3();
|
|
41557
41851
|
init_outputMeta();
|
|
41558
41852
|
|
|
@@ -41560,7 +41854,7 @@ init_outputMeta();
|
|
|
41560
41854
|
init_dist2();
|
|
41561
41855
|
init_errors3();
|
|
41562
41856
|
init_bun();
|
|
41563
|
-
import { basename as basename5, resolve as
|
|
41857
|
+
import { basename as basename5, resolve as resolve20 } from "path";
|
|
41564
41858
|
var OBJECTIVE_OPTIONS = [
|
|
41565
41859
|
{
|
|
41566
41860
|
value: "endNetWorthLog10",
|
|
@@ -41634,7 +41928,7 @@ async function runTuneWizard(args) {
|
|
|
41634
41928
|
}
|
|
41635
41929
|
const scenario = valid.scenario;
|
|
41636
41930
|
const strategyId = String(scenario.strategy?.id ?? "greedy");
|
|
41637
|
-
const suggestedOut =
|
|
41931
|
+
const suggestedOut = resolve20(args.outPath?.trim() || inferredTuneWizardPath(args.scenarioPath));
|
|
41638
41932
|
args.prompt.intro(`Tune wizard for ${basename5(args.scenarioPath)}`);
|
|
41639
41933
|
const objectiveId = await args.prompt.select("Objective", {
|
|
41640
41934
|
options: OBJECTIVE_OPTIONS.map((entry) => ({ ...entry })),
|
|
@@ -41651,7 +41945,7 @@ async function runTuneWizard(args) {
|
|
|
41651
41945
|
const tunePathRaw = await args.prompt.text("Tune spec output path", {
|
|
41652
41946
|
default: suggestedOut
|
|
41653
41947
|
});
|
|
41654
|
-
const tunePath =
|
|
41948
|
+
const tunePath = resolve20(String(tunePathRaw));
|
|
41655
41949
|
if (await fileExists(tunePath) && !args.force) {
|
|
41656
41950
|
throw usageError(`Output file already exists: ${tunePath}`, "Pass --force true to overwrite the generated tune spec.");
|
|
41657
41951
|
}
|
|
@@ -41868,7 +42162,7 @@ var tune_default = defineCommand({
|
|
|
41868
42162
|
throw usageError("Usage: idk tune <scenario> --tune <tunespec>");
|
|
41869
42163
|
}
|
|
41870
42164
|
const tuneSpecInput = wizardResult?.tuneSpec ?? await readScenarioFile(tunePath);
|
|
41871
|
-
const baselinePath = flags["baseline-artifact"] ?
|
|
42165
|
+
const baselinePath = flags["baseline-artifact"] ? resolve21(flags["baseline-artifact"]) : undefined;
|
|
41872
42166
|
const baselineRaw = baselinePath ? await readJsonFile(baselinePath) : undefined;
|
|
41873
42167
|
const seed = flags.seed ?? deriveDeterministicSeed({
|
|
41874
42168
|
command: "tune",
|
|
@@ -41932,8 +42226,8 @@ var tune_default = defineCommand({
|
|
|
41932
42226
|
};
|
|
41933
42227
|
})();
|
|
41934
42228
|
if (flags["artifact-out"]) {
|
|
41935
|
-
const scenarioAbs =
|
|
41936
|
-
const tuneAbs =
|
|
42229
|
+
const scenarioAbs = resolve21(process.cwd(), scenarioPath);
|
|
42230
|
+
const tuneAbs = resolve21(process.cwd(), tunePath);
|
|
41937
42231
|
await writeCommandReplayArtifact({
|
|
41938
42232
|
outPath: flags["artifact-out"],
|
|
41939
42233
|
command: "tune",
|
|
@@ -41999,13 +42293,13 @@ var validate_default = defineCommand({
|
|
|
41999
42293
|
// src/commands/initScenario.ts
|
|
42000
42294
|
init_zod();
|
|
42001
42295
|
init_errors3();
|
|
42002
|
-
import { resolve as
|
|
42296
|
+
import { resolve as resolve23 } from "path";
|
|
42003
42297
|
|
|
42004
42298
|
// src/lib/initWizard.ts
|
|
42005
42299
|
init_errors3();
|
|
42006
42300
|
|
|
42007
42301
|
// src/templates/scenario.ts
|
|
42008
|
-
import { resolve as
|
|
42302
|
+
import { resolve as resolve22, parse as parse5 } from "path";
|
|
42009
42303
|
var TEMPLATE_TRACKS = ["intro", "design", "personal"];
|
|
42010
42304
|
var TEMPLATE_PRESETS = ["session", "builder", "longrun"];
|
|
42011
42305
|
var INTRO_PRESET_DEFAULT = {
|
|
@@ -42873,14 +43167,14 @@ function buildInitTemplatePlan(args) {
|
|
|
42873
43167
|
const tuneSpec = createPersonalTuneSpec(preset, baseScenario);
|
|
42874
43168
|
applyPersonalBundleIdentity({ base: baseScenario, compare: compareScenario, tune: tuneSpec }, { stem, displayName });
|
|
42875
43169
|
return [
|
|
42876
|
-
{ kind: "scenario", path:
|
|
42877
|
-
{ kind: "compare", path:
|
|
42878
|
-
{ kind: "tune", path:
|
|
43170
|
+
{ kind: "scenario", path: resolve22(dir, `${stem}${ext}`), content: baseScenario },
|
|
43171
|
+
{ kind: "compare", path: resolve22(dir, `${stem}-compare-b${ext}`), content: compareScenario },
|
|
43172
|
+
{ kind: "tune", path: resolve22(dir, `${stem}-tune${ext}`), content: tuneSpec }
|
|
42879
43173
|
];
|
|
42880
43174
|
}
|
|
42881
43175
|
const content = track === "intro" ? clone2(preset === "session" ? introSession : preset === "builder" ? introBuilder : introLongrun) : createDesignScenario(preset);
|
|
42882
43176
|
applyScenarioOverrides(content, args.overrides);
|
|
42883
|
-
return [{ kind: "scenario", path:
|
|
43177
|
+
return [{ kind: "scenario", path: resolve22(args.outPath), content }];
|
|
42884
43178
|
}
|
|
42885
43179
|
function serializeTemplate(template) {
|
|
42886
43180
|
return `${JSON.stringify(template, null, 2)}
|
|
@@ -43137,7 +43431,7 @@ var initScenario_default = defineCommand({
|
|
|
43137
43431
|
})
|
|
43138
43432
|
},
|
|
43139
43433
|
async handler({ flags, prompt, terminal, runtime }) {
|
|
43140
|
-
const outPath =
|
|
43434
|
+
const outPath = resolve23(process.cwd(), flags.out);
|
|
43141
43435
|
const wizard = flags.wizard;
|
|
43142
43436
|
const wizardResult = wizard ? await runInitWizard({
|
|
43143
43437
|
prompt,
|
|
@@ -43207,11 +43501,11 @@ var init_default = defineGroup({
|
|
|
43207
43501
|
init_dist2();
|
|
43208
43502
|
init_zod();
|
|
43209
43503
|
init_outputMeta();
|
|
43210
|
-
import { resolve as
|
|
43504
|
+
import { resolve as resolve24 } from "path";
|
|
43211
43505
|
init_bun();
|
|
43212
43506
|
async function runKpiRegression(flags) {
|
|
43213
|
-
const baselinePath =
|
|
43214
|
-
const currentPath =
|
|
43507
|
+
const baselinePath = resolve24(flags.baseline);
|
|
43508
|
+
const currentPath = resolve24(flags.current);
|
|
43215
43509
|
const baseline = await readJsonFile(baselinePath);
|
|
43216
43510
|
const current = await readJsonFile(currentPath);
|
|
43217
43511
|
const output = adaptKpiRegression({
|
|
@@ -43229,6 +43523,7 @@ async function runKpiRegression(flags) {
|
|
|
43229
43523
|
await writeOutput({
|
|
43230
43524
|
format: flags.format,
|
|
43231
43525
|
outPath: flags.out,
|
|
43526
|
+
protectedInputs: [baselinePath, currentPath],
|
|
43232
43527
|
data: output,
|
|
43233
43528
|
meta: buildOutputMeta({ command: "kpi.regress" })
|
|
43234
43529
|
});
|
|
@@ -43282,7 +43577,7 @@ init_zod();
|
|
|
43282
43577
|
// src/commands/_shared/listOutput.ts
|
|
43283
43578
|
init_outputMeta();
|
|
43284
43579
|
init_bun();
|
|
43285
|
-
import { resolve as
|
|
43580
|
+
import { resolve as resolve25 } from "path";
|
|
43286
43581
|
async function writeListOutput(args) {
|
|
43287
43582
|
const outputForRender = args.format === "json" ? {
|
|
43288
43583
|
...args.payload,
|
|
@@ -43293,7 +43588,7 @@ async function writeListOutput(args) {
|
|
|
43293
43588
|
process.stdout.write(body);
|
|
43294
43589
|
return;
|
|
43295
43590
|
}
|
|
43296
|
-
const path =
|
|
43591
|
+
const path = resolve25(args.out);
|
|
43297
43592
|
await writeTextFile(path, body);
|
|
43298
43593
|
console.log(`Wrote ${args.format} output to ${path}`);
|
|
43299
43594
|
}
|
|
@@ -43442,7 +43737,7 @@ var objectives_default2 = defineGroup({
|
|
|
43442
43737
|
init_zod();
|
|
43443
43738
|
init_errors3();
|
|
43444
43739
|
init_outputMeta();
|
|
43445
|
-
import { resolve as
|
|
43740
|
+
import { resolve as resolve26 } from "path";
|
|
43446
43741
|
init_bun();
|
|
43447
43742
|
init_selfCli();
|
|
43448
43743
|
function deepEqual(a, b) {
|
|
@@ -43509,7 +43804,7 @@ var replayVerify_default = defineCommand({
|
|
|
43509
43804
|
if (!artifactPath) {
|
|
43510
43805
|
throw usageError("Usage: idk replay verify <artifact.json>");
|
|
43511
43806
|
}
|
|
43512
|
-
const artifactAbs =
|
|
43807
|
+
const artifactAbs = resolve26(process.cwd(), artifactPath);
|
|
43513
43808
|
const raw = await readJsonFile(artifactAbs).catch((error) => {
|
|
43514
43809
|
if (error instanceof Error && error.name === "CliError")
|
|
43515
43810
|
throw error;
|
|
@@ -43810,4 +44105,4 @@ export {
|
|
|
43810
44105
|
cli2 as cli
|
|
43811
44106
|
};
|
|
43812
44107
|
|
|
43813
|
-
//# debugId=
|
|
44108
|
+
//# debugId=4AAADDD1336AF66164756E2164756E21
|