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

推荐订阅源

MongoDB | Blog
MongoDB | Blog
大猫的无限游戏
大猫的无限游戏
H
Help Net Security
V
Visual Studio Blog
G
Google Developers Blog
Blog — PlanetScale
Blog — PlanetScale
Last Week in AI
Last Week in AI
C
Check Point Blog
D
Docker
M
MIT News - Artificial intelligence
P
Proofpoint News Feed
博客园 - 叶小钗
博客园 - 聂微东
The Cloudflare Blog
云风的 BLOG
云风的 BLOG
Martin Fowler
Martin Fowler
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
酷 壳 – CoolShell
酷 壳 – CoolShell
MyScale Blog
MyScale Blog
WordPress大学
WordPress大学
Engineering at Meta
Engineering at Meta
腾讯CDC
S
SegmentFault 最新的问题
博客园 - 【当耐特】

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(update): preserve package service state during cutove...
vincentkoc · 2026-05-22 · via Recent Commits to openclaw:main

@@ -27,6 +27,12 @@ const plistUnescape = (value: string): string =>

2727

.replaceAll("&lt;", "<")

2828

.replaceAll("&amp;", "&");

292930+

type ReadLaunchAgentProgramArgumentsOptions = {

31+

expectedEnvironmentWrapperPath?: string;

32+

expectedEnvironmentFilePath?: string;

33+

generatedEnvironmentLabel?: string;

34+

};

35+3036

