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/INSTALL.md +15 -2
- package/README.i18n.yaml +2 -2
- package/README.md +62 -16
- package/compatibility.json +3 -2
- package/docs/README.zh.md +61 -13
- package/docs/design.i18n.yaml +2 -2
- package/docs/design.md +1 -1
- package/docs/design.zh.md +1 -1
- package/lib/bin.js +361 -15
- package/lib/client.js +962 -248
- package/lib/index.d.ts +39 -11
- package/lib/index.js +2 -2
- package/lib/{src-Cxq99TvF.js → src-ByKP_wn9.js} +230 -57
- package/package.json +81 -54
package/lib/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { B as DSH_PLUGIN_API_PACKAGES,
|
|
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.
|
|
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",
|