@riddledc/riddle-proof 0.7.212 → 0.7.213

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -202,6 +202,12 @@ generic inline-script warning threshold. Use `--strict=true` when you
202
202
  deliberately want Riddle's non-critical script-safety warnings to block the run.
203
203
  Critical script-safety violations remain blocked by Riddle either way.
204
204
 
205
+ Hosted `run-profile` checks `/v1/balance` before submitting Riddle jobs. For a
206
+ split viewport matrix, the preflight estimates the 30-second hosted minimum per
207
+ viewport and returns an `environment_blocked` result without starting partial
208
+ jobs when the account balance cannot cover the intended sweep. Use
209
+ `--balance-preflight=false` to bypass this check.
210
+
205
211
  Use `--viewport-name <name>` to run only one named viewport from a
206
212
  multi-viewport profile while preserving viewport-scoped setup actions and
207
213
  checks:
@@ -23,16 +23,23 @@ function normalizeBaseUrl(value) {
23
23
  function fetchFor(config = {}) {
24
24
  return config.fetchImpl || fetch;
25
25
  }
26
- function resolveRiddleApiKey(config = {}) {
27
- if (config.apiKey?.trim()) return config.apiKey.trim();
28
- if (process.env.RIDDLE_API_KEY?.trim()) return process.env.RIDDLE_API_KEY.trim();
26
+ function resolveRiddleApiKeyWithSource(config = {}) {
27
+ if (config.apiKey?.trim()) return { apiKey: config.apiKey.trim(), source: "option" };
28
+ if (process.env.RIDDLE_API_KEY?.trim()) return { apiKey: process.env.RIDDLE_API_KEY.trim(), source: "env" };
29
29
  const keyFile = config.apiKeyFile || process.env.RIDDLE_API_KEY_FILE || DEFAULT_RIDDLE_API_KEY_FILE;
30
30
  if (existsSync(keyFile)) {
31
31
  const key = readFileSync(keyFile, "utf8").trim();
32
- if (key) return key;
32
+ if (key) return { apiKey: key, source: "file", file: keyFile };
33
33
  }
34
34
  throw new Error(`Riddle API key missing. Set RIDDLE_API_KEY or write ${DEFAULT_RIDDLE_API_KEY_FILE}.`);
35
35
  }
36
+ function resolveRiddleApiKeySource(config = {}) {
37
+ const { apiKey: _apiKey, ...source } = resolveRiddleApiKeyWithSource(config);
38
+ return source;
39
+ }
40
+ function resolveRiddleApiKey(config = {}) {
41
+ return resolveRiddleApiKeyWithSource(config).apiKey;
42
+ }
36
43
  async function riddleRequestJson(config, pathname, init = {}) {
37
44
  const response = await fetchFor(config)(`${normalizeBaseUrl(config.apiBaseUrl)}${pathname}`, {
38
45
  ...init,
@@ -51,6 +58,9 @@ async function riddleRequestJson(config, pathname, init = {}) {
51
58
  if (!response.ok) throw new RiddleApiError(pathname, response.status, text);
52
59
  return json ?? text;
53
60
  }
61
+ async function getRiddleBalance(config = {}) {
62
+ return riddleRequestJson(config, "/v1/balance", { method: "GET" });
63
+ }
54
64
  function previewDeployResultFromRecord(input) {
55
65
  const { record, id, label, framework, expiresAt, publishRecovered, publishError } = input;
56
66
  return {
@@ -385,7 +395,9 @@ async function pollRiddleJob(config, jobId, options = {}) {
385
395
  }
386
396
  function createRiddleApiClient(config = {}) {
387
397
  return {
398
+ apiKeySource: () => resolveRiddleApiKeySource(config),
388
399
  requestJson: (pathname, init) => riddleRequestJson(config, pathname, init),
400
+ getBalance: () => getRiddleBalance(config),
389
401
  deployPreview: (directory, label, framework = "static") => deployRiddlePreview(config, directory, label, framework),
390
402
  deployStaticPreview: (directory, label) => deployRiddleStaticPreview(config, directory, label),
391
403
  runScript: (input) => runRiddleScript(config, input),
@@ -398,8 +410,10 @@ export {
398
410
  DEFAULT_RIDDLE_API_BASE_URL,
399
411
  DEFAULT_RIDDLE_API_KEY_FILE,
400
412
  RiddleApiError,
413
+ resolveRiddleApiKeySource,
401
414
  resolveRiddleApiKey,
402
415
  riddleRequestJson,
416
+ getRiddleBalance,
403
417
  deployRiddlePreview,
404
418
  deployRiddleStaticPreview,
405
419
  parseRiddleViewport,
@@ -3561,7 +3561,7 @@ function createRiddleProofProfileConfigurationError(name, error, runner = "riddl
3561
3561
  }
3562
3562
  function createRiddleProofProfileEnvironmentBlockedResult(input) {
3563
3563
  const message = input.error instanceof Error ? input.error.message : input.error ? String(input.error) : "Riddle runner did not complete successfully.";
3564
- const environmentBlocker = extractRiddleRunnerBlocker(message);
3564
+ const environmentBlocker = input.environmentBlocker || extractRiddleRunnerBlocker(message);
3565
3565
  const warnings = collectRiddleProofProfileEnvironmentBlockedWarnings(input.profile, environmentBlocker);
3566
3566
  return {
3567
3567
  version: RIDDLE_PROOF_PROFILE_RESULT_VERSION,
package/dist/cli.cjs CHANGED
@@ -6584,16 +6584,23 @@ function normalizeBaseUrl(value) {
6584
6584
  function fetchFor(config = {}) {
6585
6585
  return config.fetchImpl || fetch;
6586
6586
  }
6587
- function resolveRiddleApiKey(config = {}) {
6588
- if (config.apiKey?.trim()) return config.apiKey.trim();
6589
- if (process.env.RIDDLE_API_KEY?.trim()) return process.env.RIDDLE_API_KEY.trim();
6587
+ function resolveRiddleApiKeyWithSource(config = {}) {
6588
+ if (config.apiKey?.trim()) return { apiKey: config.apiKey.trim(), source: "option" };
6589
+ if (process.env.RIDDLE_API_KEY?.trim()) return { apiKey: process.env.RIDDLE_API_KEY.trim(), source: "env" };
6590
6590
  const keyFile = config.apiKeyFile || process.env.RIDDLE_API_KEY_FILE || DEFAULT_RIDDLE_API_KEY_FILE;
6591
6591
  if ((0, import_node_fs5.existsSync)(keyFile)) {
6592
6592
  const key = (0, import_node_fs5.readFileSync)(keyFile, "utf8").trim();
6593
- if (key) return key;
6593
+ if (key) return { apiKey: key, source: "file", file: keyFile };
6594
6594
  }
6595
6595
  throw new Error(`Riddle API key missing. Set RIDDLE_API_KEY or write ${DEFAULT_RIDDLE_API_KEY_FILE}.`);
6596
6596
  }
6597
+ function resolveRiddleApiKeySource(config = {}) {
6598
+ const { apiKey: _apiKey, ...source } = resolveRiddleApiKeyWithSource(config);
6599
+ return source;
6600
+ }
6601
+ function resolveRiddleApiKey(config = {}) {
6602
+ return resolveRiddleApiKeyWithSource(config).apiKey;
6603
+ }
6597
6604
  async function riddleRequestJson(config, pathname, init = {}) {
6598
6605
  const response = await fetchFor(config)(`${normalizeBaseUrl(config.apiBaseUrl)}${pathname}`, {
6599
6606
  ...init,
@@ -6612,6 +6619,9 @@ async function riddleRequestJson(config, pathname, init = {}) {
6612
6619
  if (!response.ok) throw new RiddleApiError(pathname, response.status, text);
6613
6620
  return json ?? text;
6614
6621
  }
6622
+ async function getRiddleBalance(config = {}) {
6623
+ return riddleRequestJson(config, "/v1/balance", { method: "GET" });
6624
+ }
6615
6625
  function previewDeployResultFromRecord(input) {
6616
6626
  const { record, id, label, framework, expiresAt, publishRecovered, publishError } = input;
6617
6627
  return {
@@ -6946,7 +6956,9 @@ async function pollRiddleJob(config, jobId, options = {}) {
6946
6956
  }
6947
6957
  function createRiddleApiClient(config = {}) {
6948
6958
  return {
6959
+ apiKeySource: () => resolveRiddleApiKeySource(config),
6949
6960
  requestJson: (pathname, init) => riddleRequestJson(config, pathname, init),
6961
+ getBalance: () => getRiddleBalance(config),
6950
6962
  deployPreview: (directory, label, framework = "static") => deployRiddlePreview(config, directory, label, framework),
6951
6963
  deployStaticPreview: (directory, label) => deployRiddleStaticPreview(config, directory, label),
6952
6964
  runScript: (input) => runRiddleScript(config, input),
@@ -10502,7 +10514,7 @@ function profileStatusExitCode(profile, status) {
10502
10514
  }
10503
10515
  function createRiddleProofProfileEnvironmentBlockedResult(input) {
10504
10516
  const message = input.error instanceof Error ? input.error.message : input.error ? String(input.error) : "Riddle runner did not complete successfully.";
10505
- const environmentBlocker = extractRiddleRunnerBlocker(message);
10517
+ const environmentBlocker = input.environmentBlocker || extractRiddleRunnerBlocker(message);
10506
10518
  const warnings = collectRiddleProofProfileEnvironmentBlockedWarnings(input.profile, environmentBlocker);
10507
10519
  return {
10508
10520
  version: RIDDLE_PROOF_PROFILE_RESULT_VERSION,
@@ -16215,6 +16227,7 @@ function extractRiddleProofProfileResult(input) {
16215
16227
  }
16216
16228
 
16217
16229
  // src/cli.ts
16230
+ var RIDDLE_PROFILE_BALANCE_PREFLIGHT_MIN_SECONDS_PER_JOB = 30;
16218
16231
  function usage() {
16219
16232
  return [
16220
16233
  "Usage:",
@@ -16223,7 +16236,7 @@ function usage() {
16223
16236
  " riddle-proof-loop respond --state-path <path> --response-json <file|json|->",
16224
16237
  " riddle-proof-loop respond --state-path <path> --decision <decision> --summary <text> [--payload-json <file|json|->]",
16225
16238
  " riddle-proof-loop status --state-path <path>",
16226
- " riddle-proof-loop run-profile --profile <file|json|-> --url <base-url> [--runner riddle] [--viewport-name <name[,name...]>] [--strict true|false; default false] [--split-viewports true|false; default false] [--poll-attempts n] [--output <dir>|--output-dir <dir>] [--result-format json|compact-json|summary|none; default json] [--quiet]",
16239
+ " riddle-proof-loop run-profile --profile <file|json|-> --url <base-url> [--runner riddle] [--viewport-name <name[,name...]>] [--strict true|false; default false] [--split-viewports true|false; default false] [--balance-preflight true|false; default true] [--poll-attempts n] [--output <dir>|--output-dir <dir>] [--result-format json|compact-json|summary|none; default json] [--quiet]",
16227
16240
  " riddle-proof-loop run-profile aggregate --profile <file|json|-> --url <base-url> --input-dir <dir>|--inputs <path[,path...]> [--output <dir>|--output-dir <dir>] [--result-format json|compact-json|summary|none; default json]",
16228
16241
  " riddle-proof-loop run-profile recover --profile <file|json|-> --url <base-url> --job <job-id> [--viewport-name <name[,name...]>] [--output <dir>|--output-dir <dir>] [--result-format json|compact-json|summary|none; default json]",
16229
16242
  " riddle-proof-loop profile-body-assertions --artifact <file|url|-> --candidates-json <file|json|-> [--required-json <file|json|->] [--format json|body-contains]",
@@ -16282,6 +16295,9 @@ function optionBoolean(options, key) {
16282
16295
  function runProfileStrictOption(options) {
16283
16296
  return optionBoolean(options, "strict") ?? false;
16284
16297
  }
16298
+ function runProfileBalancePreflightOption(options) {
16299
+ return optionBoolean(options, "balancePreflight") ?? true;
16300
+ }
16285
16301
  function runProfileSplitViewportsOption(options) {
16286
16302
  return optionBoolean(options, "splitViewports") ?? false;
16287
16303
  }
@@ -18204,15 +18220,25 @@ function profileEnvironmentBlockerMarkdown(result) {
18204
18220
  const availableSeconds = cliFiniteNumber(blocker.available_seconds);
18205
18221
  const deficitSeconds = cliFiniteNumber(blocker.deficit_seconds);
18206
18222
  const minimumPurchaseDollars = cliFiniteNumber(blocker.minimum_purchase_dollars);
18223
+ const balancePreflight = blocker.balance_preflight === true;
18224
+ const jobCount = cliFiniteNumber(blocker.job_count);
18225
+ const secondsPerJob = cliFiniteNumber(blocker.seconds_per_job);
18226
+ const apiKeySource = cliString(blocker.api_key_source);
18227
+ const apiKeyFile = cliString(blocker.api_key_file);
18207
18228
  if (reason) lines.push(`- reason: ${reason}`);
18208
18229
  if (source || endpoint || httpStatus !== void 0) {
18209
18230
  lines.push(`- source: ${source || "runner"}${endpoint ? ` ${endpoint}` : ""}${httpStatus === void 0 ? "" : ` HTTP ${httpStatus}`}`);
18210
18231
  }
18232
+ if (balancePreflight) lines.push("- preflight: balance");
18211
18233
  if (error) lines.push(`- error: ${error}`);
18234
+ if (jobCount !== void 0 || secondsPerJob !== void 0) {
18235
+ lines.push(`- job estimate: ${jobCount ?? "unknown"} job(s), ${secondsPerJob ?? "unknown"}s minimum per job`);
18236
+ }
18212
18237
  if (requiredSeconds !== void 0 || availableSeconds !== void 0 || deficitSeconds !== void 0) {
18213
18238
  lines.push(`- seconds: required ${requiredSeconds ?? "unknown"}, available ${availableSeconds ?? "unknown"}, deficit ${deficitSeconds ?? "unknown"}`);
18214
18239
  }
18215
18240
  if (minimumPurchaseDollars !== void 0) lines.push(`- minimum purchase: $${minimumPurchaseDollars}`);
18241
+ if (apiKeySource) lines.push(`- auth: ${apiKeySource}${apiKeyFile ? ` ${apiKeyFile}` : ""}`);
18216
18242
  return lines;
18217
18243
  }
18218
18244
  function profileCliDiagnosticLine(result) {
@@ -19349,6 +19375,100 @@ function withSplitViewportChildStatusCheck(profile, result, childRuns) {
19349
19375
  const summary = status === "product_regression" ? `${profile.name} failed ${failedChecks} product invariant(s) across ${viewportCount} viewport(s).` : `${profile.name} collected split viewport artifacts but needs human review.`;
19350
19376
  return { ...result, status, checks, summary };
19351
19377
  }
19378
+ function profileRiddleJobCountForCli(profile, options) {
19379
+ return runProfileSplitViewportsOption(options) && profile.target.viewports.length > 1 ? profile.target.viewports.length : 1;
19380
+ }
19381
+ function riddleBalanceAvailableSeconds(balance) {
19382
+ const availableSeconds = cliFiniteNumber(balance.available_seconds);
19383
+ if (availableSeconds !== void 0) return availableSeconds;
19384
+ const totalSeconds = cliFiniteNumber(balance.total_seconds);
19385
+ const reservedSeconds = cliFiniteNumber(balance.reserved_seconds) ?? 0;
19386
+ return totalSeconds === void 0 ? void 0 : totalSeconds - reservedSeconds;
19387
+ }
19388
+ function riddleBalancePreflightMetadata(profile, options) {
19389
+ const split = runProfileSplitViewportsOption(options) && profile.target.viewports.length > 1;
19390
+ if (!split) return { status: "balance_preflight_blocked", terminal: false };
19391
+ return {
19392
+ mode: "split-viewports",
19393
+ job_count: profile.target.viewports.length,
19394
+ status: "balance_preflight_blocked",
19395
+ terminal: false,
19396
+ split_jobs: profile.target.viewports.map((viewport) => ({ viewport: viewport.name }))
19397
+ };
19398
+ }
19399
+ function apiKeySourceBlockerMetadata(client) {
19400
+ try {
19401
+ const source = client.apiKeySource();
19402
+ return {
19403
+ api_key_source: source.source,
19404
+ ...source.file ? { api_key_file: source.file } : {}
19405
+ };
19406
+ } catch {
19407
+ return {};
19408
+ }
19409
+ }
19410
+ function riddleApiErrorBlockerMetadata(error) {
19411
+ const record = error && typeof error === "object" ? error : void 0;
19412
+ const status = cliFiniteNumber(record?.status);
19413
+ const pathValue = cliString(record?.path);
19414
+ const body = cliString(record?.body);
19415
+ return {
19416
+ ...pathValue ? { endpoint: pathValue } : {},
19417
+ ...status === void 0 ? {} : { http_status: status },
19418
+ ...body ? { error: body } : {}
19419
+ };
19420
+ }
19421
+ async function preflightRiddleProfileBalanceForCli(profile, options, input) {
19422
+ if (!runProfileBalancePreflightOption(options)) return void 0;
19423
+ let balance;
19424
+ try {
19425
+ balance = await input.client.getBalance();
19426
+ } catch (error) {
19427
+ return createRiddleProofProfileEnvironmentBlockedResult({
19428
+ profile,
19429
+ runner: input.runner,
19430
+ error,
19431
+ environmentBlocker: {
19432
+ source: "riddle_api",
19433
+ endpoint: "/v1/balance",
19434
+ reason: "balance_preflight_failed",
19435
+ balance_preflight: true,
19436
+ ...riddleApiErrorBlockerMetadata(error),
19437
+ ...apiKeySourceBlockerMetadata(input.client)
19438
+ },
19439
+ riddle: riddleBalancePreflightMetadata(profile, options)
19440
+ });
19441
+ }
19442
+ const jobCount = profileRiddleJobCountForCli(profile, options);
19443
+ const requiredSeconds = jobCount * RIDDLE_PROFILE_BALANCE_PREFLIGHT_MIN_SECONDS_PER_JOB;
19444
+ const availableSeconds = riddleBalanceAvailableSeconds(balance);
19445
+ if (availableSeconds === void 0 || availableSeconds >= requiredSeconds) return void 0;
19446
+ const reservedSeconds = cliFiniteNumber(balance.reserved_seconds);
19447
+ const totalSeconds = cliFiniteNumber(balance.total_seconds);
19448
+ const holdsCount = cliFiniteNumber(balance.holds_count);
19449
+ return createRiddleProofProfileEnvironmentBlockedResult({
19450
+ profile,
19451
+ runner: input.runner,
19452
+ error: `Riddle balance preflight failed: ${availableSeconds}s available for ${jobCount} intended hosted job(s), minimum ${requiredSeconds}s required.`,
19453
+ environmentBlocker: {
19454
+ source: "riddle_api",
19455
+ endpoint: "/v1/balance",
19456
+ reason: "insufficient_balance",
19457
+ error: "Insufficient available balance",
19458
+ balance_preflight: true,
19459
+ job_count: jobCount,
19460
+ seconds_per_job: RIDDLE_PROFILE_BALANCE_PREFLIGHT_MIN_SECONDS_PER_JOB,
19461
+ required_seconds: requiredSeconds,
19462
+ available_seconds: availableSeconds,
19463
+ deficit_seconds: requiredSeconds - availableSeconds,
19464
+ ...reservedSeconds === void 0 ? {} : { reserved_seconds: reservedSeconds },
19465
+ ...totalSeconds === void 0 ? {} : { total_seconds: totalSeconds },
19466
+ ...holdsCount === void 0 ? {} : { holds_count: holdsCount },
19467
+ ...apiKeySourceBlockerMetadata(input.client)
19468
+ },
19469
+ riddle: riddleBalancePreflightMetadata(profile, options)
19470
+ });
19471
+ }
19352
19472
  async function runSingleRiddleProfileForCli(profile, options, input) {
19353
19473
  const { client, runner } = input;
19354
19474
  const targetUrl = resolveRiddleProofProfileTargetUrl(profile);
@@ -19571,6 +19691,8 @@ async function runProfileForCli(profile, options) {
19571
19691
  throw new Error(`Unsupported --runner ${runner}. The current CLI supports --runner riddle.`);
19572
19692
  }
19573
19693
  const client = createRiddleApiClient(riddleClientConfig(options));
19694
+ const balanceBlocked = await preflightRiddleProfileBalanceForCli(profile, options, { client, runner });
19695
+ if (balanceBlocked) return balanceBlocked;
19574
19696
  if (runProfileSplitViewportsOption(options) && profile.target.viewports.length > 1) {
19575
19697
  return runSplitViewportProfileForCli(profile, options, { client, runner });
19576
19698
  }
package/dist/cli.js CHANGED
@@ -13,12 +13,12 @@ import {
13
13
  profileStatusExitCode,
14
14
  resolveRiddleProofProfileTargetUrl,
15
15
  resolveRiddleProofProfileTimeoutSec
16
- } from "./chunk-VCTXNE67.js";
16
+ } from "./chunk-3RTVXIFZ.js";
17
17
  import {
18
18
  createRiddleApiClient,
19
19
  isTerminalRiddleJobStatus,
20
20
  parseRiddleViewport
21
- } from "./chunk-PPZ5A5MC.js";
21
+ } from "./chunk-33XO7WFI.js";
22
22
  import {
23
23
  createDisabledRiddleProofAgentAdapter,
24
24
  readRiddleProofRunStatus,
@@ -40,6 +40,7 @@ import "./chunk-VY4Y5U57.js";
40
40
  // src/cli.ts
41
41
  import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "fs";
42
42
  import path from "path";
43
+ var RIDDLE_PROFILE_BALANCE_PREFLIGHT_MIN_SECONDS_PER_JOB = 30;
43
44
  function usage() {
44
45
  return [
45
46
  "Usage:",
@@ -48,7 +49,7 @@ function usage() {
48
49
  " riddle-proof-loop respond --state-path <path> --response-json <file|json|->",
49
50
  " riddle-proof-loop respond --state-path <path> --decision <decision> --summary <text> [--payload-json <file|json|->]",
50
51
  " riddle-proof-loop status --state-path <path>",
51
- " riddle-proof-loop run-profile --profile <file|json|-> --url <base-url> [--runner riddle] [--viewport-name <name[,name...]>] [--strict true|false; default false] [--split-viewports true|false; default false] [--poll-attempts n] [--output <dir>|--output-dir <dir>] [--result-format json|compact-json|summary|none; default json] [--quiet]",
52
+ " riddle-proof-loop run-profile --profile <file|json|-> --url <base-url> [--runner riddle] [--viewport-name <name[,name...]>] [--strict true|false; default false] [--split-viewports true|false; default false] [--balance-preflight true|false; default true] [--poll-attempts n] [--output <dir>|--output-dir <dir>] [--result-format json|compact-json|summary|none; default json] [--quiet]",
52
53
  " riddle-proof-loop run-profile aggregate --profile <file|json|-> --url <base-url> --input-dir <dir>|--inputs <path[,path...]> [--output <dir>|--output-dir <dir>] [--result-format json|compact-json|summary|none; default json]",
53
54
  " riddle-proof-loop run-profile recover --profile <file|json|-> --url <base-url> --job <job-id> [--viewport-name <name[,name...]>] [--output <dir>|--output-dir <dir>] [--result-format json|compact-json|summary|none; default json]",
54
55
  " riddle-proof-loop profile-body-assertions --artifact <file|url|-> --candidates-json <file|json|-> [--required-json <file|json|->] [--format json|body-contains]",
@@ -107,6 +108,9 @@ function optionBoolean(options, key) {
107
108
  function runProfileStrictOption(options) {
108
109
  return optionBoolean(options, "strict") ?? false;
109
110
  }
111
+ function runProfileBalancePreflightOption(options) {
112
+ return optionBoolean(options, "balancePreflight") ?? true;
113
+ }
110
114
  function runProfileSplitViewportsOption(options) {
111
115
  return optionBoolean(options, "splitViewports") ?? false;
112
116
  }
@@ -2029,15 +2033,25 @@ function profileEnvironmentBlockerMarkdown(result) {
2029
2033
  const availableSeconds = cliFiniteNumber(blocker.available_seconds);
2030
2034
  const deficitSeconds = cliFiniteNumber(blocker.deficit_seconds);
2031
2035
  const minimumPurchaseDollars = cliFiniteNumber(blocker.minimum_purchase_dollars);
2036
+ const balancePreflight = blocker.balance_preflight === true;
2037
+ const jobCount = cliFiniteNumber(blocker.job_count);
2038
+ const secondsPerJob = cliFiniteNumber(blocker.seconds_per_job);
2039
+ const apiKeySource = cliString(blocker.api_key_source);
2040
+ const apiKeyFile = cliString(blocker.api_key_file);
2032
2041
  if (reason) lines.push(`- reason: ${reason}`);
2033
2042
  if (source || endpoint || httpStatus !== void 0) {
2034
2043
  lines.push(`- source: ${source || "runner"}${endpoint ? ` ${endpoint}` : ""}${httpStatus === void 0 ? "" : ` HTTP ${httpStatus}`}`);
2035
2044
  }
2045
+ if (balancePreflight) lines.push("- preflight: balance");
2036
2046
  if (error) lines.push(`- error: ${error}`);
2047
+ if (jobCount !== void 0 || secondsPerJob !== void 0) {
2048
+ lines.push(`- job estimate: ${jobCount ?? "unknown"} job(s), ${secondsPerJob ?? "unknown"}s minimum per job`);
2049
+ }
2037
2050
  if (requiredSeconds !== void 0 || availableSeconds !== void 0 || deficitSeconds !== void 0) {
2038
2051
  lines.push(`- seconds: required ${requiredSeconds ?? "unknown"}, available ${availableSeconds ?? "unknown"}, deficit ${deficitSeconds ?? "unknown"}`);
2039
2052
  }
2040
2053
  if (minimumPurchaseDollars !== void 0) lines.push(`- minimum purchase: $${minimumPurchaseDollars}`);
2054
+ if (apiKeySource) lines.push(`- auth: ${apiKeySource}${apiKeyFile ? ` ${apiKeyFile}` : ""}`);
2041
2055
  return lines;
2042
2056
  }
2043
2057
  function profileCliDiagnosticLine(result) {
@@ -3174,6 +3188,100 @@ function withSplitViewportChildStatusCheck(profile, result, childRuns) {
3174
3188
  const summary = status === "product_regression" ? `${profile.name} failed ${failedChecks} product invariant(s) across ${viewportCount} viewport(s).` : `${profile.name} collected split viewport artifacts but needs human review.`;
3175
3189
  return { ...result, status, checks, summary };
3176
3190
  }
3191
+ function profileRiddleJobCountForCli(profile, options) {
3192
+ return runProfileSplitViewportsOption(options) && profile.target.viewports.length > 1 ? profile.target.viewports.length : 1;
3193
+ }
3194
+ function riddleBalanceAvailableSeconds(balance) {
3195
+ const availableSeconds = cliFiniteNumber(balance.available_seconds);
3196
+ if (availableSeconds !== void 0) return availableSeconds;
3197
+ const totalSeconds = cliFiniteNumber(balance.total_seconds);
3198
+ const reservedSeconds = cliFiniteNumber(balance.reserved_seconds) ?? 0;
3199
+ return totalSeconds === void 0 ? void 0 : totalSeconds - reservedSeconds;
3200
+ }
3201
+ function riddleBalancePreflightMetadata(profile, options) {
3202
+ const split = runProfileSplitViewportsOption(options) && profile.target.viewports.length > 1;
3203
+ if (!split) return { status: "balance_preflight_blocked", terminal: false };
3204
+ return {
3205
+ mode: "split-viewports",
3206
+ job_count: profile.target.viewports.length,
3207
+ status: "balance_preflight_blocked",
3208
+ terminal: false,
3209
+ split_jobs: profile.target.viewports.map((viewport) => ({ viewport: viewport.name }))
3210
+ };
3211
+ }
3212
+ function apiKeySourceBlockerMetadata(client) {
3213
+ try {
3214
+ const source = client.apiKeySource();
3215
+ return {
3216
+ api_key_source: source.source,
3217
+ ...source.file ? { api_key_file: source.file } : {}
3218
+ };
3219
+ } catch {
3220
+ return {};
3221
+ }
3222
+ }
3223
+ function riddleApiErrorBlockerMetadata(error) {
3224
+ const record = error && typeof error === "object" ? error : void 0;
3225
+ const status = cliFiniteNumber(record?.status);
3226
+ const pathValue = cliString(record?.path);
3227
+ const body = cliString(record?.body);
3228
+ return {
3229
+ ...pathValue ? { endpoint: pathValue } : {},
3230
+ ...status === void 0 ? {} : { http_status: status },
3231
+ ...body ? { error: body } : {}
3232
+ };
3233
+ }
3234
+ async function preflightRiddleProfileBalanceForCli(profile, options, input) {
3235
+ if (!runProfileBalancePreflightOption(options)) return void 0;
3236
+ let balance;
3237
+ try {
3238
+ balance = await input.client.getBalance();
3239
+ } catch (error) {
3240
+ return createRiddleProofProfileEnvironmentBlockedResult({
3241
+ profile,
3242
+ runner: input.runner,
3243
+ error,
3244
+ environmentBlocker: {
3245
+ source: "riddle_api",
3246
+ endpoint: "/v1/balance",
3247
+ reason: "balance_preflight_failed",
3248
+ balance_preflight: true,
3249
+ ...riddleApiErrorBlockerMetadata(error),
3250
+ ...apiKeySourceBlockerMetadata(input.client)
3251
+ },
3252
+ riddle: riddleBalancePreflightMetadata(profile, options)
3253
+ });
3254
+ }
3255
+ const jobCount = profileRiddleJobCountForCli(profile, options);
3256
+ const requiredSeconds = jobCount * RIDDLE_PROFILE_BALANCE_PREFLIGHT_MIN_SECONDS_PER_JOB;
3257
+ const availableSeconds = riddleBalanceAvailableSeconds(balance);
3258
+ if (availableSeconds === void 0 || availableSeconds >= requiredSeconds) return void 0;
3259
+ const reservedSeconds = cliFiniteNumber(balance.reserved_seconds);
3260
+ const totalSeconds = cliFiniteNumber(balance.total_seconds);
3261
+ const holdsCount = cliFiniteNumber(balance.holds_count);
3262
+ return createRiddleProofProfileEnvironmentBlockedResult({
3263
+ profile,
3264
+ runner: input.runner,
3265
+ error: `Riddle balance preflight failed: ${availableSeconds}s available for ${jobCount} intended hosted job(s), minimum ${requiredSeconds}s required.`,
3266
+ environmentBlocker: {
3267
+ source: "riddle_api",
3268
+ endpoint: "/v1/balance",
3269
+ reason: "insufficient_balance",
3270
+ error: "Insufficient available balance",
3271
+ balance_preflight: true,
3272
+ job_count: jobCount,
3273
+ seconds_per_job: RIDDLE_PROFILE_BALANCE_PREFLIGHT_MIN_SECONDS_PER_JOB,
3274
+ required_seconds: requiredSeconds,
3275
+ available_seconds: availableSeconds,
3276
+ deficit_seconds: requiredSeconds - availableSeconds,
3277
+ ...reservedSeconds === void 0 ? {} : { reserved_seconds: reservedSeconds },
3278
+ ...totalSeconds === void 0 ? {} : { total_seconds: totalSeconds },
3279
+ ...holdsCount === void 0 ? {} : { holds_count: holdsCount },
3280
+ ...apiKeySourceBlockerMetadata(input.client)
3281
+ },
3282
+ riddle: riddleBalancePreflightMetadata(profile, options)
3283
+ });
3284
+ }
3177
3285
  async function runSingleRiddleProfileForCli(profile, options, input) {
3178
3286
  const { client, runner } = input;
3179
3287
  const targetUrl = resolveRiddleProofProfileTargetUrl(profile);
@@ -3396,6 +3504,8 @@ async function runProfileForCli(profile, options) {
3396
3504
  throw new Error(`Unsupported --runner ${runner}. The current CLI supports --runner riddle.`);
3397
3505
  }
3398
3506
  const client = createRiddleApiClient(riddleClientConfig(options));
3507
+ const balanceBlocked = await preflightRiddleProfileBalanceForCli(profile, options, { client, runner });
3508
+ if (balanceBlocked) return balanceBlocked;
3399
3509
  if (runProfileSplitViewportsOption(options) && profile.target.viewports.length > 1) {
3400
3510
  return runSplitViewportProfileForCli(profile, options, { client, runner });
3401
3511
  }
package/dist/index.cjs CHANGED
@@ -2976,6 +2976,7 @@ __export(index_exports, {
2976
2976
  extractBasicGameplayEvidence: () => extractBasicGameplayEvidence,
2977
2977
  extractPlayabilityEvidence: () => extractPlayabilityEvidence,
2978
2978
  extractRiddleProofProfileResult: () => extractRiddleProofProfileResult,
2979
+ getRiddleBalance: () => getRiddleBalance,
2979
2980
  isDuplicateCheckpointResponse: () => isDuplicateCheckpointResponse,
2980
2981
  isRiddleProofPlayabilityMode: () => isRiddleProofPlayabilityMode,
2981
2982
  isSuccessfulStatus: () => isSuccessfulStatus,
@@ -2999,6 +3000,7 @@ __export(index_exports, {
2999
3000
  redactForProofDiagnostics: () => redactForProofDiagnostics,
3000
3001
  resolveBasicGameplayProgressionCheckWithArtifactScreenshots: () => resolveBasicGameplayProgressionCheckWithArtifactScreenshots,
3001
3002
  resolveRiddleApiKey: () => resolveRiddleApiKey,
3003
+ resolveRiddleApiKeySource: () => resolveRiddleApiKeySource,
3002
3004
  resolveRiddleProofProfileRouteUrl: () => resolveRiddleProofProfileRouteUrl,
3003
3005
  resolveRiddleProofProfileTargetUrl: () => resolveRiddleProofProfileTargetUrl,
3004
3006
  resolveRiddleProofProfileTimeoutSec: () => resolveRiddleProofProfileTimeoutSec,
@@ -12294,7 +12296,7 @@ function createRiddleProofProfileConfigurationError(name, error, runner = "riddl
12294
12296
  }
12295
12297
  function createRiddleProofProfileEnvironmentBlockedResult(input) {
12296
12298
  const message = input.error instanceof Error ? input.error.message : input.error ? String(input.error) : "Riddle runner did not complete successfully.";
12297
- const environmentBlocker = extractRiddleRunnerBlocker(message);
12299
+ const environmentBlocker = input.environmentBlocker || extractRiddleRunnerBlocker(message);
12298
12300
  const warnings = collectRiddleProofProfileEnvironmentBlockedWarnings(input.profile, environmentBlocker);
12299
12301
  return {
12300
12302
  version: RIDDLE_PROOF_PROFILE_RESULT_VERSION,
@@ -18031,16 +18033,23 @@ function normalizeBaseUrl(value) {
18031
18033
  function fetchFor(config = {}) {
18032
18034
  return config.fetchImpl || fetch;
18033
18035
  }
18034
- function resolveRiddleApiKey(config = {}) {
18035
- if (config.apiKey?.trim()) return config.apiKey.trim();
18036
- if (process.env.RIDDLE_API_KEY?.trim()) return process.env.RIDDLE_API_KEY.trim();
18036
+ function resolveRiddleApiKeyWithSource(config = {}) {
18037
+ if (config.apiKey?.trim()) return { apiKey: config.apiKey.trim(), source: "option" };
18038
+ if (process.env.RIDDLE_API_KEY?.trim()) return { apiKey: process.env.RIDDLE_API_KEY.trim(), source: "env" };
18037
18039
  const keyFile = config.apiKeyFile || process.env.RIDDLE_API_KEY_FILE || DEFAULT_RIDDLE_API_KEY_FILE;
18038
18040
  if ((0, import_node_fs5.existsSync)(keyFile)) {
18039
18041
  const key = (0, import_node_fs5.readFileSync)(keyFile, "utf8").trim();
18040
- if (key) return key;
18042
+ if (key) return { apiKey: key, source: "file", file: keyFile };
18041
18043
  }
18042
18044
  throw new Error(`Riddle API key missing. Set RIDDLE_API_KEY or write ${DEFAULT_RIDDLE_API_KEY_FILE}.`);
18043
18045
  }
18046
+ function resolveRiddleApiKeySource(config = {}) {
18047
+ const { apiKey: _apiKey, ...source } = resolveRiddleApiKeyWithSource(config);
18048
+ return source;
18049
+ }
18050
+ function resolveRiddleApiKey(config = {}) {
18051
+ return resolveRiddleApiKeyWithSource(config).apiKey;
18052
+ }
18044
18053
  async function riddleRequestJson(config, pathname, init = {}) {
18045
18054
  const response = await fetchFor(config)(`${normalizeBaseUrl(config.apiBaseUrl)}${pathname}`, {
18046
18055
  ...init,
@@ -18059,6 +18068,9 @@ async function riddleRequestJson(config, pathname, init = {}) {
18059
18068
  if (!response.ok) throw new RiddleApiError(pathname, response.status, text);
18060
18069
  return json ?? text;
18061
18070
  }
18071
+ async function getRiddleBalance(config = {}) {
18072
+ return riddleRequestJson(config, "/v1/balance", { method: "GET" });
18073
+ }
18062
18074
  function previewDeployResultFromRecord(input) {
18063
18075
  const { record, id, label, framework, expiresAt, publishRecovered, publishError } = input;
18064
18076
  return {
@@ -18393,7 +18405,9 @@ async function pollRiddleJob(config, jobId, options = {}) {
18393
18405
  }
18394
18406
  function createRiddleApiClient(config = {}) {
18395
18407
  return {
18408
+ apiKeySource: () => resolveRiddleApiKeySource(config),
18396
18409
  requestJson: (pathname, init) => riddleRequestJson(config, pathname, init),
18410
+ getBalance: () => getRiddleBalance(config),
18397
18411
  deployPreview: (directory, label, framework = "static") => deployRiddlePreview(config, directory, label, framework),
18398
18412
  deployStaticPreview: (directory, label) => deployRiddleStaticPreview(config, directory, label),
18399
18413
  runScript: (input) => runRiddleScript(config, input),
@@ -18480,6 +18494,7 @@ function createRiddleApiClient(config = {}) {
18480
18494
  extractBasicGameplayEvidence,
18481
18495
  extractPlayabilityEvidence,
18482
18496
  extractRiddleProofProfileResult,
18497
+ getRiddleBalance,
18483
18498
  isDuplicateCheckpointResponse,
18484
18499
  isRiddleProofPlayabilityMode,
18485
18500
  isSuccessfulStatus,
@@ -18503,6 +18518,7 @@ function createRiddleApiClient(config = {}) {
18503
18518
  redactForProofDiagnostics,
18504
18519
  resolveBasicGameplayProgressionCheckWithArtifactScreenshots,
18505
18520
  resolveRiddleApiKey,
18521
+ resolveRiddleApiKeySource,
18506
18522
  resolveRiddleProofProfileRouteUrl,
18507
18523
  resolveRiddleProofProfileTargetUrl,
18508
18524
  resolveRiddleProofProfileTimeoutSec,
package/dist/index.d.cts CHANGED
@@ -11,4 +11,4 @@ export { BuildVisualProofSessionInput, RIDDLE_PROOF_VISUAL_SESSION_FINGERPRINT_V
11
11
  export { AssessPlayabilityOptions, RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION, RIDDLE_PROOF_PLAYABILITY_VERSION, RiddleProofPlayabilityAssessment, RiddleProofPlayabilityEvidence, assessPlayabilityEvidence, extractPlayabilityEvidence, isRiddleProofPlayabilityMode } from './playability.cjs';
12
12
  export { AssessBasicGameplayOptions, AttachBasicGameplayArtifactOptions, BASIC_GAMEPLAY_ACTION_TYPES, BASIC_GAMEPLAY_PROGRESS_CHECK_TYPES, BasicGameplayActionResult, BasicGameplayActionType, BasicGameplayArtifactResolution, BasicGameplayAssessmentSummary, BasicGameplayBoundsOffender, BasicGameplayCanvasState, BasicGameplayCatchRecord, BasicGameplayChangeSummary, BasicGameplayFailureCode, BasicGameplayFixReference, BasicGameplayMetric, BasicGameplayMobileEvidence, BasicGameplayProgressCheckType, BasicGameplayProgressionCheck, BasicGameplayProofArtifact, BasicGameplayResponsiveViewportEvidence, BasicGameplayRouteReference, BasicGameplaySnapshot, BasicGameplaySuiteFailure, BasicGameplayWarningCode, CreateBasicGameplayCatchSummaryInput, RIDDLE_PROOF_BASIC_GAMEPLAY_ASSESSMENT_VERSION, RIDDLE_PROOF_BASIC_GAMEPLAY_CATCH_VERSION, RIDDLE_PROOF_BASIC_GAMEPLAY_VERSION, RiddleProofBasicGameplayAssessment, RiddleProofBasicGameplayCatchSummary, RiddleProofBasicGameplayEvidence, RiddleProofBasicGameplayRouteAssessment, RiddleProofBasicGameplayRouteEvidence, assessBasicGameplayEvidence, assessBasicGameplayProgressionCheck, assessBasicGameplayProgressionChecks, assessBasicGameplayRoute, attachBasicGameplayArtifactScreenshotHashes, augmentBasicGameplayAssessmentWithProgressionChecks, compactBasicGameplayText, createBasicGameplayCatchRecords, createBasicGameplayCatchSummary, extractBasicGameplayEvidence, resolveBasicGameplayProgressionCheckWithArtifactScreenshots, sanitizeBasicGameplayJsonString } from './basic-gameplay.cjs';
13
13
  export { NormalizeRiddleProofProfileOptions, RIDDLE_PROOF_PROFILE_CHECK_TYPES, RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION, RIDDLE_PROOF_PROFILE_NETWORK_ABORT_ERROR_CODES, RIDDLE_PROOF_PROFILE_RESULT_VERSION, RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES, RIDDLE_PROOF_PROFILE_STATUSES, RIDDLE_PROOF_PROFILE_VERSION, RiddleProofArtifactBodyAssertionInput, RiddleProofArtifactBodyAssertionResult, RiddleProofProfile, RiddleProofProfileArtifactRef, RiddleProofProfileBaselinePolicy, RiddleProofProfileBoundsOffender, RiddleProofProfileCheck, RiddleProofProfileCheckResult, RiddleProofProfileCheckType, RiddleProofProfileEvidence, RiddleProofProfileFailureAction, RiddleProofProfileHttpStatusBodyJsonAssertion, RiddleProofProfileHttpStatusBodyJsonAssertionResult, RiddleProofProfileHttpStatusPreflightCheckResult, RiddleProofProfileHttpStatusPreflightFetch, RiddleProofProfileHttpStatusPreflightFetchResponse, RiddleProofProfileHttpStatusPreflightOptions, RiddleProofProfileHttpStatusPreflightResult, RiddleProofProfileJsonValueType, RiddleProofProfileNetworkAbortErrorCode, RiddleProofProfileNetworkMock, RiddleProofProfileNetworkMockResponse, RiddleProofProfileResult, RiddleProofProfileReturnSummaryField, RiddleProofProfileRouteEvidence, RiddleProofProfileRouteInventoryRoute, RiddleProofProfileRunner, RiddleProofProfileSetupAction, RiddleProofProfileSetupActionType, RiddleProofProfileStatus, RiddleProofProfileTarget, RiddleProofProfileViewport, RiddleProofProfileViewportEvidence, assessRiddleProofProfileEvidence, buildRiddleProofProfileScript, collectRiddleProfileArtifactRefs, collectRiddleProofProfileWarnings, createRiddleProofProfileConfigurationError, createRiddleProofProfileEnvironmentBlockedResult, createRiddleProofProfileInsufficientResult, deriveRiddleProofArtifactBodyAssertions, extractRiddleProofProfileResult, normalizeRiddleProofProfile, preflightRiddleProofProfileHttpStatusChecks, profileStatusExitCode, resolveRiddleProofProfileRouteUrl, resolveRiddleProofProfileTargetUrl, resolveRiddleProofProfileTimeoutSec, slugifyRiddleProofProfileName, summarizeRiddleProofProfileResult } from './profile.cjs';
14
- export { DEFAULT_RIDDLE_API_BASE_URL, DEFAULT_RIDDLE_API_KEY_FILE, RiddleApiError, RiddleClientConfig, RiddleFetch, RiddlePollJobOptions, RiddlePollJobResult, RiddlePollProgressSnapshot, RiddlePollSummary, RiddlePreviewDeployResult, RiddlePreviewFramework, RiddleRunScriptInput, RiddleServerPreviewInput, RiddleServerPreviewResult, createRiddleApiClient, deployRiddlePreview, deployRiddleStaticPreview, isTerminalRiddleJobStatus, parseRiddleViewport, pollRiddleJob, resolveRiddleApiKey, riddleRequestJson, runRiddleScript, runRiddleServerPreview } from './riddle-client.cjs';
14
+ export { DEFAULT_RIDDLE_API_BASE_URL, DEFAULT_RIDDLE_API_KEY_FILE, RiddleApiError, RiddleApiKeySource, RiddleBalanceResult, RiddleClientConfig, RiddleFetch, RiddlePollJobOptions, RiddlePollJobResult, RiddlePollProgressSnapshot, RiddlePollSummary, RiddlePreviewDeployResult, RiddlePreviewFramework, RiddleRunScriptInput, RiddleServerPreviewInput, RiddleServerPreviewResult, createRiddleApiClient, deployRiddlePreview, deployRiddleStaticPreview, getRiddleBalance, isTerminalRiddleJobStatus, parseRiddleViewport, pollRiddleJob, resolveRiddleApiKey, resolveRiddleApiKeySource, riddleRequestJson, runRiddleScript, runRiddleServerPreview } from './riddle-client.cjs';
package/dist/index.d.ts CHANGED
@@ -11,4 +11,4 @@ export { BuildVisualProofSessionInput, RIDDLE_PROOF_VISUAL_SESSION_FINGERPRINT_V
11
11
  export { AssessPlayabilityOptions, RIDDLE_PROOF_PLAYABILITY_ASSESSMENT_VERSION, RIDDLE_PROOF_PLAYABILITY_VERSION, RiddleProofPlayabilityAssessment, RiddleProofPlayabilityEvidence, assessPlayabilityEvidence, extractPlayabilityEvidence, isRiddleProofPlayabilityMode } from './playability.js';
12
12
  export { AssessBasicGameplayOptions, AttachBasicGameplayArtifactOptions, BASIC_GAMEPLAY_ACTION_TYPES, BASIC_GAMEPLAY_PROGRESS_CHECK_TYPES, BasicGameplayActionResult, BasicGameplayActionType, BasicGameplayArtifactResolution, BasicGameplayAssessmentSummary, BasicGameplayBoundsOffender, BasicGameplayCanvasState, BasicGameplayCatchRecord, BasicGameplayChangeSummary, BasicGameplayFailureCode, BasicGameplayFixReference, BasicGameplayMetric, BasicGameplayMobileEvidence, BasicGameplayProgressCheckType, BasicGameplayProgressionCheck, BasicGameplayProofArtifact, BasicGameplayResponsiveViewportEvidence, BasicGameplayRouteReference, BasicGameplaySnapshot, BasicGameplaySuiteFailure, BasicGameplayWarningCode, CreateBasicGameplayCatchSummaryInput, RIDDLE_PROOF_BASIC_GAMEPLAY_ASSESSMENT_VERSION, RIDDLE_PROOF_BASIC_GAMEPLAY_CATCH_VERSION, RIDDLE_PROOF_BASIC_GAMEPLAY_VERSION, RiddleProofBasicGameplayAssessment, RiddleProofBasicGameplayCatchSummary, RiddleProofBasicGameplayEvidence, RiddleProofBasicGameplayRouteAssessment, RiddleProofBasicGameplayRouteEvidence, assessBasicGameplayEvidence, assessBasicGameplayProgressionCheck, assessBasicGameplayProgressionChecks, assessBasicGameplayRoute, attachBasicGameplayArtifactScreenshotHashes, augmentBasicGameplayAssessmentWithProgressionChecks, compactBasicGameplayText, createBasicGameplayCatchRecords, createBasicGameplayCatchSummary, extractBasicGameplayEvidence, resolveBasicGameplayProgressionCheckWithArtifactScreenshots, sanitizeBasicGameplayJsonString } from './basic-gameplay.js';
13
13
  export { NormalizeRiddleProofProfileOptions, RIDDLE_PROOF_PROFILE_CHECK_TYPES, RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION, RIDDLE_PROOF_PROFILE_NETWORK_ABORT_ERROR_CODES, RIDDLE_PROOF_PROFILE_RESULT_VERSION, RIDDLE_PROOF_PROFILE_SETUP_ACTION_TYPES, RIDDLE_PROOF_PROFILE_STATUSES, RIDDLE_PROOF_PROFILE_VERSION, RiddleProofArtifactBodyAssertionInput, RiddleProofArtifactBodyAssertionResult, RiddleProofProfile, RiddleProofProfileArtifactRef, RiddleProofProfileBaselinePolicy, RiddleProofProfileBoundsOffender, RiddleProofProfileCheck, RiddleProofProfileCheckResult, RiddleProofProfileCheckType, RiddleProofProfileEvidence, RiddleProofProfileFailureAction, RiddleProofProfileHttpStatusBodyJsonAssertion, RiddleProofProfileHttpStatusBodyJsonAssertionResult, RiddleProofProfileHttpStatusPreflightCheckResult, RiddleProofProfileHttpStatusPreflightFetch, RiddleProofProfileHttpStatusPreflightFetchResponse, RiddleProofProfileHttpStatusPreflightOptions, RiddleProofProfileHttpStatusPreflightResult, RiddleProofProfileJsonValueType, RiddleProofProfileNetworkAbortErrorCode, RiddleProofProfileNetworkMock, RiddleProofProfileNetworkMockResponse, RiddleProofProfileResult, RiddleProofProfileReturnSummaryField, RiddleProofProfileRouteEvidence, RiddleProofProfileRouteInventoryRoute, RiddleProofProfileRunner, RiddleProofProfileSetupAction, RiddleProofProfileSetupActionType, RiddleProofProfileStatus, RiddleProofProfileTarget, RiddleProofProfileViewport, RiddleProofProfileViewportEvidence, assessRiddleProofProfileEvidence, buildRiddleProofProfileScript, collectRiddleProfileArtifactRefs, collectRiddleProofProfileWarnings, createRiddleProofProfileConfigurationError, createRiddleProofProfileEnvironmentBlockedResult, createRiddleProofProfileInsufficientResult, deriveRiddleProofArtifactBodyAssertions, extractRiddleProofProfileResult, normalizeRiddleProofProfile, preflightRiddleProofProfileHttpStatusChecks, profileStatusExitCode, resolveRiddleProofProfileRouteUrl, resolveRiddleProofProfileTargetUrl, resolveRiddleProofProfileTimeoutSec, slugifyRiddleProofProfileName, summarizeRiddleProofProfileResult } from './profile.js';
14
- export { DEFAULT_RIDDLE_API_BASE_URL, DEFAULT_RIDDLE_API_KEY_FILE, RiddleApiError, RiddleClientConfig, RiddleFetch, RiddlePollJobOptions, RiddlePollJobResult, RiddlePollProgressSnapshot, RiddlePollSummary, RiddlePreviewDeployResult, RiddlePreviewFramework, RiddleRunScriptInput, RiddleServerPreviewInput, RiddleServerPreviewResult, createRiddleApiClient, deployRiddlePreview, deployRiddleStaticPreview, isTerminalRiddleJobStatus, parseRiddleViewport, pollRiddleJob, resolveRiddleApiKey, riddleRequestJson, runRiddleScript, runRiddleServerPreview } from './riddle-client.js';
14
+ export { DEFAULT_RIDDLE_API_BASE_URL, DEFAULT_RIDDLE_API_KEY_FILE, RiddleApiError, RiddleApiKeySource, RiddleBalanceResult, RiddleClientConfig, RiddleFetch, RiddlePollJobOptions, RiddlePollJobResult, RiddlePollProgressSnapshot, RiddlePollSummary, RiddlePreviewDeployResult, RiddlePreviewFramework, RiddleRunScriptInput, RiddleServerPreviewInput, RiddleServerPreviewResult, createRiddleApiClient, deployRiddlePreview, deployRiddleStaticPreview, getRiddleBalance, isTerminalRiddleJobStatus, parseRiddleViewport, pollRiddleJob, resolveRiddleApiKey, resolveRiddleApiKeySource, riddleRequestJson, runRiddleScript, runRiddleServerPreview } from './riddle-client.js';
package/dist/index.js CHANGED
@@ -62,7 +62,7 @@ import {
62
62
  resolveRiddleProofProfileTimeoutSec,
63
63
  slugifyRiddleProofProfileName,
64
64
  summarizeRiddleProofProfileResult
65
- } from "./chunk-VCTXNE67.js";
65
+ } from "./chunk-3RTVXIFZ.js";
66
66
  import {
67
67
  DEFAULT_RIDDLE_API_BASE_URL,
68
68
  DEFAULT_RIDDLE_API_KEY_FILE,
@@ -70,14 +70,16 @@ import {
70
70
  createRiddleApiClient,
71
71
  deployRiddlePreview,
72
72
  deployRiddleStaticPreview,
73
+ getRiddleBalance,
73
74
  isTerminalRiddleJobStatus,
74
75
  parseRiddleViewport,
75
76
  pollRiddleJob,
76
77
  resolveRiddleApiKey,
78
+ resolveRiddleApiKeySource,
77
79
  riddleRequestJson,
78
80
  runRiddleScript,
79
81
  runRiddleServerPreview
80
- } from "./chunk-PPZ5A5MC.js";
82
+ } from "./chunk-33XO7WFI.js";
81
83
  import {
82
84
  DEFAULT_DIAGNOSTIC_ARRAY_LIMIT,
83
85
  DEFAULT_DIAGNOSTIC_HISTORY_LIMIT,
@@ -220,6 +222,7 @@ export {
220
222
  extractBasicGameplayEvidence,
221
223
  extractPlayabilityEvidence,
222
224
  extractRiddleProofProfileResult,
225
+ getRiddleBalance,
223
226
  isDuplicateCheckpointResponse,
224
227
  isRiddleProofPlayabilityMode,
225
228
  isSuccessfulStatus,
@@ -243,6 +246,7 @@ export {
243
246
  redactForProofDiagnostics,
244
247
  resolveBasicGameplayProgressionCheckWithArtifactScreenshots,
245
248
  resolveRiddleApiKey,
249
+ resolveRiddleApiKeySource,
246
250
  resolveRiddleProofProfileRouteUrl,
247
251
  resolveRiddleProofProfileTargetUrl,
248
252
  resolveRiddleProofProfileTimeoutSec,
package/dist/profile.cjs CHANGED
@@ -3608,7 +3608,7 @@ function createRiddleProofProfileConfigurationError(name, error, runner = "riddl
3608
3608
  }
3609
3609
  function createRiddleProofProfileEnvironmentBlockedResult(input) {
3610
3610
  const message = input.error instanceof Error ? input.error.message : input.error ? String(input.error) : "Riddle runner did not complete successfully.";
3611
- const environmentBlocker = extractRiddleRunnerBlocker(message);
3611
+ const environmentBlocker = input.environmentBlocker || extractRiddleRunnerBlocker(message);
3612
3612
  const warnings = collectRiddleProofProfileEnvironmentBlockedWarnings(input.profile, environmentBlocker);
3613
3613
  return {
3614
3614
  version: RIDDLE_PROOF_PROFILE_RESULT_VERSION,
@@ -467,6 +467,7 @@ declare function createRiddleProofProfileEnvironmentBlockedResult(input: {
467
467
  profile: RiddleProofProfile;
468
468
  runner?: RiddleProofProfileRunner;
469
469
  error?: unknown;
470
+ environmentBlocker?: Record<string, JsonValue>;
470
471
  riddle?: RiddleProofProfileResult["riddle"];
471
472
  artifacts?: RiddleProofProfileArtifactRef[];
472
473
  }): RiddleProofProfileResult;
package/dist/profile.d.ts CHANGED
@@ -467,6 +467,7 @@ declare function createRiddleProofProfileEnvironmentBlockedResult(input: {
467
467
  profile: RiddleProofProfile;
468
468
  runner?: RiddleProofProfileRunner;
469
469
  error?: unknown;
470
+ environmentBlocker?: Record<string, JsonValue>;
470
471
  riddle?: RiddleProofProfileResult["riddle"];
471
472
  artifacts?: RiddleProofProfileArtifactRef[];
472
473
  }): RiddleProofProfileResult;
package/dist/profile.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  resolveRiddleProofProfileTimeoutSec,
24
24
  slugifyRiddleProofProfileName,
25
25
  summarizeRiddleProofProfileResult
26
- } from "./chunk-VCTXNE67.js";
26
+ } from "./chunk-3RTVXIFZ.js";
27
27
  export {
28
28
  RIDDLE_PROOF_PROFILE_CHECK_TYPES,
29
29
  RIDDLE_PROOF_PROFILE_EVIDENCE_VERSION,
@@ -36,10 +36,12 @@ __export(riddle_client_exports, {
36
36
  createRiddleApiClient: () => createRiddleApiClient,
37
37
  deployRiddlePreview: () => deployRiddlePreview,
38
38
  deployRiddleStaticPreview: () => deployRiddleStaticPreview,
39
+ getRiddleBalance: () => getRiddleBalance,
39
40
  isTerminalRiddleJobStatus: () => isTerminalRiddleJobStatus,
40
41
  parseRiddleViewport: () => parseRiddleViewport,
41
42
  pollRiddleJob: () => pollRiddleJob,
42
43
  resolveRiddleApiKey: () => resolveRiddleApiKey,
44
+ resolveRiddleApiKeySource: () => resolveRiddleApiKeySource,
43
45
  riddleRequestJson: () => riddleRequestJson,
44
46
  runRiddleScript: () => runRiddleScript,
45
47
  runRiddleServerPreview: () => runRiddleServerPreview
@@ -69,16 +71,23 @@ function normalizeBaseUrl(value) {
69
71
  function fetchFor(config = {}) {
70
72
  return config.fetchImpl || fetch;
71
73
  }
72
- function resolveRiddleApiKey(config = {}) {
73
- if (config.apiKey?.trim()) return config.apiKey.trim();
74
- if (process.env.RIDDLE_API_KEY?.trim()) return process.env.RIDDLE_API_KEY.trim();
74
+ function resolveRiddleApiKeyWithSource(config = {}) {
75
+ if (config.apiKey?.trim()) return { apiKey: config.apiKey.trim(), source: "option" };
76
+ if (process.env.RIDDLE_API_KEY?.trim()) return { apiKey: process.env.RIDDLE_API_KEY.trim(), source: "env" };
75
77
  const keyFile = config.apiKeyFile || process.env.RIDDLE_API_KEY_FILE || DEFAULT_RIDDLE_API_KEY_FILE;
76
78
  if ((0, import_node_fs.existsSync)(keyFile)) {
77
79
  const key = (0, import_node_fs.readFileSync)(keyFile, "utf8").trim();
78
- if (key) return key;
80
+ if (key) return { apiKey: key, source: "file", file: keyFile };
79
81
  }
80
82
  throw new Error(`Riddle API key missing. Set RIDDLE_API_KEY or write ${DEFAULT_RIDDLE_API_KEY_FILE}.`);
81
83
  }
84
+ function resolveRiddleApiKeySource(config = {}) {
85
+ const { apiKey: _apiKey, ...source } = resolveRiddleApiKeyWithSource(config);
86
+ return source;
87
+ }
88
+ function resolveRiddleApiKey(config = {}) {
89
+ return resolveRiddleApiKeyWithSource(config).apiKey;
90
+ }
82
91
  async function riddleRequestJson(config, pathname, init = {}) {
83
92
  const response = await fetchFor(config)(`${normalizeBaseUrl(config.apiBaseUrl)}${pathname}`, {
84
93
  ...init,
@@ -97,6 +106,9 @@ async function riddleRequestJson(config, pathname, init = {}) {
97
106
  if (!response.ok) throw new RiddleApiError(pathname, response.status, text);
98
107
  return json ?? text;
99
108
  }
109
+ async function getRiddleBalance(config = {}) {
110
+ return riddleRequestJson(config, "/v1/balance", { method: "GET" });
111
+ }
100
112
  function previewDeployResultFromRecord(input) {
101
113
  const { record, id, label, framework, expiresAt, publishRecovered, publishError } = input;
102
114
  return {
@@ -431,7 +443,9 @@ async function pollRiddleJob(config, jobId, options = {}) {
431
443
  }
432
444
  function createRiddleApiClient(config = {}) {
433
445
  return {
446
+ apiKeySource: () => resolveRiddleApiKeySource(config),
434
447
  requestJson: (pathname, init) => riddleRequestJson(config, pathname, init),
448
+ getBalance: () => getRiddleBalance(config),
435
449
  deployPreview: (directory, label, framework = "static") => deployRiddlePreview(config, directory, label, framework),
436
450
  deployStaticPreview: (directory, label) => deployRiddleStaticPreview(config, directory, label),
437
451
  runScript: (input) => runRiddleScript(config, input),
@@ -447,10 +461,12 @@ function createRiddleApiClient(config = {}) {
447
461
  createRiddleApiClient,
448
462
  deployRiddlePreview,
449
463
  deployRiddleStaticPreview,
464
+ getRiddleBalance,
450
465
  isTerminalRiddleJobStatus,
451
466
  parseRiddleViewport,
452
467
  pollRiddleJob,
453
468
  resolveRiddleApiKey,
469
+ resolveRiddleApiKeySource,
454
470
  riddleRequestJson,
455
471
  runRiddleScript,
456
472
  runRiddleServerPreview
@@ -7,6 +7,23 @@ interface RiddleClientConfig {
7
7
  apiBaseUrl?: string;
8
8
  fetchImpl?: RiddleFetch;
9
9
  }
10
+ interface RiddleApiKeySource {
11
+ source: "option" | "env" | "file";
12
+ file?: string;
13
+ }
14
+ interface RiddleBalanceResult {
15
+ available_seconds?: number;
16
+ reserved_seconds?: number;
17
+ total_seconds?: number;
18
+ available_time?: string;
19
+ total_time?: string;
20
+ available_dollars?: number;
21
+ reserved_dollars?: number;
22
+ total_dollars?: number;
23
+ currency?: string;
24
+ holds_count?: number;
25
+ rate?: Record<string, unknown>;
26
+ }
10
27
  interface RiddlePollProgressSnapshot {
11
28
  job_id: string;
12
29
  status: string | null;
@@ -107,8 +124,10 @@ declare class RiddleApiError extends Error {
107
124
  readonly path: string;
108
125
  constructor(pathname: string, status: number, body: string);
109
126
  }
127
+ declare function resolveRiddleApiKeySource(config?: RiddleClientConfig): RiddleApiKeySource;
110
128
  declare function resolveRiddleApiKey(config?: RiddleClientConfig): string;
111
129
  declare function riddleRequestJson<T = unknown>(config: RiddleClientConfig, pathname: string, init?: RequestInit): Promise<T>;
130
+ declare function getRiddleBalance(config?: RiddleClientConfig): Promise<RiddleBalanceResult>;
112
131
  declare function deployRiddlePreview(config: RiddleClientConfig, directory: string, label: string, framework?: RiddlePreviewFramework): Promise<RiddlePreviewDeployResult>;
113
132
  declare function deployRiddleStaticPreview(config: RiddleClientConfig, directory: string, label: string): Promise<RiddlePreviewDeployResult>;
114
133
  declare function parseRiddleViewport(value?: string): {
@@ -120,7 +139,9 @@ declare function runRiddleScript(config: RiddleClientConfig, input: RiddleRunScr
120
139
  declare function isTerminalRiddleJobStatus(status: unknown): boolean;
121
140
  declare function pollRiddleJob(config: RiddleClientConfig, jobId: string, options?: RiddlePollJobOptions): Promise<RiddlePollJobResult>;
122
141
  declare function createRiddleApiClient(config?: RiddleClientConfig): {
142
+ apiKeySource: () => RiddleApiKeySource;
123
143
  requestJson: <T = unknown>(pathname: string, init?: RequestInit) => Promise<T>;
144
+ getBalance: () => Promise<RiddleBalanceResult>;
124
145
  deployPreview: (directory: string, label: string, framework?: RiddlePreviewFramework) => Promise<RiddlePreviewDeployResult>;
125
146
  deployStaticPreview: (directory: string, label: string) => Promise<RiddlePreviewDeployResult>;
126
147
  runScript: (input: RiddleRunScriptInput) => Promise<Record<string, unknown>>;
@@ -128,4 +149,4 @@ declare function createRiddleApiClient(config?: RiddleClientConfig): {
128
149
  pollJob: (jobId: string, options?: RiddlePollJobOptions) => Promise<RiddlePollJobResult>;
129
150
  };
130
151
 
131
- export { DEFAULT_RIDDLE_API_BASE_URL, DEFAULT_RIDDLE_API_KEY_FILE, RiddleApiError, type RiddleClientConfig, type RiddleFetch, type RiddlePollJobOptions, type RiddlePollJobResult, type RiddlePollProgressSnapshot, type RiddlePollSummary, type RiddlePreviewDeployResult, type RiddlePreviewFramework, type RiddleRunScriptInput, type RiddleServerPreviewInput, type RiddleServerPreviewResult, createRiddleApiClient, deployRiddlePreview, deployRiddleStaticPreview, isTerminalRiddleJobStatus, parseRiddleViewport, pollRiddleJob, resolveRiddleApiKey, riddleRequestJson, runRiddleScript, runRiddleServerPreview };
152
+ export { DEFAULT_RIDDLE_API_BASE_URL, DEFAULT_RIDDLE_API_KEY_FILE, RiddleApiError, type RiddleApiKeySource, type RiddleBalanceResult, type RiddleClientConfig, type RiddleFetch, type RiddlePollJobOptions, type RiddlePollJobResult, type RiddlePollProgressSnapshot, type RiddlePollSummary, type RiddlePreviewDeployResult, type RiddlePreviewFramework, type RiddleRunScriptInput, type RiddleServerPreviewInput, type RiddleServerPreviewResult, createRiddleApiClient, deployRiddlePreview, deployRiddleStaticPreview, getRiddleBalance, isTerminalRiddleJobStatus, parseRiddleViewport, pollRiddleJob, resolveRiddleApiKey, resolveRiddleApiKeySource, riddleRequestJson, runRiddleScript, runRiddleServerPreview };
@@ -7,6 +7,23 @@ interface RiddleClientConfig {
7
7
  apiBaseUrl?: string;
8
8
  fetchImpl?: RiddleFetch;
9
9
  }
10
+ interface RiddleApiKeySource {
11
+ source: "option" | "env" | "file";
12
+ file?: string;
13
+ }
14
+ interface RiddleBalanceResult {
15
+ available_seconds?: number;
16
+ reserved_seconds?: number;
17
+ total_seconds?: number;
18
+ available_time?: string;
19
+ total_time?: string;
20
+ available_dollars?: number;
21
+ reserved_dollars?: number;
22
+ total_dollars?: number;
23
+ currency?: string;
24
+ holds_count?: number;
25
+ rate?: Record<string, unknown>;
26
+ }
10
27
  interface RiddlePollProgressSnapshot {
11
28
  job_id: string;
12
29
  status: string | null;
@@ -107,8 +124,10 @@ declare class RiddleApiError extends Error {
107
124
  readonly path: string;
108
125
  constructor(pathname: string, status: number, body: string);
109
126
  }
127
+ declare function resolveRiddleApiKeySource(config?: RiddleClientConfig): RiddleApiKeySource;
110
128
  declare function resolveRiddleApiKey(config?: RiddleClientConfig): string;
111
129
  declare function riddleRequestJson<T = unknown>(config: RiddleClientConfig, pathname: string, init?: RequestInit): Promise<T>;
130
+ declare function getRiddleBalance(config?: RiddleClientConfig): Promise<RiddleBalanceResult>;
112
131
  declare function deployRiddlePreview(config: RiddleClientConfig, directory: string, label: string, framework?: RiddlePreviewFramework): Promise<RiddlePreviewDeployResult>;
113
132
  declare function deployRiddleStaticPreview(config: RiddleClientConfig, directory: string, label: string): Promise<RiddlePreviewDeployResult>;
114
133
  declare function parseRiddleViewport(value?: string): {
@@ -120,7 +139,9 @@ declare function runRiddleScript(config: RiddleClientConfig, input: RiddleRunScr
120
139
  declare function isTerminalRiddleJobStatus(status: unknown): boolean;
121
140
  declare function pollRiddleJob(config: RiddleClientConfig, jobId: string, options?: RiddlePollJobOptions): Promise<RiddlePollJobResult>;
122
141
  declare function createRiddleApiClient(config?: RiddleClientConfig): {
142
+ apiKeySource: () => RiddleApiKeySource;
123
143
  requestJson: <T = unknown>(pathname: string, init?: RequestInit) => Promise<T>;
144
+ getBalance: () => Promise<RiddleBalanceResult>;
124
145
  deployPreview: (directory: string, label: string, framework?: RiddlePreviewFramework) => Promise<RiddlePreviewDeployResult>;
125
146
  deployStaticPreview: (directory: string, label: string) => Promise<RiddlePreviewDeployResult>;
126
147
  runScript: (input: RiddleRunScriptInput) => Promise<Record<string, unknown>>;
@@ -128,4 +149,4 @@ declare function createRiddleApiClient(config?: RiddleClientConfig): {
128
149
  pollJob: (jobId: string, options?: RiddlePollJobOptions) => Promise<RiddlePollJobResult>;
129
150
  };
130
151
 
131
- export { DEFAULT_RIDDLE_API_BASE_URL, DEFAULT_RIDDLE_API_KEY_FILE, RiddleApiError, type RiddleClientConfig, type RiddleFetch, type RiddlePollJobOptions, type RiddlePollJobResult, type RiddlePollProgressSnapshot, type RiddlePollSummary, type RiddlePreviewDeployResult, type RiddlePreviewFramework, type RiddleRunScriptInput, type RiddleServerPreviewInput, type RiddleServerPreviewResult, createRiddleApiClient, deployRiddlePreview, deployRiddleStaticPreview, isTerminalRiddleJobStatus, parseRiddleViewport, pollRiddleJob, resolveRiddleApiKey, riddleRequestJson, runRiddleScript, runRiddleServerPreview };
152
+ export { DEFAULT_RIDDLE_API_BASE_URL, DEFAULT_RIDDLE_API_KEY_FILE, RiddleApiError, type RiddleApiKeySource, type RiddleBalanceResult, type RiddleClientConfig, type RiddleFetch, type RiddlePollJobOptions, type RiddlePollJobResult, type RiddlePollProgressSnapshot, type RiddlePollSummary, type RiddlePreviewDeployResult, type RiddlePreviewFramework, type RiddleRunScriptInput, type RiddleServerPreviewInput, type RiddleServerPreviewResult, createRiddleApiClient, deployRiddlePreview, deployRiddleStaticPreview, getRiddleBalance, isTerminalRiddleJobStatus, parseRiddleViewport, pollRiddleJob, resolveRiddleApiKey, resolveRiddleApiKeySource, riddleRequestJson, runRiddleScript, runRiddleServerPreview };
@@ -5,14 +5,16 @@ import {
5
5
  createRiddleApiClient,
6
6
  deployRiddlePreview,
7
7
  deployRiddleStaticPreview,
8
+ getRiddleBalance,
8
9
  isTerminalRiddleJobStatus,
9
10
  parseRiddleViewport,
10
11
  pollRiddleJob,
11
12
  resolveRiddleApiKey,
13
+ resolveRiddleApiKeySource,
12
14
  riddleRequestJson,
13
15
  runRiddleScript,
14
16
  runRiddleServerPreview
15
- } from "./chunk-PPZ5A5MC.js";
17
+ } from "./chunk-33XO7WFI.js";
16
18
  export {
17
19
  DEFAULT_RIDDLE_API_BASE_URL,
18
20
  DEFAULT_RIDDLE_API_KEY_FILE,
@@ -20,10 +22,12 @@ export {
20
22
  createRiddleApiClient,
21
23
  deployRiddlePreview,
22
24
  deployRiddleStaticPreview,
25
+ getRiddleBalance,
23
26
  isTerminalRiddleJobStatus,
24
27
  parseRiddleViewport,
25
28
  pollRiddleJob,
26
29
  resolveRiddleApiKey,
30
+ resolveRiddleApiKeySource,
27
31
  riddleRequestJson,
28
32
  runRiddleScript,
29
33
  runRiddleServerPreview
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.7.212",
3
+ "version": "0.7.213",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",