138 lines
4.6 KiB
JavaScript
138 lines
4.6 KiB
JavaScript
import { ENVIRONMENT_DEV } from "../protocol/index.mjs";
|
|
|
|
export const DEV_CODE_AGENT_PROVIDER_CONTRACT = Object.freeze({
|
|
contractVersion: "v1",
|
|
environment: ENVIRONMENT_DEV,
|
|
provider: "openai",
|
|
runtimeProvider: "openai-responses",
|
|
backend: "hwlab-cloud-api/openai-responses",
|
|
model: "gpt-5.5",
|
|
requiredEnv: Object.freeze([
|
|
"OPENAI_API_KEY",
|
|
"HWLAB_CODE_AGENT_OPENAI_BASE_URL"
|
|
]),
|
|
secretRefs: Object.freeze([
|
|
Object.freeze({
|
|
env: "OPENAI_API_KEY",
|
|
secretName: "hwlab-code-agent-provider",
|
|
secretKey: "openai-api-key"
|
|
})
|
|
]),
|
|
egress: Object.freeze({
|
|
env: "HWLAB_CODE_AGENT_OPENAI_BASE_URL",
|
|
target: "dev-egress-proxy",
|
|
defaultBaseUrl: "http://172.26.26.227:17680/v1/responses",
|
|
forbiddenDirectBaseUrl: "https://api.openai.com/v1/responses",
|
|
reason: "DEV cloud-api pods must use the D601 egress/proxy path for OpenAI Responses."
|
|
}),
|
|
forbidden: Object.freeze({
|
|
prodAllowed: false,
|
|
secretPlaintextAllowed: false,
|
|
directPublicOpenAiFromDevPod: false
|
|
})
|
|
});
|
|
|
|
export function codeAgentSecretRefPlaceholder() {
|
|
const ref = DEV_CODE_AGENT_PROVIDER_CONTRACT.secretRefs[0];
|
|
return `secretRef:${ref.secretName}/${ref.secretKey}`;
|
|
}
|
|
|
|
export function buildCodeAgentProviderManifestPlaceholder() {
|
|
const contract = DEV_CODE_AGENT_PROVIDER_CONTRACT;
|
|
return {
|
|
contractVersion: contract.contractVersion,
|
|
environment: contract.environment,
|
|
provider: contract.provider,
|
|
runtimeProvider: contract.runtimeProvider,
|
|
backend: contract.backend,
|
|
model: contract.model,
|
|
requiredEnv: [...contract.requiredEnv],
|
|
secretRefs: contract.secretRefs.map((item) => ({ ...item })),
|
|
egress: { ...contract.egress },
|
|
fixtureEvidence: {
|
|
providerConnected: false,
|
|
summary: "This manifest contract records provider env names, Secret reference names, and DEV egress policy only. It is not live provider evidence."
|
|
}
|
|
};
|
|
}
|
|
|
|
export function inspectCodeAgentProviderEnv(env = {}) {
|
|
const contract = DEV_CODE_AGENT_PROVIDER_CONTRACT;
|
|
const missingEnv = contract.requiredEnv.filter((name) => !hasEnvValue(env, name));
|
|
const secretRef = contract.secretRefs[0];
|
|
const baseUrl = firstNonEmpty(env.HWLAB_CODE_AGENT_OPENAI_BASE_URL);
|
|
const directPublicOpenAi =
|
|
baseUrl === contract.egress.forbiddenDirectBaseUrl ||
|
|
/^https:\/\/api\.openai\.com\/v1\/responses\/?$/u.test(baseUrl);
|
|
const egressReady = Boolean(baseUrl) && !directPublicOpenAi;
|
|
const ready = missingEnv.length === 0 && egressReady;
|
|
|
|
return {
|
|
contractVersion: contract.contractVersion,
|
|
environment: contract.environment,
|
|
provider: contract.runtimeProvider,
|
|
model: firstNonEmpty(env.HWLAB_CODE_AGENT_MODEL, contract.model),
|
|
backend: contract.backend,
|
|
status: ready ? "available" : "blocked",
|
|
ready,
|
|
requiredEnv: contract.requiredEnv.map((name) => ({
|
|
name,
|
|
present: hasEnvValue(env, name),
|
|
redacted: name === secretRef.env,
|
|
source: name === secretRef.env ? "k8s-secret-ref" : "runtime-env"
|
|
})),
|
|
missingEnv,
|
|
secretRefs: [
|
|
{
|
|
env: secretRef.env,
|
|
secretName: secretRef.secretName,
|
|
secretKey: secretRef.secretKey,
|
|
present: hasEnvValue(env, secretRef.env),
|
|
envInjected: hasEnvValue(env, secretRef.env),
|
|
secretPresent: "not_observed_by_runtime",
|
|
secretKeyPresent: "not_observed_by_runtime",
|
|
redacted: true
|
|
}
|
|
],
|
|
egress: {
|
|
env: contract.egress.env,
|
|
present: hasEnvValue(env, contract.egress.env),
|
|
target: contract.egress.target,
|
|
baseUrlConfigured: Boolean(baseUrl),
|
|
directPublicOpenAi,
|
|
ready: egressReady,
|
|
valueRedacted: true
|
|
},
|
|
safety: {
|
|
devOnly: true,
|
|
prodAllowed: contract.forbidden.prodAllowed,
|
|
secretsRead: false,
|
|
secretMaterialRead: false,
|
|
valuesRedacted: true,
|
|
directPublicOpenAiFromDevPod: directPublicOpenAi
|
|
},
|
|
blocker: ready ? null : codeAgentProviderBlocker({ missingEnv, directPublicOpenAi, baseUrl })
|
|
};
|
|
}
|
|
|
|
function codeAgentProviderBlocker({ missingEnv, directPublicOpenAi, baseUrl }) {
|
|
if (directPublicOpenAi) {
|
|
return "DEV Code Agent OpenAI base URL points at public api.openai.com instead of the DEV egress/proxy path";
|
|
}
|
|
if (!baseUrl) {
|
|
return "DEV Code Agent OpenAI base URL is missing; provider calls must use the DEV egress/proxy path";
|
|
}
|
|
return `Code Agent provider runtime config is missing ${missingEnv.join(", ")}`;
|
|
}
|
|
|
|
function hasEnvValue(env, name) {
|
|
return typeof env?.[name] === "string" && env[name].trim().length > 0;
|
|
}
|
|
|
|
function firstNonEmpty(...values) {
|
|
for (const value of values) {
|
|
if (typeof value === "string" && value.trim()) return value.trim();
|
|
}
|
|
return "";
|
|
}
|