fix: remove OpenClaw chat-first BOOTSTRAP.md on startup (#917)

This commit is contained in:
paisley
2026-04-25 15:43:29 +08:00
committed by GitHub
parent 9a360eb43f
commit 04e234bbde
2 changed files with 94 additions and 3 deletions

View File

@@ -195,6 +195,26 @@ export async function repairClawXOnlyBootstrapFiles(): Promise<void> {
}
}
/**
* ClawX ships a default desktop identity and does not need OpenClaw's
* chat-first personalization script. Once the Gateway has seeded the regular
* workspace files, remove BOOTSTRAP.md so sessions start normally.
*/
export async function removeChatFirstBootstrapFiles(): Promise<void> {
const workspaceDirs = await resolveAllWorkspaceDirs();
for (const workspaceDir of workspaceDirs) {
const bootstrapPath = join(workspaceDir, 'BOOTSTRAP.md');
if (!(await fileExists(bootstrapPath))) continue;
try {
await unlink(bootstrapPath);
logger.info(`Removed chat-first bootstrap file from ClawX workspace (${workspaceDir})`);
} catch {
logger.warn(`Failed to remove chat-first bootstrap file: ${bootstrapPath}`);
}
}
}
// ── Context merging ──────────────────────────────────────────────
/**
@@ -267,12 +287,16 @@ const MAX_RETRIES = 15;
*/
export async function ensureClawXContext(): Promise<void> {
let skipped = await mergeClawXContextOnce();
if (skipped === 0) return;
if (skipped === 0) {
await removeChatFirstBootstrapFiles();
return;
}
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
await new Promise((r) => setTimeout(r, RETRY_INTERVAL_MS));
skipped = await mergeClawXContextOnce();
if (skipped === 0) {
await removeChatFirstBootstrapFiles();
logger.info(`ClawX context merge completed after ${attempt} retry(ies)`);
return;
}
@@ -280,4 +304,5 @@ export async function ensureClawXContext(): Promise<void> {
}
logger.warn(`ClawX context merge: ${skipped} file(s) still missing after ${MAX_RETRIES} retries`);
await removeChatFirstBootstrapFiles();
}

View File

@@ -1,5 +1,32 @@
import { describe, it, expect } from 'vitest';
import { mergeClawXSection, stripFirstRunSection } from '../../electron/utils/openclaw-workspace';
import { access, mkdir, readFile, rm, writeFile } from 'fs/promises';
import { join } from 'path';
import { beforeEach, describe, it, expect, vi } from 'vitest';
const { testHome } = vi.hoisted(() => ({
testHome: `/tmp/clawx-openclaw-workspace-${Math.random().toString(36).slice(2)}`,
}));
vi.mock('os', async () => {
const actual = await vi.importActual<typeof import('os')>('os');
const mocked = {
...actual,
homedir: () => testHome,
};
return {
...mocked,
default: mocked,
};
});
import {
mergeClawXSection,
removeChatFirstBootstrapFiles,
stripFirstRunSection,
} from '../../electron/utils/openclaw-workspace';
beforeEach(async () => {
await rm(testHome, { recursive: true, force: true });
});
describe('stripFirstRunSection', () => {
it('removes the First Run section when it exists', () => {
@@ -137,3 +164,42 @@ describe('stripFirstRunSection', () => {
expect(merged).toContain('<!-- clawx:end -->');
});
});
describe('removeChatFirstBootstrapFiles', () => {
it('removes only BOOTSTRAP.md from the default workspace', async () => {
const workspaceDir = join(testHome, '.openclaw', 'workspace');
await mkdir(workspaceDir, { recursive: true });
await writeFile(join(workspaceDir, 'BOOTSTRAP.md'), 'chat-first bootstrap', 'utf-8');
await writeFile(join(workspaceDir, 'SOUL.md'), 'existing soul', 'utf-8');
await removeChatFirstBootstrapFiles();
await expect(access(join(workspaceDir, 'BOOTSTRAP.md'))).rejects.toThrow();
await expect(readFile(join(workspaceDir, 'SOUL.md'), 'utf-8')).resolves.toBe('existing soul');
});
it('removes BOOTSTRAP.md from configured agent workspaces', async () => {
const openclawDir = join(testHome, '.openclaw');
const mainWorkspace = join(openclawDir, 'workspace-main');
const agentWorkspace = join(openclawDir, 'workspace-agent');
await mkdir(mainWorkspace, { recursive: true });
await mkdir(agentWorkspace, { recursive: true });
await writeFile(join(mainWorkspace, 'BOOTSTRAP.md'), 'main bootstrap', 'utf-8');
await writeFile(join(agentWorkspace, 'BOOTSTRAP.md'), 'agent bootstrap', 'utf-8');
await writeFile(
join(openclawDir, 'openclaw.json'),
JSON.stringify({
agents: {
defaults: { workspace: mainWorkspace },
list: [{ workspace: agentWorkspace }],
},
}),
'utf-8',
);
await removeChatFirstBootstrapFiles();
await expect(access(join(mainWorkspace, 'BOOTSTRAP.md'))).rejects.toThrow();
await expect(access(join(agentWorkspace, 'BOOTSTRAP.md'))).rejects.toThrow();
});
});