dsh-codex-connect 0.1.0-alpha.4.21 → 0.1.0-alpha.4.23

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/lib/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { B as DSH_PLUGIN_API_PACKAGES, Dt as logoutOpenAICodex, Et as loginOpenAICodex, J as readInstalledPackageVersion, Mt as OpenAICodexCredentialStore, Nt as openAICodexAuthPath, Ot as openAICodexAuthStatus, St as normalizeTrustedOrigin, c as migrateOpenAICodexSearchHistory, j as CODEX_CONNECT_VERSION, jt as OPENAI_CODEX_PROVIDER, k as diagnoseOpenAICodex, kt as openAICodexModelCatalog, l as OPENAI_CODEX_BASE_URL, pt as normalizeOpenAICodexProxyUrl, q as evaluateCompatibility, xt as OpenAICodexTrustedOriginsStore } from "./src-Cxq99TvF.js";
2
+ import { At as openAICodexModelCatalog, B as DSH_PLUGIN_API_PACKAGES, Ct as normalizeTrustedOrigin, Dt as loginOpenAICodex, J as readInstalledPackageVersion, Mt as OPENAI_CODEX_PROVIDER, Nt as OpenAICodexCredentialStore, Ot as logoutOpenAICodex, Pt as openAICodexAuthPath, St as OpenAICodexTrustedOriginsStore, c as migrateOpenAICodexSearchHistory, j as CODEX_CONNECT_VERSION, k as diagnoseOpenAICodex, kt as openAICodexAuthStatus, l as OPENAI_CODEX_BASE_URL, mt as normalizeOpenAICodexProxyUrl, q as evaluateCompatibility } from "./src-ByKP_wn9.js";
3
3
  import { createHash } from "node:crypto";
4
4
  import { Agent, ProxyAgent, fetch } from "undici";
5
5
  import { fileURLToPath } from "node:url";
@@ -9,17 +9,17 @@ import { createInterface } from "node:readline/promises";
9
9
  //#region src/capability-probe.ts
10
10
  /** Bounded, standalone Responses probe; never used by conversation routing. */
11
11
  /** A security limit, not a model output-token setting. */
