惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Visual Studio Blog
Recent Announcements
Recent Announcements
雷峰网
雷峰网
The GitHub Blog
The GitHub Blog
罗磊的独立博客
月光博客
月光博客
J
Java Code Geeks
A
About on SuperTechFans
Microsoft Security Blog
Microsoft Security Blog
D
Docker
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
F
Fortinet All Blogs
U
Unit 42
C
Check Point Blog
Martin Fowler
Martin Fowler
有赞技术团队
有赞技术团队
博客园 - 叶小钗
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
酷 壳 – CoolShell
酷 壳 – CoolShell
Blog — PlanetScale
Blog — PlanetScale
大猫的无限游戏
大猫的无限游戏
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
阮一峰的网络日志
阮一峰的网络日志
MyScale Blog
MyScale Blog

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
fix(qa): ignore unsafe Ubuntu VM fallbacks · openclaw/ope...
vincentkoc · 2026-06-17 · via Recent Commits to openclaw:main
Original file line numberDiff line numberDiff line change

@@ -89,15 +89,11 @@ export function resolveUbuntuVmName(requested: string, explicit = false): string

8989

}

9090

const fallback =

9191

names

92-

.map((name) => ({ name, version: /ubuntu\s+(\d+(?:\.\d+)*)/i.exec(name)?.[1] }))

93-

.filter((item): item is { name: string; version: string } => Boolean(item.version))

94-

.map((item) => ({

95-

name: item.name,

96-

parts: item.version.split(".").map(Number),

97-

}))

92+

.map((name) => ({ name, parts: parseUbuntuVersionParts(name) }))

93+

.filter((item): item is { name: string; parts: number[] } => Boolean(item.parts))

9894

.filter((item) => item.parts[0] >= 24)

9995

.toSorted((a, b) => compareVersions(b.parts, a.parts))[0]?.name ??

100-

names.find((name) => /ubuntu/i.test(name));

96+

names.find(isSafeUbuntuFallbackName);

10197

if (!fallback) {

10298

die(`VM not found: ${requested}`);

10399

}

@@ -130,6 +126,23 @@ function listVms(timeoutMs = PRLCTL_STATUS_TIMEOUT_MS): PrlctlVmListItem[] {

130126

) as PrlctlVmListItem[];

131127

}

132128
129+

function parseUbuntuVersionParts(name: string): number[] | undefined {

130+

const version = /ubuntu\s+(\d+(?:\.\d+)*)/i.exec(name)?.[1];

131+

const parts = version?.split(".").map((part) => Number(part));

132+

if (!parts?.every((part) => Number.isSafeInteger(part))) {

133+

return undefined;

134+

}

135+

return parts;

136+

}

137+
138+

function isSafeUbuntuFallbackName(name: string): boolean {

139+

if (!/ubuntu/i.test(name)) {

140+

return false;

141+

}

142+

const hasVersion = /ubuntu\s+\d+(?:\.\d+)*/i.test(name);

143+

return !hasVersion || Boolean(parseUbuntuVersionParts(name));

144+

}

145+
133146

function compareVersions(a: number[], b: number[]): number {

134147

for (let index = 0; index < Math.max(a.length, b.length); index++) {

135148

const diff = (a[index] ?? 0) - (b[index] ?? 0);

Original file line numberDiff line numberDiff line change

@@ -602,6 +602,7 @@ set -euo pipefail

602602

if [[ "$1" == "list" ]]; then

603603

cat <<'JSON'

604604

[

605+

{"name": "Ubuntu 9007199254740993.04"},

605606

{"name": "Ubuntu 26.04"},

606607

{"name": "Ubuntu 25.10"},

607608

{"name": "Ubuntu 23.10"},

@@ -619,6 +620,7 @@ const isPrlctl = [process.argv0, process.execPath].some((value) =>

619620

if (isPrlctl) {

620621

if (process.argv.some((arg) => arg.includes("list"))) {

621622

console.log(JSON.stringify([

623+

{ name: "Ubuntu 9007199254740993.04" },

622624

{ name: "Ubuntu 26.04" },

623625

{ name: "Ubuntu 25.10" },

624626

{ name: "Ubuntu 23.10" },

@@ -640,6 +642,49 @@ if (isPrlctl) {

640642

}

641643

});

642644
645+

it("skips unsafe Ubuntu version names in fallback resolver", () => {

646+

const tempDir = mkdtempSync(join(tmpdir(), "openclaw-parallels-vm-helper-"));

647+

writeFakePrlctl(

648+

tempDir,

649+

`#!/usr/bin/env bash

650+

set -euo pipefail

651+

if [[ "$1" == "list" ]]; then

652+

cat <<'JSON'

653+

[

654+

{"name": "Ubuntu 9007199254740993.04"},

655+

{"name": "Ubuntu 23.10"}

656+

]

657+

JSON

658+

exit 0

659+

fi

660+

exit 1

661+

`,

662+

`import { basename } from "node:path";

663+

const isPrlctl = [process.argv0, process.execPath].some((value) =>

664+

basename(value).toLowerCase() === "prlctl.exe",

665+

);

666+

if (isPrlctl) {

667+

if (process.argv.some((arg) => arg.includes("list"))) {

668+

console.log(JSON.stringify([

669+

{ name: "Ubuntu 9007199254740993.04" },

670+

{ name: "Ubuntu 23.10" },

671+

]));

672+

process.exit(0);

673+

}

674+

process.exit(1);

675+

}

676+

`,

677+

);

678+
679+

try {

680+

const output = withEnv(fakePrlctlEnv(tempDir), () => resolveUbuntuVmName("Ubuntu missing"));

681+
682+

expect(output).toBe("Ubuntu 23.10");

683+

} finally {

684+

rmSync(tempDir, { force: true, recursive: true });

685+

}

686+

});

687+
643688

it("uses the only macOS VM when the default name is unavailable", () => {

644689

const tempDir = mkdtempSync(join(tmpdir(), "openclaw-parallels-macos-vm-helper-"));

645690

writeFakePrlctl(