+104
-18
@@ -415,6 +415,10 @@ function requiredAccessConfigString(value, label) {
|
||||
return text;
|
||||
}
|
||||
|
||||
function optionalObject(value) {
|
||||
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
||||
}
|
||||
|
||||
async function gitValue(args) {
|
||||
const result = await execFileAsync("git", args, { cwd: repoRoot, timeout: 10000, maxBuffer: 1024 * 1024 });
|
||||
return result.stdout.trim();
|
||||
@@ -987,6 +991,13 @@ function secretNameForProfile(secretName, profile) {
|
||||
return secretName;
|
||||
}
|
||||
|
||||
function secretRefObjectForProfile(secretRef, profile) {
|
||||
const ref = configString(secretRef);
|
||||
const slashIndex = ref.indexOf("/");
|
||||
assert.ok(slashIndex > 0 && slashIndex < ref.length - 1, `secretRef ${JSON.stringify(secretRef)} must be name/key`);
|
||||
return { name: secretNameForProfile(ref.slice(0, slashIndex), profile), key: ref.slice(slashIndex + 1), optional: true };
|
||||
}
|
||||
|
||||
function rewritePodSecretRefs(podSpec, profile) {
|
||||
if (!podSpec || !isRuntimeLane(profile)) return;
|
||||
for (const volume of podSpec.volumes ?? []) {
|
||||
@@ -996,20 +1007,7 @@ function rewritePodSecretRefs(podSpec, profile) {
|
||||
|
||||
function deployEnvEntry(name, value, namespace, profile = "dev") {
|
||||
if (typeof value === "string" && value.startsWith("secretRef:")) {
|
||||
const ref = value.slice("secretRef:".length);
|
||||
const slashIndex = ref.indexOf("/");
|
||||
if (slashIndex > 0 && slashIndex < ref.length - 1) {
|
||||
return {
|
||||
name,
|
||||
valueFrom: {
|
||||
secretKeyRef: {
|
||||
name: secretNameForProfile(ref.slice(0, slashIndex), profile),
|
||||
key: ref.slice(slashIndex + 1),
|
||||
optional: true
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
return { name, valueFrom: { secretKeyRef: secretRefObjectForProfile(value.slice("secretRef:".length), profile) } };
|
||||
}
|
||||
return { name, value: namespaceScopedHost(value, namespace) };
|
||||
}
|
||||
@@ -5638,7 +5636,81 @@ function secretPlaneIssueLabelValue(issueRef) {
|
||||
return label;
|
||||
}
|
||||
|
||||
function opencodeServerManifest({ profile = "v03", source }) {
|
||||
function opencodeRuntimeConfigForProfile(deploy, profile) {
|
||||
const config = optionalObject(deploy?.lanes?.[profile]?.opencode);
|
||||
const providerProfile = configString(config.providerProfile) || "dsflash-go";
|
||||
const providerId = configString(config.providerId) || providerProfile;
|
||||
const model = configString(config.model) || "deepseek-v4-flash";
|
||||
const smallModel = configString(config.smallModel) || model;
|
||||
const apiKeyEnv = configString(config.apiKeyEnv) || opencodeApiKeyEnvName(providerId);
|
||||
const contextLimit = opencodePositiveInteger(config.contextLimit, 1000000, `deploy.lanes.${profile}.opencode.contextLimit`);
|
||||
const outputLimit = opencodePositiveInteger(config.outputLimit, 384000, `deploy.lanes.${profile}.opencode.outputLimit`);
|
||||
return {
|
||||
providerProfile,
|
||||
providerId,
|
||||
displayName: configString(config.displayName) || `AgentRun ${providerProfile}`,
|
||||
model,
|
||||
smallModel,
|
||||
baseURL: configString(config.baseURL) || "https://opencode.ai/zen/go/v1",
|
||||
npm: configString(config.npm) || "@ai-sdk/openai-compatible",
|
||||
apiKeyEnv,
|
||||
apiKeySecretRef: configString(config.apiKeySecretRef) || "hwlab-code-agent-provider/opencode-api-key",
|
||||
contextLimit,
|
||||
outputLimit,
|
||||
agentrunSecretNamespace: configString(config.agentrunSecretNamespace) || "agentrun-v02",
|
||||
agentrunSecretName: configString(config.agentrunSecretName) || `agentrun-v01-provider-${providerProfile}`,
|
||||
agentrunSecretKeys: Array.isArray(config.agentrunSecretKeys) && config.agentrunSecretKeys.length > 0
|
||||
? config.agentrunSecretKeys.map(configString).filter(Boolean)
|
||||
: ["auth.json", "config.toml", "model-catalog.json"]
|
||||
};
|
||||
}
|
||||
|
||||
function opencodePositiveInteger(value, fallback, label) {
|
||||
if (value === undefined || value === null) return fallback;
|
||||
assert.ok(Number.isInteger(value) && value > 0, `${label} must be a positive integer`);
|
||||
return value;
|
||||
}
|
||||
|
||||
function opencodeApiKeyEnvName(providerId) {
|
||||
const normalized = String(providerId || "provider").toUpperCase().replace(/[^A-Z0-9]+/gu, "_").replace(/^_+|_+$/gu, "");
|
||||
return `OPENCODE_${normalized || "PROVIDER"}_API_KEY`;
|
||||
}
|
||||
|
||||
function opencodeConfigContent(config) {
|
||||
return JSON.stringify({
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
autoupdate: false,
|
||||
share: "disabled",
|
||||
model: `${config.providerId}/${config.model}`,
|
||||
small_model: `${config.providerId}/${config.smallModel}`,
|
||||
provider: {
|
||||
[config.providerId]: {
|
||||
npm: config.npm,
|
||||
name: config.displayName,
|
||||
options: {
|
||||
baseURL: config.baseURL,
|
||||
apiKey: `{env:${config.apiKeyEnv}}`,
|
||||
timeout: 600000,
|
||||
headerTimeout: 600000,
|
||||
chunkTimeout: 300000
|
||||
},
|
||||
models: {
|
||||
[config.model]: {
|
||||
name: config.model,
|
||||
reasoning: true,
|
||||
tool_call: true,
|
||||
limit: {
|
||||
context: config.contextLimit,
|
||||
output: config.outputLimit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function opencodeServerManifest({ profile = "v03", source, deploy = null }) {
|
||||
assert.ok(isRuntimeLane(profile), `opencode-server profile must be a runtime lane, got ${profile}`);
|
||||
const namespace = namespaceNameForProfile(profile);
|
||||
const name = "opencode-server";
|
||||
@@ -5655,9 +5727,22 @@ function opencodeServerManifest({ profile = "v03", source }) {
|
||||
"hwlab.pikastech.local/service-id": name,
|
||||
"hwlab.pikastech.local/source-commit": source.full
|
||||
};
|
||||
const annotations = { "hwlab.pikastech.local/rendered-by": "scripts/gitops-render.mjs" };
|
||||
const opencodeConfig = opencodeRuntimeConfigForProfile(deploy, profile);
|
||||
const configContent = opencodeConfigContent(opencodeConfig);
|
||||
const configSha256 = createHash("sha256").update(configContent).digest("hex");
|
||||
const apiKeySecretRef = secretRefObjectForProfile(opencodeConfig.apiKeySecretRef, profile);
|
||||
const annotations = {
|
||||
"hwlab.pikastech.local/rendered-by": "scripts/gitops-render.mjs",
|
||||
"hwlab.pikastech.local/opencode-provider-profile": opencodeConfig.providerProfile,
|
||||
"hwlab.pikastech.local/opencode-provider-id": opencodeConfig.providerId,
|
||||
"hwlab.pikastech.local/opencode-model": opencodeConfig.model,
|
||||
"hwlab.pikastech.local/opencode-config-sha256": configSha256,
|
||||
"hwlab.pikastech.local/opencode-api-key-secret-ref": `${apiKeySecretRef.name}/${apiKeySecretRef.key}`,
|
||||
"hwlab.pikastech.local/agentrun-provider-secret-ref": `${opencodeConfig.agentrunSecretNamespace}/${opencodeConfig.agentrunSecretName}`,
|
||||
"hwlab.pikastech.local/agentrun-provider-secret-keys": opencodeConfig.agentrunSecretKeys.join(","),
|
||||
"hwlab.pikastech.local/values-printed": "false"
|
||||
};
|
||||
const authSecretName = secretNameForProfile("hwlab-opencode-server-auth", profile);
|
||||
const configContent = JSON.stringify({ autoupdate: false, share: "disabled" });
|
||||
return {
|
||||
apiVersion: "v1",
|
||||
kind: "List",
|
||||
@@ -5711,6 +5796,7 @@ function opencodeServerManifest({ profile = "v03", source }) {
|
||||
{ name: "XDG_CONFIG_HOME", value: "/workspace/.config" },
|
||||
{ name: "XDG_DATA_HOME", value: "/workspace/.local/share" },
|
||||
{ name: "OPENCODE_CONFIG_CONTENT", value: configContent },
|
||||
{ name: opencodeConfig.apiKeyEnv, valueFrom: { secretKeyRef: apiKeySecretRef } },
|
||||
{ name: "OPENCODE_SERVER_USERNAME", valueFrom: { secretKeyRef: { name: authSecretName, key: "username" } } },
|
||||
{ name: "OPENCODE_SERVER_PASSWORD", valueFrom: { secretKeyRef: { name: authSecretName, key: "password" } } }
|
||||
],
|
||||
@@ -5905,7 +5991,7 @@ async function plannedFiles(args) {
|
||||
if (workbenchRedis) putJson(`${runtimePath}/workbench-redis.yaml`, workbenchRuntimeRedisManifest({ profile, config: workbenchRedis, source }));
|
||||
if (isRuntimeLane(profile)) putJson(`${runtimePath}/openfga.yaml`, v02OpenFgaManifest({ profile, source }));
|
||||
if (isRuntimeLane(profile)) putJson(`${runtimePath}/observability.yaml`, observabilityManifest({ deploy, profile, namespace, labels: profileLabels, annotations, metricsSidecarScript }));
|
||||
if (isRuntimeLane(profile)) putJson(`${runtimePath}/opencode.yaml`, opencodeServerManifest({ profile, source }));
|
||||
if (isRuntimeLane(profile)) putJson(`${runtimePath}/opencode.yaml`, opencodeServerManifest({ profile, source, deploy }));
|
||||
if (includeDeviceAgent) putJson(`${runtimePath}/device-agent-71-freq.yaml`, deviceAgent71FreqManifest({ profile, source, registryPrefix: args.registryPrefix, catalog: artifactCatalog, useDeployImages: args.useDeployImages }));
|
||||
putJson(`${runtimePath}/node-frpc.yaml`, nodeFrpcManifest({ profile, source }));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user