function parseGeneratedEnvValue(value: string): string {

3137

const trimmed = value.trim();

3238

if (!trimmed.startsWith("'") || !trimmed.endsWith("'")) {

@@ -35,17 +41,93 @@ function parseGeneratedEnvValue(value: string): string {

3541

return trimmed.slice(1, -1).replaceAll("'\\''", "'");

3642

}

374344+

function includesGeneratedEnvironmentPathToken(value: string | undefined, token: string): boolean {

45+

return Boolean(value?.replaceAll("\\", "/").includes(token));

46+

}

47+48+

function includesGeneratedEnvironmentDirToken(value: string | undefined): boolean {

49+

return Boolean(value?.replaceAll("\\", "/").includes("/service-env/"));

50+

}

51+52+

function resolveSiblingGeneratedEnvFilePath(

53+

envFilePath: string,

54+

options?: ReadLaunchAgentProgramArgumentsOptions,

55+

): string | undefined {

56+

const label = options?.generatedEnvironmentLabel?.trim();

57+

if (!label) {

58+

return undefined;

59+

}

60+

const serviceEnvMarker = "/service-env/";

61+

const markerIndex = envFilePath.replaceAll("\\", "/").lastIndexOf(serviceEnvMarker);

62+

if (markerIndex < 0) {

63+

return undefined;

64+

}

65+

// Custom state dirs can also contain service-env; use the generated env dir closest to the file.

66+

const serviceEnvDirEnd = markerIndex + serviceEnvMarker.length - 1;

67+

return `${envFilePath.slice(0, serviceEnvDirEnd)}/${label}.env`;

68+

}

69+70+

function isGeneratedEnvWrapperArgs(

71+

programArguments: string[],

72+

options?: ReadLaunchAgentProgramArgumentsOptions,

73+

): boolean {

74+

const wrapperPath = programArguments[0];

75+

const envFilePath = programArguments[1];

76+

if (!wrapperPath || !envFilePath) {

77+

return false;

78+

}

79+

if (!options) {

80+

return wrapperPath.endsWith("-env-wrapper.sh");

81+

}

82+

if (

83+

options.expectedEnvironmentWrapperPath &&

84+

options.expectedEnvironmentFilePath &&

85+

wrapperPath === options.expectedEnvironmentWrapperPath &&

86+

envFilePath === options.expectedEnvironmentFilePath

87+

) {

88+

return true;

89+

}

90+

const label = options.generatedEnvironmentLabel?.trim();

91+

if (!label) {

92+

return false;

93+

}

94+

// Legacy/corrupted plists may preserve the label-derived wrapper name inside

95+

// a mangled service-env path. Still unwrap it so the next rewrite can repair.

96+

return (

97+

includesGeneratedEnvironmentDirToken(wrapperPath) &&

98+

includesGeneratedEnvironmentDirToken(envFilePath) &&

99+

includesGeneratedEnvironmentPathToken(wrapperPath, `${label}-env-wrapper.sh`) &&

100+

includesGeneratedEnvironmentPathToken(envFilePath, `${label}.env`)

101+

);

102+

}

103+38104

async function readLaunchAgentEnvironmentFile(

39105

programArguments: string[],

106+

options?: ReadLaunchAgentProgramArgumentsOptions,

40107

): Promise<Record<string, string>> {

41108

const envFilePath = programArguments[1];

42-

if (!programArguments[0]?.endsWith("-env-wrapper.sh") || !envFilePath) {

109+

if (!isGeneratedEnvWrapperArgs(programArguments, options) || !envFilePath) {

43110

return {};

44111

}

45112

let content = "";

46-

try {

47-

content = await fs.readFile(envFilePath, "utf8");

48-

} catch {

113+

const candidateEnvFilePaths = Array.from(

114+

new Set(

115+

[

116+

envFilePath,

117+

resolveSiblingGeneratedEnvFilePath(envFilePath, options),

118+

options?.expectedEnvironmentFilePath,

119+

].filter((candidate): candidate is string => Boolean(candidate)),

120+

),

121+

);

122+

for (const candidate of candidateEnvFilePaths) {

123+

try {

124+

content = await fs.readFile(candidate, "utf8");

125+

break;

126+

} catch {

127+

// Keep trying; mangled wrapper args may still have the canonical env file.

128+

}

129+

}

130+

if (!content) {

49131

return {};

50132

}

51133

const environment: Record<string, string> = {};

@@ -68,8 +150,11 @@ async function readLaunchAgentEnvironmentFile(

68150

return environment;

69151

}

7015271-

function unwrapGeneratedEnvWrapperArgs(programArguments: string[]): string[] {

72-

if (!programArguments[0]?.endsWith("-env-wrapper.sh") || !programArguments[1]) {

153+

function unwrapGeneratedEnvWrapperArgs(

154+

programArguments: string[],

155+

options?: ReadLaunchAgentProgramArgumentsOptions,

156+

): string[] {

157+

if (!isGeneratedEnvWrapperArgs(programArguments, options)) {

73158

return programArguments;

74159

}

75160

return programArguments.slice(2);

@@ -100,6 +185,26 @@ export async function readLaunchAgentProgramArgumentsFromFile(plistPath: string)

100185

environment?: Record<string, string>;

101186

environmentValueSources?: Record<string, GatewayServiceEnvironmentValueSource>;

102187

sourcePath?: string;

188+

} | null>;

189+

export async function readLaunchAgentProgramArgumentsFromFile(

190+

plistPath: string,

191+

options: ReadLaunchAgentProgramArgumentsOptions,

192+

): Promise<{

193+

programArguments: string[];

194+

workingDirectory?: string;

195+

environment?: Record<string, string>;

196+

environmentValueSources?: Record<string, GatewayServiceEnvironmentValueSource>;

197+

sourcePath?: string;

198+

} | null>;

199+

export async function readLaunchAgentProgramArgumentsFromFile(

200+

plistPath: string,

201+

options?: ReadLaunchAgentProgramArgumentsOptions,

202+

): Promise<{

203+

programArguments: string[];

204+

workingDirectory?: string;

205+

environment?: Record<string, string>;

206+

environmentValueSources?: Record<string, GatewayServiceEnvironmentValueSource>;

207+

sourcePath?: string;

103208

} | null> {

104209

try {

105210

const plist = await fs.readFile(plistPath, "utf8");

@@ -128,8 +233,8 @@ export async function readLaunchAgentProgramArgumentsFromFile(plistPath: string)

128233

inlineEnvironment[key] = value;

129234

}

130235

}

131-

const fileEnvironment = await readLaunchAgentEnvironmentFile(args);

132-

const effectiveProgramArguments = unwrapGeneratedEnvWrapperArgs(args);

236+

const fileEnvironment = await readLaunchAgentEnvironmentFile(args, options);

237+

const effectiveProgramArguments = unwrapGeneratedEnvWrapperArgs(args, options);

133238

const environment = { ...inlineEnvironment, ...fileEnvironment };

134239

const environmentValueSources: Record<string, GatewayServiceEnvironmentValueSource> = {};

135240

for (const key of Object.keys(inlineEnvironment)) {