12
- const MAX_PROBE_RESPONSE_BYTES = 65536;
13
- function record(value) {
12
+ const MAX_PROBE_RESPONSE_BYTES$1 = 65536;
13
+ function record$1(value) {
14
14
  return typeof value === "object" && value !== null && !Array.isArray(value);
15
15
  }
16
- function completedResponse(value, model) {
17
- if (!record(value) || value["status"] !== "completed" || !Array.isArray(value["output"])) return false;
16
+ function completedResponse$1(value, model) {
17
+ if (!record$1(value) || value["status"] !== "completed" || !Array.isArray(value["output"])) return false;
18
18
  if (value["model"] !== model) return false;
19
- return value["output"].some((item) => record(item) && item["type"] === "message" && item["role"] === "assistant" && Array.isArray(item["content"]) && item["content"].some((part) => record(part) && part["type"] === "output_text" && typeof part["text"] === "string" && part["text"].trim().length > 0));
19
+ return value["output"].some((item) => record$1(item) && item["type"] === "message" && item["role"] === "assistant" && Array.isArray(item["content"]) && item["content"].some((part) => record$1(part) && part["type"] === "output_text" && typeof part["text"] === "string" && part["text"].trim().length > 0));
20
20
  }
21
21
  /** Inspect complete SSE frames, not substrings inside error text or schema errors. */
22
- function completedStream(text, model) {
22
+ function completedStream$1(text, model) {
23
23
  let completed = false;
24
24
  let data = [];
25
25
  for (const line of text.split(/\r\n|\r|\n/u)) if (line === "") {
@@ -33,10 +33,10 @@ function completedStream(text, model) {
33
33
  } catch {
34
34
  return false;
35
35
  }
36
- if (!record(event)) return false;
36
+ if (!record$1(event)) return false;
37
37
  if (event["type"] === "error" || event["type"] === "response.failed" || event["type"] === "response.incomplete") return false;
38
38
  if (event["type"] === "response.completed" || event["type"] === "response.done") {
39
- if (completed || !completedResponse(event["response"], model)) return false;
39
+ if (completed || !completedResponse$1(event["response"], model)) return false;
40
40
  completed = true;
41
41
  }
42
42
  } else if (line.startsWith("data:")) data.push(line.slice(5).replace(/^ /u, ""));
@@ -112,7 +112,7 @@ async function probeCodexResponses(request, createDispatcher = (proxyUrl) => pro
112
112
  const { value, done } = await reader.read();
113
113
  if (done) break;
114
114
  size += value.byteLength;
115
- if (size > MAX_PROBE_RESPONSE_BYTES) {
115
+ if (size > MAX_PROBE_RESPONSE_BYTES$1) {
116
116
  await reader.cancel();
117
117
  return {
118
118
  outcome: "incomplete",
@@ -125,7 +125,7 @@ async function probeCodexResponses(request, createDispatcher = (proxyUrl) => pro
125
125
  reader.releaseLock();
126
126
  }
127
127
  return {
128
- outcome: completedStream(new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks)), request.model) ? "completed" : "incomplete",
128
+ outcome: completedStream$1(new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks)), request.model) ? "completed" : "incomplete",
129
129
  httpStatus
130
130
  };
131
131
  } catch {
@@ -151,7 +151,7 @@ function result(status, reason, action) {
151
151
  function safeVersion(value) {
152
152
  return value !== null && value !== void 0 && /^v?\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/u.test(value) ? value : null;
153
153
  }
154
- function observed(evidence) {
154
+ function observed$1(evidence) {
155
155
  switch (evidence.outcome) {
156
156
  case "completed": return result("supported", "completed-response-for-selected-model", "This proves only the fixed standalone prompt; test the active Harness profile separately.");
157
157
  case "http-rejected": switch (evidence.httpStatus) {
@@ -208,7 +208,7 @@ var CodexCapabilityDiagnostics = class {
208
208
  packageVersions: versions
209
209
  });
210
210
  const missing = Object.values(versions).some((value) => value === null) || !/^v?\d+\.\d+\.\d+$/u.test(versions["node"] ?? "");
211
- const runtime = DSH_PLUGIN_API_PACKAGES.some((name) => versions[name] !== null && versions[name] !== "0.1.1-rc.2") || compatibility.status === "incompatible" ? result("rejected", "declared-version-mismatch", "Use DSH API 0.1.1-rc.2 and pi-ai 0.82.1 together. DSH 0.1.0-rc.7 requires Codex Connect 0.1.0-alpha.4.14.") : missing || compatibility.status === "unknown" ? result("unknown", "version-metadata-unavailable", "Run this command from the plugin installation in the intended profile.") : result("supported", "declared-host-versions-match", "Host package versions satisfy the declared requirements; this is not a live profile or browser compatibility test.");
211
+ const runtime = DSH_PLUGIN_API_PACKAGES.some((name) => versions[name] !== null && versions[name] !== "0.1.2-alpha.2") || compatibility.status === "incompatible" ? result("rejected", "declared-version-mismatch", "Use DSH API 0.1.2-alpha.2 and pi-ai ^0.84.2 together. DSH 0.1.0-rc.7 requires Codex Connect 0.1.0-alpha.4.14.") : missing || compatibility.status === "unknown" ? result("unknown", "version-metadata-unavailable", "Run this command from the plugin installation in the intended profile.") : result("supported", "declared-host-versions-match", "Host package versions satisfy the declared requirements; this is not a live profile or browser compatibility test.");
212
212
  const model = this.deps.catalog().find((item) => item.id === request.model)?.id ?? null;
213
213
  const unknownNetwork = result("unknown", "not-probed", "Run capabilities --model <catalog-id> --probe explicitly; this sends a fixed short request and may consume quota.");
214
214
  const checks = {
@@ -295,7 +295,7 @@ var CodexCapabilityDiagnostics = class {
295
295
  };
296
296
  }
297
297
  if (evidence.httpStatus !== void 0) report.probe.httpStatus = evidence.httpStatus;
298
- checks.responses = observed(evidence);
298
+ checks.responses = observed$1(evidence);
299
299
  checks.transport = { ...checks.responses };
300
300
  if (evidence.outcome === "completed") {
301
301
  checks.oauth = result("supported", "authorized-completed-response", "Authorization succeeded for this model and fixed request at the recorded time only.");
@@ -369,6 +369,349 @@ async function runCapabilityCommand(args) {
369
369
  }
370
370
  }
371
371
  //#endregion
372
+ //#region src/auto-review-probe.ts
373
+ /** Fixed, standalone probe for the hidden Codex approval reviewer. */
374
+ /** Hidden reviewer route selected by the first-party Codex catalog. */
375
+ const CODEX_AUTO_REVIEW_MODEL = "codex-auto-review";
376
+ /** A response-reading limit, not a model output-token setting. */
377
+ const MAX_PROBE_RESPONSE_BYTES = 65536;
378
+ const instructions = "This is a capability diagnostic. Do not call tools or execute any action. Return one strict JSON assessment for the supplied synthetic approval request, including risk_level, user_authorization, outcome, and rationale.";
379
+ const input = "Synthetic approval request only; nothing will be executed. Planned action JSON: {\"type\":\"diagnostic-no-op\",\"sideEffects\":false}";
380
+ const assessmentSchema = {
381
+ type: "object",
382
+ additionalProperties: false,
383
+ properties: {
384
+ risk_level: {
385
+ type: "string",
386
+ enum: [
387
+ "low",
388
+ "medium",
389
+ "high",
390
+ "critical"
391
+ ]
392
+ },
393
+ user_authorization: {
394
+ type: "string",
395
+ enum: [
396
+ "unknown",
397
+ "low",
398
+ "medium",
399
+ "high"
400
+ ]
401
+ },
402
+ outcome: {
403
+ type: "string",
404
+ enum: ["allow", "deny"]
405
+ },
406
+ rationale: { type: "string" }
407
+ },
408
+ required: [
409
+ "risk_level",
410
+ "user_authorization",
411
+ "outcome",
412
+ "rationale"
413
+ ]
414
+ };
415
+ function record(value) {
416
+ return typeof value === "object" && value !== null && !Array.isArray(value);
417
+ }
418
+ function assessment(text) {
419
+ let value;
420
+ try {
421
+ value = JSON.parse(text);
422
+ } catch {
423
+ return false;
424
+ }
425
+ if (!record(value)) return false;
426
+ if (Object.keys(value).some((key) => ![
427
+ "risk_level",
428
+ "user_authorization",
429
+ "outcome",
430
+ "rationale"
431
+ ].includes(key))) return false;
432
+ if (![
433
+ "low",
434
+ "medium",
435
+ "high",
436
+ "critical"
437
+ ].includes(String(value["risk_level"]))) return false;
438
+ if (![
439
+ "unknown",
440
+ "low",
441
+ "medium",
442
+ "high"
443
+ ].includes(String(value["user_authorization"]))) return false;
444
+ if (!["allow", "deny"].includes(String(value["outcome"]))) return false;
445
+ return typeof value["rationale"] === "string";
446
+ }
447
+ function completedResponse(value) {
448
+ if (!record(value) || value["status"] !== "completed" || typeof value["model"] !== "string" || !Array.isArray(value["output"])) return void 0;
449
+ return value["output"].flatMap((item) => record(item) && item["type"] === "message" && item["role"] === "assistant" && Array.isArray(item["content"]) ? item["content"].flatMap((part) => record(part) && part["type"] === "output_text" && typeof part["text"] === "string" ? [part["text"]] : []) : []);
450
+ }
451
+ /** Inspect complete SSE frames and accept exactly one matching terminal response. */
452
+ function completedStream(text) {
453
+ let completed = false;
454
+ let terminalTexts;
455
+ const streamedTexts = [];
456
+ let data = [];
457
+ for (const line of text.split(/\r\n|\r|\n/u)) if (line === "") {
458
+ if (data.length === 0) continue;
459
+ const payload = data.join("\n");
460
+ data = [];
461
+ if (payload === "[DONE]") continue;
462
+ let event;
463
+ try {
464
+ event = JSON.parse(payload);
465
+ } catch {
466
+ return false;
467
+ }
468
+ if (!record(event) || event["type"] === "error" || event["type"] === "response.failed" || event["type"] === "response.incomplete") return false;
469
+ if (event["type"] === "response.output_text.done") {
470
+ if (typeof event["text"] !== "string") return false;
471
+ streamedTexts.push(event["text"]);
472
+ }
473
+ if (event["type"] === "response.completed" || event["type"] === "response.done") {
474
+ terminalTexts = completedResponse(event["response"]);
475
+ if (completed || terminalTexts === void 0) return false;
476
+ completed = true;
477
+ }
478
+ } else if (line.startsWith("data:")) data.push(line.slice(5).replace(/^ /u, ""));
479
+ if (!completed || data.length !== 0) return false;
480
+ const texts = terminalTexts.length > 0 ? terminalTexts : streamedTexts;
481
+ return texts.length === 1 && assessment(texts[0]);
482
+ }
483
+ /**
484
+ * Send one secret-free synthetic approval to the hidden reviewer model.
485
+ * The result is evidence only and never authorizes or executes an action.
486
+ * @param request - OAuth credential, explicit network policy, deadline, and optional cancellation.
487
+ * @param createDispatcher - owned connection factory; tests use an offline dispatcher.
488
+ * @returns bounded evidence without model-generated or provider error text.
489
+ */
490
+ async function probeCodexAutoReview(request, createDispatcher = (proxyUrl) => proxyUrl === void 0 ? new Agent() : new ProxyAgent(proxyUrl)) {
491
+ const dispatcher = createDispatcher(request.proxyUrl);
492
+ const controller = new AbortController();
493
+ let timedOut = false;
494
+ let cancelled = request.signal?.aborted ?? false;
495
+ const cancel = () => {
496
+ cancelled = true;
497
+ controller.abort();
498
+ };
499
+ request.signal?.addEventListener("abort", cancel, { once: true });
500
+ if (cancelled) controller.abort();
501
+ const timer = setTimeout(() => {
502
+ timedOut = true;
503
+ controller.abort();
504
+ }, request.timeoutMs);
505
+ let httpStatus;
506
+ try {
507
+ const response = await fetch(`${OPENAI_CODEX_BASE_URL}/responses`, {
508
+ dispatcher,
509
+ method: "POST",
510
+ redirect: "manual",
511
+ signal: controller.signal,
512
+ headers: {
513
+ authorization: `Bearer ${request.access}`,
514
+ "chatgpt-account-id": request.accountId,
515
+ "content-type": "application/json",
516
+ accept: "text/event-stream",
517
+ originator: "deepseek-harness"
518
+ },
519
+ body: JSON.stringify({
520
+ model: CODEX_AUTO_REVIEW_MODEL,
521
+ instructions,
522
+ input: [{
523
+ role: "user",
524
+ content: [{
525
+ type: "input_text",
526
+ text: input
527
+ }]
528
+ }],
529
+ text: { format: {
530
+ type: "json_schema",
531
+ name: "codex_auto_review_assessment",
532
+ strict: true,
533
+ schema: assessmentSchema
534
+ } },
535
+ stream: true,
536
+ store: false
537
+ })
538
+ });
539
+ httpStatus = response.status;
540
+ if (!response.ok) {
541
+ await response.body?.cancel();
542
+ return {
543
+ outcome: [
544
+ 400,
545
+ 401,
546
+ 403,
547
+ 404,
548
+ 405,
549
+ 422
550
+ ].includes(httpStatus) ? "http-rejected" : "transient",
551
+ httpStatus
552
+ };
553
+ }
554
+ const contentType = response.headers.get("content-type");
555
+ if (httpStatus !== 200 || contentType !== null && !contentType.toLowerCase().startsWith("text/event-stream") || response.body === null) {
556
+ await response.body?.cancel();
557
+ return {
558
+ outcome: "incomplete",
559
+ httpStatus
560
+ };
561
+ }
562
+ const reader = response.body.getReader();
563
+ const chunks = [];
564
+ let size = 0;
565
+ try {
566
+ while (true) {
567
+ const { value, done } = await reader.read();
568
+ if (done) break;
569
+ size += value.byteLength;
570
+ if (size > MAX_PROBE_RESPONSE_BYTES) {
571
+ await reader.cancel();
572
+ return {
573
+ outcome: "incomplete",
574
+ httpStatus
575
+ };
576
+ }
577
+ chunks.push(value);
578
+ }
579
+ } finally {
580
+ reader.releaseLock();
581
+ }
582
+ return {
583
+ outcome: completedStream(new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks))) ? "completed" : "incomplete",
584
+ httpStatus
585
+ };
586
+ } catch {
587
+ return {
588
+ outcome: cancelled ? "cancelled" : timedOut ? "timeout" : "network-error",
589
+ ...httpStatus === void 0 ? {} : { httpStatus }
590
+ };
591
+ } finally {
592
+ clearTimeout(timer);
593
+ request.signal?.removeEventListener("abort", cancel);
594
+ await dispatcher.destroy();
595
+ }
596
+ }
597
+ //#endregion
598
+ //#region src/auto-review-cli.ts
599
+ /** Opt-in diagnostic for the hidden Codex approval reviewer. */
600
+ function check(status, reason, action) {
601
+ return {
602
+ status,
603
+ reason,
604
+ action
605
+ };
606
+ }
607
+ function observed(evidence) {
608
+ switch (evidence.outcome) {
609
+ case "completed": return check("supported", "completed-structured-review", "The OAuth route accepted the hidden reviewer and returned its approval schema; this does not enable DSH approval integration.");
610
+ case "http-rejected": return check("rejected", `http-${String(evidence.httpStatus ?? "rejected")}`, "The supported OAuth route rejected this hidden reviewer request; keep automatic approval disabled.");
611
+ case "transient": return check("unknown", "transient-or-redirect-response", "Check network, quota, or service availability before explicitly retrying.");
612
+ case "incomplete": return check("unknown", "no-complete-structured-review", "HTTP success without one matching structured assessment is not capability evidence.");
613
+ case "timeout": return check("unknown", "probe-deadline", "The diagnostic exceeded its deadline; no approval capability was established.");
614
+ case "cancelled": return check("unknown", "probe-cancelled", "The diagnostic was cancelled; no approval capability was established.");
615
+ case "network-error": return check("unknown", "network-or-stream-error", "Check the network or pass an explicit --proxy; no server decision was confirmed.");
616
+ }
617
+ }
618
+ /** Run the standalone reviewer probe without booting Harness or changing settings. */
619
+ async function runAutoReviewProbeCommand(args, deps = {
620
+ diagnose: diagnoseOpenAICodex,
621
+ credentials: new OpenAICodexCredentialStore(),
622
+ probe: probeCodexAutoReview,
623
+ now: Date.now
624
+ }) {
625
+ let json = false;
626
+ let proxyUrl;
627
+ let timeoutMs = 3e4;
628
+ const seen = /* @__PURE__ */ new Set();
629
+ const invalid = () => {
630
+ process.stderr.write("Usage: dsh-codex-connect auto-review-probe [--proxy <http(s)-origin>] [--timeout-ms <1..60000>] [--json]\n");
631
+ return 2;
632
+ };
633
+ for (let index = 0; index < args.length; index += 1) {
634
+ const flag = args[index];
635
+ if (seen.has(flag)) return invalid();
636
+ seen.add(flag);
637
+ if (flag === "--json") json = true;
638
+ else if (flag === "--proxy" || flag === "--timeout-ms") {
639
+ const value = args[++index];
640
+ if (value === void 0 || value.startsWith("--")) return invalid();
641
+ if (flag === "--proxy") {
642
+ proxyUrl = normalizeOpenAICodexProxyUrl(value);
643
+ if (proxyUrl === void 0) return invalid();
644
+ } else {
645
+ if (!/^\d+$/u.test(value) || Number(value) < 1 || Number(value) > 6e4) return invalid();
646
+ timeoutMs = Number(value);
647
+ }
648
+ } else return invalid();
649
+ }
650
+ const local = await deps.diagnose();
651
+ const runtime = local.compatibility.status === "compatible" ? check("supported", "declared-host-versions-match", "Installed package versions satisfy the probe prerequisites; this is not active-profile validation.") : check(local.compatibility.status === "incompatible" ? "rejected" : "unknown", "runtime-compatibility-unavailable", "Install the supported Codex Connect and DSH package set before probing.");
652
+ let oauth = local.credentialFile.state === "owner-only" ? check("unknown", "credential-metadata-only", "An owner-only file does not prove authorization; the explicit probe reads its unexpired stored credential.") : check("rejected", "credential-file-unusable", "Sign in or repair the owner-only credential file before probing.");
653
+ let reviewer = check("unknown", "not-probed", "Run this command explicitly only when one fixed diagnostic request is acceptable.");
654
+ let probe = { state: "skipped" };
655
+ if (runtime.status === "supported" && oauth.status !== "rejected") {
656
+ let credential;
657
+ try {
658
+ credential = await deps.credentials.read(OPENAI_CODEX_PROVIDER);
659
+ } catch {
660
+ oauth = check("rejected", "credential-unreadable", "Repair the credential file or sign in again; diagnostic output omits parser details.");
661
+ }
662
+ if (credential?.type !== "oauth" || typeof credential.accountId !== "string") {
663
+ if (oauth.reason !== "credential-unreadable") oauth = check("rejected", "credential-missing", "Sign in before explicitly probing the hidden reviewer.");
664
+ } else if (credential.expires <= deps.now()) oauth = check("unknown", "access-token-expired", "Use the normal sign-in or refresh flow, then repeat the probe; diagnostics never refresh credentials.");
665
+ else {
666
+ let evidence;
667
+ try {
668
+ evidence = await deps.probe({
669
+ access: credential.access,
670
+ accountId: credential.accountId,
671
+ proxyUrl,
672
+ timeoutMs
673
+ });
674
+ } catch {
675
+ evidence = { outcome: "network-error" };
676
+ }
677
+ probe = {
678
+ state: "fresh",
679
+ ...evidence.httpStatus === void 0 ? {} : { httpStatus: evidence.httpStatus }
680
+ };
681
+ reviewer = observed(evidence);
682
+ if (evidence.outcome === "completed") oauth = check("supported", "authorized-completed-review", "Authorization succeeded for this fixed reviewer request at the recorded time only.");
683
+ else if (evidence.httpStatus === 401) oauth = check("rejected", "http-401", "Sign in again, then explicitly repeat the probe.");
684
+ }
685
+ }
686
+ const report = {
687
+ schemaVersion: 1,
688
+ package: "dsh-codex-connect",
689
+ version: CODEX_CONNECT_VERSION,
690
+ scope: "auto-review-route-only",
691
+ model: CODEX_AUTO_REVIEW_MODEL,
692
+ network: proxyUrl === void 0 ? "direct" : "explicit-proxy",
693
+ checks: {
694
+ runtime,
695
+ oauth,
696
+ reviewer
697
+ },
698
+ probe
699
+ };
700
+ process.stdout.write(json ? `${JSON.stringify(report)}\n` : [
701
+ `Codex Connect ${report.version}: hidden approval-review capability probe`,
702
+ "Scope: one synthetic no-op; no command is executed and no approval integration is enabled.",
703
+ `Model: ${report.model}; network: ${report.network}; probe: ${report.probe.state}`,
704
+ ...Object.entries(report.checks).map(([id, value]) => `${id}: ${value.status} (${value.reason})\n ${value.action}`),
705
+ ""
706
+ ].join("\n"));
707
+ const primary = [
708
+ runtime,
709
+ oauth,
710
+ reviewer
711
+ ];
712
+ return primary.some((value) => value.status === "rejected") ? 1 : primary.some((value) => value.status === "unknown") ? 2 : 0;
713
+ }
714
+ //#endregion
372
715
  //#region src/bin.ts
373
716
  /** Standalone credential CLI for the optional OpenAI Codex bundle. */
374
717
  const JSON_SCHEMA_VERSION = 1;
@@ -435,8 +778,10 @@ function printHelp() {
435
778
  " dsh-codex-connect trusted-origins [--json]",
436
779
  " dsh-codex-connect untrust-origin <origin>",
437
780
  " dsh-codex-connect capabilities [--model <catalog-id>] [--probe] [--proxy <http(s)-origin>] [--timeout-ms <1..60000>] [--json]",
781
+ " dsh-codex-connect auto-review-probe [--proxy <http(s)-origin>] [--timeout-ms <1..60000>] [--json]",
438
782
  "",
439
783
  " doctor inspect secret-free runtime and OAuth file metadata",
784
+ " auto-review-probe test the hidden approval reviewer with one synthetic no-op",
440
785
  " login sign in with a separate ChatGPT OAuth session",
441
786
  " logout remove the dsh credential without changing ~/.codex",
442
787
  " migrate-history find or repair Alpha 4.10 private search events (dry-run by default)",
@@ -445,7 +790,7 @@ function printHelp() {
445
790
  " trusted-origins list the currently allowed browser origins",
446
791
  " untrust-origin remove one exact browser origin from the allowlist",
447
792
  " --device-code use headless device-code login (login only)",
448
- " --json emit one JSON document (doctor/status/trusted-origins/migrate-history)",
793
+ " --json emit one JSON document (doctor/status/capabilities/auto-review-probe/trusted-origins/migrate-history)",
449
794
  ""
450
795
  ].join("\n"));
451
796
  }
@@ -483,6 +828,7 @@ async function run(argv) {
483
828
  }
484
829
  const [rawAction, ...flags] = argv;
485
830
  if (rawAction === "capabilities") return runCapabilityCommand(flags);
831
+ if (rawAction === "auto-review-probe") return runAutoReviewProbeCommand(flags);
486
832
  if (![
487
833
  "doctor",
488
834
  "login",