test(release): harden Windows smoke model setup

This commit is contained in:
Peter Steinberger
2026-05-01 23:25:52 +01:00
parent ebece95058
commit cce08881ec
5 changed files with 81 additions and 24 deletions

View File

@@ -129,7 +129,7 @@ run_flow() {
local doctor_expected="$5"
local install_log="/tmp/openclaw-doctor-switch-${name}-install.log"
local doctor_log="/tmp/openclaw-doctor-switch-${name}-doctor.log"
local command_timeout="${OPENCLAW_DOCKER_DOCTOR_SWITCH_COMMAND_TIMEOUT:-300s}"
local command_timeout="${OPENCLAW_DOCKER_DOCTOR_SWITCH_COMMAND_TIMEOUT:-900s}"
echo "== Flow: $name =="
openclaw_test_state_create "switch-${name}" empty
@@ -175,7 +175,7 @@ run_proxy_env_flow() {
local name="proxy-env-cleanup"
local install_log="/tmp/openclaw-doctor-switch-${name}-install.log"
local doctor_log="/tmp/openclaw-doctor-switch-${name}-doctor.log"
local command_timeout="${OPENCLAW_DOCKER_DOCTOR_SWITCH_COMMAND_TIMEOUT:-300s}"
local command_timeout="${OPENCLAW_DOCKER_DOCTOR_SWITCH_COMMAND_TIMEOUT:-900s}"
echo "== Flow: $name =="
openclaw_test_state_create "switch-${name}" empty
@@ -216,7 +216,7 @@ run_wrapper_flow() {
local env_repair_log="/tmp/openclaw-doctor-switch-${name}-env-repair.log"
local doctor_log="/tmp/openclaw-doctor-switch-${name}-doctor.log"
local clear_log="/tmp/openclaw-doctor-switch-${name}-clear.log"
local command_timeout="${OPENCLAW_DOCKER_DOCTOR_SWITCH_COMMAND_TIMEOUT:-300s}"
local command_timeout="${OPENCLAW_DOCKER_DOCTOR_SWITCH_COMMAND_TIMEOUT:-900s}"
echo "== Flow: $name =="
openclaw_test_state_create "switch-${name}" empty

View File

@@ -2,10 +2,13 @@ import { posixAgentWorkspaceScript, windowsAgentWorkspaceScript } from "./agent-
import { shellQuote } from "./host-command.ts";
import {
psSingleQuote,
windowsModelProviderTimeoutScript,
windowsAgentTurnConfigPatchScript,
windowsOpenClawResolver,
} from "./powershell.ts";
import { modelProviderConfigBatchJson } from "./provider-auth.ts";
import {
modelProviderConfigBatchJson,
resolveParallelsModelTimeoutSeconds,
} from "./provider-auth.ts";
import type { Platform, ProviderAuth } from "./types.ts";
export interface NpmUpdateScriptInput {
@@ -201,10 +204,7 @@ if ($LASTEXITCODE -ne 0) {
"gateway restart exited with code $LASTEXITCODE; probing readiness before failing" | Out-Host
}
Wait-OpenClawGateway
Invoke-OpenClaw models set ${psSingleQuote(input.auth.modelId)}
${windowsModelProviderTimeoutScript(input.auth.modelId)}
Invoke-OpenClaw config set agents.defaults.skipBootstrap true --strict-json
Invoke-OpenClaw config set tools.profile minimal
${windowsAgentTurnConfigPatchScript(input.auth.modelId)}
$sessionPath = Join-Path $env:USERPROFILE '.openclaw\\agents\\main\\sessions\\parallels-npm-update-windows.jsonl'
Remove-Item $sessionPath -Force -ErrorAction SilentlyContinue
${windowsAgentWorkspaceScript("Parallels npm update smoke test assistant.")}
@@ -215,7 +215,7 @@ for ($attempt = 1; $attempt -le 2; $attempt++) {
$sessionsDir = Join-Path $env:USERPROFILE '.openclaw\\agents\\main\\sessions'
$sessionPath = Join-Path $sessionsDir "$sessionId.jsonl"
Remove-Item $sessionPath -Force -ErrorAction SilentlyContinue
$output = Invoke-OpenClaw agent --local --agent main --session-id $sessionId --message 'Reply with exact ASCII text OK only.' --thinking minimal --json 2>&1
$output = Invoke-OpenClaw agent --local --agent main --session-id $sessionId --model ${psSingleQuote(input.auth.modelId)} --message 'Reply with exact ASCII text OK only.' --thinking minimal --timeout ${resolveParallelsModelTimeoutSeconds("windows")} --json 2>&1
if ($null -ne $output) { $output | ForEach-Object { $_ } }
if ($LASTEXITCODE -ne 0) { throw "agent failed with exit code $LASTEXITCODE" }
if (($output | Out-String) -match '"finalAssistant(Raw|Visible)Text":\\s*"OK"') {

View File

@@ -1,5 +1,6 @@
import {
configPathMapKey,
modelProviderConfigBatchJson,
providerIdFromModelId,
providerTimeoutConfigJson,
} from "./provider-auth.ts";
@@ -49,6 +50,56 @@ Remove-Item $providerTimeoutBatchPath -Force -ErrorAction SilentlyContinue
if ($providerTimeoutExit -ne 0) { throw "model provider timeout config set failed" }`;
}
export function windowsAgentTurnConfigPatchScript(modelId: string): string {
const batchJson = modelProviderConfigBatchJson(modelId, "windows");
const payloadJson = JSON.stringify({
modelId,
operations: batchJson ? (JSON.parse(batchJson) as unknown) : [],
});
return `$agentTurnConfigPatchPath = $env:OPENCLAW_CONFIG_PATH
if (-not $agentTurnConfigPatchPath) { $agentTurnConfigPatchPath = Join-Path $env:USERPROFILE '.openclaw\\openclaw.json' }
$env:OPENCLAW_PARALLELS_AGENT_CONFIG_PATCH = @'
${payloadJson}
'@
$env:OPENCLAW_PARALLELS_AGENT_CONFIG_PATH = $agentTurnConfigPatchPath
node.exe -e @'
const fs = require("node:fs");
const path = require("node:path");
const configPath = process.env.OPENCLAW_PARALLELS_AGENT_CONFIG_PATH;
const payload = JSON.parse(process.env.OPENCLAW_PARALLELS_AGENT_CONFIG_PATCH || "{}");
const cfg = fs.existsSync(configPath) ? JSON.parse(fs.readFileSync(configPath, "utf8")) : {};
cfg.agents = cfg.agents && typeof cfg.agents === "object" ? cfg.agents : {};
cfg.agents.defaults = cfg.agents.defaults && typeof cfg.agents.defaults === "object" ? cfg.agents.defaults : {};
cfg.agents.defaults.skipBootstrap = true;
const existingModel = cfg.agents.defaults.model && typeof cfg.agents.defaults.model === "object" ? cfg.agents.defaults.model : {};
cfg.agents.defaults.model = { ...existingModel, primary: payload.modelId };
cfg.agents.defaults.models = cfg.agents.defaults.models && typeof cfg.agents.defaults.models === "object" ? cfg.agents.defaults.models : {};
cfg.tools = cfg.tools && typeof cfg.tools === "object" ? cfg.tools : {};
cfg.tools.profile = "minimal";
for (const op of payload.operations || []) {
const segments = String(op.path || "").match(/(?:[^.[\\]]+)|(?:\\["((?:\\\\.|[^"\\\\])*)"\\])/g) || [];
let cursor = cfg;
for (let i = 0; i < segments.length; i++) {
const raw = segments[i];
const key = raw.startsWith("[") ? JSON.parse(raw.slice(1, -1)) : raw;
if (i === segments.length - 1) {
const existing = cursor[key] && typeof cursor[key] === "object" && !Array.isArray(cursor[key]) ? cursor[key] : {};
cursor[key] = op.value && typeof op.value === "object" && !Array.isArray(op.value) ? { ...existing, ...op.value } : op.value;
} else {
cursor[key] = cursor[key] && typeof cursor[key] === "object" && !Array.isArray(cursor[key]) ? cursor[key] : {};
cursor = cursor[key];
}
}
}
fs.mkdirSync(path.dirname(configPath), { recursive: true });
fs.writeFileSync(configPath, JSON.stringify(cfg, null, 2) + "\\n", { mode: 0o600 });
'@
$agentTurnConfigPatchExit = $LASTEXITCODE
Remove-Item Env:OPENCLAW_PARALLELS_AGENT_CONFIG_PATCH -Force -ErrorAction SilentlyContinue
Remove-Item Env:OPENCLAW_PARALLELS_AGENT_CONFIG_PATH -Force -ErrorAction SilentlyContinue
if ($agentTurnConfigPatchExit -ne 0) { throw "agent turn config patch failed" }`;
}
export const windowsOpenClawResolver = String.raw`function Resolve-OpenClawCommand {
if ($script:OpenClawResolvedCommand) { return $script:OpenClawResolvedCommand }
$shimCandidates = @()

View File

@@ -14,6 +14,7 @@ import {
resolveHostIp,
resolveHostPort,
resolveLatestVersion,
resolveParallelsModelTimeoutSeconds,
resolveWindowsProviderAuth,
resolveSnapshot,
run,
@@ -36,7 +37,7 @@ import { PhaseRunner } from "./phase-runner.ts";
import {
encodePowerShell,
psSingleQuote,
windowsModelProviderTimeoutScript,
windowsAgentTurnConfigPatchScript,
windowsOpenClawResolver,
} from "./powershell.ts";
import { ensureGuestGit, prepareMinGitZip } from "./windows-git.ts";
@@ -891,13 +892,7 @@ if ($LASTEXITCODE -ne 0) { throw "gateway ${action} failed with exit code $LASTE
`$ErrorActionPreference = 'Continue'
$PSNativeCommandUseErrorActionPreference = $false
${windowsPortableGitPathScript}
Invoke-OpenClaw models set ${psSingleQuote(this.auth.modelId)}
if ($LASTEXITCODE -ne 0) { throw "models set failed" }
${windowsModelProviderTimeoutScript(this.auth.modelId)}
Invoke-OpenClaw config set agents.defaults.skipBootstrap true --strict-json
if ($LASTEXITCODE -ne 0) { throw "config set failed" }
Invoke-OpenClaw config set tools.profile minimal
if ($LASTEXITCODE -ne 0) { throw "tools profile config set failed" }
${windowsAgentTurnConfigPatchScript(this.auth.modelId)}
${windowsAgentWorkspaceScript("Parallels Windows smoke test assistant.")}
Set-Item -Path ('Env:' + ${psSingleQuote(this.auth.apiKeyEnv)}) -Value ${psSingleQuote(this.auth.apiKeyValue)}
$agentOk = $false
@@ -913,10 +908,14 @@ for ($attempt = 1; $attempt -le 2; $attempt++) {
'main',
'--session-id',
$sessionId,
'--model',
${psSingleQuote(this.auth.modelId)},
'--message',
'Reply with exact ASCII text OK only.',
'--thinking',
'minimal',
'--timeout',
'${resolveParallelsModelTimeoutSeconds("windows")}',
'--json'
)
$output = Invoke-OpenClaw @args 2>&1

View File

@@ -327,8 +327,10 @@ console.log(JSON.stringify(result));
expect(script, scriptPath).toContain("AgentWorkspaceScript");
expect(script, scriptPath).toContain("parallels-");
expect(script, scriptPath).toContain("agents.defaults.skipBootstrap");
expect(script, scriptPath).toContain("tools.profile");
if (scriptPath !== TS_PATHS.windows) {
expect(script, scriptPath).toContain("agents.defaults.skipBootstrap");
expect(script, scriptPath).toContain("tools.profile");
}
expect(script, scriptPath).toContain("--thinking");
expect(script, scriptPath).toContain("minimal");
expect(script, scriptPath).toContain("finalAssistant(Raw|Visible)Text");
@@ -337,8 +339,11 @@ console.log(JSON.stringify(result));
expect(readFileSync(TS_PATHS.macos, "utf8")).toContain("config set --batch-file");
expect(readFileSync(TS_PATHS.linux, "utf8")).toContain("modelProviderConfigBatchJson");
expect(readFileSync(TS_PATHS.linux, "utf8")).toContain("config set --batch-file");
expect(readFileSync(TS_PATHS.windows, "utf8")).toContain("windowsModelProviderTimeoutScript");
expect(readFileSync(TS_PATHS.powershell, "utf8")).toContain("config set --batch-file");
expect(readFileSync(TS_PATHS.windows, "utf8")).toContain("windowsAgentTurnConfigPatchScript");
const powershell = readFileSync(TS_PATHS.powershell, "utf8");
expect(powershell).toContain("config set --batch-file");
expect(powershell).toContain("agents.defaults.skipBootstrap");
expect(powershell).toContain("tools.profile");
const npmUpdateScripts = readFileSync(TS_PATHS.npmUpdateScripts, "utf8");
expect(npmUpdateScripts).toContain("posixAgentWorkspaceScript");
@@ -347,7 +352,7 @@ console.log(JSON.stringify(result));
expect(npmUpdateScripts).toContain("--thinking minimal");
expect(npmUpdateScripts).toContain("finalAssistant(Raw|Visible)Text");
expect(npmUpdateScripts).toContain("posixAssertAgentOkScript");
expect(npmUpdateScripts).toContain("windowsModelProviderTimeoutScript");
expect(npmUpdateScripts).toContain("windowsAgentTurnConfigPatchScript");
expect(npmUpdateScripts).toContain("modelProviderConfigBatchJson");
expect(npmUpdateScripts).toContain("config set --batch-file");
});
@@ -476,7 +481,9 @@ console.log(JSON.stringify(result));
expect(script).toContain('guestPowerShellBackground(\n "agent-turn"');
expect(script).toContain("OPENCLAW_PARALLELS_WINDOWS_AGENT_TIMEOUT_S");
expect(script).toContain("OPENCLAW_PARALLELS_WINDOWS_AGENT_TIMEOUT_S || 1500");
expect(script).toContain("windowsModelProviderTimeoutScript(this.auth.modelId)");
expect(script).toContain("windowsAgentTurnConfigPatchScript(this.auth.modelId)");
expect(script).toContain("--model");
expect(script).toContain('resolveParallelsModelTimeoutSeconds("windows")');
expect(script).toContain("finalAssistant(Raw|Visible)Text");
expect(script).toContain("parallels-windows-smoke-retry-$attempt");
expect(script).toContain("agent turn attempt $attempt failed or finished without OK response");