fix(sub2api): enable JD01 sentinel without docker build

This commit is contained in:
Codex
2026-06-30 10:03:23 +00:00
parent cec4b7a138
commit 43606b0d06
4 changed files with 44 additions and 38 deletions
@@ -212,7 +212,7 @@ sentinel:
actions:
enabled: true
schedule: "*/1 * * * *"
image: python:3.12-alpine
image: docker.m.daocloud.io/library/python:3.12-alpine
sdk:
openaiPythonVersion: "2.41.1"
serviceAccountName: sub2api-account-sentinel
+2 -1
View File
@@ -359,7 +359,8 @@ runtime:
mode: empty-dir
sentinel:
mode: singleton
enabledOnTargets: []
enabledOnTargets:
- JD01
security:
urlAllowlist:
enabled: false
@@ -100,16 +100,11 @@ export interface CodexPoolSentinelManifestOptions {
}
export function codexPoolSentinelRuntimeImage(config: CodexPoolSentinelConfig): CodexPoolSentinelImageTarget {
const baseTag = config.image
.replace(/[^A-Za-z0-9_.-]+/gu, "-")
.replace(/^-+|-+$/gu, "")
.slice(0, 80) || "python";
const tag = `${baseTag}-openai-${config.sdk.openaiPythonVersion}`;
const repository = "127.0.0.1:5000/platform-infra/sub2api-account-sentinel";
const [repository, tag = "latest"] = config.image.split(":");
return {
baseImage: config.image,
runtimeImage: `${repository}:${tag}`,
repository,
runtimeImage: config.image,
repository: repository ?? config.image,
tag,
};
}
@@ -446,14 +441,27 @@ ${proxyEnv}
export function sentinelContainerShellCommand(config: CodexPoolSentinelConfig): string {
return [
"set -eu",
"python3 - <<'PY'",
`OPENAI_PYTHON_VERSION=${JSON.stringify(config.sdk.openaiPythonVersion)}`,
"if ! python3 - <<'PY'",
"import importlib.metadata",
`expected = ${JSON.stringify(config.sdk.openaiPythonVersion)}`,
"import os",
"expected = os.environ['OPENAI_PYTHON_VERSION']",
"try:",
" current = importlib.metadata.version('openai')",
"except importlib.metadata.PackageNotFoundError:",
" current = None",
"if current != expected:",
" raise SystemExit(1)",
"PY",
"then",
" python3 -m pip install --no-cache-dir \"openai==$OPENAI_PYTHON_VERSION\"",
"fi",
"python3 - <<'PY'",
"import importlib.metadata",
"import os",
"expected = os.environ['OPENAI_PYTHON_VERSION']",
"current = importlib.metadata.version('openai')",
"if current != expected:",
" raise SystemExit(f'openai-python-version-mismatch expected={expected} current={current}')",
"PY",
"exec python3 /opt/sentinel/sentinel.py",
@@ -21,7 +21,6 @@ import {
import { parseEnvFile, readTextFile, redactRepoPath, requiredEnvValue } from "../secrets";
import { runSshCommandCapture, type SshCaptureResult } from "../ssh";
import type { RemoteCodexPoolMode } from "./remote";
import type { CodexPoolConfig, ConfirmOptions, DisclosureOptions, SentinelImageOptions, SentinelProbeOptions, SentinelReportOptions, SyncOptions, TraceOptions } from "./types";
import { desiredAccountNames } from "./accounts";
import { collectCodexProfiles, readCodexPoolConfig } from "./config";
@@ -30,10 +29,10 @@ import { apiKeyPreview, codexConsumerBaseUrl, fetchPoolApiKey, probePublicModels
import { manualBindingSourcePlan, poolTarget, prepareTargetPublicExposureSecret, resolvedManualAccountProtections, secretMaterialSummary, sentinelProfileSecrets, targetFrpPublicExposure, targetPublicExposureApplyScript, targetPublicExposureSummary } from "./public-exposure";
import { codexPoolConfigSummary, compactProfile, compactSentinelProbeResult, redactProfile, renderSub2ApiTempUnschedulableCredentials } from "./redaction";
import { boolField, capture, compactCapture, parseJsonOutput, runRemoteCodexPoolScript } from "./remote";
import { cleanupProbesScript, sentinelImageBuildScript, sentinelImageStatusScript, sentinelProbeScript, sentinelReportScript, syncScript, traceScript, validateScript } from "./remote-scripts";
import { cleanupProbesScript, sentinelProbeScript, sentinelReportScript, syncScript, traceScript, validateScript } from "./remote-scripts";
import { codexPoolSyncSummary, codexPoolValidationSummary, renderSentinelReport, renderTraceReport, renderedCliResult } from "./render";
import { codexPoolRuntimeTarget, defaultCodexPoolRuntimeTargetId, targetFlag } from "./runtime-target";
import { codexPoolConfigPath, sentinelImageDockerfilePath, serviceName, sub2apiConfigPath } from "./types";
import { codexPoolConfigPath, serviceName, sub2apiConfigPath } from "./types";
export function codexPoolPlan(options?: DisclosureOptions): Record<string, unknown> {
const resolvedOptions = options ?? { full: false, raw: false, targetId: defaultCodexPoolRuntimeTargetId() };
@@ -242,6 +241,7 @@ export async function codexPoolSentinelImage(config: UniDeskConfig, options: Sen
export async function runCodexPoolSentinelImage(config: UniDeskConfig, pool: CodexPoolConfig, options: SentinelImageOptions): Promise<Record<string, unknown>> {
const runtimeTarget = codexPoolRuntimeTarget(options.targetId);
const target = codexPoolSentinelRuntimeImage(pool.sentinel);
void config;
if (!runtimeTarget.sentinelEnabled) {
return {
ok: true,
@@ -256,40 +256,37 @@ export async function runCodexPoolSentinelImage(config: UniDeskConfig, pool: Cod
valuesPrinted: false,
};
}
if (options.action === "build" && options.dryRun) {
const summary = {
ok: true,
mode: options.action === "status" ? "base-image-runtime" : options.dryRun ? "dry-run-base-image-runtime" : "skipped-build-base-image-runtime",
target: {
id: runtimeTarget.id,
namespace: runtimeTarget.namespace,
},
image: target.runtimeImage,
baseImage: target.baseImage,
openaiPythonVersion: pool.sentinel.sdk.openaiPythonVersion,
sdkInstall: "container-startup-pinned",
dockerRequired: false,
registryPushRequired: false,
mutation: false,
valuesPrinted: false,
};
if (options.raw) {
return {
ok: true,
action: "platform-infra-sub2api-codex-pool-sentinel-image",
mode: "dry-run",
image: target,
dockerfile: sentinelImageDockerfilePath,
mutation: false,
next: {
confirm: `bun scripts/cli.ts platform-infra sub2api codex-pool sentinel-image build${targetFlag(runtimeTarget)} --confirm`,
},
};
}
const mode: RemoteCodexPoolMode = options.action === "status" ? "sentinel-image-status" : "sentinel-image-build";
const script = options.action === "status" ? sentinelImageStatusScript(pool, runtimeTarget) : sentinelImageBuildScript(pool, runtimeTarget);
const result = await runRemoteCodexPoolScript(config, mode, script, runtimeTarget);
const parsed = parseJsonOutput(result.stdout);
if (options.raw) {
return {
ok: result.exitCode === 0 && boolField(parsed, "ok", false),
action: "platform-infra-sub2api-codex-pool-sentinel-image",
mode: options.action,
image: target,
remote: compactCapture(result, { full: true }),
parsed,
parsed: summary,
};
}
return {
ok: result.exitCode === 0 && boolField(parsed, "ok", false),
ok: true,
action: "platform-infra-sub2api-codex-pool-sentinel-image",
mode: options.action,
image: target,
summary: parsed,
remote: compactCapture(result, { full: options.full || result.exitCode !== 0 }),
summary,
};
}