mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-22 10:25:20 -06:00
78502eda6d
* feat(workers): run device sessions from Gateway bundles Install the current Gateway bundle before a device environment becomes ready, verify it at attach and tunnel boundaries, launch only from the immutable namespaced bundle directory, and retire stale environments for idempotent reprovisioning. Remove the local execution mode and preserve the node-local build claim only as temporary inventory metadata for the final projection/cleanup slice. * docs(runners): record Gateway bundle cutover * test(ci): repair runner validation fixtures # Conflicts: # src/scripts/test-projects.test.ts * fix(workers): surface outdated node recovery Keep legacy runner inventory diagnostic-only while exposing the update-and-reconnect action through node, environment, provider, placement, and Control UI surfaces. * fix(workers): reject legacy inventory with recovery * fix(workers): bundle worker deploy closure * test(workers): close bundle cutover gates * fix(workers): compose browser runtime at build * fix(workers): satisfy bundle cutover gates * fix(workers): route temp runtime through infra * docs(workers): align bundle host guidance * fix(ui): fence outdated session destinations
144 lines
4.5 KiB
TypeScript
144 lines
4.5 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const mocks = vi.hoisted(() => ({
|
|
execFile: vi.fn(),
|
|
loadBundledPluginPublicSurfaceModuleSyncCore: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("node:child_process", () => ({
|
|
execFile: mocks.execFile,
|
|
}));
|
|
|
|
vi.mock("../plugin-sdk/facade-loader.js", () => ({
|
|
loadBundledPluginPublicSurfaceModuleSyncCore: mocks.loadBundledPluginPublicSurfaceModuleSyncCore,
|
|
}));
|
|
|
|
import { createWorkerBrowserToolRuntime } from "./browser-runtime.js";
|
|
|
|
describe("worker Browser runtime", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it("loads only the bundled Browser runtime and launches the fixed executable without arguments", async () => {
|
|
const dispose = vi.fn().mockResolvedValue(undefined);
|
|
const createAttachedBrowserToolRuntime = vi.fn().mockResolvedValue({
|
|
tool: { name: "browser" },
|
|
dispose,
|
|
});
|
|
mocks.loadBundledPluginPublicSurfaceModuleSyncCore.mockReturnValue({
|
|
createAttachedBrowserToolRuntime,
|
|
});
|
|
mocks.execFile.mockImplementation(
|
|
(
|
|
_file: string,
|
|
_args: string[],
|
|
_options: object,
|
|
callback: (error: Error | null) => void,
|
|
) => {
|
|
callback(null);
|
|
return {};
|
|
},
|
|
);
|
|
|
|
const runtime = await createWorkerBrowserToolRuntime({
|
|
descriptor: {
|
|
cdpUrl: "http://127.0.0.1:9222",
|
|
launcherPath: "/usr/local/bin/openclaw-worker-browser",
|
|
},
|
|
sessionKey: "worker:session-1",
|
|
stateDir: "/tmp/worker-state",
|
|
workspaceDir: "/tmp/workspace",
|
|
});
|
|
|
|
expect(mocks.loadBundledPluginPublicSurfaceModuleSyncCore).toHaveBeenCalledWith({
|
|
dirName: "browser",
|
|
artifactBasename: "runtime-api.js",
|
|
trackedPluginId: "browser",
|
|
});
|
|
expect(createAttachedBrowserToolRuntime).toHaveBeenCalledWith({
|
|
cdpUrl: "http://127.0.0.1:9222",
|
|
ensureAttachTarget: expect.any(Function),
|
|
agentSessionKey: "worker:session-1",
|
|
agentDir: "/tmp/worker-state",
|
|
workspaceDir: "/tmp/workspace",
|
|
});
|
|
|
|
const ensureAttachTarget = createAttachedBrowserToolRuntime.mock.calls[0]?.[0]
|
|
.ensureAttachTarget as () => Promise<void>;
|
|
await ensureAttachTarget();
|
|
expect(mocks.execFile).toHaveBeenCalledWith(
|
|
"/usr/local/bin/openclaw-worker-browser",
|
|
[],
|
|
{
|
|
timeout: 30_000,
|
|
maxBuffer: 64 * 1024,
|
|
windowsHide: true,
|
|
},
|
|
expect.any(Function),
|
|
);
|
|
|
|
await runtime.dispose();
|
|
expect(dispose).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it("uses the build-composed Browser runtime without filesystem discovery", async () => {
|
|
const createAttachedBrowserToolRuntime = vi.fn().mockResolvedValue({
|
|
tool: { name: "browser" },
|
|
dispose: vi.fn().mockResolvedValue(undefined),
|
|
});
|
|
|
|
await createWorkerBrowserToolRuntime({
|
|
descriptor: {
|
|
cdpUrl: "http://127.0.0.1:9222",
|
|
launcherPath: "/usr/local/bin/openclaw-worker-browser",
|
|
},
|
|
sessionKey: "worker:session-1",
|
|
stateDir: "/tmp/worker-state",
|
|
workspaceDir: "/tmp/workspace",
|
|
runtime: { createAttachedBrowserToolRuntime },
|
|
});
|
|
|
|
expect(createAttachedBrowserToolRuntime).toHaveBeenCalledOnce();
|
|
expect(mocks.loadBundledPluginPublicSurfaceModuleSyncCore).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("surfaces launcher failure without loading another browser route", async () => {
|
|
const createAttachedBrowserToolRuntime = vi.fn().mockResolvedValue({
|
|
tool: { name: "browser" },
|
|
dispose: vi.fn().mockResolvedValue(undefined),
|
|
});
|
|
mocks.loadBundledPluginPublicSurfaceModuleSyncCore.mockReturnValue({
|
|
createAttachedBrowserToolRuntime,
|
|
});
|
|
mocks.execFile.mockImplementation(
|
|
(
|
|
_file: string,
|
|
_args: string[],
|
|
_options: object,
|
|
callback: (error: Error | null) => void,
|
|
) => {
|
|
callback(new Error("launcher timed out"));
|
|
return {};
|
|
},
|
|
);
|
|
|
|
await createWorkerBrowserToolRuntime({
|
|
descriptor: {
|
|
cdpUrl: "http://127.0.0.1:9222",
|
|
launcherPath: "/usr/local/bin/openclaw-worker-browser",
|
|
},
|
|
sessionKey: "worker:session-1",
|
|
stateDir: "/tmp/worker-state",
|
|
workspaceDir: "/tmp/workspace",
|
|
});
|
|
const ensureAttachTarget = createAttachedBrowserToolRuntime.mock.calls[0]?.[0]
|
|
.ensureAttachTarget as () => Promise<void>;
|
|
|
|
await expect(ensureAttachTarget()).rejects.toThrow(
|
|
"Worker Browser launcher failed: launcher timed out",
|
|
);
|
|
expect(mocks.loadBundledPluginPublicSurfaceModuleSyncCore).toHaveBeenCalledOnce();
|
|
});
|
|
});
|