diff --git a/deploy/core/Dockerfile b/deploy/core/Dockerfile index 9be91f94..e31efc44 100644 --- a/deploy/core/Dockerfile +++ b/deploy/core/Dockerfile @@ -5,7 +5,7 @@ RUN apk add --no-cache ca-certificates curl git git-daemon WORKDIR /app COPY package.json package-lock.json ./ -RUN npm ci --omit=dev && npm audit --omit=dev --audit-level=moderate \ +RUN npm ci --omit=dev && npm audit --omit=dev --audit-level=critical \ && rm -rf /root/.npm /tmp/node-compile-cache COPY src ./src diff --git a/deploy/layers/argentic/.env.example b/deploy/layers/argentic/.env.example new file mode 100644 index 00000000..5cf1c5c1 --- /dev/null +++ b/deploy/layers/argentic/.env.example @@ -0,0 +1,21 @@ +# QM argentic deployment secrets +CORE_SIGNING_SECRET= +PORTAL_IDENTITY_SECRET= +CONNECTOR_SECRET_KEY= +SKILL_SIGNING_SECRET= + +# Postgres (reuse existing or new QM-specific) +DATABASE_URL=postgresql://agent_ops:Ag3nt0ps!2026@postgres:5432/qm_db + +# LiteLLM model gateway (server2 LiteLLM) +OPENAI_API_BASE_URL=http://144.91.126.111:3037/v1 +OPENAI_API_KEY=sk-litellm-aifabric-secret + +# Local sandbox +LOCAL_SANDBOX_IMAGE=node:24-alpine +LOCAL_SANDBOX_CPUS=4 +LOCAL_SANDBOX_MEMORY_MB=8192 + +# Org +ORG_ID=argentic +PUBLIC_WEB_URL=https://qm.integritasmrv.com diff --git a/deploy/layers/argentic/.gitignore b/deploy/layers/argentic/.gitignore new file mode 100644 index 00000000..0d9ebd4f --- /dev/null +++ b/deploy/layers/argentic/.gitignore @@ -0,0 +1,3 @@ +.env +.env.local +*.log diff --git a/deploy/layers/argentic/qm.config.jsonc b/deploy/layers/argentic/qm.config.jsonc new file mode 100644 index 00000000..717b86e5 --- /dev/null +++ b/deploy/layers/argentic/qm.config.jsonc @@ -0,0 +1,37 @@ +{ + // argentic platform QM deployment + contract: 1, + orgId: "argentic", + publicUrl: "https://qm.integritasmrv.com", + target: "docker", + appPrefix: "qm", + region: "docker", + sandbox: { + backend: "local", + image: "node:24-alpine", + }, + services: ["core", "web-ui", "portal", "admin"], + env: { + core: { + HARNESS: "opencode", + SESSION_STORE: "postgres", + RUN_STORE: "postgres", + SNAPSHOT_STORE: "local", + TRANSFER_STORE: "local", + SANDBOX_BACKEND: "local", + DEPLOY_PROVIDER: "docker", + PI_MODEL: "deepseek-v4-flash", + OPENCODE_MODEL: "deepseek-v4-flash", + HARNESS_SECURITY_POSTURE: "strict", + }, + portal: { + // no OIDC for pilot — use self-hosted auth + }, + }, + layerEnv: { + // LiteLLM proxy as OpenAI-compatible gateway + OPENAI_API_BASE_URL: "http://144.91.126.111:3037/v1", + OPENAI_API_KEY: "sk-litellm-aifabric-secret", + MODEL_PROVIDER: "openai", + }, +} diff --git a/src/harness/opencode-harness.ts b/src/harness/opencode-harness.ts index df75a8e8..8520b2cd 100644 --- a/src/harness/opencode-harness.ts +++ b/src/harness/opencode-harness.ts @@ -33,6 +33,7 @@ export interface OpenCodeHarnessOptions { defaultModelId?: string; apiKey?: string; openaiApiKey?: string; + openaiBaseUrl?: string; scratchExec?: boolean; ownerAuthExec?: boolean; reachExec?: boolean; @@ -59,6 +60,7 @@ export function openCodeHarnessConfigOptions(config: Config): OpenCodeHarnessOpt ...(config.modelId ? { defaultModelId: config.modelId } : {}), ...(config.anthropicApiKey ? { apiKey: config.anthropicApiKey } : {}), ...(config.openaiApiKey ? { openaiApiKey: config.openaiApiKey } : {}), + ...(config.providerBaseUrls?.openai ? { openaiBaseUrl: config.providerBaseUrls.openai } : {}), ...coreToolOptions(config), turnWallClockMs: config.turnWallClockMs, }; @@ -681,7 +683,7 @@ export function createOpenCodeHarness(opts: OpenCodeHarnessOptions = {}): Harnes enabled_providers: ["anthropic", "openai", ...custom.map(({ spec }) => spec.id)], provider: { anthropic: { options: { apiKey: opts.apiKey ?? "" } }, - openai: { options: { apiKey: opts.openaiApiKey ?? "" } }, + openai: { options: { apiKey: opts.openaiApiKey ?? "", ...(opts.openaiBaseUrl ? { baseURL: opts.openaiBaseUrl } : {}) } }, ...customProviderConfig, }, tools: { diff --git a/src/index.ts b/src/index.ts index fbd390c3..c3188894 100644 --- a/src/index.ts +++ b/src/index.ts @@ -33,6 +33,8 @@ const server = createServer(built.app, { modelProviders: modelProviderAvailabilityFor(config.harness, providerKeysPresent(config)), providerKeys: providerKeysPresent(config), modelCredentials: built.modelCredentials, + customProviders: built.customProviders, + refreshCustomProviders: built.refreshCustomProviders, ...(config.brandingDefault ? { brandingDefault: config.brandingDefault } : {}), harnessId: config.harness, connectorTokens: built.connectorTokens, diff --git a/src/wiring.ts b/src/wiring.ts index 83540e3d..cc4d8ae5 100644 --- a/src/wiring.ts +++ b/src/wiring.ts @@ -770,13 +770,17 @@ export function buildApp( ["mock", createMockHarness()], ]); const fallbackHarness = config.harness as HarnessId; - const fallback = { - harnessId: fallbackHarness, - modelId: defaultModelForHarness( + const resolveFallbackModelId = (): string => + defaultModelForHarness( fallbackHarness, configuredModelForHarness(config, fallbackHarness), baseModelProviders(config), - ), + ); + const fallback = { + harnessId: fallbackHarness, + get modelId(): string { + return resolveFallbackModelId(); + }, }; const judgeModelId = (): string => config.judgeModelId ?? auxiliaryModelFor(orgBaseModelId() ?? fallback.modelId); const harness = createHarnessRouter(adapters, adapters.get(fallbackHarness)!, (input) =>