Download src/claws/bootstrap.test.ts from SaylorTwift/openclaw: direct link, hf CLI and curl.
- Browser
- Download file 23.1 kB
-
https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/claws/bootstrap.test.ts
- Command line
-
hf download hf://SaylorTwift/openclaw/src/claws/bootstrap.test.ts
-
curl -L -o bootstrap.test.ts https://huggingface.co/SaylorTwift/openclaw/resolve/main/src/claws/bootstrap.test.ts
23.1 kB
| import { mkdir, readFile, rm, writeFile } from "node:fs/promises"; | |
| import { join } from "node:path"; | |
| import { afterEach, describe, expect, it } from "vitest"; | |
| import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js"; | |
| import { MAX_WORKSPACE_BOOTSTRAP_FILE_BYTES } from "../agents/workspace-bootstrap-read.js"; | |
| import { readWorkspaceStateSnapshot } from "../agents/workspace-state-store.js"; | |
| import { withTempHomeConfig } from "../config/test-helpers.js"; | |
| import type { OpenClawConfig } from "../config/types.openclaw.js"; | |
| import { closeOpenClawStateDatabaseForTest } from "../state/openclaw-state-db.js"; | |
| import { setTestEnvValue } from "../test-utils/env.js"; | |
| import { applyClawAddPlan } from "./add.js"; | |
| import { seedClawPackageBootstrap } from "./bootstrap.js"; | |
| import { quiescentClawMonitorGateway } from "./lifecycle-remove.test-support.js"; | |
| import { applyClawRemovePlan, buildClawRemovePlan, readClawStatus } from "./lifecycle-state.js"; | |
| import { buildClawAddPlan } from "./lifecycle.js"; | |
| import { | |
| persistClawInstallRecord, | |
| readClawInstallRecord, | |
| updateClawInstallRecord, | |
| } from "./provenance.js"; | |
| import { readClawManifestFile } from "./reader.js"; | |
| import { parseClawManifest } from "./schema.js"; | |
| const tempDirs = useAutoCleanupTempDirTracker(afterEach); | |
| afterEach(() => closeOpenClawStateDatabaseForTest()); | |
| async function createPackage(bootstrap = "# First run\n\nAsk which repositories matter.\n") { | |
| const root = tempDirs.make("openclaw-claw-bootstrap-"); | |
| await mkdir(root, { recursive: true }); | |
| await writeFile( | |
| join(root, "package.json"), | |
| JSON.stringify({ | |
| name: "@acme/bootstrap-worker", | |
| version: "1.0.0", | |
| openclaw: { claw: "CLAW.md" }, | |
| }), | |
| "utf8", | |
| ); | |
| await writeFile( | |
| join(root, "CLAW.md"), | |
| [ | |
| "---", | |
| "schemaVersion: 1", | |
| "agent:", | |
| " id: bootstrap-worker", | |
| "---", | |
| "", | |
| "# Bootstrap Worker", | |
| "", | |
| "Help with selected repositories.", | |
| ].join("\n"), | |
| "utf8", | |
| ); | |
| await writeFile(join(root, "BOOTSTRAP.md"), bootstrap, "utf8"); | |
| return root; | |
| } | |
| describe("package-root BOOTSTRAP.md", () => { | |
| it("integrity-binds bootstrap and plans a distinct native action", async () => { | |
| const root = await createPackage(); | |
| const first = await readClawManifestFile(root); | |
| expect(first).toMatchObject({ | |
| ok: true, | |
| packageBootstrap: { | |
| sourcePath: "BOOTSTRAP.md", | |
| digest: expect.stringMatching(/^sha256:[a-f0-9]{64}$/), | |
| }, | |
| }); | |
| if (!first.ok || !first.packageBootstrap) { | |
| throw new Error("expected package bootstrap"); | |
| } | |
| const plan = await buildClawAddPlan({ | |
| manifest: first.manifest, | |
| clawMarkdownBody: first.clawMarkdownBody, | |
| packageBootstrap: first.packageBootstrap, | |
| source: first.source, | |
| context: { workspace: join(root, "workspace") }, | |
| }); | |
| expect(plan.actions).toContainEqual( | |
| expect.objectContaining({ | |
| kind: "bootstrap", | |
| id: "BOOTSTRAP.md", | |
| action: "write", | |
| digest: first.packageBootstrap.digest, | |
| details: expect.objectContaining({ lifecycle: "native-seed-once" }), | |
| }), | |
| ); | |
| await writeFile(join(root, "BOOTSTRAP.md"), "# Changed\n", "utf8"); | |
| const second = await readClawManifestFile(root); | |
| expect(second.ok).toBe(true); | |
| if (!second.ok) { | |
| throw new Error("expected changed package to parse"); | |
| } | |
| expect(second.source.integrity).not.toBe(first.source.integrity); | |
| }); | |
| it("seeds native state once and never recreates a consumed bootstrap", async () => { | |
| const root = await createPackage(); | |
| const read = await readClawManifestFile(root); | |
| if (!read.ok || !read.packageBootstrap) { | |
| throw new Error("expected package bootstrap"); | |
| } | |
| const workspace = join(root, "workspace"); | |
| const env = { OPENCLAW_STATE_DIR: join(root, "state") }; | |
| const plan = await buildClawAddPlan({ | |
| manifest: read.manifest, | |
| clawMarkdownBody: read.clawMarkdownBody, | |
| packageBootstrap: read.packageBootstrap, | |
| source: read.source, | |
| context: { workspace }, | |
| }); | |
| let config: OpenClawConfig = {}; | |
| const added = await applyClawAddPlan(plan, { | |
| env, | |
| nowMs: 1_000, | |
| consentPlanIntegrity: plan.planIntegrity, | |
| commitConfig: async (transform) => { | |
| config = transform(config); | |
| }, | |
| }); | |
| expect(added.status).toBe("complete"); | |
| await expect(readFile(join(workspace, "BOOTSTRAP.md"), "utf8")).resolves.toContain( | |
| "which repositories", | |
| ); | |
| expect((await readWorkspaceStateSnapshot(workspace, { env })).setup).toMatchObject({ | |
| bootstrapSeededAt: new Date(1_000).toISOString(), | |
| }); | |
| await expect(readClawStatus("bootstrap-worker", { env, config })).resolves.toMatchObject({ | |
| summary: { pendingBootstrap: 1 }, | |
| records: [{ bootstrapState: "pending" }], | |
| }); | |
| await rm(join(workspace, "BOOTSTRAP.md")); | |
| await expect(seedClawPackageBootstrap(plan, { env, nowMs: 2_000 })).resolves.toBe("consumed"); | |
| await expect(readFile(join(workspace, "BOOTSTRAP.md"), "utf8")).rejects.toThrow(); | |
| await expect(readClawStatus("bootstrap-worker", { env, config })).resolves.toMatchObject({ | |
| summary: { pendingBootstrap: 0 }, | |
| records: [{ bootstrapState: "complete" }], | |
| }); | |
| }); | |
| it("seeds the consented package bootstrap before the agent config is published", async () => { | |
| const root = await createPackage(); | |
| const read = await readClawManifestFile(root); | |
| if (!read.ok || !read.packageBootstrap) { | |
| throw new Error("expected package bootstrap"); | |
| } | |
| const workspace = join(root, "workspace"); | |
| const env = { OPENCLAW_STATE_DIR: join(root, "state") }; | |
| const plan = await buildClawAddPlan({ | |
| manifest: read.manifest, | |
| clawMarkdownBody: read.clawMarkdownBody, | |
| packageBootstrap: read.packageBootstrap, | |
| source: read.source, | |
| context: { workspace }, | |
| }); | |
| let config: OpenClawConfig = {}; | |
| const order: string[] = []; | |
| const added = await applyClawAddPlan(plan, { | |
| env, | |
| nowMs: 1_000, | |
| consentPlanIntegrity: plan.planIntegrity, | |
| commitConfig: async (transform) => { | |
| order.push("config-commit"); | |
| config = transform(config); | |
| }, | |
| seedPackageBootstrap: async (seedPlan, seedOptions) => { | |
| order.push("bootstrap-seed"); | |
| return await seedClawPackageBootstrap(seedPlan, seedOptions); | |
| }, | |
| }); | |
| expect(added.status).toBe("complete"); | |
| expect(order).toEqual(["bootstrap-seed", "config-commit"]); | |
| }); | |
| it("keeps the agent unpublished and resumable when package bootstrap seeding fails", async () => { | |
| const root = await createPackage(); | |
| const read = await readClawManifestFile(root); | |
| if (!read.ok || !read.packageBootstrap) { | |
| throw new Error("expected package bootstrap"); | |
| } | |
| const workspace = join(root, "workspace"); | |
| const env = { OPENCLAW_STATE_DIR: join(root, "state") }; | |
| const plan = await buildClawAddPlan({ | |
| manifest: read.manifest, | |
| clawMarkdownBody: read.clawMarkdownBody, | |
| packageBootstrap: read.packageBootstrap, | |
| source: read.source, | |
| context: { workspace }, | |
| }); | |
| let config: OpenClawConfig = {}; | |
| const added = await applyClawAddPlan(plan, { | |
| env, | |
| nowMs: 1_000, | |
| consentPlanIntegrity: plan.planIntegrity, | |
| commitConfig: async (transform) => { | |
| config = transform(config); | |
| }, | |
| seedPackageBootstrap: async () => { | |
| throw new Error("seed failed"); | |
| }, | |
| }); | |
| expect(added).toMatchObject({ | |
| status: "partial", | |
| configCommitted: false, | |
| error: { code: "bootstrap_write_failed" }, | |
| }); | |
| expect(config).toEqual({}); | |
| expect(readClawInstallRecord("bootstrap-worker", { env })?.status).toBe("workspace_ready"); | |
| expect( | |
| (await readWorkspaceStateSnapshot(workspace, { env })).setup.bootstrapSeededAt, | |
| ).toBeUndefined(); | |
| await expect(readFile(join(workspace, "BOOTSTRAP.md"), "utf8")).rejects.toThrow(); | |
| const resumed = await applyClawAddPlan(plan, { | |
| env, | |
| nowMs: 2_000, | |
| consentPlanIntegrity: plan.planIntegrity, | |
| commitConfig: async (transform) => { | |
| config = transform(config); | |
| }, | |
| }); | |
| expect(resumed.status).toBe("complete"); | |
| await expect(readFile(join(workspace, "BOOTSTRAP.md"), "utf8")).resolves.toContain( | |
| "which repositories", | |
| ); | |
| await expect(readClawStatus("bootstrap-worker", { env, config })).resolves.toMatchObject({ | |
| records: [{ bootstrapState: "pending" }], | |
| }); | |
| }); | |
| it("recovers from a stock bootstrap seeded by a concurrent session", async () => { | |
| const root = await createPackage(); | |
| const read = await readClawManifestFile(root); | |
| if (!read.ok || !read.packageBootstrap) { | |
| throw new Error("expected package bootstrap"); | |
| } | |
| const workspace = join(root, "workspace"); | |
| const env = { OPENCLAW_STATE_DIR: join(root, "state") }; | |
| const plan = await buildClawAddPlan({ | |
| manifest: read.manifest, | |
| clawMarkdownBody: read.clawMarkdownBody, | |
| packageBootstrap: read.packageBootstrap, | |
| source: read.source, | |
| context: { workspace }, | |
| }); | |
| let config: OpenClawConfig = {}; | |
| const added = await applyClawAddPlan(plan, { | |
| env, | |
| nowMs: 1_000, | |
| consentPlanIntegrity: plan.planIntegrity, | |
| commitConfig: async (transform) => { | |
| config = transform(config); | |
| }, | |
| seedPackageBootstrap: async (seedPlan, seedOptions) => { | |
| await writeFile(join(workspace, "BOOTSTRAP.md"), "# Stock onboarding\n", "utf8"); | |
| return await seedClawPackageBootstrap(seedPlan, seedOptions); | |
| }, | |
| }); | |
| expect(added).toMatchObject({ status: "partial", configCommitted: false }); | |
| expect(config).toEqual({}); | |
| expect(readClawInstallRecord("bootstrap-worker", { env })?.status).toBe("workspace_ready"); | |
| await rm(join(workspace, "BOOTSTRAP.md")); | |
| const resumed = await applyClawAddPlan(plan, { | |
| env, | |
| nowMs: 2_000, | |
| consentPlanIntegrity: plan.planIntegrity, | |
| commitConfig: async (transform) => { | |
| config = transform(config); | |
| }, | |
| }); | |
| expect(resumed.status).toBe("complete"); | |
| await expect(readClawStatus("bootstrap-worker", { env, config })).resolves.toMatchObject({ | |
| records: [{ bootstrapState: "pending" }], | |
| }); | |
| }); | |
| it("does not expose the agent while package bootstrap seeding is in flight", async () => { | |
| const root = await createPackage(); | |
| const read = await readClawManifestFile(root); | |
| if (!read.ok || !read.packageBootstrap) { | |
| throw new Error("expected package bootstrap"); | |
| } | |
| const workspace = join(root, "workspace"); | |
| const env = { OPENCLAW_STATE_DIR: join(root, "state") }; | |
| const addPlan = await buildClawAddPlan({ | |
| manifest: read.manifest, | |
| clawMarkdownBody: read.clawMarkdownBody, | |
| packageBootstrap: read.packageBootstrap, | |
| source: read.source, | |
| context: { workspace }, | |
| }); | |
| let config: OpenClawConfig = {}; | |
| let releaseSeed!: () => void; | |
| const seedReleased = new Promise<void>((resolve) => { | |
| releaseSeed = resolve; | |
| }); | |
| let reportSeedStarted!: () => void; | |
| const seedStarted = new Promise<void>((resolve) => { | |
| reportSeedStarted = resolve; | |
| }); | |
| const addPromise = applyClawAddPlan(addPlan, { | |
| env, | |
| consentPlanIntegrity: addPlan.planIntegrity, | |
| commitConfig: async (transform) => { | |
| config = transform(config); | |
| }, | |
| seedPackageBootstrap: async (plan, options) => { | |
| reportSeedStarted(); | |
| await seedReleased; | |
| return seedClawPackageBootstrap(plan, options); | |
| }, | |
| }); | |
| await seedStarted; | |
| expect(config.agents?.entries?.["bootstrap-worker"]).toBeUndefined(); | |
| releaseSeed(); | |
| await expect(addPromise).resolves.toMatchObject({ | |
| status: "complete", | |
| configCommitted: true, | |
| }); | |
| await expect(readFile(join(workspace, "BOOTSTRAP.md"), "utf8")).resolves.toContain( | |
| "which repositories", | |
| ); | |
| }); | |
| it("removes a seeded partial install when the later configuration commit fails", async () => { | |
| const root = await createPackage(); | |
| const read = await readClawManifestFile(root); | |
| if (!read.ok || !read.packageBootstrap) { | |
| throw new Error("expected package bootstrap"); | |
| } | |
| const workspace = join(root, "workspace"); | |
| const env = { OPENCLAW_STATE_DIR: join(root, "state") }; | |
| const addPlan = await buildClawAddPlan({ | |
| manifest: read.manifest, | |
| clawMarkdownBody: read.clawMarkdownBody, | |
| packageBootstrap: read.packageBootstrap, | |
| source: read.source, | |
| context: { workspace }, | |
| }); | |
| const added = await applyClawAddPlan(addPlan, { | |
| env, | |
| consentPlanIntegrity: addPlan.planIntegrity, | |
| commitConfig: async () => { | |
| throw new Error("config failed"); | |
| }, | |
| }); | |
| expect(added).toMatchObject({ | |
| status: "partial", | |
| configCommitted: false, | |
| error: { code: "config_commit_failed" }, | |
| }); | |
| await expect(readFile(join(workspace, "BOOTSTRAP.md"), "utf8")).resolves.toContain( | |
| "which repositories", | |
| ); | |
| await expect(readClawStatus("bootstrap-worker", { env, config: {} })).resolves.toMatchObject({ | |
| records: [{ install: { status: "workspace_ready" }, bootstrapState: "pending" }], | |
| }); | |
| const removePlan = await buildClawRemovePlan("bootstrap-worker", { env, config: {} }); | |
| expect(removePlan.blockers).toEqual([]); | |
| expect(removePlan.actions).toContainEqual( | |
| expect.objectContaining({ | |
| kind: "bootstrap", | |
| action: "delete", | |
| blocked: false, | |
| details: expect.objectContaining({ expectedState: "pending" }), | |
| }), | |
| ); | |
| const removed = await withTempHomeConfig({}, async ({ configPath }) => { | |
| setTestEnvValue("OPENCLAW_CONFIG_PATH", configPath); | |
| setTestEnvValue("OPENCLAW_STATE_DIR", env.OPENCLAW_STATE_DIR); | |
| return applyClawRemovePlan(removePlan, { | |
| monitorGateway: quiescentClawMonitorGateway, | |
| env, | |
| config: {}, | |
| consentPlanIntegrity: removePlan.planIntegrity, | |
| purgeSessions: async () => undefined, | |
| trashPath: async () => true, | |
| }); | |
| }); | |
| expect(removed).toMatchObject({ | |
| status: "complete", | |
| bootstrap: { path: "BOOTSTRAP.md", action: "deleted" }, | |
| }); | |
| expect(readClawInstallRecord("bootstrap-worker", { env })).toBeUndefined(); | |
| }); | |
| it("omits bootstrap from update-style target plans", async () => { | |
| const root = await createPackage(); | |
| const read = await readClawManifestFile(root); | |
| if (!read.ok || !read.packageBootstrap) { | |
| throw new Error("expected package bootstrap"); | |
| } | |
| const plan = await buildClawAddPlan({ | |
| manifest: read.manifest, | |
| packageBootstrap: read.packageBootstrap, | |
| includePackageBootstrap: false, | |
| source: read.source, | |
| context: { workspace: join(root, "workspace") }, | |
| }); | |
| expect(plan.actions.some((action) => action.kind === "bootstrap")).toBe(false); | |
| }); | |
| it("preserves bootstrap provenance when update omits the seed-once action", async () => { | |
| const root = await createPackage(); | |
| const read = await readClawManifestFile(root); | |
| if (!read.ok || !read.packageBootstrap) { | |
| throw new Error("expected package bootstrap"); | |
| } | |
| const env = { OPENCLAW_STATE_DIR: join(root, "state") }; | |
| const context = { workspace: join(root, "workspace") }; | |
| const addPlan = await buildClawAddPlan({ | |
| manifest: read.manifest, | |
| packageBootstrap: read.packageBootstrap, | |
| source: read.source, | |
| context, | |
| }); | |
| const initial = persistClawInstallRecord(addPlan, { env }); | |
| const updatePlan = await buildClawAddPlan({ | |
| manifest: read.manifest, | |
| packageBootstrap: read.packageBootstrap, | |
| includePackageBootstrap: false, | |
| source: read.source, | |
| context, | |
| }); | |
| updateClawInstallRecord(updatePlan, { env }); | |
| expect(readClawInstallRecord("bootstrap-worker", { env })?.bootstrap).toEqual( | |
| initial.bootstrap, | |
| ); | |
| }); | |
| it("removes an unchanged pending bootstrap with the Claw", async () => { | |
| const root = await createPackage(); | |
| const read = await readClawManifestFile(root); | |
| if (!read.ok || !read.packageBootstrap) { | |
| throw new Error("expected package bootstrap"); | |
| } | |
| const workspace = join(root, "workspace"); | |
| const env = { OPENCLAW_STATE_DIR: join(root, "state") }; | |
| const addPlan = await buildClawAddPlan({ | |
| manifest: read.manifest, | |
| packageBootstrap: read.packageBootstrap, | |
| source: read.source, | |
| context: { workspace }, | |
| }); | |
| let config: OpenClawConfig = {}; | |
| await applyClawAddPlan(addPlan, { | |
| env, | |
| consentPlanIntegrity: addPlan.planIntegrity, | |
| commitConfig: async (transform) => { | |
| config = transform(config); | |
| }, | |
| }); | |
| const removePlan = await buildClawRemovePlan("bootstrap-worker", { env, config }); | |
| expect(removePlan.actions).toContainEqual( | |
| expect.objectContaining({ kind: "bootstrap", action: "delete", blocked: false }), | |
| ); | |
| expect(removePlan.actions).toContainEqual( | |
| expect.objectContaining({ kind: "workspace", action: "trash" }), | |
| ); | |
| const removed = await withTempHomeConfig(config, async ({ configPath }) => { | |
| setTestEnvValue("OPENCLAW_CONFIG_PATH", configPath); | |
| setTestEnvValue("OPENCLAW_STATE_DIR", env.OPENCLAW_STATE_DIR); | |
| return applyClawRemovePlan(removePlan, { | |
| monitorGateway: quiescentClawMonitorGateway, | |
| env, | |
| config, | |
| consentPlanIntegrity: removePlan.planIntegrity, | |
| purgeSessions: async () => undefined, | |
| trashPath: async () => true, | |
| }); | |
| }); | |
| expect(removed).toMatchObject({ | |
| status: "complete", | |
| bootstrap: { path: "BOOTSTRAP.md", action: "deleted" }, | |
| }); | |
| await expect(readFile(join(workspace, "BOOTSTRAP.md"), "utf8")).rejects.toThrow(); | |
| }); | |
| it("removes an unchanged large pending bootstrap within the native size limit", async () => { | |
| const content = "# First run\n\n" + "x".repeat(1024 * 1024 + 32); | |
| expect(Buffer.byteLength(content)).toBeLessThanOrEqual(MAX_WORKSPACE_BOOTSTRAP_FILE_BYTES); | |
| const root = await createPackage(content); | |
| const read = await readClawManifestFile(root); | |
| if (!read.ok || !read.packageBootstrap) { | |
| throw new Error("expected package bootstrap"); | |
| } | |
| const workspace = join(root, "workspace"); | |
| const env = { OPENCLAW_STATE_DIR: join(root, "state") }; | |
| const addPlan = await buildClawAddPlan({ | |
| manifest: read.manifest, | |
| packageBootstrap: read.packageBootstrap, | |
| source: read.source, | |
| context: { workspace }, | |
| }); | |
| let config: OpenClawConfig = {}; | |
| await applyClawAddPlan(addPlan, { | |
| env, | |
| consentPlanIntegrity: addPlan.planIntegrity, | |
| commitConfig: async (transform) => { | |
| config = transform(config); | |
| }, | |
| }); | |
| await expect(readClawStatus("bootstrap-worker", { env, config })).resolves.toMatchObject({ | |
| records: [{ bootstrapState: "pending" }], | |
| }); | |
| const removePlan = await buildClawRemovePlan("bootstrap-worker", { env, config }); | |
| expect(removePlan.actions).toContainEqual( | |
| expect.objectContaining({ kind: "bootstrap", action: "delete", blocked: false }), | |
| ); | |
| const removed = await withTempHomeConfig(config, async ({ configPath }) => { | |
| setTestEnvValue("OPENCLAW_CONFIG_PATH", configPath); | |
| setTestEnvValue("OPENCLAW_STATE_DIR", env.OPENCLAW_STATE_DIR); | |
| return applyClawRemovePlan(removePlan, { | |
| monitorGateway: quiescentClawMonitorGateway, | |
| env, | |
| config, | |
| consentPlanIntegrity: removePlan.planIntegrity, | |
| purgeSessions: async () => undefined, | |
| trashPath: async () => true, | |
| }); | |
| }); | |
| expect(removed).toMatchObject({ | |
| status: "complete", | |
| bootstrap: { path: "BOOTSTRAP.md", action: "deleted" }, | |
| }); | |
| await expect(readFile(join(workspace, "BOOTSTRAP.md"), "utf8")).rejects.toThrow(); | |
| }); | |
| it("preserves a locally modified pending bootstrap and its workspace", async () => { | |
| const root = await createPackage(); | |
| const read = await readClawManifestFile(root); | |
| if (!read.ok || !read.packageBootstrap) { | |
| throw new Error("expected package bootstrap"); | |
| } | |
| const workspace = join(root, "workspace"); | |
| const env = { OPENCLAW_STATE_DIR: join(root, "state") }; | |
| const addPlan = await buildClawAddPlan({ | |
| manifest: read.manifest, | |
| packageBootstrap: read.packageBootstrap, | |
| source: read.source, | |
| context: { workspace }, | |
| }); | |
| let config: OpenClawConfig = {}; | |
| await applyClawAddPlan(addPlan, { | |
| env, | |
| consentPlanIntegrity: addPlan.planIntegrity, | |
| commitConfig: async (transform) => { | |
| config = transform(config); | |
| }, | |
| }); | |
| await writeFile(join(workspace, "BOOTSTRAP.md"), "# My edited onboarding\n", "utf8"); | |
| const removePlan = await buildClawRemovePlan("bootstrap-worker", { env, config }); | |
| expect(removePlan.actions).toContainEqual( | |
| expect.objectContaining({ kind: "bootstrap", action: "retain", blocked: false }), | |
| ); | |
| expect(removePlan.actions).toContainEqual( | |
| expect.objectContaining({ kind: "workspace", action: "retain" }), | |
| ); | |
| const removed = await withTempHomeConfig(config, async ({ configPath }) => { | |
| setTestEnvValue("OPENCLAW_CONFIG_PATH", configPath); | |
| setTestEnvValue("OPENCLAW_STATE_DIR", env.OPENCLAW_STATE_DIR); | |
| return applyClawRemovePlan(removePlan, { | |
| monitorGateway: quiescentClawMonitorGateway, | |
| env, | |
| config, | |
| consentPlanIntegrity: removePlan.planIntegrity, | |
| purgeSessions: async () => undefined, | |
| trashPath: async () => true, | |
| }); | |
| }); | |
| expect(removed).toMatchObject({ | |
| status: "complete", | |
| bootstrap: { path: "BOOTSTRAP.md", action: "retainedModified" }, | |
| }); | |
| await expect(readFile(join(workspace, "BOOTSTRAP.md"), "utf8")).resolves.toContain( | |
| "My edited onboarding", | |
| ); | |
| }); | |
| it("rejects an empty package bootstrap", async () => { | |
| const root = await createPackage(" \n\t"); | |
| await expect(readClawManifestFile(root)).resolves.toMatchObject({ | |
| ok: false, | |
| diagnostics: [expect.objectContaining({ code: "package_bootstrap_empty" })], | |
| }); | |
| }); | |
| it.each(["BOOTSTRAP.md", "BOOTSTRAP.md/notes.md", "bootstrap.md/notes.md"])( | |
| "reserves %s for the native seed-once lifecycle", | |
| (path) => { | |
| const result = parseClawManifest({ | |
| schemaVersion: 1, | |
| agent: { id: "bootstrap-worker" }, | |
| workspace: { | |
| files: [{ source: "assets/BOOTSTRAP.md", path }], | |
| }, | |
| }); | |
| expect(result.ok).toBe(false); | |
| expect(result.diagnostics).toContainEqual( | |
| expect.objectContaining({ | |
| path: "$.workspace.files[0].path", | |
| message: expect.stringContaining("native seed-once lifecycle"), | |
| }), | |
| ); | |
| }, | |
| ); | |
| }); | |