codex-plugin-doctor 1.46.1 → 1.47.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.
@@ -12,6 +12,9 @@ export function buildJsonReport(result, options) {
12
12
  ...(result.runtimeScorecard
13
13
  ? { runtimeScorecard: result.runtimeScorecard }
14
14
  : {}),
15
+ ...(result.runtimeExecution
16
+ ? { runtimeExecution: result.runtimeExecution }
17
+ : {}),
15
18
  findingCounts: {
16
19
  fail: failCount,
17
20
  warn: warnCount,
@@ -23,6 +23,9 @@ export function buildMarkdownReport(result, options) {
23
23
  if (result.baselineSummary) {
24
24
  lines.push(`| Baseline New | ${result.baselineSummary.new} |`, `| Baseline Matched | ${result.baselineSummary.matched} |`, `| Baseline Resolved | ${result.baselineSummary.resolved} |`);
25
25
  }
26
+ if (result.runtimeExecution) {
27
+ lines.push("", `Runtime backend: ${result.runtimeExecution.backend.toUpperCase()}`, `Runtime isolation: network=${result.runtimeExecution.network}, package=${result.runtimeExecution.packageMount}`);
28
+ }
26
29
  if (result.findings.length === 0 && !result.suppressedFindings?.length) {
27
30
  lines.push("", result.baselineSummary ? "No new findings." : "No findings.");
28
31
  return lines.join("\n");
@@ -42,6 +42,9 @@ export function renderTextReport(result, options = {}) {
42
42
  if (result.baselineSummary) {
43
43
  lines.push(`Baseline: ${result.baselineSummary.new} new, ${result.baselineSummary.matched} matched, ${result.baselineSummary.resolved} resolved`);
44
44
  }
45
+ if (result.runtimeExecution) {
46
+ lines.push(`Runtime backend: ${result.runtimeExecution.backend.toUpperCase()}`, `Runtime isolation: network=${result.runtimeExecution.network}, package=${result.runtimeExecution.packageMount}`);
47
+ }
45
48
  if (result.findings.length === 0 && !result.suppressedFindings?.length) {
46
49
  if (result.runtimeScorecard) {
47
50
  lines.push("", "Runtime Scorecard", "----------------");
package/dist/run-cli.js CHANGED
@@ -20,6 +20,7 @@ import { buildDoctorExportBundle, renderDoctorExportBundle, renderDoctorExportBu
20
20
  import { buildDoctorAttestation, renderDoctorAttestation, renderDoctorAttestationJson, renderDoctorAttestationVerification, renderDoctorAttestationVerificationJson, verifyDoctorAttestation } from "./core/attestation.js";
21
21
  import { buildDoctorOutputContract, renderDoctorOutputContract, renderDoctorOutputContractJson } from "./core/output-contract.js";
22
22
  import { buildDoctorValidationCorpusReport, renderDoctorValidationCorpusJson, renderDoctorValidationCorpusReport } from "./core/validation-corpus.js";
23
+ import { buildExternalValidationCorpusReport, ExternalCorpusManifestError } from "./core/external-validation-corpus.js";
23
24
  import { buildDoctorPerformanceReport, renderDoctorPerformanceReport, renderDoctorPerformanceReportJson } from "./core/performance-report.js";
24
25
  import { buildDoctorRuntimePlan, evaluateRuntimeApproval, renderDoctorRuntimePlan, renderDoctorRuntimePlanMarkdown, renderDoctorRuntimePlanJson, runtimeApprovalPassed } from "./core/runtime-plan.js";
25
26
  import { buildDoctorRuntimePolicyReport, renderDoctorRuntimePolicy, renderDoctorRuntimePolicyJson } from "./core/runtime-policy.js";
@@ -81,8 +82,24 @@ const defaultIo = {
81
82
  }
82
83
  }
83
84
  };
85
+ class CliUsageError extends Error {
86
+ }
87
+ function parseRuntimeSandbox(flags, options = { requireRuntime: true }) {
88
+ const index = flags.indexOf("--sandbox");
89
+ if (index === -1) {
90
+ return null;
91
+ }
92
+ const value = flags[index + 1];
93
+ if (value !== "docker") {
94
+ throw new CliUsageError("Expected --sandbox docker.");
95
+ }
96
+ if (options.requireRuntime !== false && !flags.includes("--runtime")) {
97
+ throw new CliUsageError("--sandbox docker requires --runtime.");
98
+ }
99
+ return value;
100
+ }
84
101
  function printUsage(io) {
85
- io.writeStderr("Usage: codex-plugin-doctor check <path|--installed> [filter] [--policy codex-publish|mcp-strict|security] [--compat] [--json|--markdown|--badge-json|--badge-markdown] [--output <path>] [--history <path>] [--runtime] [--require-runtime-approval --runtime-approval-digest <digest>] [--verbose-runtime] [--explain] [--no-animations] [--ascii] [--changed-since <ref>]\n codex-plugin-doctor audit --installed [filter] [--policy codex-publish|mcp-strict|security] [--security] [--compat] [--json] [--output <path>] [--cache] [--changed]\n codex-plugin-doctor audit deps <path> [--policy codex-publish|mcp-strict|security] [--recommend] [--json|--sarif] [--output <path>]\n codex-plugin-doctor mcp <path> [--json] [--output <path>]\n codex-plugin-doctor security <path> [--policy security] [--json|--scorecard]\n codex-plugin-doctor compat <path> [--all|--client <client>] [--json] [--scorecard] [--output <path>] [--install-preview|--apply --backup]\n codex-plugin-doctor suppress add <path> [--fingerprint <sha256> --reason <text> --expires-at YYYY-MM-DD] [--config <path>] [--json]\n codex-plugin-doctor suppress list <path> [--config <path>] [--json]\n codex-plugin-doctor suppress remove <path> [--fingerprint <sha256>|--index <n>] [--config <path>] [--json]\n codex-plugin-doctor fix <path> (--dry-run|--interactive --backup|--apply --backup)\n codex-plugin-doctor history <history.jsonl> [--json] [--fail-on-regression]\n codex-plugin-doctor watch <path> [--runtime] [--json] [--output <path>] [--debounce-ms <ms>] [--max-iterations <n>] [--fail-fast] [--accumulate-json <path>]\n codex-plugin-doctor doctor [npm <package>|contract|corpus|runtime-plan <path> [--json|--markdown] [--output <path>]|runtime-policy <path> [--json] [--output <path>]|review-bundle <path> --output <dir> --sign-key-env NAME [--json] [--allow-dirty] [--allow-untagged]|review-bundle verify <bundle-dir> --target <path> --sign-key-env NAME [--json] [--output <path>] [--failures-only]|review-bundle diff --before <dir> --after <dir> [--json]|attest <path> [--sign-key-env NAME]|attest verify <attestation.json> --target <path> --sign-key-env NAME|release-evidence <path> --sign-key-env NAME [--allow-dirty] [--allow-untagged] [--require-runtime-approval --runtime-approval-digest <digest>]|release-evidence verify <evidence.json> --target <path> --sign-key-env NAME|release-evidence asset <path> --tag <tag> --output <evidence.json> --sign-key-env NAME [--upload]|mcp <path>|inspector <path>|diff --before <path> --after <path>|recommend <path>|trust <path>|perf <path> [--max-total-ms <ms>] [--max-stage-ms stage=ms]|export --bundle <path>|snapshot|clients|--json|--update-check]\n codex-plugin-doctor init [path] [--template skill-only|mcp-stdio|mcp-http|full-runtime]\n codex-plugin-doctor init-ci [path]\n codex-plugin-doctor init-git-hooks [path] [--force] [--json]\n codex-plugin-doctor init-git-hooks [path] --remove [--json]\n codex-plugin-doctor completion bash|zsh|fish\n codex-plugin-doctor config validate <path> [--json]\n codex-plugin-doctor release check <path> [--json]\n codex-plugin-doctor self-test\n codex-plugin-doctor list --installed\n codex-plugin-doctor explain <finding-id>\n codex-plugin-doctor --version\n\nFirst run:\n codex-plugin-doctor doctor\n codex-plugin-doctor self-test\n codex-plugin-doctor init my-plugin\n codex-plugin-doctor check . --runtime --explain");
102
+ io.writeStderr("Usage: codex-plugin-doctor check <path|--installed> [filter] [--policy codex-publish|mcp-strict|security] [--compat] [--json|--markdown|--badge-json|--badge-markdown] [--output <path>] [--history <path>] [--runtime] [--sandbox docker] [--require-runtime-approval --runtime-approval-digest <digest>] [--verbose-runtime] [--explain] [--no-animations] [--ascii] [--changed-since <ref>]\n codex-plugin-doctor audit --installed [filter] [--policy codex-publish|mcp-strict|security] [--security] [--compat] [--json] [--output <path>] [--cache] [--changed]\n codex-plugin-doctor audit deps <path> [--policy codex-publish|mcp-strict|security] [--recommend] [--json|--sarif] [--output <path>]\n codex-plugin-doctor mcp <path> [--json] [--output <path>]\n codex-plugin-doctor security <path> [--policy security] [--json|--scorecard]\n codex-plugin-doctor compat <path> [--all|--client <client>] [--json] [--scorecard] [--output <path>] [--install-preview|--apply --backup]\n codex-plugin-doctor suppress add <path> [--fingerprint <sha256> --reason <text> --expires-at YYYY-MM-DD] [--config <path>] [--json]\n codex-plugin-doctor suppress list <path> [--config <path>] [--json]\n codex-plugin-doctor suppress remove <path> [--fingerprint <sha256>|--index <n>] [--config <path>] [--json]\n codex-plugin-doctor fix <path> (--dry-run|--interactive --backup|--apply --backup)\n codex-plugin-doctor history <history.jsonl> [--json] [--fail-on-regression]\n codex-plugin-doctor watch <path> [--runtime] [--json] [--output <path>] [--debounce-ms <ms>] [--max-iterations <n>] [--fail-fast] [--accumulate-json <path>]\n codex-plugin-doctor doctor [npm <package>|contract|corpus [--manifest <corpus.json>] [--json] [--output <path>]|runtime-plan <path> [--runtime --sandbox docker] [--json|--markdown] [--output <path>]|runtime-policy <path> [--runtime --sandbox docker] [--json] [--output <path>]|review-bundle <path> --output <dir> --sign-key-env NAME [--json] [--allow-dirty] [--allow-untagged]|review-bundle verify <bundle-dir> --target <path> --sign-key-env NAME [--json] [--output <path>] [--failures-only]|review-bundle diff --before <dir> --after <dir> [--json]|attest <path> [--sign-key-env NAME]|attest verify <attestation.json> --target <path> --sign-key-env NAME|release-evidence <path> --sign-key-env NAME [--runtime --sandbox docker] [--allow-dirty] [--allow-untagged] [--require-runtime-approval --runtime-approval-digest <digest>]|release-evidence verify <evidence.json> --target <path> --sign-key-env NAME|release-evidence asset <path> --tag <tag> --output <evidence.json> --sign-key-env NAME [--upload]|mcp <path>|inspector <path>|diff --before <path> --after <path>|recommend <path>|trust <path>|perf <path> [--max-total-ms <ms>] [--max-stage-ms stage=ms]|export --bundle <path>|snapshot|clients|--json|--update-check]\n codex-plugin-doctor init [path] [--template skill-only|mcp-stdio|mcp-http|full-runtime]\n codex-plugin-doctor init-ci [path]\n codex-plugin-doctor init-git-hooks [path] [--force] [--json]\n codex-plugin-doctor init-git-hooks [path] --remove [--json]\n codex-plugin-doctor completion bash|zsh|fish\n codex-plugin-doctor config validate <path> [--json]\n codex-plugin-doctor release check <path> [--json] [--runtime --sandbox docker]\n codex-plugin-doctor self-test\n codex-plugin-doctor list --installed\n codex-plugin-doctor explain <finding-id>\n codex-plugin-doctor --version\n\nFirst run:\n codex-plugin-doctor doctor\n codex-plugin-doctor self-test\n codex-plugin-doctor init my-plugin\n codex-plugin-doctor check . --runtime --explain");
86
103
  io.writeStderr("Suppression governance: codex-plugin-doctor suppress list <path> [--fail-on-expired] [--fail-on-invalid] [--warn-expiring-within-days <days>]\n codex-plugin-doctor suppress prune <path> [--apply] [--json]");
87
104
  io.writeStderr("Baseline gating: codex-plugin-doctor baseline create <path> --output <path> [--runtime]\n codex-plugin-doctor check <path> --baseline <path>");
88
105
  }
@@ -805,6 +822,17 @@ function parseSelectedFixActionIndexes(answer, actionCount) {
805
822
  }
806
823
  export async function runCli(args, io = defaultIo, options = {}) {
807
824
  const [command, maybePath, ...remainingArgs] = args;
825
+ const runtimeSandboxSupported = (command === "check" &&
826
+ !args.includes("--installed") &&
827
+ !args.includes("--changed-since")) ||
828
+ (command === "release" && maybePath === "check") ||
829
+ (command === "doctor" &&
830
+ (["runtime-plan", "runtime-policy"].includes(maybePath ?? "") ||
831
+ (maybePath === "release-evidence" && remainingArgs[0] !== "verify")));
832
+ if (args.includes("--sandbox") && !runtimeSandboxSupported) {
833
+ io.writeStderr("--sandbox is supported only by single-package check, release check, runtime-plan, runtime-policy, and release-evidence.");
834
+ return 2;
835
+ }
808
836
  if (command === "--version" || command === "-v" || command === "version") {
809
837
  io.writeStdout(packageVersion);
810
838
  return 0;
@@ -933,6 +961,14 @@ export async function runCli(args, io = defaultIo, options = {}) {
933
961
  const markdownOutput = runtimePlanFlags.includes("--markdown");
934
962
  const outputIndex = runtimePlanFlags.indexOf("--output");
935
963
  const outputPath = outputIndex === -1 ? null : runtimePlanFlags[outputIndex + 1];
964
+ let runtimeSandbox;
965
+ try {
966
+ runtimeSandbox = parseRuntimeSandbox(runtimePlanFlags, { requireRuntime: false });
967
+ }
968
+ catch (error) {
969
+ io.writeStderr(error.message);
970
+ return 2;
971
+ }
936
972
  if (!targetPath) {
937
973
  io.writeStderr("Missing target path for runtime plan.");
938
974
  return 2;
@@ -945,7 +981,7 @@ export async function runCli(args, io = defaultIo, options = {}) {
945
981
  io.writeStderr("Use either --json or --markdown, not both.");
946
982
  return 2;
947
983
  }
948
- const plan = await buildDoctorRuntimePlan(targetPath);
984
+ const plan = await buildDoctorRuntimePlan(targetPath, new Date().toISOString(), runtimeSandbox ? { sandbox: runtimeSandbox } : {});
949
985
  const renderedPlan = jsonOutput
950
986
  ? renderDoctorRuntimePlanJson(plan)
951
987
  : markdownOutput
@@ -965,6 +1001,14 @@ export async function runCli(args, io = defaultIo, options = {}) {
965
1001
  const jsonOutput = runtimePolicyFlags.includes("--json");
966
1002
  const outputIndex = runtimePolicyFlags.indexOf("--output");
967
1003
  const outputPath = outputIndex === -1 ? null : runtimePolicyFlags[outputIndex + 1];
1004
+ let runtimeSandbox;
1005
+ try {
1006
+ runtimeSandbox = parseRuntimeSandbox(runtimePolicyFlags, { requireRuntime: false });
1007
+ }
1008
+ catch (error) {
1009
+ io.writeStderr(error.message);
1010
+ return 2;
1011
+ }
968
1012
  if (!targetPath) {
969
1013
  io.writeStderr("Missing target path for runtime policy.");
970
1014
  return 2;
@@ -973,7 +1017,7 @@ export async function runCli(args, io = defaultIo, options = {}) {
973
1017
  io.writeStderr("Missing path after --output.");
974
1018
  return 2;
975
1019
  }
976
- const report = await buildDoctorRuntimePolicyReport(targetPath);
1020
+ const report = await buildDoctorRuntimePolicyReport(targetPath, new Date().toISOString(), runtimeSandbox ? { sandbox: runtimeSandbox } : {});
977
1021
  const renderedReport = jsonOutput
978
1022
  ? renderDoctorRuntimePolicyJson(report)
979
1023
  : renderDoctorRuntimePolicy(report);
@@ -1138,6 +1182,15 @@ export async function runCli(args, io = defaultIo, options = {}) {
1138
1182
  const runtimeApprovalDigest = runtimeApprovalDigestIndex === -1
1139
1183
  ? null
1140
1184
  : assetFlags[runtimeApprovalDigestIndex + 1];
1185
+ const runtime = assetFlags.includes("--runtime");
1186
+ let runtimeSandbox;
1187
+ try {
1188
+ runtimeSandbox = parseRuntimeSandbox(assetFlags);
1189
+ }
1190
+ catch (error) {
1191
+ io.writeStderr(error.message);
1192
+ return 2;
1193
+ }
1141
1194
  if (!targetPath) {
1142
1195
  io.writeStderr("Missing target path for release evidence asset.");
1143
1196
  return 2;
@@ -1193,13 +1246,13 @@ export async function runCli(args, io = defaultIo, options = {}) {
1193
1246
  allowUntagged,
1194
1247
  requireRuntimeApproval,
1195
1248
  runtimeApprovalDigest,
1249
+ runtime,
1250
+ ...(runtimeSandbox ? { sandbox: runtimeSandbox } : {}),
1196
1251
  environment: {
1197
1252
  env: terminalContext.env,
1198
1253
  platform: terminalContext.platform
1199
1254
  },
1200
- runCheck: options.runCheckImpl
1201
- ? (pathToCheck) => options.runCheckImpl(pathToCheck)
1202
- : undefined,
1255
+ runCheck: options.runCheckImpl ?? runCheck,
1203
1256
  performanceThresholds: parsedThresholds.thresholds
1204
1257
  });
1205
1258
  await writeFile(resolvedOutputPath, renderDoctorReleaseEvidenceJson(evidence), "utf8");
@@ -1298,6 +1351,15 @@ export async function runCli(args, io = defaultIo, options = {}) {
1298
1351
  const runtimeApprovalDigest = runtimeApprovalDigestIndex === -1
1299
1352
  ? null
1300
1353
  : evidenceFlags[runtimeApprovalDigestIndex + 1];
1354
+ const runtime = evidenceFlags.includes("--runtime");
1355
+ let runtimeSandbox;
1356
+ try {
1357
+ runtimeSandbox = parseRuntimeSandbox(evidenceFlags);
1358
+ }
1359
+ catch (error) {
1360
+ io.writeStderr(error.message);
1361
+ return 2;
1362
+ }
1301
1363
  if (outputIndex !== -1 && (!outputPath || outputPath.startsWith("--"))) {
1302
1364
  io.writeStderr("Missing path after --output.");
1303
1365
  return 2;
@@ -1336,13 +1398,13 @@ export async function runCli(args, io = defaultIo, options = {}) {
1336
1398
  allowUntagged,
1337
1399
  requireRuntimeApproval,
1338
1400
  runtimeApprovalDigest,
1401
+ runtime,
1402
+ ...(runtimeSandbox ? { sandbox: runtimeSandbox } : {}),
1339
1403
  environment: {
1340
1404
  env: terminalContext.env,
1341
1405
  platform: terminalContext.platform
1342
1406
  },
1343
- runCheck: options.runCheckImpl
1344
- ? (pathToCheck) => options.runCheckImpl(pathToCheck)
1345
- : undefined,
1407
+ runCheck: options.runCheckImpl ?? runCheck,
1346
1408
  performanceThresholds: parsedThresholds.thresholds
1347
1409
  });
1348
1410
  const reportJson = renderDoctorReleaseEvidenceJson(report);
@@ -1357,16 +1419,39 @@ export async function runCli(args, io = defaultIo, options = {}) {
1357
1419
  const jsonOutput = remainingArgs.includes("--json");
1358
1420
  const outputIndex = remainingArgs.indexOf("--output");
1359
1421
  const outputPath = outputIndex === -1 ? null : remainingArgs[outputIndex + 1];
1422
+ const manifestIndex = remainingArgs.indexOf("--manifest");
1423
+ const manifestPath = manifestIndex === -1 ? null : remainingArgs[manifestIndex + 1];
1360
1424
  if (outputIndex !== -1 && (!outputPath || outputPath.startsWith("--"))) {
1361
1425
  io.writeStderr("Missing path after --output.");
1362
1426
  return 2;
1363
1427
  }
1364
- const report = await buildDoctorValidationCorpusReport({
1365
- environment: {
1366
- env: terminalContext.env,
1367
- platform: terminalContext.platform
1428
+ if (manifestIndex !== -1 && (!manifestPath || manifestPath.startsWith("--"))) {
1429
+ io.writeStderr("Missing path after --manifest.");
1430
+ return 2;
1431
+ }
1432
+ let report;
1433
+ try {
1434
+ report = manifestPath
1435
+ ? await buildExternalValidationCorpusReport(manifestPath, {
1436
+ environment: {
1437
+ env: terminalContext.env,
1438
+ platform: terminalContext.platform
1439
+ }
1440
+ })
1441
+ : await buildDoctorValidationCorpusReport({
1442
+ environment: {
1443
+ env: terminalContext.env,
1444
+ platform: terminalContext.platform
1445
+ }
1446
+ });
1447
+ }
1448
+ catch (error) {
1449
+ if (error instanceof ExternalCorpusManifestError) {
1450
+ io.writeStderr(error.message);
1451
+ return 2;
1368
1452
  }
1369
- });
1453
+ throw error;
1454
+ }
1370
1455
  const reportJson = renderDoctorValidationCorpusJson(report);
1371
1456
  if (outputPath) {
1372
1457
  await writeFile(outputPath, reportJson, "utf8");
@@ -2262,6 +2347,14 @@ export async function runCli(args, io = defaultIo, options = {}) {
2262
2347
  : remainingArgs.slice(1);
2263
2348
  const jsonOutput = releaseFlags.includes("--json");
2264
2349
  const runtimeProbeEnabled = releaseFlags.includes("--runtime");
2350
+ let runtimeSandbox;
2351
+ try {
2352
+ runtimeSandbox = parseRuntimeSandbox(releaseFlags);
2353
+ }
2354
+ catch (error) {
2355
+ io.writeStderr(error.message);
2356
+ return 2;
2357
+ }
2265
2358
  const requireRuntimeApproval = releaseFlags.includes("--require-runtime-approval");
2266
2359
  const runtimeApprovalDigestIndex = releaseFlags.indexOf("--runtime-approval-digest");
2267
2360
  const runtimeApprovalDigest = runtimeApprovalDigestIndex === -1
@@ -2277,7 +2370,7 @@ export async function runCli(args, io = defaultIo, options = {}) {
2277
2370
  return 2;
2278
2371
  }
2279
2372
  if (requireRuntimeApproval) {
2280
- const runtimePlan = await buildDoctorRuntimePlan(targetPath);
2373
+ const runtimePlan = await buildDoctorRuntimePlan(targetPath, new Date().toISOString(), runtimeSandbox ? { sandbox: runtimeSandbox } : {});
2281
2374
  const approval = evaluateRuntimeApproval(runtimePlan, {
2282
2375
  required: true,
2283
2376
  approvedDigest: runtimeApprovalDigest
@@ -2291,6 +2384,7 @@ export async function runCli(args, io = defaultIo, options = {}) {
2291
2384
  env: terminalContext.env,
2292
2385
  platform: terminalContext.platform,
2293
2386
  runtime: runtimeProbeEnabled,
2387
+ ...(runtimeSandbox ? { runtimeSandbox } : {}),
2294
2388
  runCheck: options.runCheckImpl
2295
2389
  });
2296
2390
  const output = jsonOutput
@@ -2331,6 +2425,14 @@ export async function runCli(args, io = defaultIo, options = {}) {
2331
2425
  : maybePath && maybePath.startsWith("--")
2332
2426
  ? [maybePath, ...remainingArgs]
2333
2427
  : remainingArgs;
2428
+ let runtimeSandbox;
2429
+ try {
2430
+ runtimeSandbox = parseRuntimeSandbox(normalizedFlags);
2431
+ }
2432
+ catch (error) {
2433
+ io.writeStderr(error.message);
2434
+ return 2;
2435
+ }
2334
2436
  const jsonOutput = normalizedFlags.includes("--json");
2335
2437
  const markdownOutput = normalizedFlags.includes("--markdown");
2336
2438
  const badgeJsonOutput = normalizedFlags.includes("--badge-json");
@@ -2458,7 +2560,7 @@ export async function runCli(args, io = defaultIo, options = {}) {
2458
2560
  });
2459
2561
  const runCheckImpl = options.runCheckImpl ?? runCheck;
2460
2562
  if (!checkInstalled && effectiveRuntimeProbeEnabled && requireRuntimeApproval) {
2461
- const runtimePlan = await buildDoctorRuntimePlan(targetPath);
2563
+ const runtimePlan = await buildDoctorRuntimePlan(targetPath, new Date().toISOString(), runtimeSandbox ? { sandbox: runtimeSandbox } : {});
2462
2564
  const approval = evaluateRuntimeApproval(runtimePlan, {
2463
2565
  required: true,
2464
2566
  approvedDigest: runtimeApprovalDigest
@@ -2580,9 +2682,10 @@ export async function runCli(args, io = defaultIo, options = {}) {
2580
2682
  const doctorConfig = applyPolicyToDoctorConfig(applyCheckProfile(await loadDoctorConfig(targetPath, configPath), checkProfile), policy);
2581
2683
  const configuredResult = applyDoctorConfig(await runCheckImpl(targetPath, {
2582
2684
  runtime: effectiveRuntimeProbeEnabled,
2583
- runtimeTranscript: effectiveRuntimeProbeEnabled && verboseRuntime
2584
- ? (line) => io.writeStderr(line)
2585
- : undefined
2685
+ ...(runtimeSandbox ? { runtimeSandbox } : {}),
2686
+ ...(effectiveRuntimeProbeEnabled && verboseRuntime
2687
+ ? { runtimeTranscript: (line) => io.writeStderr(line) }
2688
+ : {})
2586
2689
  }), doctorConfig);
2587
2690
  const result = baseline
2588
2691
  ? applyValidationBaseline(configuredResult, baseline, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-plugin-doctor",
3
- "version": "1.46.1",
3
+ "version": "1.47.0",
4
4
  "description": "CLI-first validator for Codex plugins, skills, and MCP package surfaces with runtime MCP protocol validation.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",